diff --git a/wire/core/ProcessWire.php b/wire/core/ProcessWire.php index 757e0485..075547d4 100644 --- a/wire/core/ProcessWire.php +++ b/wire/core/ProcessWire.php @@ -45,7 +45,7 @@ class ProcessWire extends Wire { * Reversion revision number * */ - const versionRevision = 43; + const versionRevision = 44; /** * Version suffix string (when applicable) diff --git a/wire/modules/Fieldtype/FieldtypePage.module b/wire/modules/Fieldtype/FieldtypePage.module index c693d955..f7513a20 100644 --- a/wire/modules/Fieldtype/FieldtypePage.module +++ b/wire/modules/Fieldtype/FieldtypePage.module @@ -133,10 +133,14 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule $template = null; $template_id = $field->get('template_id'); + $template_ids = $field->get('template_ids'); $derefAsPage = $field->get('derefAsPage'); $allowUnpub = $field->get('allowUnpub'); - - if($template_id) $template = $this->wire('templates')->get($template_id); + + if(empty($template_ids) || count($template_ids) <= 1) { + // we only use $template optimization if only one template selected + if($template_id) $template = $this->wire('templates')->get($template_id); + } // handle $value if it's blank, Page, or PageArray if($derefAsPage > 0) { @@ -385,7 +389,8 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule $result = false; $parent_id = $field->get('parent_id'); - $template_id = $field->get('template_id'); + $template_id = $field->get('template_ids'); + if(empty($template_id)) $template_id = $field->get('template_id'); if(Selectors::stringHasOperator($value)) { // selector string @@ -434,7 +439,7 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule if(!$result->id) $result = $this->wire('pages')->get("parent_id=$parentIDs, name=" . $this->wire('sanitizer')->selectorValue($this->wire('sanitizer')->pageNameUTF8($value))); - } else if($template_id) { + } else if(!empty($template_id)) { // set by title $templateIDs = is_array($template_id) ? implode('|', $template_id) : $template_id; $result = $this->wire('pages')->get("templates_id=$templateIDs, title=" . $this->wire('sanitizer')->selectorValue($value)); @@ -787,7 +792,8 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule $value = $this->wire('sanitizer')->selectorValue($value); $findPagesSelector = $field->get('findPagesSelector'); $parent_id = $field->get('parent_id'); - $template_id = $field->get('template_id'); + $template_id = $field->get('template_ids'); + if(empty($template_id)) $template_id = $field->get('template_id'); if(in_array($subfield, $this->nativeNames)) { // fine then, we can handle that here when needed (like !=) @@ -902,7 +908,8 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule $subfields = array(); $fieldgroups = array(); - $template_id = $field->get('template_id'); + $template_id = $field->get('template_ids'); + if(empty($template_id)) $template_id = $field->get('template_id'); $parent_id = $field->get('parent_id'); if($template_id) { @@ -1028,18 +1035,19 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule // convert parent ID to parent path $data['parent_id'] = $this->wire('pages')->get((int) $data['parent_id'])->path; } - if(!empty($data['template_id'])) { - if(is_array($data['template_id'])) { + foreach(array('template_id', 'template_ids') as $key) { + if(empty($data[$key])) continue; + if(is_array($data[$key])) { // convert array of template ids to template names - foreach($data['template_id'] as $key => $id) { + foreach($data[$key] as $k => $id) { if(ctype_digit("$id")) continue; $template = $this->wire('templates')->get((int) $id); - if($template) $data['template_id'][$key] = $template->name; + if($template) $data[$key][$k] = $template->name; } - } else if(ctype_digit("$data[template_id]")) { + } else if(ctype_digit((string) $data[$key])) { // convert template id to template name - $template = $this->wire('templates')->get((int) $data['template_id']); - if($template) $data['template_id'] = $template->name; + $template = $this->wire('templates')->get((int) $data[$key]); + if($template) $data[$key] = $template->name; } } return $data; @@ -1071,23 +1079,24 @@ class FieldtypePage extends FieldtypeMulti implements Module, ConfigurableModule $data['parent_id'] = $id; } // template - if(!empty($data['template_id'])) { + foreach(array('template_id', 'template_ids') as $property) { + if(empty($data[$property])) continue; // template_id can be an id or array of IDs, but we will be importing a template name or array of them $errors = array(); - $isArray = is_array($data['template_id']); - if(!$isArray) $data['template_id'] = array($data['template_id']); - foreach($data['template_id'] as $key => $name) { - if(ctype_digit("$name")) continue; + $isArray = is_array($data[$property]); + if(!$isArray) $data[$property] = array($data[$property]); + foreach($data[$property] as $key => $name) { + if(ctype_digit("$name")) continue; // we have a template name rather than id $template = $this->wire('templates')->get($this->wire('sanitizer')->name($name)); if($template) { - $data['template_id'][$key] = $template->id; + $data[$property][$key] = $template->id; } else { $errors[] = $this->_('Unable to find template') . " - $name."; } } - if(!$isArray) $data['template_id'] = reset($data['template_id']); - if(count($errors)) $data['errors']['template_id'] = implode(" \n", $errors); + if(!$isArray) $data[$property] = reset($data[$property]); + if(count($errors)) $data['errors'][$property] = implode(" \n", $errors); } return $data; } diff --git a/wire/modules/Inputfield/InputfieldPage/InputfieldPage.module b/wire/modules/Inputfield/InputfieldPage/InputfieldPage.module index 4667cb2d..1cde9f0c 100644 --- a/wire/modules/Inputfield/InputfieldPage/InputfieldPage.module +++ b/wire/modules/Inputfield/InputfieldPage/InputfieldPage.module @@ -11,6 +11,7 @@ * Can be accessed from $this or from $field: * * @property int $template_id + * @property array $template_ids * @property int $parent_id * @property string $inputfield * @property string $labelFieldName Field name to use for label (note: this will be "." if $labelFieldFormat is in use). @@ -70,6 +71,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { protected static $defaultConfig = array( 'parent_id' => 0, 'template_id' => 0, + 'template_ids' => array(), 'inputfield' => '', 'labelFieldName' => '', 'labelFieldFormat' => '', @@ -142,7 +144,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { * * @param array|string $class * @param string $property - * @return $this + * @return InputfieldPage|Inputfield * */ public function addClass($class, $property = 'class') { @@ -159,7 +161,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { * * @param array|string $key * @param array|int|string $value - * @return $this + * @return InputfieldPage|Inputfield * */ public function setAttribute($key, $value) { @@ -211,7 +213,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { * */ public static function isValidPage(Page $page, $field, Page $editPage = null) { - + if(!$field instanceof Field) $field = $page->wire('fields')->get($field); if(!$field instanceof Field) throw new WireException('isValidPage requires a valid Field or field name'); @@ -228,6 +230,8 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { $findPagesSelector = $field->get('findPagesSelector'); $parent_id = $field->get('parent_id'); $template_id = $field->get('template_id'); + $template_ids = $field->get('template_ids'); + if(!is_array($template_ids)) $template_ids = array(); if($findPagesSelector) { $selector = $findPagesSelector; @@ -268,10 +272,22 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { $valid = $reflector->implementsInterface('InputfieldPageListSelection'); } } - - if($template_id && $page->template->id != $template_id) { + + $hasRequiredTemplate = true; + if(!empty($template_ids)) { + if($template_id && !in_array($template_id, $template_ids)) { + array_unshift($template_ids, $template_id); + } + $hasRequiredTemplate = in_array($page->template->id, $template_ids); + } else if($template_id) { + $hasRequiredTemplate = $page->template->id == $template_id; + } + if(!$hasRequiredTemplate) { $valid = false; - if($editPage) $editPage->set('_isValidPage', "Page $page does not have required template $template_id"); + $requiredTemplate = empty($template_ids) ? $template_id : implode(',', $template_ids); + if($editPage) { + $editPage->set('_isValidPage', "Page $page does not have required template(s): $requiredTemplate"); + } } return $valid; @@ -295,6 +311,15 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { // ensures it accepts any config value (like those for delegate inputfields) return true; } + + public function getSetting($key) { + $value = parent::getSetting($key); + if(empty($value) && $key == 'template_id') { + $templateIDs = parent::getSetting('template_ids'); + if(!empty($templateIDs)) $value = reset($templateIDs); + } + return $value; + } /** * Return PageArray of selectable pages for this input @@ -308,6 +333,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { $lockedModes = array(Inputfield::collapsedNoLocked, Inputfield::collapsedYesLocked); $statusUnder = $this->allowUnpub ? Page::statusTrash : Page::statusUnpublished; $children = null; + $templateIDs = $this->getTemplateIDs(true); if($this->configMode) { $children = $this->wire('pages')->newPageArray(); @@ -335,15 +361,15 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { } else if($this->parent_id) { $parent = $this->wire('pages')->get($this->parent_id); if($parent) { - if($this->template_id) { - $children = $parent->children("templates_id={$this->template_id}, check_access=0, status<$statusUnder"); + if($templateIDs) { + $children = $parent->children("templates_id=$templateIDs, check_access=0, status<$statusUnder"); } else { $children = $parent->children("check_access=0, status<$statusUnder"); } } - } else if($this->template_id) { - $children = $this->pages->find("templates_id={$this->template_id}, check_access=0, status<$statusUnder"); + } else if($templateIDs) { + $children = $this->pages->find("templates_id=$templateIDs, check_access=0, status<$statusUnder"); } else { $children = $this->wire('pages')->newPageArray(); @@ -354,7 +380,26 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { } return $children; - } + } + + /** + * Return array or string of configured template IDs + * + * @param bool $getString Specify true to return a 1|2|3 style string rather than an array + * @return array|string + * + */ + public function getTemplateIDs($getString = false) { + $templateIDs = $this->getSetting('template_ids'); + $templateID = $this->getSetting('template_id'); + if(empty($templateIDs)) { + if(empty($templateID)) return $getString ? '' : array(); + $templateIDs = array($templateID); + } else { + if(!in_array($templateID, $templateIDs)) array_unshift($templateIDs, $templateID); + } + return $getString ? implode('|', $templateIDs) : $templateIDs; + } /** * Populate any variables in findPagesSelector @@ -369,6 +414,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { // if an $inputfield is passed in, then we want to retrieve dependent values directly // from the form, rather than from the $page + /** @var InputfieldWrapper $form */ if($inputfield) { // locate the $form $n = 0; @@ -480,6 +526,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { $parent_id = $this->getSetting('parent_id'); $template_id = $this->getSetting('template_id'); + $template_ids = $this->getTemplateIDs(); $findPagesCode = $this->getSetting('findPagesCode'); $findPagesSelector = $this->getSetting('findPagesSelector'); $labelFieldName = $this->getSetting('labelFieldName'); @@ -494,6 +541,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { } if($template_id) $inputfield->template_id = $template_id; + if(!empty($template_ids)) $inputfield->template_ids = $template_ids; if($findPagesSelector) { $inputfield->findPagesSelector = self::getFindPagesSelector($page, $findPagesSelector); @@ -801,7 +849,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { foreach($titles as $title) { // check if there is an existing page using this title - $selector = "include=all, templates_id={$this->template_id}, title=" . $this->wire('sanitizer')->selectorValue($title); + $selector = "include=all, templates_id=$template_id, title=" . $this->wire('sanitizer')->selectorValue($title); $existingPage = $parent->child($selector); if($existingPage->id) { // use existing page @@ -865,6 +913,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { } else if($value instanceof PageArray) { // derefAsPageArray + /** @var PageArray $value */ if(!count($value)) return true; } else { @@ -901,17 +950,37 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { $field->attr('value', (int) $this->parent_id); $field->description = $this->_('Select the parent of the pages that are selectable.'); $field->required = false; - $fieldset->append($field); + $fieldset->append($field); /** @var InputfieldSelect $field */ $field = $this->modules->get('InputfieldSelect'); - $field->setAttribute('name', 'template_id'); + $field->setAttribute('name', 'template_id'); $field->label = $this->_('Template of selectable page(s)'); - $field->attr('value', (int) $this->template_id); $field->description = $this->_('Select the template of the pages that are selectable. May be used instead of, or in addition to, the parent above. NOTE: Not compatible with PageListSelect input field types.'); // Description for Template of selectable pages - foreach($this->templates as $template) $field->addOption($template->id, $template->name); + foreach($this->templates as $template) { + $field->addOption($template->id, $template->name); + } + $field->attr('value', $this->getSetting('template_id')); $field->collapsed = Inputfield::collapsedBlank; - $fieldset->append($field); + $fieldset->append($field); + + $templateIDs = $this->getTemplateIDs(); + /** @var InputfieldAsmSelect $field */ + $field = $this->modules->get('InputfieldAsmSelect'); + $field->attr('name', 'template_ids'); + $field->label = $this->_('Need more templates? Select them here'); + $field->description = $this->_('Optionally select multiple templates to use for page selections.'); + $field->description .= ' ' . $this->_('This may not be supported by all input types.'); + foreach($this->templates as $template) { + $field->addOption($template->id, $template->name); + } + $field->attr('value', $templateIDs); + $field->collapsed = Inputfield::collapsedBlank; + $field->showIf = 'template_id!=0'; + if(count($templateIDs) == 1 && reset($templateIDs) == $this->getSetting('template_id')) { + $field->collapsed = Inputfield::collapsedYes; + } + $fieldset->append($field); /** @var InputfieldText $field */ $field = $this->modules->get('InputfieldText'); diff --git a/wire/modules/Inputfield/InputfieldPageAutocomplete/InputfieldPageAutocomplete.module b/wire/modules/Inputfield/InputfieldPageAutocomplete/InputfieldPageAutocomplete.module index 672fa83f..d9fb7fa5 100644 --- a/wire/modules/Inputfield/InputfieldPageAutocomplete/InputfieldPageAutocomplete.module +++ b/wire/modules/Inputfield/InputfieldPageAutocomplete/InputfieldPageAutocomplete.module @@ -10,6 +10,7 @@ * * @property int $parent_id Limit results to this parent, or if combined with findPagesSelector, the search is performed as $pages->get($parent_id)->find() rather than $pages->find(). * @property int $template_id Limit results to pages using this template. + * @property array $template_ids Limit results to pages using this templates (alternate to the single template_id). * @property string $labelFieldName Field to display in the results. (default=title) * @property string $labelFieldFormat Format string to display in the results, overrides labelFieldName when used (default=blank). * @property string $searchFields Field(s) to search for text. Separate multiple by a space. (default=title) @@ -48,7 +49,8 @@ class InputfieldPageAutocomplete extends Inputfield implements InputfieldHasArra $this->set('parent_id', 0); // limit results to pages using this template - $this->set('template_id', 0); + $this->set('template_id', 0); + $this->set('template_ids', array()); // field to display in the results $this->set('labelFieldName', 'title'); @@ -259,7 +261,11 @@ _OUT; else $value = array(); foreach($value as $k => $v) { - $value[$k] = (int) $v; + if(empty($v)) { + unset($value[$k]); + } else { + $value[$k] = (int) $v; + } } $this->attr('value', $value); @@ -287,7 +293,9 @@ _OUT; } } - if($this->template_id) { + if(count($this->template_ids)) { + $selector .= ",templates_id=" . implode('|', $this->template_ids); + } else if($this->template_id) { $selector .= ",templates_id={$this->template_id}"; }