From ecd491c5d841f40629d143b12f44a9b8178a5960 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Wed, 30 Jun 2021 09:38:10 -0400 Subject: [PATCH] Update ProcessPageEdit to enable removal of system status when superuser in advanced mode (though I don't recommend using it unless you are trying to fix something that's broken), plus make a couple more status options available to superuser in advanced mode. Also some other unrelated and minor optimizations in this module. --- .../ProcessPageEdit/ProcessPageEdit.module | 213 ++++++++++-------- 1 file changed, 124 insertions(+), 89 deletions(-) diff --git a/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module b/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module index acc8b19b..eb1218b4 100644 --- a/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module +++ b/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module @@ -8,7 +8,7 @@ * For more details about how Process modules work, please see: * /wire/core/Process.php * - * ProcessWire 3.x, Copyright 2018 by Ryan Cramer + * ProcessWire 3.x, Copyright 2021 by Ryan Cramer * https://processwire.com * * @property string $noticeUnknown @@ -54,7 +54,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod return array( 'title' => 'Page Edit', 'summary' => 'Edit a Page', - 'version' => 109, + 'version' => 110, 'permanent' => true, 'permission' => 'page-edit', 'icon' => 'edit', @@ -345,8 +345,13 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod $this->set('viewAction', 'this'); return parent::__construct(); } - + + /** + * Wired to API + * + */ public function wired() { + parent::wired(); if($this->wire('process') instanceof WirePageEditor) { // keep existing process, which may be building on top of this one } else { @@ -437,7 +442,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod if($context) $this->requestContext = $this->sanitizer->name($context); // optional language GET var - $languages = $this->wire('languages'); + $languages = $this->wire()->languages; if($languages) { $this->hasLanguagePageNames = $this->modules->isInstalled('LanguageSupportPageNames'); if($this->hasLanguagePageNames) { @@ -476,40 +481,35 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod protected function ___loadPage($id) { /** @var Page|NullPage $page */ - $page = $this->wire('pages')->get((int) $id); + $page = $this->wire()->pages->get((int) $id); if($page instanceof NullPage) { throw new WireException($this->noticeUnknown); // page doesn't exist } $editable = $page->editable(); - - /** @var User $user */ - $user = $this->user; - - /** @var Config $config */ - $config = $this->config; - - /** @var Config $config */ - $input = $this->input; if($page instanceof User) { // special case when page is a User + $userAdmin = $this->user->hasPermission('user-admin'); - $userAdmin = $user->hasPermission('user-admin'); - $field = $input->get('field') ? $this->wire('fields')->get($input->get->fieldName('field')) : null; - - if($userAdmin && $this->wire('process') != 'ProcessUser') { + if($userAdmin && $this->wire()->process != 'ProcessUser') { // only allow user pages to be edited from the access section (at least for non-superusers) - $this->session->redirect($config->urls->admin . 'access/users/edit/?id=' . $page->id); + $this->session->redirect($this->config->urls->admin . 'access/users/edit/?id=' . $page->id); + } - } else if(!$userAdmin && $page->id === $user->id && $field && $config->ajax) { - // user is editing themself and we're responding to an ajax request for a field - /** @var PagePermissions $pagePermissions */ - $pagePermissions = $this->modules->get('PagePermissions'); - $editable = $pagePermissions->userFieldEditable($field); - // prevent a later potential redirect to user editor - if($editable) $this->setEditor = false; + if(!$userAdmin && $page->id === $this->user->id && $this->config->ajax) { + // user that lacks user-admin permission editing themself during ajax request + $fieldName = $this->input->get->fieldName('field'); + $field = $fieldName ? $this->wire()->fields->get($fieldName) : null; + if($field instanceof Field) { + // respond to ajax request for field that is editable + /** @var PagePermissions $pagePermissions */ + $pagePermissions = $this->modules->get('PagePermissions'); + $editable = $pagePermissions->userFieldEditable($field); + // prevent a later potential redirect to user editor + if($editable) $this->setEditor = false; + } } } @@ -633,7 +633,8 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod $out .= ""; } - $out .= "initPageEditForm();"; // ends up being slightly faster than ready() (or at least appears that way) + $func = 'initPageEditForm();'; // to prevent IDE from flagging as unknown function + $out .= "$func"; // ends up being slightly faster than ready() (or at least appears that way) return $out; } @@ -665,6 +666,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod if($this->requestModal) return array(); $viewable = $this->page->viewable(); + $process = $this->wire()->process; $actions = array(); $actions['exit'] = array( @@ -680,9 +682,8 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod 'label' => $this->_('%s + View'), 'class' => '', ); - - if($this->wire('process') == $this && $this->page->id > 1) { - + + if("$process" === "$this" && $this->page->id > 1) { $parent = $this->page->parent(); if($parent->addable()) $actions['add'] = array( 'value' => 'add', @@ -1459,14 +1460,10 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod */ protected function buildFormPrevPaths() { - /** @var WireInput $input */ - $input = $this->wire('input'); - /** @var Modules $modules */ - $modules = $this->wire('modules'); - /** @var Sanitizer $sanitizer */ - $sanitizer = $this->wire('sanitizer'); - /** @var Languages|null $languages */ - $languages = $this->wire('languages'); + $input = $this->input; + $modules = $this->modules; + $sanitizer = $this->sanitizer; + $languages = $this->wire()->languages; if($this->isPost && $input->post('_prevpath_add') === null) return null; if(!$modules->isInstalled('PagePathHistory')) return null; @@ -1697,34 +1694,14 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod protected function buildFormStatus() { $status = (int) $this->page->status; - $statuses = array(); $debug = $this->config->debug; - $advanced = $this->config->advanced; + $statuses = $this->getAllowedStatuses(); /** @var InputfieldCheckboxes $field */ $field = $this->modules->get('InputfieldCheckboxes'); $field->attr('name', 'status'); $field->icon = 'sliders'; - if(!$this->page->template->noUnpublish && $this->page->publishable()) { - $statuses[Page::statusUnpublished] = $this->_('Unpublished: Not visible on site'); // Settings: Unpublished status checkbox label - } - if($this->user->hasPermission('page-hide', $this->page)) { - $statuses[Page::statusHidden] = $this->_('Hidden: Excluded from lists and searches'); // Settings: Hidden status checkbox label - } - if($this->user->hasPermission('page-lock', $this->page)) { - $statuses[Page::statusLocked] = $this->_('Locked: Not editable'); // Settings: Locked status checkbox label - } - - if($this->user->isSuperuser()) { - $statuses[Page::statusUnique] = sprintf($this->_('Unique: Require page name ā€œ%sā€ to be globally unique'), $this->page->name) . - ($this->wire('languages') ? ' ' . $this->_('(in default language only)') : ''); - if($advanced) { - $statuses[Page::statusSystemID] = "System: Non-deleteable and locked ID (status not removeable via API)"; - $statuses[Page::statusSystem] = "System: Non-deleteable and locked ID, name, template, parent (status not removeable via API)"; - } - } - $value = array(); foreach($statuses as $s => $label) { @@ -1803,7 +1780,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod if(!$this->page->isTrash()) return false; if(!$this->page->restorable()) return false; - $info = $this->wire('pages')->trasher()->getRestoreInfo($this->page); + $info = $this->wire()->pages->trasher()->getRestoreInfo($this->page); if(!$info['restorable']) return false; /** @var InputfieldWrapper $wrapper */ @@ -2195,7 +2172,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod $this->page->setQuietly('_forceAddStatus', 0); } - $languages = $this->wire('languages'); + $languages = $this->wire()->languages; $errorAction = (int) $this->page->template->errorAction; foreach($form as $inputfield) { @@ -2383,34 +2360,30 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod */ protected function processInputStatus(Inputfield $inputfield) { - $status = $inputfield->value; + $inputStatusFlags = $inputfield->val(); + if(!is_array($inputStatusFlags)) $inputStatusFlags = array(); + foreach($inputStatusFlags as $k => $v) $inputStatusFlags[$k] = (int) $v; + + $allowedStatusFlags = array_keys($this->getAllowedStatuses()); + $statusLabels = array_flip(Page::getStatuses()); $value = $this->page->status; - if(!is_array($status)) $status = array(); - - $statusFlags = array(); - if($this->user->hasPermission('page-hide', $this->page)) $statusFlags[] = Page::statusHidden; - if($this->page->publishable()) $statusFlags[] = Page::statusUnpublished; - if($this->user->hasPermission('page-lock', $this->page)) $statusFlags[] = Page::statusLocked; - - if($this->user->isSuperuser()) { - $statusFlags[] = Page::statusUnique; - if($this->config->advanced) { - $statusFlags[] = Page::statusSystemID; - $statusFlags[] = Page::statusSystem; - } - } - - foreach($statusFlags as $flag) { - if(in_array($flag, $status)) { - if(!($value & $flag)) $value = $value | $flag; - + foreach($allowedStatusFlags as $flag) { + if(in_array($flag, $inputStatusFlags, true)) { + if($value & $flag) { + // already has flag + } else { + $value = $value | $flag; // add status + $this->message(sprintf($this->_('Added status: %s'), $statusLabels[$flag]), Notice::debug); + } } else if($value & $flag) { - $value = $value & ~$flag; + $value = $value & ~$flag; // remove flag + $this->message(sprintf($this->_('Removed status: %s'), $statusLabels[$flag]), Notice::debug); } } - + $this->page->status = $value; + return true; } @@ -2500,6 +2473,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod if(!$this->ajaxEditable($page)) throw new WirePermissionException($this->noticeNoAccess); $this->session->CSRF->validate(); // throws exception when invalid + /** @var InputfieldWrapper $form */ $form = $this->wire(new InputfieldWrapper()); $form->useDependencies = false; $keys = array(); @@ -2963,6 +2937,61 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod return $this->tabs; } + /** + * Get allowed page statuses + * + * @return array Array of [ statusFlagInteger => 'Status flag label' ] + * @since 3.0.181 + * + */ + public function getAllowedStatuses() { + + $page = $this->page; + $config = $this->wire()->config; + $statuses = array(); + $superuser = $this->user->isSuperuser(); + + if(!$this->page->template->noUnpublish && $this->page->publishable()) { + $statuses[Page::statusUnpublished] = $this->_('Unpublished: Not visible on site'); // Settings: Unpublished status checkbox label + } + + if($this->user->hasPermission('page-hide', $this->page)) { + $statuses[Page::statusHidden] = $this->_('Hidden: Excluded from lists and searches'); // Settings: Hidden status checkbox label + } + + if($this->user->hasPermission('page-lock', $this->page)) { + $statuses[Page::statusLocked] = $this->_('Locked: Not editable'); // Settings: Locked status checkbox label + } + + if($superuser) { + $uniqueNote = ($this->wire('languages') ? ' ' . $this->_('(in default language only)') : ''); + $statuses[Page::statusUnique] = sprintf($this->_('Unique: Require page name ā€œ%sā€ to be globally unique'), $this->page->name) . $uniqueNote; + } + + if($superuser && $config->advanced) { + // additional statuses available to superuser in advanced mode + $hasSystem = $page->hasStatus(Page::statusSystem) || $page->hasStatus(Page::statusSystemID) || $page->hasStatus(Page::statusSystemOverride); + $statuses[Page::statusSystem] = "System: Non-deleteable and locked ID, name, template, parent (status not removeable without override)"; + $statuses[Page::statusSystemID] = "System ID: Non-deleteable and locked ID (status not removeable without override)"; + if($hasSystem) $statuses[Page::statusSystemOverride] = "System Override: Override (must be added temporarily in its own save before system status can be removed)"; + $statuses[Page::statusDraft] = "Draft: Page has a separate draft version"; + $statuses[Page::statusOn] = "On: Internal toggle when combined with other statuses (only for specific cases, otherwise ignored)"; + /* + * Additional statuses that are possible but shouldn't be editable (uncomment temporarily if needed) + * + * $statuses[Page::statusTemp] = "Temp: Unpublished page more than 1 day old may be automatically deleted"; + * $statuses[Page::statusFlagged] = "Flagged: Page is flagged as incomplete, needing review, or having some issue"; + * $statuses[Page::statusTrash] = "Internal trash: Indicates that page is in the trash"; + * $statuses[Page::statusReserved] = "Internal-reserved: Status reserved for future use"; + * $statuses[Page::statusInternal] = "Internal-internal: Status for internal or future use"; + * + */ + } + + return $statuses; + } + + /** * Get PageBookmarks array * @@ -3147,11 +3176,16 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod * */ public function getModuleConfigInputfields(array $data) { - + + $config = $this->wire()->config; + $pages = $this->wire()->pages; + $modules = $this->wire()->modules; $inputfields = new InputfieldWrapper(); - $this->wire($inputfields); - $f = $this->wire('modules')->get('InputfieldRadios'); + $this->wire($inputfields); + + /** @var InputfieldRadios $f */ + $f = $modules->get('InputfieldRadios'); $f->name = 'viewAction'; $f->label = $this->_('Default "view" location/action'); $f->description = $this->_('The default type of action used when the "view" tab is clicked on in the page editor.'); @@ -3161,7 +3195,8 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod $f->addOption($name, $label); } - $configData = $this->wire('config')->pageEdit; + /** @var array $configData */ + $configData = $config->pageEdit; if(isset($data['viewAction'])) { $f->attr('value', $data['viewAction']); } else if(is_array($configData) && !empty($configData['viewNew'])) { @@ -3174,8 +3209,8 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod $bookmarks = $this->getPageBookmarks(); $bookmarks->addConfigInputfields($inputfields); - $admin = $this->wire('pages')->get($this->wire('config')->adminRootPageID); - $page = $this->wire('pages')->get($admin->path . 'page/edit/'); + $admin = $pages->get($config->adminRootPageID); + $page = $pages->get($admin->path . 'page/edit/'); $bookmarks->checkProcessPage($page); return $inputfields;