diff --git a/wire/config.php b/wire/config.php index faa5374a..bfa8e141 100644 --- a/wire/config.php +++ b/wire/config.php @@ -1025,6 +1025,30 @@ $config->pageEdit = array( 'editCrumbs' => false, ); +/** + * PageAdd default settings + * + * #property string noSuggestTemplates Disable suggestions for new pages (1=disable all, or specify template names separated by space) + * + */ +$config->pageAdd = array( + 'noSuggestTemplates' => '', +); + +/** + * Disable template suggestions when adding new pages? + * + * Applies when adding a new page where more than one template may be selected for the newly added page. + * + * - true: Always disable template suggestions (forcing user to make selection) + * - false: Never disable template suggestions (default) + * - array: Array of template names or IDs where suggestions should be disabled when children are added. + * + * @var bool|array + * +$config->noSuggestTemplate = false; + */ + /*** 9. MISC ************************************************************************************/ @@ -1057,7 +1081,7 @@ $config->logIP = false; * * Module name of default admin theme for guest and users that haven't already selected one * - * Core options include: **AdminThemeDefault** or **AdminThemeReno**. + * Core options include: **AdminThemeDefault** or **AdminThemeReno** or **AdminThemeUikit**. * Additional options will depend on what other 3rd party AdminTheme modules you have installed. * * @var string @@ -1195,6 +1219,7 @@ $config->lazyPageChunkSize = 250; * */ + /*** 10. RUNTIME ******************************************************************************** * * The following are runtime-only settings and cannot be changed from /site/config.php diff --git a/wire/core/Config.php b/wire/core/Config.php index 04e494ab..7c51db4e 100644 --- a/wire/core/Config.php +++ b/wire/core/Config.php @@ -113,6 +113,7 @@ * * @property array $pageList Settings specific to Page lists. #pw-group-modules * @property array $pageEdit Settings specific to Page editors. #pw-group-modules + * @property array $pageAdd Settings specific to Page adding. #pw-group-modules * @property string $moduleServiceURL URL where the modules web service can be accessed #pw-group-modules * @property string $moduleServiceKey API key for modules web service #pw-group-modules * @property bool $moduleCompile Allow use of compiled modules? #pw-group-modules @@ -182,7 +183,7 @@ class Config extends WireData { * $url = $config->urls->admin; * ~~~~~ * - * @param string $for Predefined ProcessWire URLs property or module name + * @param string|Wire $for Predefined ProcessWire URLs property or module name * @return string|null * */ @@ -195,7 +196,7 @@ class Config extends WireData { * * #pw-internal * - * @param string $for Predefined ProcessWire URLs property or module name + * @param string|Wire $for Predefined ProcessWire URLs property or module name * @return null|string * */ diff --git a/wire/core/Paths.php b/wire/core/Paths.php index 75541b45..b7ea593c 100644 --- a/wire/core/Paths.php +++ b/wire/core/Paths.php @@ -113,7 +113,10 @@ class Paths extends WireData { public function get($key) { static $_http = null; if($key == 'root') return $this->_root; - if(strpos($key, 'http') === 0) { + $http = ''; + if(is_object($key)) { + $key = "$key"; + } else if(strpos($key, 'http') === 0) { if(is_null($_http)) { $scheme = $this->wire('input')->scheme; if(!$scheme) $scheme = 'http'; @@ -123,8 +126,6 @@ class Paths extends WireData { $http = $_http; $key = substr($key, 4); $key[0] = strtolower($key[0]); - } else { - $http = ''; } if($key == 'root') { $value = $http . $this->_root; diff --git a/wire/modules/Process/ProcessPageAdd/ProcessPageAdd.module b/wire/modules/Process/ProcessPageAdd/ProcessPageAdd.module index 755536f5..ac91fee6 100644 --- a/wire/modules/Process/ProcessPageAdd/ProcessPageAdd.module +++ b/wire/modules/Process/ProcessPageAdd/ProcessPageAdd.module @@ -8,23 +8,87 @@ * For more details about how Process modules work, please see: * /wire/core/Process.php * - * ProcessWire 3.x, Copyright 2016 by Ryan Cramer + * ProcessWire 3.x, Copyright 2017 by Ryan Cramer * https://processwire.com + * + * @method string executeTemplate() + * @method bool processQuickAdd(Page $parent, Template $template) + * @method InputfieldForm buildForm() + * @method bool processInput(InputfieldForm $form) + * @method array getAllowedTemplates($parent = null) * */ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEditor { - protected $form; - protected $parent = null; + /** + * @var InputfieldForm + * + */ + protected $form; + + /** + * @var Page|null + * + */ + protected $parent = null; + + /** + * @var int + * + */ protected $parent_id = 0; - protected $page; - protected $template = null; + + /** + * @var Page + * + */ + protected $page; + + /** + * @var Template|null + * + */ + protected $template = null; + + /** + * @var array|null + * + */ protected $allowedTemplates = null; //cache + + /** + * @var array + * + */ protected $predefinedTemplates = array(); - protected $predefinedParents = array(); + + /** + * @var PageArray|array + * + */ + protected $predefinedParents = array(); + + /** + * @var WirePageEditor|ProcessPageAdd + * + */ protected $editor; // WirePageEditor + /** + * Settings that may be specified with $config->pageAdd array + * + * @var array + * + */ + protected $settings = array( + 'noSuggestTemplates' => '', // Disable suggestions: 1|true=disable all, or space-separated template names + ); + + /** + * @return array + * + */ public static function getModuleInfo() { return array( 'title' => __('Page Add', __FILE__), @@ -36,19 +100,37 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit 'useNavJSON' => true, ); } - + + /** + * Construct and populate default config + * + */ public function __construct() { $this->editor = $this; parent::__construct(); $this->set('noAutoPublish', false); $this->set('shortcutSort', array()); + $settings = $this->wire('config')->pageAdd; + if(is_array($settings)) $this->settings = array_merge($this->settings, $settings); } + /** + * Module init + * + */ public function init() { $this->page = null; return parent::init(); } + /** + * Set property + * + * @param string $key + * @param mixed $value + * @return Process|ProcessPageAdd + * + */ public function set($key, $value) { if($key == 'parent_id') $this->parent_id = (int) $value; else if($key == 'template' && $value instanceof Template) $this->template = $value; @@ -156,12 +238,14 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit unset($data['modified']); // get additional from PageBookmarks - $bookmarks = $this->getPageBookmarks($this); + $bookmarks = $this->getPageBookmarks(); $options2 = $bookmarks->initNavJSON(array('add' => 'ignore-me')); $lastItem = null; $listLength = count($data['list']); $n = 0; + foreach(array_values($options2['items']) as $p) { + /** @var Page $p */ if($p->id == 'bookmark' && !$user->isSuperuser()) continue; $item = array( 'url' => ($p->id == 'bookmark' ? 'bookmarks/?role=0' : "?parent_id=$p->id"), @@ -186,6 +270,13 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit return json_encode($data); } + /** + * Ask user to select template and parent + * + * @return string + * @throws WireException + * + */ public function ___executeTemplate() { $templateID = (int) $this->input->get->template_id; @@ -243,7 +334,13 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit $form->add($f); return $form->render(); } - + + /** + * Render an HTML definition list template selection for when no parent/template is known + * + * @return string + * + */ public function renderChooseTemplate() { $data = $this->executeNavJSON(array('getArray' => true)); $out = ''; @@ -301,7 +398,15 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit } return $out; } - + + /** + * Method to handle AJAX call to check of a given page name exists for a parent + * + * Returns error or OK message in HTML + * + * @return string + * + */ public function executeExists() { $parentID = (int) $this->wire('input')->get('parent_id'); if(!$parentID) return ''; @@ -319,6 +424,14 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit return $out; } + /** + * Main execution, first screen of adding a Page + * + * @return string + * @throws Wire404Exception + * @throws WireException + * + */ public function ___execute() { $input = $this->wire('input'); @@ -380,7 +493,11 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit $this->form->setTrackChanges(); if($input->post('submit_save') || $input->post('submit_publish') || $input->post('submit_publish_add')) { - $this->processInput($this->form); + if($this->processInput($this->form)) { + // redirect occurs within processInput + } else { + // errors occurred during process input, re-render form + } } $this->setupBreadcrumbs(); @@ -390,6 +507,9 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit /** * Returns an array of templates that are allowed to be used here + * + * @param Page|null $parent + * @return array * */ protected function ___getAllowedTemplates($parent = null) { @@ -609,10 +729,14 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit /** * Build the form fields for adding a page + * + * @return InputfieldForm + * @throws WireException * */ protected function ___buildForm() { + /** @var InputfieldForm $form */ $form = $this->modules->get('InputfieldForm'); $form->attr('id', 'ProcessPageAdd'); @@ -641,6 +765,7 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit } } } else if($this->template) { + /** @var Field $field */ $field = $this->template->fieldgroup->getField('title', true); if($field) { if(in_array($field->collapsed, array(Inputfield::collapsedNoLocked, Inputfield::collapsedYesLocked))) { @@ -653,10 +778,12 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit } } + /** @var InputfieldPageName $field */ $field = $this->modules->get('InputfieldPageName'); $field->parentPage = $this->parent; $field->attr('name', '_pw_page_name'); $field->required = true; + if($this->template) { $field->slashUrls = $this->template->slashUrls; $label = $this->template->getNameLabel(); @@ -665,6 +792,8 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit } else { $languages = $this->wire('languages'); } + + /** @var Languages $languages */ if($languages && $this->parent && $this->parent_id > 0) { foreach($languages as $language) { if($language->isDefault()) continue; @@ -704,9 +833,25 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit } else { // multiple templates are possible so give them a select - + + /** @var InputfieldSelect $field */ $field = $this->modules->get('InputfieldSelect'); - + + $noSuggest = $this->settings['noSuggestTemplates']; + if(empty($noSuggest)) { + $noSuggest = false; + } else { + // determine whether to show blank option at top + $ptpl = $this->parent ? $this->parent->template->name : ''; + $noSuggestArray = is_array($noSuggest) ? $noSuggest : explode(' ', $noSuggest); + if(((string) $noSuggest) === "1" || ($ptpl && in_array($ptpl, $noSuggestArray))) { + $field->addOption('', '', array('data-publish' => false, 'data-nolang' => false)); + $noSuggest = true; + } else { + $noSuggest = false; + } + } + foreach($allowedTemplates as $template) { if(!count($this->predefinedTemplates) && $this->template && $template->id != $this->template->id) continue; $numFields = count($template->fieldgroup); @@ -723,7 +868,7 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit )); } - $field->attr('value', $defaultTemplateId); + if(!$noSuggest) $field->attr('value', $defaultTemplateId); } $field->label = $this->_('Template'); // Template field label @@ -731,7 +876,7 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit $field->icon = 'cubes'; $field->required = true; $field instanceof InputfieldHidden ? $form->append($field) : $form->prepend($field); - + if(count($this->predefinedParents) > 1) { $field = $this->modules->get('InputfieldSelect'); $field->attr('name', 'parent_id'); @@ -753,7 +898,8 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit $field->attr('value', $value); $form->append($field); } - + + /** @var InputfieldSubmit $field */ $field = $this->modules->get('InputfieldSubmit'); $field->attr('name', 'submit_save'); $field->attr('value', $this->_('Save')); @@ -767,6 +913,7 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit if($publishPermission->id && !$this->wire('user')->hasPermission('page-publish')) $allowPublish = false; } if($allowPublish) { + /** @var InputfieldSubmit $field */ $field = $this->modules->get('InputfieldSubmit'); $field->attr('id+name', 'submit_publish'); $field->attr('value', $this->_('Save + Publish')); @@ -783,7 +930,6 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit } } - // $allowedTemplates = $this->getAllowedTemplates(); if(count($allowedTemplates) == 1) { $t = reset($allowedTemplates); $form->description = $this->getTemplateLabel($t); @@ -794,6 +940,9 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit /** * Return the label for the given Template + * + * @param Template $template + * @return string * */ protected function getTemplateLabel(Template $template) { @@ -896,41 +1045,57 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit } else { return false; } - + + return true; } + /** + * Populate a session message indicating info about created page + * + * @param Page $page + * + */ protected function createdPageMessage(Page $page) { - $this->session->message(sprintf($this->_('Created page %1$s using template: %2$s'), $page->parent->url . $page->name, $page->template->name)); + $this->session->message( + sprintf( + $this->_('Created page %1$s using template: %2$s'), + $page->parent->url . $page->name, $page->template->name + ) + ); } /** * Save the submitted page add form + * + * @param InputfieldForm $form + * @throws WireException + * @return bool * */ - protected function ___processInput(Inputfield $form) { + protected function ___processInput(InputfieldForm $form) { $this->page = $this->wire('pages')->newPage(); // must exist before processInput for language hooks $form->processInput($this->input->post); - $nameField = $form->children->get('_pw_page_name'); + /** @var InputfieldPageName $nameField */ + $nameField = $form->getChildByName('_pw_page_name'); $name = $nameField->value; if(!strlen($name)) { - $this->error($this->_("Missing required field: name")); + $nameField->error($this->_("Missing required field: name")); return false; } - /* - if($this->parent->child("name=$name, include=all")->id) { - $nameField->error($this->_("The name you selected is already in use. Please select another.")); - return false; - } - */ - $template = $this->template; if(is_null($template)) { - $templatesId = (int) $form->children->get('template')->value; - $template = $this->templates->get($templatesId); + /** @var InputfieldSelect $templateField */ + $templateField = $form->getChildByName('template'); + $templatesId = (int) $templateField->val(); + $template = $templatesId ? $this->templates->get($templatesId) : null; + if(!$template) { + $templateField->error($this->_('Template selection is required')); + return false; + } } if(!$this->isAllowedTemplate($template, $this->parent)) { @@ -1001,6 +1166,8 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit } else { $this->session->redirect("../edit/?id={$this->page->id}&new=1" . ($this->input->get->modal ? "&modal=1" : '')); } + + return true; } /** @@ -1012,19 +1179,28 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit $breadcrumbs = $this->wire(new Breadcrumbs()); $breadcrumbs->add(new Breadcrumb($this->config->urls->admin . 'page/list/', "Pages")); foreach($this->parent->parents()->append($this->parent) as $p) { + /** @var Page $p */ $breadcrumbs->add(new Breadcrumb($this->config->urls->admin . "page/list/?open=" . $p->id, $p->get("title|name"))); } $this->wire('breadcrumbs', $breadcrumbs); } /** + * Get the Page that is being edited + * * @return Page|null * */ public function getPage() { return $this->page ? $this->page : $this->wire('pages')->newNullPage(); } - + + /** + * Set the WirePageEditor that is calling this Process + * + * @param WirePageEditor $editor + * + */ public function setEditor(WirePageEditor $editor) { $this->editor = $editor; } @@ -1079,7 +1255,7 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit $bookmarks = $this->getPageBookmarks(); $form = $bookmarks->editBookmarksForm(); - $roleID = $this->wire('input')->get('role'); + $roleID = $this->wire('input')->get('role'); // no integer sanitization is intentional if(!is_null($roleID) && $roleID == 0 && $this->wire('user')->isSuperuser()) { $f = $this->getShortcutSortField(); @@ -1094,13 +1270,20 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit return $form->render(); } - + + /** + * Get Inputfield that lets you define shorcuts and sort order + * + * @return InputfieldAsmSelect + * + */ public function getShortcutSortField() { $this->wire('session')->remove($this, 'nav'); $data = $this->executeNavJSON(array('getArray' => true)); $name = 'shortcutSort'; - + + /** @var InputfieldAsmSelect $f */ $f = $this->wire('modules')->get('InputfieldAsmSelect'); $f->label = $this->_('Template shortcut sort order'); $f->description = $this->_('To change the order of the "Add New" page-template shortcuts, drag and drop the options to the order you want them in.'); @@ -1109,7 +1292,6 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit $f->icon = 'sort'; $f->setAsmSelectOption('removeLabel', ''); - $out = ''; $value = array(); foreach($data['list'] as $item) { @@ -1131,7 +1313,14 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit return $f; } - + + /** + * Get module configuration inputs + * + * @param array $data + * @return InputfieldWrapper + * + */ public function getModuleConfigInputfields(array $data) { $form = $this->wire(new InputfieldWrapper()); diff --git a/wire/modules/Process/ProcessTemplate/ProcessTemplate.module b/wire/modules/Process/ProcessTemplate/ProcessTemplate.module index 627b92d7..98e10bb4 100644 --- a/wire/modules/Process/ProcessTemplate/ProcessTemplate.module +++ b/wire/modules/Process/ProcessTemplate/ProcessTemplate.module @@ -1340,13 +1340,15 @@ class ProcessTemplate extends Process { $fieldset->icon = 'toggle-on'; $fieldset->description = $this->_('You should generally leave these toggles unchecked unless you have a specific need covered here.'); $form->add($fieldset); + + $label0 = $this->_('specify 0 to disable'); /** @var InputfieldCheckbox $field */ $field = $this->modules->get('InputfieldCheckbox'); $field->attr('name', 'noChangeTemplate'); $field->label = $this->_("Don't allow pages to change their template?"); $field->description = $this->_("When checked, pages using this template will be unable to change to another template."); // noChangeTemplate option, description - if($this->wire('config')->advanced) $field->notes = $this->_('API: $template->noChangeTemplate = 1; // or 0 to disable'); // noChangeTemplate option, API notes + if($this->wire('config')->advanced) $field->notes = 'API: $template->noChangeTemplate = 1; // ' . $label0; $field->attr('value', 1); if($template->noChangeTemplate) { @@ -1363,7 +1365,7 @@ class ProcessTemplate extends Process { $field->attr('name', 'noUnpublish'); $field->label = $this->_("Don't allow unpublished pages"); $field->description = $this->_("When checked, pages using this template may only exist in a published state and may not be unpublished."); // noUnpublish option, description - if($this->wire('config')->advanced) $field->notes = $this->_('API: $template->noUnpublish = 1; // or 0 to disable'); // noUnpublish option, API notes + if($this->wire('config')->advanced) $field->notes = 'API: $template->noUnpublish = 1; // ' . $label0; $field->attr('value', 1); if($template->noUnpublish) { @@ -1380,7 +1382,7 @@ class ProcessTemplate extends Process { $field->attr('name', 'allowChangeUser'); $field->label = $this->_("Allow the 'created user' to be changed on pages?"); $field->description = $this->_("When checked, pages using this template will have an option to change the 'created by user' (for superusers only). It will also enable the \$page->createdUser or \$page->created_users_id fields to be saved via the API."); // allowChangeUser option, description - if($this->wire('config')->advanced) $field->notes = $this->_('API: $template->allowChangeUser = 1; // or 0 to disable. default is 0.'); // allowChangeUser option, API notes + if($this->wire('config')->advanced) $field->notes = 'API: $template->allowChangeUser = 1; // ' . $label0; $field->attr('value', 1); if($template->allowChangeUser) { @@ -1397,7 +1399,7 @@ class ProcessTemplate extends Process { $field->attr('name', 'noMove'); $field->label = $this->_("Don't allow pages to be moved?"); $field->description = $this->_("If you want to prevent pages using this template from being moved (changing parent) then check this box."); // noMove option, description - if($this->wire('config')->advanced) $field->notes = $this->_('API: $template->noMove = 1; // or 0 to disable'); // noMove option, API notes + if($this->wire('config')->advanced) $field->notes = 'API: $template->noMove = 1; // ' . $label0; $field->attr('value', 1); if($template->noMove) { @@ -1415,7 +1417,7 @@ class ProcessTemplate extends Process { $field->attr('name', 'noLang'); $field->label = $this->_("Disable multi-language support for this template?"); $field->description = $this->_("When checked, pages using this template will only use the default language."); - if($this->wire('config')->advanced) $field->notes = $this->_('API: $template->noLang = 1; // default is 0.'); + if($this->wire('config')->advanced) $field->notes = 'API: $template->noLang = 1; // ' . $label0; $field->attr('value', 1); if($template->noLang) { @@ -1429,21 +1431,6 @@ class ProcessTemplate extends Process { // -------------------- - /* - $field = $this->modules->get('InputfieldText'); - $field->attr('name', 'pageNameFormat'); - $field->label = $this->_('Page Name Format'); - $field->description = sprintf($this->_('Optionally specify a format for page names used when a new page is added. This enables the "Page Add" step to be skipped when adding a new page. In order to work, the template used by parent page(s) must define this template (%s) as the only one allowed for children (see the "family" tab on the template used by the parent).'), $this->template->name); // Page name format, description - $field->notes = $this->_('You may enter any text you like here. When a new page is created and the name is not unique, an incrementing number will be appended to the end of it until it is unique.') . "\n"; // Page name format notes: general - $field->notes .= $this->_('**If you want your page names to derive their value from the title field:** enter "title" above (without the quotes).') . "\n"; // Page name format notes: title - $field->notes .= $this->_('**If you want your page names to reflect the created date/time:** enter a [date format](http://www.php.net/manual/en/function.date.php) above. Your date format must include at least one non-alphanumeric character (like a slash "/" or colon ":" for example) in order to be recognized as a date format. Example: "Y/m/d H:i:s" would result in page names like: "2014-10-30-12-39-01". As you can see, non-alphanumeric characters still present in a formatted date are converted to hyphens.') . ' '; // Page name format notes: date - $field->attr('value', $template->pageNameFormat); - $field->collapsed = Inputfield::collapsedBlank; - $form->append($field); - */ - - // -------------------- - return $form; }