Jump to content

How-To? Allowing guests edit existing pages (with multiple images) via a form


ngrmm
 Share

Recommended Posts

My idea was to create a form with matching fields as the existing page template and populate those form fields with the values. The form would overwrite the existing page fields values.
But this would not work with an existing image field. There is no way of to populate the file-upload-field with the existing images and allowing the user to sort, delete and add new images, right?

Is there a way to have guests edit published pages with images?

Link to comment
Share on other sites

Let me start with this: No matter what your goal is, allowing unmoderated edit access to your page is never a good idea! With guests, do you really mean not-logged-in users?

Assuming you mean "specific people with a login", why not let them use the backend and all of the tools available there to accomplish this task?

You can introduce a new Role and limit that to just being able to edit whatever pages you need them to.

With this approach, you do not have to be concerned about validating and sanitizing your form which in turn is another possible weak spot in itself.

If not, there is the PRO Module "FormBuilder" which might come in handy in your case. It particularly lets you create pages from form submissions (even unmoderated). With it's hookable interface, you can add whatever functionality you need.

  • Like 3
Link to comment
Share on other sites

Actually I will use FormBuilder. The form will create pages. And I will use hooks to generate an email after the page is saved and send it to the user (just guests).

In the email the user will find two links. One for deleting the created page and one for editing.
The edit link will have the guest email and a random alphanumeric string as url segments. This way they will be able to only edit „their“ pages

When they click this link, they would see a form which is already populated. This works with text fields or checkboxes. My Problem are image fields.

I do not want the user to have any edit roles or some kind of backend access.
 

Link to comment
Share on other sites

Well in this case, ProcessWire won't give you an out-of-the-box solution. Other things coming to mind are PageFrontEdit (bundled with PW) but this also only works with an active login session.

You would need to use a frontend component to handle the user interaction and also handle the backend on your own.

Nonetheless, you are opening almost direct edit capability to anyone but must reimplement everything the PW backend would offer you for free. Also note that "not knowing a direct edit link" doesn't infer security or authentication. You are still adding backend edit capabilities to the frontend without any form of access control. The sheer existence of such a function is a big security hole. And on top of all this, file uploads. I strongly advise against this!

  • Like 1
Link to comment
Share on other sites

By using the api you should get almost any things you try to achieve, FormBuilder or not.

3 hours ago, ngrmm said:

My Problem are image fields.

What is the problem ?  Please be more specific.

Link to comment
Share on other sites

let's say I would use LoginRegisterPro to be on the secure side.
There is still the issue on how to edit existing page image fields, without the giving any edit permissions.
@flydev There is a page with an image field with some uploaded images. How can you offer en edit form to delete the uploaded images and add some new images?

I know that a loggedin user (via LoginRegisterPro, with no backend page-edit permission) is able to edit his own profile page on the frontend. An image field on the profile page would be editable with the help of InputfieldFrontendFile.
But I was not able to figure out on how to extend this editing possibility to other templates. Ryan mentioned in another thread that the modules section on processwire.com works that way.

An regarding giving access to pw-backend: my problem here is, that a user with edit permission can view the page-tree, use the backend search field, … 
I would like to only allow editing those pages, which are related to users and nothing else.

Link to comment
Share on other sites

Then you could restrict access to the "Pages" page by using the example provided by @Wanze here.

In much the same way, you could hook Page::editable to only allow pages be edited which belong to the user.

Further the link provided in your email would then be like

https://youdomain.com/adminurl/page/edit/?id=pageId

Like that your quasi-guests are locked out of seemingly the whole backend.

Another possibility is to add all the fields to their user profile and add only profile-edit capability. This will show an empty backend with a "edit profile" button. And the url in the email then needs to be

https://youdomain.com/adminurl/profile
  • Like 1
Link to comment
Share on other sites

1 hour ago, ngrmm said:

An regarding giving access to pw-backend: my problem here is, that a user with edit permission can view the page-tree, use the backend search field, … 

I don't know if that can help you, but on my project, backend side, I hide all pages that a user can't edit, with this hook:

/**
 * In PW admin, hide pages that user can't edit.
 */
$wire->addHookAfter('Page::listable', function (HookEvent $event) {
    /* @var Page $page */
    $page = $event->object;

    if ($page->path == "/")
        $event->return = true;
    else
        $event->return = wire()->user->hasPermission("page-edit", $page);
});

There is no impact on the frontend side.

  • Like 1
Link to comment
Share on other sites

1 hour ago, ngrmm said:

An image field on the profile page would be editable with the help of InputfieldFrontendFile

