Blame | Last modification | View Log | Download
<?php namespace ProcessWire;/*** ProcessWire Page Permissions Module** Adds convenience methods to all Page objects for checking permissions, i.e.** if($page->editable()) { do something }* if(!$page->viewable()) { echo "sorry you can't view this"; }* ...and so on...** ProcessWire 3.x, Copyright 2016 by Ryan Cramer* https://processwire.com** Optional special permissions that are optional (by default, not installed):** 1. page-publish: when installed, editable() returns false, when it would otherwise return true,* on published pages, if user doesn't have page-publish permission in their roles.** 2. page-edit-created: when installed, editable() returns false, when it would otherwise* return true, if user's role has this permission and they are not the $page->createdUser.* This is a permission that reduces access rather than increasing it. Note that* page-edit-created does nothing if the user doesn't have page-edit permission.** 3. page-rename: when installed, user must have this permission in their role before they* can change the name of a published page. They can still change the name of an unpublished* page. When not installed, this permission falls back to page-edit.** 4. page-edit-lang-default: when installed on a multi-language site, user must have this* permission in order edit multi-language fields in "default" language. This permission* is also required to create or delete pages (if user already has other permissions that enable* them to create or delete pages).** 5. page-edit-lang-[language_name]: when installed on a multi-language site, user must have* this permission to edit multi-language fields in the [language_name] language.** 6. page-edit-lang-none: when installed on a multi-language site, user must have this* permission to edit non-multi-language fields. They must also have it to create or delete* pages (if user already has other permissions that enable that).** 7. user-admin-all: when installed, a user must have this permission in order to edit other* users of all roles (except superuser of course). When installed, the regular user-admin* permission only acts as a pre-requisite for editing users, and enables only listing users,* and editing users that have only the 'guest' role. The user-admin-all essentially grants* the same thing as user-admin permission does on a system that has no user-admin-all* permission installed. That's what it does, but why is it here? The entire purpose is to* support user-admin-[role] permissions, as described in the next item below:** 8. user-admin-[role_name]: when installed on a site that also has user-admin-all permission* installed, a user must have this permission (along with user-admin permission) in order* to edit users in role [role_name], or to grant that role to other guest users, or to* revoke it from them. Think of this permission as granting a user administrative privileges* to just a specific group of users. Note that there would be no reason to combine this* permission with the user-admin-all permission, as user-admin-all permission grants admin* privileges to all roles.**/class PagePermissions extends WireData implements Module {public static function getModuleInfo() {return array('title' => 'Page Permissions','version' => 105,'summary' => 'Adds various permission methods to Page objects that are used by Process modules.','permanent' => true,'singular' => true,'autoload' => true,);}/*** Do we have page-publish permission in the system?** @var null|bool Null=unset state**/protected $hasPagePublish = null;/*** Do we have page-edit-created permission in the system?** @var null|bool Null=unset state**/protected $hasPageEditCreated = null;/*** Array of optional permission name => boolean of whether the system has it** @var array**/protected $hasOptionalPermissions = array();/*** Establish permission hooks**/public function init() {$this->addHook('Page::editable', $this, 'editable');$this->addHook('Page::publishable', $this, 'publishable');$this->addHook('Page::viewable', $this, 'viewable');$this->addHook('Page::listable', $this, 'listable');$this->addHook('Page::deleteable', $this, 'deleteable');$this->addHook('Page::deletable', $this, 'deleteable');$this->addHook('Page::trashable', $this, 'trashable');$this->addHook('Page::restorable', $this, 'restorable');$this->addHook('Page::addable', $this, 'addable');$this->addHook('Page::moveable', $this, 'moveable');$this->addHook('Page::sortable', $this, 'sortable');// $this->addHook('Page::fieldViewable', $this, 'hookFieldViewable');// $this->addHook('Page::fieldEditable', $this, 'hookFieldEditable');// $this->addHook('Template::createable', $this, 'createable');}/*** Hook that adds a Page::editable([$field]) method to determine if $page is editable by current user** A field name may optionally be specified as the first argument, in which case the field on that* page will also be checked for access.** When using field, specify boolean false for second argument to bypass PageEditable check, as an* optimization, if you have already determined that the page is editable.** @param HookEvent $event**/public function editable($event) {/** @var Page $page */$page = $event->object;$field = $event->arguments(0);$checkPageEditable = true;if($field && $event->arguments(1) === false) $checkPageEditable = false;if($checkPageEditable && !$this->pageEditable($page)) {$event->return = false;} else if($field) {$event->return = $this->fieldEditable($page, $field, false);} else {$event->return = true;}}/*** Is the given page editable?** @param Page $page* @return bool**/public function pageEditable(Page $page) {if($this->wire('hooks')->isHooked('PagePermissions::pageEditable()')) {return $this->__call('pageEditable', array($page));} else {return $this->___pageEditable($page);}}/*** Hookable implmentation for: Is the given page editable?** @param Page $page* @return bool**/protected function ___pageEditable(Page $page) {$user = $this->wire('user');// superuser can always do whatever they wantif($user->isSuperuser()) return true;// note there is an exception in the case of system pages, which require superuser to editif(($page->status & Page::statusSystem) && $page->template != 'language') return false;// If page is locked and user doesn't have permission to unlock, don't let them editif($page->status & Page::statusLocked) {if(!$user->hasPermission("page-lock", $page)) return false;}// special conditions apply if the page is a Languageif($page->template == 'language' && $user->hasPermission('page-edit') && $user->hasPermission('lang-edit')) {return $this->hasLangPermission("page-edit-lang-$page->name", $user);}// special conditions apply if the page is a Userif($page instanceof User || in_array($page->template->id, $this->wire('config')->userTemplateIDs)) {return $this->userEditable($page);}// if the user doesn't have page-edit permission, don't let them go furtherif(!$user->hasPermission("page-edit", $page)) return false;// check if the system has a page-edit-created permission installedif(is_null($this->hasPageEditCreated)) {$this->hasPageEditCreated = $this->wire('permissions')->has('page-edit-created');}if($this->hasPageEditCreated && $user->hasPermission('page-edit-created', $page)) {// page-edit-created permission is installed, so we have to account for it// if user is not the one that created this page, don't allow them to edit itif($page->created_users_id != $user->id) return false;}// now check if there is a page-publish permission in the system, and use it if soif(is_null($this->hasPagePublish)) {$this->hasPagePublish = $this->wire('permissions')->get('page-publish')->id > 0;}if($this->hasPagePublish) {// if user has the page-publish permission here, then we're goodif($user->hasPermission('page-publish', $page)) return true;// if the page is unpublished then we're fine tooif($page->hasStatus(Page::statusUnpublished)) return true;// otherwise user cannot edit this pagereturn false;}return true;}/*** Returns whether the given user ($page) is editable by the current user** @param User|Page $page* @param array $options* - `viewable` (bool): Specify true if only a viewable check is needed (default=false)* @return bool**/public function userEditable(Page $page, array $options = array()) {/** @var User $user */$user = $this->wire('user');/** @var Process|ProcessProfile|ProcessPageView|ProcessUser|ProcessPageList|ProcessPageLister $process */$process = $this->wire('process');/** @var Config $config */$config = $this->wire('config');$defaults = array('viewable' => false, // specify true if method is being used to determine viewable state);$options = count($options) ? array_merge($defaults, $options) : $defaults;if(!$page->id) return false;if($page->className() != 'User') $page = $this->wire('users')->get($page->id);if(!$page || $page instanceof NullPage) return false;if($user->id === $page->id && !$user->isGuest() && $user->hasPermission('profile-edit')) {// user is the same as the page being edited or viewedif($process == 'ProcessProfile') {// user editing themself in ProcssProfilereturn true;} else if($this->wire('page') && $this->wire('page')->process == 'ProcessProfile') {// user editing themself in ProcessProfile, when process not yet establishedreturn true;} else if($process == 'ProcessPageView' && $config->pagefileSecure && $options['viewable']) {// user is viewing a file that is part of their User page when pagefileSecure mode activereturn $process->getResponseType() == ProcessPageView::responseTypeFile;}}// if the current process is something other than ProcessUser, they don't have permissionif(!$options['viewable']) {if($process != 'ProcessUser' && (!$process instanceof ProcessPageList) && (!$process instanceof ProcessPageLister)) {return false;}}// if user doesn't have user-admin permission, they have no edit accessif(!$user->hasPermission('user-admin')) return false;// if the user page being edited has a superuser role, and the current user doesn't,// never let them edit regardless of any other permissions$superuserRole = $this->wire('roles')->get($config->superUserRolePageID);if($page->roles->has($superuserRole) && !$user->roles->has($superuserRole)) return false;// if we reach this point then check if there are more granular user-admin permissions available// special permissions: user-admin-all, and user-admin-[role]$userAdminAll = $this->wire('permissions')->get('user-admin-all');// if there are no special permissions, then let them throughif(!$userAdminAll->id) return true;// if user has user-admin-all permission, they are good to editif($user->hasPermission($userAdminAll)) return true;// there are role-specific permissions in the system, and user must have appropriate one to edit$userEditable = false;$guestRoleID = $config->guestUserRolePageID;$n = 0;foreach($page->roles as $role) {$n++;if($role->id == $guestRoleID) continue;if($user->hasPermission("user-admin-$role->name")) {// found a matching permission for role, so it is editable$userEditable = true;break;}}if($userEditable) return true;// if there is only role (guest), then no specific permission needed for thatif($n == 0 || ($n == 1 && $page->roles->first()->id == $guestRoleID)) return true;return false;}public function userViewable(Page $page, array $options = array()) {$options['viewable'] = true;return $this->userEditable($page, $options);}/*** Can the current user add/remove the given role from other users?** @param string|Role|int $role* @return bool**/public function userCanAssignRole($role) {$user = $this->wire('user');// superuser can assign any roleif($user->isSuperuser()) return true;// user-admin permission is a pre-requisite for any kind of role assignmentif(!$user->hasPermission('user-admin')) return false;// make sure we have a Role objectif(!$role instanceof Role) $role = $this->wire('roles')->get($role);if(!$role->id) return false;// cannot assign superuser roleif($role->id == $this->wire('config')->superUserRolePageID) return false;// user with user-admin can always assign guest roleif($role->id == $this->wire('config')->guestUserRolePageID) return true;// check for user-admin-all permission$userAdminAll = $this->wire('permissions')->get('user-admin-all');if(!$userAdminAll->id) {// if there is no user-admin-all permission, then user-admin permission is// all that is necessary to edit other users of any roles except superuserreturn true;}if($user->hasPermission($userAdminAll)) {// if user has user-admin-all permission, then they can assign any roles except superuserreturn true;}// return whether user has permission specific to rolereturn $user->hasPermission("user-admin-$role->name");}/*** Is the given Field or field name viewable?** This provides the implementation for the Page::fieldViewable($field) method.** @param Page $page* @param string|Field $name Field name* @param bool $checkPageViewable Check if the page is viewable? Default=true.* Specify false here as an optimization if you've already confirmed $page is viewable.* @return bool**/public function fieldViewable(Page $page, $name, $checkPageViewable = true) {if(empty($name)) return false;if($checkPageViewable && !$page->viewable(false)) {return false;}if(is_object($name)) {if($name instanceof Field) {// Field object$field = $name;$name = $field->name;} else {// objects that aren't fields aren't viewablereturn false;}} else {$_name = $this->wire('sanitizer')->fieldName($name);// if field name doesn't follow known format, return falseif($_name !== $name) return false;$name = $_name;$field = $this->wire('fields')->get($name);}if($field) {// delegate to Field::viewable methodreturn $field->useRoles ? $field->viewable($page) : true;} else if($this->wire($name)) {// API vars are not viewablereturn false;}// something else that we don't consider access forreturn true;}/*** Is the given field name editable?** This provides the implementation for the Page::fieldEditable($field) method.** @param Page $page* @param string|Field $name Field name* @param bool $checkPageEditable Check if the page is editable? Default=true.* Specify false here as an optimization if you've already confirmed $page is editable.* @return bool**/public function fieldEditable(Page $page, $name, $checkPageEditable = true) {if(empty($name)) return false;if($checkPageEditable && !$this->pageEditable($page)) return false;if(is_object($name) && $name instanceof Field) $name = $name->name;if(!is_string($name)) return false;if(!strlen($name)) return true;if($name == 'id' && ($page->status & Page::statusSystemID)) return false;$user = $this->wire('user');if($page->status & Page::statusSystem) {if(in_array($name, array('id', 'name', 'template', 'templates_id', 'parent', 'parent_id'))) {return false;}}if($name == 'template' || $name == 'templates_id') {if($page->template->noChangeTemplate) return false;if(!$user->hasPermission('page-template', $page)) return false;}if($name == 'name') {// if page has no name (and not homepage), then it needs one, so it is allowedif($page->id > 1 && !strlen($page->name)) return true;// if page is not yet published, user with page-edit can still change nameif($page->isUnpublished()) return true;// otherwise verify page-rename permissionreturn $user->hasPermission('page-rename', $page);}if($name == 'parent' || $name == 'parent_id') {if($page->template->noMove) return false;if(!$user->hasPermission('page-move', $page)) return false;}if($name == 'sortfield') {if(!$user->hasPermission('page-sort', $page)) return false;}if($name == 'roles') {if(!$user->hasPermission('user-admin')) return false;}if($user->id === $page->id && !$user->isSuperuser() && !$user->hasPermission('user-admin')) {return $this->userFieldEditable($name, $user);}// check per-field edit access$field = $this->wire('fields')->get($name);if($field && $field->useRoles) {return $field->editable($page);}return true;}/*** Is the given field editable by the current user in their user profile?** @param Field|string $name Field or Field name* @param User|null User to check (default=current user)* @return bool**/public function userFieldEditable($name, User $user = null) {if(is_object($name) && $name instanceof Field) $name = $name->name;if(empty($name) || !is_string($name)) return false;if(is_null($user)) $user = $this->wire('user');if(!$user->isLoggedin()) return false;if(!$user->hasPermission('profile-edit')) return false;$data = $this->wire('modules')->getModuleConfigData('ProcessProfile');$profileFields = isset($data['profileFields']) ? $data['profileFields'] : array();if(in_array($name, $profileFields)) return true;return false;}/*** Hook for Page::viewable() or Page::viewable($user) method** Is the page viewable by the current user? (or specified user)** - Optionally specify User object to hook as first argument to check for a specific User.* - Optionally specify a field name (or Field object) as first argument to check for specific field.* - Optionally specify Language object or language name as first argument to check if viewable* in that language (requires LanguageSupportPageNames module).* - Optionally specify boolean false as first or second argument to bypass template filename check.** @param HookEvent $event**/public function viewable($event) {/** @var Page $page */$page = $event->object;$viewable = true;$user = $this->wire('user');$arg0 = $event->arguments(0);$arg1 = $event->arguments(1);$field = null; // field name or Field object, if specified as arg0$checkFile = true; // return false if template filename doesn't exist$status = $page->status;// allow specifying User instance as argument 0// this gives you a "viewable to user" capabilityif($arg0) {if($arg0 instanceof User) {// user specified$user = $arg0;} else if($arg0 instanceof Field || is_string($arg0)) {// field name, Field object or language name specified// @todo: prevent possible collision of field name and language name$field = $arg0;$checkFile = false;}}if($arg0 === false || $arg1 === false) {// bypass template filename check$checkFile = false;}// if page has corrupted status, this need not affect viewable accessif($status & Page::statusCorrupted) $status = $status & ~Page::statusCorrupted;// perform several viewable checks, in orderif($status >= Page::statusUnpublished) {// unpublished pages are not viewable, but see override below this if/else statement$viewable = false;} else if(!$page->template || ($checkFile && !$page->template->filenameExists())) {// template file does not exist$viewable = false;} else if($user->isSuperuser()) {// superuser always allowed$viewable = true;} else if($page->hasField('process') && $page->get('process')) {// delegate access to permissions defined with Process module$viewable = $this->processViewable($page);} else if($page instanceof User && !$user->isGuest() && ($user->hasPermission('user-admin') || $page->id === $user->id)) {// user administrator or user viewing themself$viewable = $this->userViewable($page);} else if(!$user->hasPermission("page-view", $page)) {// user lacks basic view permission to page$viewable = false;} else if($page->isTrash()) {// pages in trash are not viewable, except to superuser$viewable = false;}// if the page is editable by the current user, force it to be viewable (if not viewable due to being unpublished)if(!$viewable && !$user->isGuest() && ($status & Page::statusUnpublished)) {if($page->editable() && (!$checkFile || $page->template->filenameExists())) $viewable = true;}if($field && $viewable) {$viewable = $this->fieldViewable($page, $field, false);}$event->return = $viewable;}/*** Does the user have explicit permission to access the given process?** Access to the process takes over 'page-view' access to the page so that the administrator* doesn't need to setup a separate role just for 'view' access in the admin. Instead, they just* give the existing roles access to the admin process and then 'view' access is assumed for that page.** @param Page $page* @return bool**/protected function processViewable(Page $page) {$user = $this->wire('user');$process = $page->process;if($user->isGuest()) return false;if($user->isSuperuser()) return true;return $this->wire('modules')->hasPermission($process, $user, $page, true);}/*** Is the page listable by the current user?** A listable page may appear in a listing, but doesn't mean that the user can actually* view the page or that the page is renderable.** @param HookEvent $event**/public function listable($event) {$page = $event->object;$user = $this->wire('user');$listable = true;if($page instanceof NullPage) $listable = false;else if($user->isSuperuser()) $listable = true;else if($page instanceof User && $user->hasPermission('user-admin')) $listable = true;else if($page->hasStatus(Page::statusUnpublished) && !$page->editable()) $listable = false;else if($page->process && !$this->processViewable($page)) $listable = false;else if($page->isTrash()) $listable = $this->trashListable($page);else if(($accessTemplate = $page->getAccessTemplate()) && $accessTemplate->guestSearchable) $listable = true;else if(!$user->hasPermission("page-view", $page)) $listable = false;$event->return = $listable;}/*** Return whether or not given page in Trash is listable** @param Page|null $page Page, or specify null for a general "trash is listable" request* @return bool**/public function trashListable($page = null) {/** @var User $user */$user = $this->wire('user');// trash and anything in it always visible to superuserif($user->isSuperuser()) return true;// determine if system has page-edit-trash-created permission installed$petc = 'page-edit-trash-created';if(!$this->wire('permissions')->has($petc)) $petc = false;if($user->hasPermission('page-delete')) {// has page-delete globally} else if($petc && $user->hasPermission($petc)) {// has page-edit-trash-created globally} else if($user->hasPermission('page-delete', true)) {// has page-delete added specifically at a template} else if($petc && $user->hasPermission($petc, true)) {// has page-edit-trash-created added specifically at a template} else {// user does not have any of the permissions above, so trash is not listablereturn false;}// if request not asking about specific page, return general "trash is listable?" requestif($page === null || !$page->id) return true;// if request is for the actual Trash page, consider this to be a general requestif($page->id == $this->wire('config')->trashPageID) return true;// page is listable in the trash only if it is also editablereturn $this->pageEditable($page);}/*** Is the page deleteable by the current user?** @param HookEvent $event**/public function deleteable($event) {/** @var Page $page */$page = $event->object;/** @var User $user */$user = $this->wire('user');if($page->isLocked()) {$deleteable = false;} else if($page instanceof User && $user->hasPermission('user-admin')) {/** @var User $page */$deleteable = true;if($page->id == $user->id) $deleteable = false; // can't delete selfif($page->hasRole('superuser') && !$user->hasRole('superuser')) $deleteable = false; // non-superuser can't delete superuser} else {$deleteable = $this->pages->isDeleteable($page);if($deleteable && !$user->isSuperuser()) {// make sure the page is editable and user has page-delete permission, if not dealing with superuser$deleteable = $page->editable() && $user->hasPermission("page-delete", $page);}if($deleteable && $this->wire('languages')) {// in multi-language environment, if user can't edit default language or can't edit non-multi-language fields,// then deny access to delete the pageif(!$this->hasPageEditLangDefault($user, $page) || !$this->hasPageEditLangNone($user, $page)) {$deleteable = false;}}}$event->return = $deleteable;}/*** Is the page trashable by the current user?** Optionally specify boolean true for first argument to make this method behave as: "Is page deleteable OR trashable?"** @param HookEvent $event**/public function trashable($event) {/** @var Page $page */$page = $event->object;$event->return = false;if($event->arguments(0) !== true) {if($page->hasStatus(Page::statusTrash) || $page->template->noTrash) {// if page is already in trash, or template doesn't allow placement in trash, we return falsereturn;}}if(!$page->isLocked()) {$this->deleteable($event);if(!$event->return && $this->wire('permissions')->has('page-edit-trash-created') && $page->editable()) {// page can be trashable if user created it$user = $this->wire('user');$trashable = ($page->created_users_id === $user->id && $user->hasPermission('page-edit-trash-created', $page));$event->return = $trashable;}}}/*** Is page restorable from trash?** @param HookEvent $event**/public function restorable($event) {/** @var Page $page */$page = $event->object;/** @var User $user */$user = $this->wire('user');$event->return = false;if($page->isLocked()) return;if(!$page->isTrash() && !$page->rootParent()->isTrash()) return;if(!$user->isSuperuser() && !$page->editable()) return;$info = $this->wire('pages')->trasher()->getRestoreInfo($page);if(!$info['restorable']) return;/** @var Page $parent */$parent = $info['parent'];// check if parent does not allow this user to add pages hereif(!$parent->id || !$parent->addable($page)) return;$event->return = true;}/*** Can the current user add child pages to this page?** Optionally specify the page to be added as the first argument for additional access checking.* i.e. if($page->addable($somePage))** @param HookEvent $event**/public function addable($event) {/** @var Page $page */$page = $event->object;$user = $this->wire('user');$addable = false;$addPage = null;$_ADDABLE = false; // if we really mean it (as in, do not perform secondary checks)$superuser = $user->isSuperuser();if($page->template->noChildren) {$addable = false;} else if($superuser) {$addable = true;$_ADDABLE = true;} else if(in_array($page->id, $this->wire('config')->usersPageIDs) && $user->hasPermission('user-admin')) {// users with user-admin access adding a page to users: add access is assumed// rather than us having a separate 'users' template where access is defined$addable = true;$_ADDABLE = true;} else if($user->hasPermission('page-add', $page)) {// user has page-add permission, now we need to check that they have access// on the templates in this context$addable = $this->addableTemplate($page, $user);}// check if a $page is provided as the first argument for additional access checkingif($addable) {$addPage = $event->arguments(0);if(!$addPage || !$addPage instanceof Page || !$addPage->id) $addPage = null;if($addPage && $addPage->template && $page->template) {if(count($page->template->childTemplates) && !in_array($addPage->template->id, $page->template->childTemplates)) {$addable = false;}}}// check additional permissions if in multi-language environmentif($addable && !$_ADDABLE && $addPage && $this->wire('languages')) {if(!$this->hasPageEditLangDefault($user, $addPage) || !$this->hasPageEditLangNone($user, $addPage)) {// if user can't edit default language, or can't edit non-multi-language fields, then deny add access$addable = false;}}$event->return = $addable;}/*** Checks that a parent is addable within the context of its template (i.e. has page-create for the template)** When this function is called, it has already been determined that the user has page-add permission.* So this is just narrowing down to make sure they have access on a template.** @param Page $page* @param User $user* @return bool**/protected function addableTemplate(Page $page, User $user) {$has = false;if(count($page->template->childTemplates)) {// page's template defines specific templates for children// see if the user has access to one of themforeach($page->template->childTemplates as $id) {$template = $this->wire('templates')->get($id);if(!$template->useRoles) $template = $page->getAccessTemplate('edit');if($template && $user->hasPermission('page-create', $template)) $has = true;if($has) break;}} else if(in_array($page->id, $this->wire('config')->usersPageIDs) && $user->hasPermission('user-admin')) {// user-admin permission implies create access to the 'user' template$has = true;} else {// page's template does not specify templates for children// so check to see if they have edit access to ANY template that can be usedforeach($this->wire('templates') as $template) {// if($template->noParents) continue;if($template->parentTemplates && !in_array($page->template->id, $template->parentTemplates)) continue;// if($template->flags & Template::flagSystem) continue;//$has = $user->hasPermission('page-edit', $template);$has = $user->hasPermission('page-create', $template);if($has) break;}}return $has;}/*** Is the given page moveable (i.e. change parent)?** Without arguments, it just checks that the user is allowed to move the page (not where they are allowed to)* Optionally specify a $parent page as the first argument to check if they are allowed to move to that parent.** @param HookEvent $event**/public function moveable($event) {/** @var Page $page */$page = $event->object;/** @var Page|null $parent */$parent = $event->arguments(0);if(!$parent || !$parent instanceof Page || !$parent->id) $parent = null;if($page->id == 1) {$moveable = false;} else {$moveable = $page->editable('parent');}if($moveable && $parent) {$moveable = $parent->addable($page);} else if($parent && $parent->isTrash() && $parent->id == $this->wire('config')->trashPageID) {$moveable = $page->deletable();}$event->return = $moveable;}/*** Is the given page sortable by the current user?** @param HookEvent $event**/public function sortable($event) {/** @var Page $page */$page = $event->object;$sortable = false;if($page->id > 1 && $page->editable() && $this->user->hasPermission('page-sort', $page->parent)) $sortable = true;$event->return = $sortable;}/*** Is the page publishable by the current user?** A field name may optionally be specified as the first argument, in which case the field on that page will also be checked for access.** @param HookEvent $event**/public function publishable($event) {/** @var User $user */$user = $this->wire('user');$event->return = true;if($user->isSuperuser()) return;/** @var Page $page */$page = $event->object;// if page isn't editable, it certainly can't be publishableif(!$page->editable()) {$event->return = false;return;}// if there is no page-publish permission, then it's publishable$hasPublish = $this->wire('permissions')->has('page-publish');if(!$hasPublish) return;// if Page is a user, and user has user-admin permission, they can also publish the userif($page instanceof User && $user->hasPermission('user-admin')) return;// check if user has the permission assignedif($user->hasPermission('page-publish', $page)) return;// if we made it here, then page is not publishable$event->return = false;}/*** Returns true if given user has the optional language permission, or false if not** In a non-multi-language system, this method will always return true.* In a multi-language system that doesn't have the permission installed, this always returns true.* In a multi-language system that DOES have it installed, methods returns true when user has it via one of their roles.* This method assumes the user is already known to have any pre-requisite permissions.** @param string $name Permission name i.e. page-edit-lang-none, page-edit-lang-default, etc.* @param User $user* @param Page|Template $context Optional Page or Template context* @return bool**/protected function hasLangPermission($name, User $user, $context = null) {if($user->isSuperuser()) return true;if(!array_key_exists($name, $this->hasOptionalPermissions)) {if($this->wire('languages')) {$this->hasOptionalPermissions[$name] = $this->wire('permissions')->get($name)->id > 0;} else {// not applicable since multi-language not installed$this->hasOptionalPermissions[$name] = false;}}if($this->hasOptionalPermissions[$name]) {// now check if the user has this permissionreturn $user->hasPermission($name, $context);} else {// system doesn't need to consider this permissionreturn true;}}/*** Returns true if given user is allowed to edit values in default language** @param User $user* @param Page|Template $context* @return bool**/protected function hasPageEditLangDefault(User $user, $context = null) {return $this->hasLangPermission('page-edit-lang-default', $user, $context);}/*** Returns true if given user is allowed to edit non-multi-language values** @param User $user* @param Page|Template $context* @return bool**/protected function hasPageEditLangNone(User $user, $context = null) {return $this->hasLangPermission('page-edit-lang-none', $user, $context);}/*** Can the user create pages from this template?** Optional argument 1 may be a parent page for context, i.e. can we create a page with this parent.*public function createable($event) {$template = $event->object;$user = $this->fuel('user');$createable = false;if($template->noParents) {$createable = false;} else if($user->isSuperuser()) {$createable = true;} else if($user->hasPermission('page-create', $template)) {$createable = true;}// check if a parent $page is provided as the first argument for additional access checkingif($createable && isset($event->arguments[0]) && $event->arguments[0] instanceof Page) {$parent = $event->arguments[0];if($parent->template->noChildren || (count($parent->template->childTemplates) && !in_array($template->id, $parent->template->childTemplates))) $createable = false;if($createable) $createable = $parent->addable();}$event->return = $createable;}*//*** Hook for Page::fieldViewable($field) method** @param HookEvent $event* @return bool|null*public function hookFieldViewable(HookEvent $event) {$field = $event->arguments(0);$page = $event->object;$event->return = $this->fieldViewable($page, $field);}*//*** Hook for Page::fieldEditable($field) method** @param HookEvent $event*public function hookFieldEditable(HookEvent $event) {$field = $event->arguments(0);$page = $event->object;$event->return = $this->fieldEditable($page, $field);}*/}