Jump to content

how to make ProcessPageEdit-Form non editable for some roles?


bernhard
 Share

Recommended Posts

hi everybody,

i developed an intranet application for a client. now we want to grant access to some more employees that get the "employee" role. employees should be able to see all the data (meaning they have to have edit access to see all the input forms holding data for all the fields) but not EDIT this data.

which approach would you go?

i see this options

  1. change all field access settings to make fields non-editable by this role but visible in page editor (would work, but i want to save me from that work)
  2. set field access settings via api (how?)
  3. hook Field::editable --> does not work. i guess it is only called when field->useRoles is true. Don't know how to hook correctly here.
  4. prevent only the page save for this pages
  5. totally different approach?

thanks for your help!

  • Like 1
Link to comment
Share on other sites

thank you for the quick reply. i tried a lot yesterday without progress. i just had idea 4 while writing the post and it was really easy:

$this->addHookBefore('Pages::save', function($event) {
    $user = $this->wire->user;
    // checks depending on the page that was saved?
    // $page = $event->arguments(0);

    // early return if user is allowed to save in general
    if($user->isSuperuser()) return;
    if($user->hasRole('senior')) return;
    if($user->hasRole('marketing')) return;
    if($user->hasRole('office-management')) return;

    // throw error if user is not allowed to save
    throw new WireException('You are not allowed to edit this page!');
});

this works quite well. of course with the drawback that all save and add-new buttons are visible (also on pagetable fields and some other spots). but i think that's the quickest and cleanest solution for my setup...

but still i would be interested how to hook the editable state of a field. so i would still appreciate some advice here :)

Link to comment
Share on other sites

1 hour ago, bernhard said:

but still i would be interested how to hook the editable state of a field. so i would still appreciate some advice here :)

You can hook into ProcessPageEdit::buildForm and modify its arguments. You can add readonly attribute to all text fields (disabled works too), remove submit buttons and disable uploads.

wire()->addHook('ProcessPageEdit::buildForm', function (HookEvent $e) {
    $editorRoles = ['senior', 'marketing', 'office-management'];
    foreach ($editorRoles as $role) {
        if (wire()->user->hasRole($role)) return;
    }

    // set all text fields readonly
    $form = $e->arguments(0);
    foreach ($form->find("type%=text") as $ta) {
        $ta->attr('readonly', true);
    }

    // remove submit buttons
    foreach ($form->find("type%=submit") as $button) {
        $form->remove($button);
    }

    // disable uploads
    foreach ($form->find("name%=image|file") as $uploadable) {
        $uploadable->noUpload = true;
    }
});

