From 56e35b55ea3be87f25d6395f143fe3b1e8ba141a Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Fri, 20 Oct 2017 09:43:08 -0400 Subject: [PATCH] Add support for using repeaters in user profile editor per issue processwire/processwire-issues#407 --- .../FieldtypeRepeater.module | 39 ++++++++++++- .../InputfieldRepeater.module | 9 +++ wire/modules/PagePermissions.module | 11 ++-- .../ProcessPageEdit/ProcessPageEdit.module | 55 ++++++++++++++++--- 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module index 9ef3e6c5..4ac39b41 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/FieldtypeRepeater.module @@ -119,8 +119,10 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { $template->set('pageClass', $class); $template->save(); } + + $process = $this->wire('page')->process; - if($this->wire('page')->process == 'ProcessPageEdit') { + if($process == 'ProcessPageEdit' || $process == 'ProcessProfile') { $this->addHookBefore('ProcessPageEdit::ajaxSave', $this, 'hookProcessPageEditAjaxSave'); @@ -200,15 +202,48 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule { * */ public function hookPagePermissionsPageEditableAjax(HookEvent $event) { + if($event->return) return; + $page = $event->arguments(0); if(!$page instanceof RepeaterPage) return; + + $forField = $page->getForField(); $n = 0; + while($page instanceof RepeaterPage) { + $forField = $page->getForField(); $page = $page->getForPage(); if(++$n > 20) break; } - if(!$page instanceof RepeaterPage) $event->return = $page->editable(); + + if(!$page instanceof RepeaterPage) { + // found the original owning page (forPage) + + $editable = null; + /** @var User $user */ + $user = $this->wire('user'); + /** @var WireInput $input */ + $input = $this->wire('input'); + /** @var Field|null $field */ + $field = $input->get('field') ? $this->wire('fields')->get($input->get->fieldName('field')) : $forField; + + if($page instanceof User && $field && $field->type instanceof FieldtypeRepeater) { + // editing a repeater field in a User + if($user->hasPermission('user-admin')) { + $editable = true; + } else if($page->id === $user->id) { + // user editing themself, repeater field + /** @var PagePermissions $pagePermissions */ + $pagePermissions = $this->wire('modules')->get('PagePermissions'); + $editable = $pagePermissions->userFieldEditable($field); + } + } + + if($editable === null) $editable = $page->editable(); + + $event->return = $editable; + } } /** diff --git a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module index 4e771641..9ff119c5 100644 --- a/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module +++ b/wire/modules/Fieldtype/FieldtypeRepeater/InputfieldRepeater.module @@ -588,6 +588,9 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { * */ public function renderReady(Inputfield $parent = null, $renderValueMode = false) { + + /** @var User $user */ + $user = $this->wire('user'); $this->wire('modules')->get('JqueryCore')->use('cookie'); $this->wire('modules')->get('JqueryUI')->use('vex'); @@ -620,6 +623,12 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList { $this->wrapAttr('data-depth', (int) $this->repeaterDepth); list($editorUrl, $queryString) = explode('?', $this->page->editUrl()); + + if(strpos($editorUrl, '/users/edit/') && !$user->isSuperuser() && !$user->hasPermission('user-admin')) { + // to accommodate repeater in user profile, use main page editor + $editorUrl = str_replace('/access/users/edit/', '/page/edit/', $editorUrl); + } + if($queryString) {} $this->wire('config')->js('InputfieldRepeater', array( diff --git a/wire/modules/PagePermissions.module b/wire/modules/PagePermissions.module index 7fb115de..83d80a17 100644 --- a/wire/modules/PagePermissions.module +++ b/wire/modules/PagePermissions.module @@ -116,10 +116,11 @@ class PagePermissions extends WireData implements Module { /** * 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. + * 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. + * 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 * @@ -448,12 +449,14 @@ class PagePermissions extends WireData implements Module { /** * Is the given field editable by the current user in their user profile? * - * @param string $name Field name + * @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; diff --git a/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module b/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module index 7a6db509..cb60853e 100644 --- a/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module +++ b/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module @@ -216,6 +216,12 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod */ protected $editor = null; + /** + * Tell the Page what Process is being used to edit it? + * + */ + protected $setEditor = true; + /** * Names of changed fields * @@ -356,17 +362,47 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod * */ protected function ___loadPage($id) { - + + /** @var Page|NullPage $page */ $page = $this->wire('pages')->get((int) $id); - if($page instanceof NullPage) throw new WireException($this->noticeUnknown); // Init error: page doesn't exist + if($page instanceof NullPage) { + throw new WireException($this->noticeUnknown); // page doesn't exist + } + + $editable = $page->editable(); - if(!$page->editable()) { - if($page instanceof User && $this->wire('user')->hasPermission('user-admin') && $this->wire('process') != 'ProcessUser') { + /** @var User $user */ + $user = $this->wire('user'); + + /** @var Config $config */ + $config = $this->wire('config'); + + /** @var Config $config */ + $input = $this->wire('input'); + + if($page instanceof User) { + // special case when page is a User + + $userAdmin = $user->hasPermission('user-admin'); + $field = $input->get('field') ? $this->wire('fields')->get($input->get->fieldName('field')) : null; + + if($userAdmin && $this->wire('process') != 'ProcessUser') { // only allow user pages to be edited from the access section (at least for non-superusers) - $this->wire('session')->redirect($this->wire('config')->urls->admin . 'access/users/edit/?id=' . $page->id); + $this->wire('session')->redirect($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->wire('modules')->get('PagePermissions'); + $editable = $pagePermissions->userFieldEditable($field); + // prevent a later potential redirect to user editor + if($editable) $this->setEditor = false; } - throw new WirePermissionException($this->noticeNoAccess); // Init: user doesn't have access + } + + if(!$editable) { + throw new WirePermissionException($this->noticeNoAccess); } return $page; @@ -383,8 +419,11 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod if(!$this->page) throw new WireException("No page found"); - $this->page->setEditor($this->editor ? $this->editor : $this); - + if($this->setEditor) { + // note that setting the editor can force a redirect to a ProcessPageType editor + $this->page->setEditor($this->editor ? $this->editor : $this); + } + if($this->config->ajax && (isset($_SERVER['HTTP_X_FIELDNAME']) || count($_POST))) { $this->ajaxSave($this->page); return '';