diff --git a/wire/core/Inputfield.php b/wire/core/Inputfield.php index 351b7e47..600172e2 100644 --- a/wire/core/Inputfield.php +++ b/wire/core/Inputfield.php @@ -3,7 +3,7 @@ /** * ProcessWire Inputfield - base class for Inputfield modules. * - * ProcessWire 3.x, Copyright 2016 by Ryan Cramer + * ProcessWire 3.x, Copyright 2021 by Ryan Cramer * https://processwire.com * * An Inputfield for an actual form input field widget, and this is provided as the base class @@ -101,6 +101,7 @@ * @property null|bool|Fieldtype $hasFieldtype The Fieldtype using this Inputfield, or boolean false when known not to have a Fieldtype, or null when not known. #pw-group-other * @property null|Field $hasField The Field object associated with this Inputfield, or null when not applicable or not known. #pw-group-other * @property null|Page $hasPage The Page object associated with this Inputfield, or null when not applicable or not known. #pw-group-other + * @property null|Inputfield $hasInputfield If this Inputfield is owned/managed by another (other than parent/child relationship), it may be set here. 3.0.176+ #pw-group-other * @property bool|null $useLanguages When multi-language support active, can be set to true to make it provide inputs for each language, where supported (default=false). #pw-group-behavior * @property null|bool|int $entityEncodeLabel Set to boolean false to specifically disable entity encoding of field header/label (default=true). #pw-group-output * @property null|bool $entityEncodeText Set to boolean false to specifically disable entity encoding for other text: description, notes, etc. (default=true). #pw-group-output diff --git a/wire/modules/Inputfield/InputfieldPage/InputfieldPage.module b/wire/modules/Inputfield/InputfieldPage/InputfieldPage.module index a6d29018..2d8c4c79 100644 --- a/wire/modules/Inputfield/InputfieldPage/InputfieldPage.module +++ b/wire/modules/Inputfield/InputfieldPage/InputfieldPage.module @@ -509,6 +509,9 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { $inputfield = $this->wire()->modules->get($this->getInputfieldClass()); if(!$inputfield) return null; + $inputfield->set('hasField', $this->hasField); + $inputfield->set('hasInputfield', $this); + $page = $this->page; $input = $this->wire()->input; $process = $this->wire()->process; @@ -721,7 +724,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { if(!$inputfield) return ''; $key = "_{$this->name}_add_items"; - if($inputfield instanceof InputfieldHasArrayValue) { + if($inputfield instanceof InputfieldHasArrayValue || $inputfield instanceof InputfieldSupportsArrayValue) { // multi value $description = $this->_('Enter the titles of the items you want to add, one per line. They will be created and added to your selection when you save the page.'); $input = ""; @@ -732,7 +735,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { } $notes = sprintf($this->_('New pages will be added to %s'), $parent->path); - $label =" " . $this->_('Create New'); + $label = wireIconMarkup('plus-circle', 'fw') . $this->_('Create New'); $out = "
" . @@ -1275,7 +1278,8 @@ class InputfieldPage extends Inputfield implements ConfigurableModule { if($inputfield) { // tell it it's under control of a parent, regardless of whether this one is hasFieldtype true or not. $info = $this->modules->getModuleInfo($inputfield); - $inputfield->hasFieldtype = true; + $inputfield->hasFieldtype = $this->hasFieldtype ? $this->hasFieldtype : true; + $inputfield->hasInputfield = $this; /** @var InputfieldFieldset $fieldset */ $fieldset = $this->modules->get('InputfieldFieldset'); $n = 0; diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.css b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.css index 971c98db..e557059d 100644 --- a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.css +++ b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.css @@ -1 +1 @@ -.InputfieldTextTags label.pw-hidden{display:none}.InputfieldTextTags input.InputfieldTextTagsSelect:not(.selectized),.InputfieldTextTags input.InputfieldTextTagsInput:not(.selectized){color:#f0f3f7}.Inputfield .selectize-input{border:1px solid #b1c3d4 #cbd7e3 #cbd7e3 #cbd7e3;border-color:#b1c3d4 #cbd7e3 #cbd7e3 #cbd7e3;box-shadow:none}.Inputfield .selectize-control .selectize-input.has-items>div{background:#f0f3f7;white-space:nowrap;border:1px solid #cbd7e3;border-radius:3px}.Inputfield .selectize-control .selectize-input.has-items>div a.remove{color:#555}.Inputfield .selectize-input:not(.has-items){background:#f0f3f7} +.InputfieldTextTags label.pw-hidden{display:none}.InputfieldTextTags input.InputfieldTextTagsSelect:not(.selectized),.InputfieldTextTags input.InputfieldTextTagsInput:not(.selectized){color:#f0f3f7}.InputfieldTextTags .InputfieldPageAdd{display:none}.Inputfield .selectize-input{border:1px solid #b1c3d4 #cbd7e3 #cbd7e3 #cbd7e3;border-color:#b1c3d4 #cbd7e3 #cbd7e3 #cbd7e3;box-shadow:none}.Inputfield .selectize-control .selectize-input.has-items>div{background:#f0f3f7;white-space:nowrap;border:1px solid #cbd7e3;border-radius:3px}.Inputfield .selectize-control .selectize-input.has-items>div a.remove{color:#555}.Inputfield .selectize-input:not(.has-items){background:#f0f3f7} diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js index b32f9b5d..8789d8ce 100644 --- a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js +++ b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.js @@ -2,9 +2,6 @@ function InputfieldTextTags($parent) { if(typeof $parent === "undefined") $parent = $('.InputfieldForm'); - var $inputs = jQuery('.InputfieldTextTagsInput:not(.selectized)', $parent); - var $selects = jQuery('.InputfieldTextTagsSelect:not(.selectized)', $parent); - var defaults = { plugins: [ 'remove_button', 'drag_drop' ], delimiter: ' ', @@ -23,75 +20,114 @@ function InputfieldTextTags($parent) { } } }; - + + // initialize input where all tags are input by the user, there are no predefined selectable tags + function initInput($input) { + var o = JSON.parse($input.attr('data-opts')); + var options = defaults; + options.delimiter = o.delimiter; + options.closeAfterSelect = o.closeAfterSelect; + options.persist = false; + $input.selectize(options); + } + + // initialize select with predefined selectable tags, optionally with user-entered as well + function initSelect($select) { + var o = JSON.parse($select.attr('data-opts')); + var cfgName = typeof o.cfgName === "undefined" ? '' : o.cfgName; + var tags = cfgName.length ? ProcessWire.config[cfgName] : o.tags; + var tagsList = []; + var n = 0; + + for(var tag in tags) { + var label = tags[tag]; + tagsList[n] = { value: tag, label: label }; + n++; + } + + var options = jQuery.extend(defaults, { + allowUserTags: o.allowUserTags, + delimiter: o.delimiter, + closeAfterSelect: o.closeAfterSelect, + persist: true, + valueField: 'value', + labelField: 'label', + searchField: [ 'value', 'label' ], + options: tagsList, + createFilter: function(input) { + if(o.allowUserTags) return true; + allow = false; + for(var n = 0; n < tags.length; n++) { + if(typeof tags[input] !== "undefined") { + allow = true; + break; + } + } + return allow; + }, + render: { + item: function(item, escape) { + if(typeof item.label === "undefined" || !item.label.length) item.label = item.value; + return '
' + escape(item.label) + '
'; + }, + option: function(item, escape) { + if(typeof item.label === "undefined" || !item.label.length) item.label = item.value; + return '
' + escape(item.label) + '
'; + } + } + /* + onDropdownOpen: function($dropdown) { + $dropdown.closest('li, .InputfieldImageEdit').css('z-index', 100); + }, + onDropdownClose: function($dropdown) { + $dropdown.closest('li, .InputfieldImageEdit').css('z-index', 'auto'); + }, + */ + }); + + if(o.tagsUrl.length) { + options.load = function(query, callback) { + if(!query.length) return callback(); + var tagsUrl = o.tagsUrl.replace('{q}', encodeURIComponent(query)); + jQuery.ajax({ + url: tagsUrl, + type: 'GET', + error: function() { callback() }, + success: function(items) { + for(var n = 0; n < items.length; n++) { + var item = items[n]; + if(typeof item === "object") { + if(typeof item.label === "undefined") { + item.label = item.value; + items[n] = item; + } + } else { + items[n] = { value: item, label: item }; + } + } + callback(items) + } + }); + } + } + + $select.selectize(options); + } + + var $inputs = jQuery('.InputfieldTextTagsInput:not(.selectized)', $parent); + var $selects = jQuery('.InputfieldTextTagsSelect:not(.selectized)', $parent); + if($inputs.length) { $inputs.each(function() { $input = $(this); - var o = JSON.parse($input.attr('data-opts')); - var options = defaults; - options.delimiter = o.delimiter; - options.closeAfterSelect = o.closeAfterSelect; - options.persist = false; - $input.selectize(options); + initInput($input); }); } if($selects.length) { $selects.each(function() { var $select = $(this); - var o = JSON.parse($select.attr('data-opts')); - var cfgName = typeof o.cfgName === "undefined" ? '' : o.cfgName; - var tags = cfgName.length ? ProcessWire.config[cfgName] : o.tags; - var tagsList = []; - var n = 0; - - for(var tag in tags) { - var label = tags[tag]; - tagsList[n] = { value: tag, label: label }; - n++; - } - - var options = jQuery.extend(defaults, { - allowUserTags: o.allowUserTags, - delimiter: o.delimiter, - closeAfterSelect: o.closeAfterSelect, - persist: true, - valueField: 'value', - labelField: 'label', - searchField: [ 'value', 'label' ], - options: tagsList, - createFilter: function(input) { - if(o.allowUserTags) return true; - allow = false; - for(var n = 0; n < tags.length; n++) { - if(typeof tags[input] !== "undefined") { - allow = true; - break; - } - } - return allow; - }, - render: { - item: function(item, escape) { - if(typeof item.label === "undefined" || !item.label.length) item.label = item.value; - return '
' + escape(item.label) + '
'; - }, - option: function(item, escape) { - if(typeof item.label === "undefined" || !item.label.length) item.label = item.value; - return '
' + escape(item.label) + '
'; - } - } - /* - onDropdownOpen: function($dropdown) { - $dropdown.closest('li, .InputfieldImageEdit').css('z-index', 100); - }, - onDropdownClose: function($dropdown) { - $dropdown.closest('li, .InputfieldImageEdit').css('z-index', 'auto'); - }, - */ - }); - - $select.selectize(options); + initSelect($select); }); } } diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js index fb8d2546..e80bc6e7 100644 --- a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js +++ b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.min.js @@ -1 +1 @@ -function InputfieldTextTags($parent){if(typeof $parent==="undefined")$parent=$(".InputfieldForm");var $inputs=jQuery(".InputfieldTextTagsInput:not(.selectized)",$parent);var $selects=jQuery(".InputfieldTextTagsSelect:not(.selectized)",$parent);var defaults={plugins:["remove_button","drag_drop"],delimiter:" ",persist:true,submitOnReturn:false,openOnFocus:true,closeAfterSelect:true,copyClassesToDropdown:false,createOnBlur:true,selectOnTab:true,maxItems:null,create:function(input){return{value:input,text:input}}};if($inputs.length){$inputs.each(function(){$input=$(this);var o=JSON.parse($input.attr("data-opts"));var options=defaults;options.delimiter=o.delimiter;options.closeAfterSelect=o.closeAfterSelect;options.persist=false;$input.selectize(options)})}if($selects.length){$selects.each(function(){var $select=$(this);var o=JSON.parse($select.attr("data-opts"));var cfgName=typeof o.cfgName==="undefined"?"":o.cfgName;var tags=cfgName.length?ProcessWire.config[cfgName]:o.tags;var tagsList=[];var n=0;for(var tag in tags){var label=tags[tag];tagsList[n]={value:tag,label:label};n++}var options=jQuery.extend(defaults,{allowUserTags:o.allowUserTags,delimiter:o.delimiter,closeAfterSelect:o.closeAfterSelect,persist:true,valueField:"value",labelField:"label",searchField:["value","label"],options:tagsList,createFilter:function(input){if(o.allowUserTags)return true;allow=false;for(var n=0;n"+escape(item.label)+"
"},option:function(item,escape){if(typeof item.label==="undefined"||!item.label.length)item.label=item.value;return"
"+escape(item.label)+"
"}}});$select.selectize(options)})}}jQuery(document).ready(function($){InputfieldTextTags();$(document).on("reloaded",".InputfieldTextTags",function(){InputfieldTextTags($(this))})}); \ No newline at end of file +function InputfieldTextTags($parent){if(typeof $parent==="undefined")$parent=$(".InputfieldForm");var defaults={plugins:["remove_button","drag_drop"],delimiter:" ",persist:true,submitOnReturn:false,openOnFocus:true,closeAfterSelect:true,copyClassesToDropdown:false,createOnBlur:true,selectOnTab:true,maxItems:null,create:function(input){return{value:input,text:input}}};function initInput($input){var o=JSON.parse($input.attr("data-opts"));var options=defaults;options.delimiter=o.delimiter;options.closeAfterSelect=o.closeAfterSelect;options.persist=false;$input.selectize(options)}function initSelect($select){var o=JSON.parse($select.attr("data-opts"));var cfgName=typeof o.cfgName==="undefined"?"":o.cfgName;var tags=cfgName.length?ProcessWire.config[cfgName]:o.tags;var tagsList=[];var n=0;for(var tag in tags){var label=tags[tag];tagsList[n]={value:tag,label:label};n++}var options=jQuery.extend(defaults,{allowUserTags:o.allowUserTags,delimiter:o.delimiter,closeAfterSelect:o.closeAfterSelect,persist:true,valueField:"value",labelField:"label",searchField:["value","label"],options:tagsList,createFilter:function(input){if(o.allowUserTags)return true;allow=false;for(var n=0;n"+escape(item.label)+""},option:function(item,escape){if(typeof item.label==="undefined"||!item.label.length)item.label=item.value;return"
"+escape(item.label)+"
"}}});if(o.tagsUrl.length){options.load=function(query,callback){if(!query.length)return callback();var tagsUrl=o.tagsUrl.replace("{q}",encodeURIComponent(query));jQuery.ajax({url:tagsUrl,type:"GET",error:function(){callback()},success:function(items){for(var n=0;n 'label' ], or newline separated string of "tag=label", or use addTag() to populate. + * @property string $tagsUrl Remote URL to find tags from, must have a '{q}' in it somewhere, which will be replaced with the query. * @property int|bool $allowUserTags Allow user-entered tags? * @property int|bool $closeAfterSelect Close select dropdown box after user makes selection? * @property string $delimiter One of 's' (for space ' '), 'p' (for pipe '|') or 'c' (for comma). @@ -39,10 +40,18 @@ class InputfieldTextTags extends Inputfield return array( 'title' => __('Text Tags', __FILE__), // Module Title 'summary' => __('Enables input of user entered tags or selection of predefined tags.', __FILE__), // Module Summary - 'version' => 1, + 'version' => 2, 'icon' => 'tags', ); } + + /** + * Tags set in 'value' attribute that were not in predefined list + * + * @var array + * + */ + protected $addedTags = array(); /** * Construct @@ -52,6 +61,7 @@ class InputfieldTextTags extends Inputfield */ public function __construct() { $this->set('tagsList', array()); + $this->set('tagsUrl', ''); $this->set('allowUserTags', 0); $this->set('closeAfterSelect', 1); $this->set('delimiter', 's'); @@ -103,6 +113,8 @@ class InputfieldTextTags extends Inputfield if($key === 'tagsList') return $this->setTagsList($value); list(,$languageId) = explode('tagsList', $key, 2); return $this->setTagsList($value, (int) $languageId); + } else if($key === 'allowUserTags' || $key === 'closeAfterSelect') { + $value = (int) $value; } return parent::set($key, $value); } @@ -401,7 +413,7 @@ class InputfieldTextTags extends Inputfield if(!$language && $this->wire()->langauges) $language = $this->wire()->user->language; $tags = $this->getTagsList($language); if(isset($tags[$tag])) return $tags[$tag]; - if($this->allowUserTags) return $tag; + if($this->allowUserTags()) return $tag; return ''; } @@ -480,7 +492,7 @@ class InputfieldTextTags extends Inputfield $language = $this->wire()->user->language; $tags = $this->getTagsList($language && $language->id ? $language : null); $classes = array(); - $classes[] = count($tags) ? 'InputfieldTextTagsSelect' : 'InputfieldTextTagsInput'; + $classes[] = count($tags) || $this->tagsUrl ? 'InputfieldTextTagsSelect' : 'InputfieldTextTagsInput'; if($this->allowUserTags) { $value = $this->tagStringToArray($this->val()); @@ -503,9 +515,10 @@ class InputfieldTextTags extends Inputfield $class = $this->className(); $opts = array( - 'allowUserTags' => $this->allowUserTags, + 'allowUserTags' => $this->allowUserTags(), 'closeAfterSelect' => $this->closeAfterSelect, 'delimiter' => $this->delimiter(), + 'tagsUrl' => $this->tagsUrl, ); if($this->hasField) { @@ -519,6 +532,7 @@ class InputfieldTextTags extends Inputfield // other usages $opts['tags'] = $tags; } + $attrs['data-opts'] = json_encode($opts, JSON_UNESCAPED_UNICODE); $attrs['class'] = trim(implode(' ', $classes)); $attrs['value'] = $this->encodeNumericTags($this->val()); @@ -556,6 +570,10 @@ class InputfieldTextTags extends Inputfield $val = $this->val(); $value = $this->validateValue($val); if($val !== $value) $this->val($value); + if($this->isPageField() && count($this->addedTags)) { + $input['_' . $this->attr('name') . '_add_items'] = implode("\n", $this->addedTags); + $this->addedTags = array(); + } return $this; } @@ -587,19 +605,32 @@ class InputfieldTextTags extends Inputfield * */ protected function validateValue($tags) { - if(!is_array($tags)) { - $tags = $this->tagStringToArray($tags); - } - if(!$this->allowUserTags) { - $validTags = $this->getTagsList(); - foreach(array_keys($tags) as $tag) { - if(!isset($validTags[$tag])) { + + $sanitizer = $this->wire()->sanitizer; + + if(!is_array($tags)) $tags = $this->tagStringToArray($tags); + + $allowUserTags = $this->allowUserTags(); + $isPageField = $this->isPageField(); + $validTags = $this->getTagsList(); + $delimiter = $this->delimiter(); + + foreach(array_keys($tags) as $tag) { + if(!isset($validTags[$tag])) { + if(!$allowUserTags && ($isPageField || !$this->tagsUrl)) { unset($tags[$tag]); $this->error(sprintf($this->_('Removed invalid tag value: %s'), $tag)); + } else { + $tag = $sanitizer->text($tag); + $label = $tag; + if(strpos($tag, $delimiter)) $tag = str_replace($delimiter, '-', $tag); + $this->addedTags[$tag] = $label; + if($isPageField) unset($tags[$tag]); // handled by processInput() } } } - return trim(implode($this->delimiter(), $tags)); + + return trim(implode($delimiter, $tags)); } /** @@ -701,19 +732,59 @@ class InputfieldTextTags extends Inputfield /** * Get or set delimiter * - * @param bool $getName + * @param bool $getName Get delimiter name rather than character? * @return string * */ protected function delimiter($getName = false) { $ds = array('s' => ' ', 'c' => ',', 'p' => '|'); - $d = $this->delimiter; + // $d = $this->delimiter; + $d = $this->isPageField() ? 'p' : $this->delimiter; if($getName) return $d; if(isset($ds[$d])) return $ds[$d]; if(in_array($d, $ds)) return $d; return ' '; } + /** + * Are we collecting input for a Page field? + * + * @return bool|Field|Inputfield + * + */ + protected function isPageField() { + if($this->hasField && $this->hasFieldtype instanceof FieldtypePage) { + return $this->hasField; + } else if($this->hasInputfield && $this->hasInputfield instanceof InputfieldPage) { + return $this->hasInputfield; + } else { + return false; + } + } + + /** + * Are we collecting input for a text field? + * + * @return bool + * + */ + protected function isTextField() { + $fieldtype = $this->hasFieldtype; + return (!$fieldtype || "$fieldtype" === 'FieldtypeText'); + } + + /** + * Allow user-entered tags? (considering Page field 'addable' context when applicable) + * + * @return bool + * + */ + protected function allowUserTags() { + $pageField = $this->isPageField(); + if($pageField) return (bool) $pageField->get('addable'); + return (bool) $this->allowUserTags; + } + /** * Config * @@ -723,12 +794,13 @@ class InputfieldTextTags extends Inputfield * */ public function ___getConfigInputfields() { - + $moduleInfo = self::getModuleInfo(); $modules = $this->wire()->modules; - $inputfields = $this->hasFieldtype ? new InputfieldWrapper() : parent::___getConfigInputfields(); + $inputfields = parent::___getConfigInputfields(); $languages = $this->wire()->languages; - + $isTextField = $this->isTextField(); + /** @var InputfieldFieldset $fieldset */ $fieldset = $modules->get('InputfieldFieldset'); $fieldset->attr('name', '_tags_settings'); @@ -736,54 +808,97 @@ class InputfieldTextTags extends Inputfield $fieldset->icon = 'tags'; $inputfields->prepend($fieldset); - if($this->hasFieldtype && "$this->hasFieldtype" !== 'FieldtypeText') { - $fieldset->description = $this->_('There are currently no configurable settings.'); - return $inputfields; + if($isTextField) { + /** @var InputfieldTextarea $f */ + $f = $modules->get('InputfieldTextarea'); + $f->attr('name', 'tagsList'); + $f->label = $this->label = $this->_('Predefined tags list'); + $f->description = $this->_('Enter predefined tags, 1 per line. To define separate tag and label, specify `tag=label` on the line.'); + $f->notes = $this->_('Tags may not contain the delimiter selected below but labels can.'); + $f->val($this->tagsListArrayToString($this->tagsList)); + if($languages) { + $f->description .= ' ' . $this->_('To define separate labels per-language, re-enter each tag (with label) for each language.'); + $f->useLanguages = true; + foreach($languages as $language) { + if(!$language->isDefault()) $f->set("value$language", $this->tagsListArrayToString($this->get("tagsList$language"))); + } + } + $fieldset->add($f); + + } else { + /** @var InputfieldHidden $f */ + $f = $modules->get('InputfieldHidden'); + $f->attr('name', 'tagsList'); + $f->val(''); + $fieldset->add($f); + } + + if($isTextField) { + /** @var InputfieldText $f */ + $exampleUrl = $this->wire()->config->urls->httpRoot . 'find-tags/?q={q}'; + $f = $modules->get('InputfieldText'); + $f->attr('name', 'tagsUrl'); + $f->label = $this->_('Predefined tags URL'); + $f->description = + $this->_('When you enter a URL, it will be queried for tags matching user input in auto-complete fashion.') . ' ' . + $this->_('Use this instead of the “predefined tags list” above when the quantity of selectable tags is larger than is practical to list individually.') . ' ' . + $this->_('The given URL must contain the placeholder `{q}` in it somewhere, which will be replaced with the text the user types.') . ' ' . + $this->_('You will also have to define a URL handler like in the example shown below.'); + $f->appendMarkup = + "

" . sprintf($this->_('URL handler example in %s for URL: %s'), '/site/init.php', "$exampleUrl") . '

' . + "
" .
+				'$wire->addHook("/find-tags/", function($e) { ' .
+				"\n  " . '$q = $e->input->get("q", "text,selectorValue");' .
+				"\n  " . 'if(strlen($q) < 3) return [];' .
+				"\n  " . 'return $e->pages->find("parent=/categories/, title%=$q")->explode("title");' .
+				"\n});" .
+				"
" . + "

" . $this->_('This example finds titles from pages to use as tags, but you may use whatever data source you want.') . "

"; + $f->val($this->tagsUrl); + $f->collapsed = Inputfield::collapsedBlank; + $fieldset->add($f); + } else { + /** @var InputfieldHidden $f */ + $f = $modules->get('InputfieldHidden'); + $f->attr('name', 'tagsUrl'); + $f->val(''); + $fieldset->add($f); } - /** @var InputfieldTextarea $f */ - $f = $modules->get('InputfieldTextarea'); - $f->attr('name', 'tagsList'); - $f->label = $this->label = $this->_('Predefined tags'); - $f->description = $this->_('Enter predefined tags, 1 per line. To define separate tag and label, specify `tag=label` on the line.'); - $f->notes = $this->_('Tags may not contain the delimiter selected below but labels can.'); - $f->val($this->tagsListArrayToString($this->tagsList)); - if($languages) { - $f->description .= ' ' . $this->_('To define separate labels per-language, re-enter each tag (with label) for each language.'); - $f->useLanguages = true; - foreach($languages as $language) { - if(!$language->isDefault()) $f->set("value$language", $this->tagsListArrayToString($this->get("tagsList$language"))); - } + if($isTextField) { + /** @var InputfieldToggle $f */ + $f = $modules->get('InputfieldToggle'); + $f->attr('name', 'allowUserTags'); + $f->label = $this->_('Allow user to enter their own tags?'); + $f->val($this->allowUserTags); + $fieldset->add($f); + } else { + /** @var InputfieldHidden $f */ + $f = $modules->get('InputfieldHidden'); + $f->attr('name', 'allowUserTags'); + $f->val('0'); + $fieldset->add($f); } - $fieldset->add($f); - - /** @var InputfieldToggle $f */ - $f = $modules->get('InputfieldToggle'); - $f->attr('name', 'allowUserTags'); - $f->label = $this->_('Allow user to enter their own tags?'); - $f->val($this->allowUserTags); - $f->columnWidth = 33; - $fieldset->add($f); - + /** @var InputfieldToggle $f */ $f = $modules->get('InputfieldToggle'); $f->attr('name', 'closeAfterSelect'); - $f->label = $this->_('Close dropdown after each selection is made'); - $f->columnWidth = 34; + $f->label = $this->_('Close dropdown after each selection is made?'); $f->val($this->closeAfterSelect); $fieldset->add($f); - /** @var InputfieldRadios $f */ - $f = $modules->get('InputfieldRadios'); - $f->attr('name', 'delimiter'); - $f->label = $this->_('Tag delimiter'); - $f->addOption('s', $this->_('Space')); - $f->addOption('c', $this->_('Comma')); - $f->addOption('p', $this->_('Pipe')); - $f->optionColumns = 1; - $f->columnWidth = 33; - $f->val($this->delimiter(true)); - $fieldset->add($f); + if($isTextField) { + /** @var InputfieldRadios $f */ + $f = $modules->get('InputfieldRadios'); + $f->attr('name', 'delimiter'); + $f->label = $this->_('Tag delimiter'); + $f->addOption('s', $this->_('Space')); + $f->addOption('c', $this->_('Comma')); + $f->addOption('p', $this->_('Pipe')); + $f->optionColumns = 1; + $f->val($this->delimiter(true)); + $fieldset->add($f); + } return $inputfields; } diff --git a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.scss b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.scss index 4204ad65..7b401537 100644 --- a/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.scss +++ b/wire/modules/Inputfield/InputfieldTextTags/InputfieldTextTags.scss @@ -10,6 +10,9 @@ $tag-border-colors: #b1c3d4 #cbd7e3 #cbd7e3 #cbd7e3; input.InputfieldTextTagsInput:not(.selectized) { color: $tag-background-color; } + .InputfieldPageAdd { + display: none; // handled internally + } } .Inputfield {