I was going to suggest it. Unfortunately, I don't own it I think as I have the very first version. I will take a look. But you could ask Ryan directly.

Playing with InputfieldImage and InputfieldFile from frontend is not trivial, as is. If you do not get an answer, I will come back to this thread once I get my hand on the module source-code.

Edit:

Just in case, you can also implement a custom form for files (https://gist.github.com/jacmaes/6691946) and play with a bit of JS to render things dynamic. And to generate random links, you can take a look at this (I am still using it, even if the module is born almost ten years ago: https://github.com/plauclair/FieldGenerator)

Edited by flydev
hints / suggestions
  • Like 1
Link to comment
Share on other sites

1 minute ago, flydev said:

I was going to suggest it. Unfortunately, I don't own it I think as I have the very first version. I will take a look. But you could ask Ryan directly.

Playing with InputfieldImage and InputfieldFile from frontend is not trivial, as is. If you do not get an answer, I will come back to this thread once I get my hand on the module source-code.

I already asked him an will let you know what he says

  • Like 1
Link to comment
Share on other sites

Well this should be easily solvable by injecting some simple CSS based on e.g. the role of the current user.

div#pw-mastheads, footer#pw-footer {
    display: none;
}

You can hook into ProcessProfile's execute method, check the user's role and add your CSS file with

wire()->config->styles->add('...');
Link to comment
Share on other sites

This is a bit hackish and add only obscurity. There is a better way that not everyone are aware of to achieve what OP ask if it's ok to let users log in into the backend, I didn't followed all the topic previously.

Keep in mind that you can override parts of the AdminTheme.  To give you an example, let's assuming you are on a default setup with AdminThemeUikit, then follow theses steps:

  1. create a new directory AdminThemeUikit in site/templates
  2. create a file called _restricted-masthead.php and paste the code you will find at the end of this post
  3. copy the file _main.php from /wire/modules/AdminTheme/AdminThemeUikit/_main.php into /site/templates/AdminThemeUikit
  4. adjust the behavior of the admin theme as you want in _main.php (example below, just replace the full code)

 

  • Code of _main.php 👇
Spoiler
<?php namespace ProcessWire;

use function Composer\Autoload\includeFile;

/**
 * _main.php: Main markup template file for AdminThemeUikit
 *
 * FileCompiler=0
 *
 */

if(!defined("PROCESSWIRE")) die();

/** @var Config $config */
/** @var AdminThemeUikit $adminTheme */
/** @var User $user */
/** @var Modules $modules */
/** @var Notices $notices */
/** @var Page $page */
/** @var Process $process */
/** @var Sanitizer $sanitizer */
/** @var WireInput $input */
/** @var Paths $urls */
/** @var string $layout */
/** @var Process $process */

$adminTheme->renderExtraMarkup('x'); // forces it to cache
if(!isset($content)) $content = '';


/// restricted user
//  if user has role `restricted`, then get the page's url of the page id 1105
//  and redirect the user to this page once logged (page 1105 is just an example). 
$restricted = $user->hasRole('restricted'); 
if ($restricted) {
  $hisPageUrl = $pages->get(1105)->editUrl;
  $id = $input->get('id');
  if((int)$id !== 1105)
    $session->redirect($hisPageUrl);
}
// /restricted user


?><!DOCTYPE html>
<html class="pw" lang="<?php echo $adminTheme->_('en');
	/* this intentionally on a separate line */ ?>">
<head>
	<?php 
	$adminTheme->includeFile('_head.php', array('layout' => $layout));
	echo $adminTheme->renderExtraMarkup('head'); 
	?>
</head>
<body class='<?php echo $adminTheme->getBodyClass(); ?>'>

<?php 
  if (!$restricted) { // IF NOT in restricted area, show default layout
    if($layout == 'sidenav') {
      $adminTheme->includeFile('_sidenav-masthead.php');
    } else if($layout == 'sidenav-tree' || $layout == 'sidenav-tree-alt') {
      // masthead not rendered in this frame
      echo $adminTheme->renderNotices($notices);
      echo "<div class='uk-margin-small'></div>";
      
    } else if($layout == 'modal') {
      // no masthead
      echo $adminTheme->renderNotices($notices);
      
    } else {
      $adminTheme->includeFile('_masthead.php');
    }
  }
  // IF in restricted area, show the restricted layout
  else {
    include_once($config->paths->templates . 'AdminThemeUikit/_restricted-masthead.php');
  }
  	
	$headline = $adminTheme->getHeadline();
	$headlinePos = strpos($content, "$headline</h1>");
	if($headlinePos && $headlinePos > 500) $headline = '';
	
	$adminTheme->includeFile('_content.php', array(
		'headline' => $headline, 
		'content' => &$content, 
		'layout' => $layout
	));
	
  // Show footer only IF NOT restricted
  if (!$restricted) {
    if(!$adminTheme->isModal) {
      $adminTheme->includeFile('_footer.php');
      if($adminTheme->isLoggedIn && strpos($layout, 'sidenav') !== 0) {
        $adminTheme->includeFile('_offcanvas.php');
      }
    }
  }

	echo $adminTheme->renderExtraMarkup('body');
	$adminTheme->includeFile('_body-scripts.php', array('layout' => $layout));