But removing submit buttons leaves dropdown items behind which I think is added with JS, so you may need use JS to remove it. (I'm using AdminThemeUIKit, other themes might be different)

chrome_2017-04-26_14-03-04.png.bea6a92b6a555b720c97c8a4e94b974a.png

[].forEach.call(document.querySelectorAll('.pw-button-dropdown'), dd => dd.parentNode.removeChild(dd));

I'm not sure about other fields, though.

  • Like 3
Link to comment
Share on other sites

To change settings for all fields via API you could use:
 

/**
 * make all fields accessible to view only for all roles except superuser
 *
 */
foreach ($fields as $field) {
    if ($field->hasFlag(8) && $field->name !== 'title') continue; // skip system fields `Field::flagSystem`
    $field->addFlag(160); // Add flags `Field::flagAccess` = 32 + `Field::flagAccessEditor` = 128

    // Applicable only if the `Field::flagAccess` is set to this field's flags.
    $field->editRoles = array(); // leave empty if you want to give access to superuser only
    $field->viewRoles = array(1026); // assign field view permission to specific roles (IDs)

    // save the field
    $field->save();
}

 

  • Like 5
Link to comment
Share on other sites

  • 3 years later...

I figured out a way to do this ( @bernhard's original request, solution #3), but it wasn't easy. I added a page-edit-edit-fields permission which can be added or revoked per-template, and without which none of the fields are editable in the page editor. It required 3 hooks to make the permission work.

/site/ready.php

/**
 * Implements a custom page-edit-edit-fields permission. By default, all fields will be view only when the page-edit permission is granted. Adding the page-edit-edit-fields permission makes all fields on the page editable.
 * This could be accomplished by implementing access settings on every field of the template, but this makes it much easier.
 *
 * Individual field access settings, if enabled, will override this feature.
 *
 * Both this hook and the one below are required to make this work.
 */
$this->wire()->addHookAfter('Field::editable', 'fieldEditableHook');
function fieldEditableHook(HookEvent $event) {
	$return = $event->return;
	$page = $event->arguments(0);
	$user = $event->arguments(1);
	$field = $event->object;

	if($return === true && $page !== null) { // No need to check our page-edit-edit-fields permission if the field is already not editable or we're not checking page context
		if(!$user) $user = $event->wire('user'); // If no user passed in, use the current user

		// If the current user does not have page-edit-edit-fields permission, disable edit
		if(!$user->hasPermission('page-edit-edit-fields', $page)) {
			$return = false;
		}
	}

	$event->return = $return;
}

$this->wire()->addHookAfter('Field::viewable', 'fieldViewableHook');
function fieldViewableHook(HookEvent $event) {
	$return = $event->return;
	$page = $event->arguments(0);
	$user = $event->arguments(1);
	$field = $event->object;

	if($return === false && $page !== null) { // No need to check our page-edit-edit-fields permission if the field is already viewable
		if(!$user) $user = $event->wire('user'); // If no user passed in, use the current user

		// If the current user does not have page-edit-edit-fields permission, enable viewable permission
		if(!$user->hasPermission('page-edit-edit-fields', $page)) {
			$return = true;
		}
	}

	$event->return = $return;
}

$this->wire()->addHookBefore('Field::getInputfield', function(HookEvent $event) {
	// Exclude any field types we don't want to make uneditable by default
	$excludedFieldtypes = [
		'FieldtypeLeafletMapMarker',
	];

	$field = $event->object;
	$page = $event->arguments(0);
	$user = $event->wire('user');

	if(in_array($field->type, $excludedFieldtypes)) return;

	// If the current user does not have page-edit-edit-fields permission, disable edit regardless of the field's roles
	if(!$field->useRoles && !$user->hasPermission('page-edit-edit-fields', $page)) {
		$field->set('useRoles', true);
		//$field->addFlag(Field::flagAccess); // May not be necessary
		//$field->addFlag(Field::flagAccessAPI); // May not be necessary
		$field->addFlag(Field::flagAccessEditor);
	}
});

 

  • Like 1
Link to comment
Share on other sites

Here is a cleaner and more thoroughly tested version, which eliminates some of the code duplication. The Field::viewable hook is no longer needed. I'm not sure why the Field::editable is still necessary, but it doesn't work without it.

/site/ready.php

/**
 * Implements a custom page-edit-edit-fields permission. By default, all fields will be view only when the page-edit permission is granted. Adding the page-edit-edit-fields permission makes all fields on the page editable.
 * This could be accomplished by implementing access settings on every field of the template, but this makes it much easier.
 *
 * Individual field access settings, if enabled, will override this feature.
 */
$this->wire()->addHookBefore('Field::getInputfield', function(HookEvent $event) {

    $page = $event->arguments(0);

    if($page instanceof User) return;

    // Exclude any field types we don't want to make uneditable by default
    $excludedFieldtypes = [
        'FieldtypeFieldsetClose',
        'FieldtypeFieldsetOpen',
        'FieldtypeFieldsetTabOpen',
        'FieldtypeFieldsetPage',
        'FieldtypeFieldsetGroup',
    ];

    $field = $event->object;
    $context = $event->arguments(1);
    $user = $event->wire('user');

    if($context || in_array($field->type, $excludedFieldtypes)) return;

    // If the current user does not have page-edit-edit-fields permission, disable edit regardless of the field's roles
    if(!$field->useRoles && !$user->hasPermission('page-edit-edit-fields', $page)) {
        $field->setRoles('view', [$event->wire('config')->guestUserRolePageID]); // Set all roles to have view permission by passing in the guest role
        $field->setRoles('edit', []);
        $field->addFlag(Field::flagAccessAPI);
        $field->addFlag(Field::flagAccessEditor);
        $field->set('useRoles', true);
        $field->set('disabledByHook', true);
    }
});

/**
 * Only the above Field::getInputfield hook should be required since it sets $field->setRoles('edit', []); but for some reason the main content tab will still appear editable unless we add this hook
 */
$this->wire()->addHookAfter('Field::editable', function() {
    $return = $event->return;
    $field = $event->object;

    // If the current was field disabled by our other hook then reflect that here as well
    if($field->disabledByHook) {
        $return = false;
    }

    $event->return = $return;
});

 

Link to comment
Share on other sites

Another way?

// Make all field inputfields non-editable in Page Edit
$wire->addHookBefore('ProcessPageEdit::execute', function(HookEvent $event) {
	if(!$event->wire()->user->hasRole('read-only')) return;
	$event->wire()->addHookAfter('Field::getInputfield', function(HookEvent $event) {
		$event->return->editable(false);
	});
});
// Remove the Settings tab
$wire->addHookAfter('ProcessPageEdit::buildForm', function(HookEvent $event) {
	if(!$event->wire()->user->hasRole('read-only')) return;
	$form = $event->return;
	$tab = $form->find('id=ProcessPageEditSettings')->first();
	$form->remove($tab);
	$event->object->removeTab('ProcessPageEditSettings');
});

 

  • Like 1
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...