?>
	
</body>
</html><?php

 

 

  • Code of _restricted-masthead.php 👇
Spoiler
<?php namespace ProcessWire;

if(!defined("PROCESSWIRE")) die();

/** @var AdminThemeUikit $adminTheme */
/** @var User $user */
/** @var array $extras */
/** @var Paths $urls */
/** @var Config $config */
/** @var Notices $notices */

$logoOptions = array('height' => '40px');
?>
<div id='pw-mastheads'>
	<header id='pw-masthead-mobile' class='pw-masthead uk-hidden uk-background-muted'>
		<div class='pw-container uk-container uk-container-expand<?php if(!$adminTheme->isLoggedIn) echo ' uk-text-center'; ?>'>
				<a href='<?php echo $adminTheme->isLoggedIn ? $config->urls->admin : $config->urls->root; ?>' class='pw-logo-link'>
					<?php echo $adminTheme->getLogo($logoOptions); ?>
				</a>
		</div>	
	</header>
	<header id='pw-masthead' class='pw-masthead uk-background-muted' data-pw-height='80'> <?php /* data-pw-height='73' */ ?>
		<div class='pw-container uk-container uk-container-expand'>
			<nav class='uk-navbar uk-navbar-container uk-navbar-transparent' uk-navbar>
				<div class='uk-navbar-left'>
					<a class="pw-logo-link uk-logo uk-margin-right" href='<?php echo $adminTheme->isLoggedIn ? $config->urls->admin : $config->urls->root; ?>'>
						<?php echo $adminTheme->getLogo($logoOptions); ?>
					</a>
				</div>
				<?php if($adminTheme->isLoggedIn): ?>
        <div class="uk-navbar-center uk-text-bold uk-text-danger">
          <h2>RESTRICTED AREA</h2>
        </div>
				<div class="uk-navbar-right">
					<ul class='uk-navbar-nav uk-margin-right pw-user-nav'>
						<li>
							<a id="tools-toggle" class="pw-dropdown-toggle" href="<?php echo $urls->admin; ?>profile/">
								<?php echo $adminTheme->renderUserNavLabel(); ?>
							</a>
							<ul class="pw-dropdown-menu" data-my="left top" data-at="left bottom" style="display: none;">
								<?php if($config->debug && $adminTheme->isSuperuser && strpos($adminTheme->layout, 'sidenav') === false): ?>
								<li>	
									<a href='#' onclick="$('#debug_toggle').click(); return false;">
										<?php echo $adminTheme->renderNavIcon('bug') . __('Debug', __FILE__); ?>
									</a>
								</li>
								<?php  endif; ?>
								<?php echo $adminTheme->renderUserNavItems(); ?>
							</ul>
						</li>
					</ul>	
				</div>
				<?php endif; // loggedin ?>
			</nav>
		</div>
	</header>
	<?php 
	if(strpos($adminTheme->layout, 'sidenav') === false) {
		echo $adminTheme->renderNotices($notices);
		echo $adminTheme->renderExtraMarkup('masthead');
	}
	?>
</div>	

 

 

  • Result

 - https://streamable.com/jg2l0p

 

More informations there: 

https://github.com/processwire/processwire/blob/dev/wire/modules/AdminTheme/AdminThemeUikit/README.md

 

 

Enjoy ✌️

  • Like 2
Link to comment
Share on other sites

  • 2 months later...

@flydev just for you to know, I asked Ryan about InputfieldFrontendFile

His Answer: 

Quote

The InputfieldFrontendFile module was designed for LoginRegisterPro, but doesn't technically require LoginRegisterPro. I've not used it outside of LoginRegisterPro profile editor though, so am uncertain of how well it might work outside of that. Though it does seem feasible. I don't think it will work in FormBuilder though because FormBuilder is strict about file uploads with the assumption of an anonymous user, placing various limits on uploads and their location. Whereas InputfieldFrontendFile assumes it is dealing with an authenticated user at least, though it also uses a lot more caution than a trusted user (admin) upload. A custom solution might be needed to manage a full image gallery for the front-end. 

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...