mirror of
https://github.com/processwire/processwire.git
synced 2025-08-12 17:54:44 +02:00
Additional updates to InputfieldTextTags
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* ProcessWire Inputfield - base class for Inputfield modules.
|
* 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
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
* An Inputfield for an actual form input field widget, and this is provided as the base class
|
* 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|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|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|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 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|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
|
* @property null|bool $entityEncodeText Set to boolean false to specifically disable entity encoding for other text: description, notes, etc. (default=true). #pw-group-output
|
||||||
|
@@ -509,6 +509,9 @@ class InputfieldPage extends Inputfield implements ConfigurableModule {
|
|||||||
$inputfield = $this->wire()->modules->get($this->getInputfieldClass());
|
$inputfield = $this->wire()->modules->get($this->getInputfieldClass());
|
||||||
if(!$inputfield) return null;
|
if(!$inputfield) return null;
|
||||||
|
|
||||||
|
$inputfield->set('hasField', $this->hasField);
|
||||||
|
$inputfield->set('hasInputfield', $this);
|
||||||
|
|
||||||
$page = $this->page;
|
$page = $this->page;
|
||||||
$input = $this->wire()->input;
|
$input = $this->wire()->input;
|
||||||
$process = $this->wire()->process;
|
$process = $this->wire()->process;
|
||||||
@@ -721,7 +724,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule {
|
|||||||
if(!$inputfield) return '';
|
if(!$inputfield) return '';
|
||||||
$key = "_{$this->name}_add_items";
|
$key = "_{$this->name}_add_items";
|
||||||
|
|
||||||
if($inputfield instanceof InputfieldHasArrayValue) {
|
if($inputfield instanceof InputfieldHasArrayValue || $inputfield instanceof InputfieldSupportsArrayValue) {
|
||||||
// multi value
|
// 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.');
|
$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 = "<textarea id='$key' name='$key' rows='5'></textarea>";
|
$input = "<textarea id='$key' name='$key' rows='5'></textarea>";
|
||||||
@@ -732,7 +735,7 @@ class InputfieldPage extends Inputfield implements ConfigurableModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$notes = sprintf($this->_('New pages will be added to %s'), $parent->path);
|
$notes = sprintf($this->_('New pages will be added to %s'), $parent->path);
|
||||||
$label ="<i class='fa fa-plus-circle'></i> " . $this->_('Create New');
|
$label = wireIconMarkup('plus-circle', 'fw') . $this->_('Create New');
|
||||||
|
|
||||||
$out =
|
$out =
|
||||||
"<div class='InputfieldPageAdd'>" .
|
"<div class='InputfieldPageAdd'>" .
|
||||||
@@ -1275,7 +1278,8 @@ class InputfieldPage extends Inputfield implements ConfigurableModule {
|
|||||||
if($inputfield) {
|
if($inputfield) {
|
||||||
// tell it it's under control of a parent, regardless of whether this one is hasFieldtype true or not.
|
// tell it it's under control of a parent, regardless of whether this one is hasFieldtype true or not.
|
||||||
$info = $this->modules->getModuleInfo($inputfield);
|
$info = $this->modules->getModuleInfo($inputfield);
|
||||||
$inputfield->hasFieldtype = true;
|
$inputfield->hasFieldtype = $this->hasFieldtype ? $this->hasFieldtype : true;
|
||||||
|
$inputfield->hasInputfield = $this;
|
||||||
/** @var InputfieldFieldset $fieldset */
|
/** @var InputfieldFieldset $fieldset */
|
||||||
$fieldset = $this->modules->get('InputfieldFieldset');
|
$fieldset = $this->modules->get('InputfieldFieldset');
|
||||||
$n = 0;
|
$n = 0;
|
||||||
|
@@ -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}
|
||||||
|
@@ -2,9 +2,6 @@ function InputfieldTextTags($parent) {
|
|||||||
|
|
||||||
if(typeof $parent === "undefined") $parent = $('.InputfieldForm');
|
if(typeof $parent === "undefined") $parent = $('.InputfieldForm');
|
||||||
|
|
||||||
var $inputs = jQuery('.InputfieldTextTagsInput:not(.selectized)', $parent);
|
|
||||||
var $selects = jQuery('.InputfieldTextTagsSelect:not(.selectized)', $parent);
|
|
||||||
|
|
||||||
var defaults = {
|
var defaults = {
|
||||||
plugins: [ 'remove_button', 'drag_drop' ],
|
plugins: [ 'remove_button', 'drag_drop' ],
|
||||||
delimiter: ' ',
|
delimiter: ' ',
|
||||||
@@ -24,21 +21,18 @@ function InputfieldTextTags($parent) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if($inputs.length) {
|
// initialize input where all tags are input by the user, there are no predefined selectable tags
|
||||||
$inputs.each(function() {
|
function initInput($input) {
|
||||||
$input = $(this);
|
|
||||||
var o = JSON.parse($input.attr('data-opts'));
|
var o = JSON.parse($input.attr('data-opts'));
|
||||||
var options = defaults;
|
var options = defaults;
|
||||||
options.delimiter = o.delimiter;
|
options.delimiter = o.delimiter;
|
||||||
options.closeAfterSelect = o.closeAfterSelect;
|
options.closeAfterSelect = o.closeAfterSelect;
|
||||||
options.persist = false;
|
options.persist = false;
|
||||||
$input.selectize(options);
|
$input.selectize(options);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if($selects.length) {
|
// initialize select with predefined selectable tags, optionally with user-entered as well
|
||||||
$selects.each(function() {
|
function initSelect($select) {
|
||||||
var $select = $(this);
|
|
||||||
var o = JSON.parse($select.attr('data-opts'));
|
var o = JSON.parse($select.attr('data-opts'));
|
||||||
var cfgName = typeof o.cfgName === "undefined" ? '' : o.cfgName;
|
var cfgName = typeof o.cfgName === "undefined" ? '' : o.cfgName;
|
||||||
var tags = cfgName.length ? ProcessWire.config[cfgName] : o.tags;
|
var tags = cfgName.length ? ProcessWire.config[cfgName] : o.tags;
|
||||||
@@ -91,7 +85,49 @@ function InputfieldTextTags($parent) {
|
|||||||
*/
|
*/
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
$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);
|
||||||
|
initInput($input);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if($selects.length) {
|
||||||
|
$selects.each(function() {
|
||||||
|
var $select = $(this);
|
||||||
|
initSelect($select);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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<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"<div>"+escape(item.label)+"</div>"},option:function(item,escape){if(typeof item.label==="undefined"||!item.label.length)item.label=item.value;return"<div>"+escape(item.label)+"</div>"}}});$select.selectize(options)})}}jQuery(document).ready(function($){InputfieldTextTags();$(document).on("reloaded",".InputfieldTextTags",function(){InputfieldTextTags($(this))})});
|
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<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"<div>"+escape(item.label)+"</div>"},option:function(item,escape){if(typeof item.label==="undefined"||!item.label.length)item.label=item.value;return"<div>"+escape(item.label)+"</div>"}}});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);initInput($input)})}if($selects.length){$selects.each(function(){var $select=$(this);initSelect($select)})}}jQuery(document).ready(function($){InputfieldTextTags();$(document).on("reloaded",".InputfieldTextTags",function(){InputfieldTextTags($(this))})});
|
@@ -22,6 +22,7 @@
|
|||||||
* ~~~~~
|
* ~~~~~
|
||||||
*
|
*
|
||||||
* @property array|string $tagsList Array of tags [ 'tag' => 'label' ], or newline separated string of "tag=label", or use addTag() to populate.
|
* @property array|string $tagsList Array of tags [ 'tag' => '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 $allowUserTags Allow user-entered tags?
|
||||||
* @property int|bool $closeAfterSelect Close select dropdown box after user makes selection?
|
* @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).
|
* @property string $delimiter One of 's' (for space ' '), 'p' (for pipe '|') or 'c' (for comma).
|
||||||
@@ -39,11 +40,19 @@ class InputfieldTextTags extends Inputfield
|
|||||||
return array(
|
return array(
|
||||||
'title' => __('Text Tags', __FILE__), // Module Title
|
'title' => __('Text Tags', __FILE__), // Module Title
|
||||||
'summary' => __('Enables input of user entered tags or selection of predefined tags.', __FILE__), // Module Summary
|
'summary' => __('Enables input of user entered tags or selection of predefined tags.', __FILE__), // Module Summary
|
||||||
'version' => 1,
|
'version' => 2,
|
||||||
'icon' => 'tags',
|
'icon' => 'tags',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tags set in 'value' attribute that were not in predefined list
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected $addedTags = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct
|
* Construct
|
||||||
*
|
*
|
||||||
@@ -52,6 +61,7 @@ class InputfieldTextTags extends Inputfield
|
|||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->set('tagsList', array());
|
$this->set('tagsList', array());
|
||||||
|
$this->set('tagsUrl', '');
|
||||||
$this->set('allowUserTags', 0);
|
$this->set('allowUserTags', 0);
|
||||||
$this->set('closeAfterSelect', 1);
|
$this->set('closeAfterSelect', 1);
|
||||||
$this->set('delimiter', 's');
|
$this->set('delimiter', 's');
|
||||||
@@ -103,6 +113,8 @@ class InputfieldTextTags extends Inputfield
|
|||||||
if($key === 'tagsList') return $this->setTagsList($value);
|
if($key === 'tagsList') return $this->setTagsList($value);
|
||||||
list(,$languageId) = explode('tagsList', $key, 2);
|
list(,$languageId) = explode('tagsList', $key, 2);
|
||||||
return $this->setTagsList($value, (int) $languageId);
|
return $this->setTagsList($value, (int) $languageId);
|
||||||
|
} else if($key === 'allowUserTags' || $key === 'closeAfterSelect') {
|
||||||
|
$value = (int) $value;
|
||||||
}
|
}
|
||||||
return parent::set($key, $value);
|
return parent::set($key, $value);
|
||||||
}
|
}
|
||||||
@@ -401,7 +413,7 @@ class InputfieldTextTags extends Inputfield
|
|||||||
if(!$language && $this->wire()->langauges) $language = $this->wire()->user->language;
|
if(!$language && $this->wire()->langauges) $language = $this->wire()->user->language;
|
||||||
$tags = $this->getTagsList($language);
|
$tags = $this->getTagsList($language);
|
||||||
if(isset($tags[$tag])) return $tags[$tag];
|
if(isset($tags[$tag])) return $tags[$tag];
|
||||||
if($this->allowUserTags) return $tag;
|
if($this->allowUserTags()) return $tag;
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,7 +492,7 @@ class InputfieldTextTags extends Inputfield
|
|||||||
$language = $this->wire()->user->language;
|
$language = $this->wire()->user->language;
|
||||||
$tags = $this->getTagsList($language && $language->id ? $language : null);
|
$tags = $this->getTagsList($language && $language->id ? $language : null);
|
||||||
$classes = array();
|
$classes = array();
|
||||||
$classes[] = count($tags) ? 'InputfieldTextTagsSelect' : 'InputfieldTextTagsInput';
|
$classes[] = count($tags) || $this->tagsUrl ? 'InputfieldTextTagsSelect' : 'InputfieldTextTagsInput';
|
||||||
|
|
||||||
if($this->allowUserTags) {
|
if($this->allowUserTags) {
|
||||||
$value = $this->tagStringToArray($this->val());
|
$value = $this->tagStringToArray($this->val());
|
||||||
@@ -503,9 +515,10 @@ class InputfieldTextTags extends Inputfield
|
|||||||
$class = $this->className();
|
$class = $this->className();
|
||||||
|
|
||||||
$opts = array(
|
$opts = array(
|
||||||
'allowUserTags' => $this->allowUserTags,
|
'allowUserTags' => $this->allowUserTags(),
|
||||||
'closeAfterSelect' => $this->closeAfterSelect,
|
'closeAfterSelect' => $this->closeAfterSelect,
|
||||||
'delimiter' => $this->delimiter(),
|
'delimiter' => $this->delimiter(),
|
||||||
|
'tagsUrl' => $this->tagsUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
if($this->hasField) {
|
if($this->hasField) {
|
||||||
@@ -519,6 +532,7 @@ class InputfieldTextTags extends Inputfield
|
|||||||
// other usages
|
// other usages
|
||||||
$opts['tags'] = $tags;
|
$opts['tags'] = $tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
$attrs['data-opts'] = json_encode($opts, JSON_UNESCAPED_UNICODE);
|
$attrs['data-opts'] = json_encode($opts, JSON_UNESCAPED_UNICODE);
|
||||||
$attrs['class'] = trim(implode(' ', $classes));
|
$attrs['class'] = trim(implode(' ', $classes));
|
||||||
$attrs['value'] = $this->encodeNumericTags($this->val());
|
$attrs['value'] = $this->encodeNumericTags($this->val());
|
||||||
@@ -556,6 +570,10 @@ class InputfieldTextTags extends Inputfield
|
|||||||
$val = $this->val();
|
$val = $this->val();
|
||||||
$value = $this->validateValue($val);
|
$value = $this->validateValue($val);
|
||||||
if($val !== $value) $this->val($value);
|
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;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,19 +605,32 @@ class InputfieldTextTags extends Inputfield
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected function validateValue($tags) {
|
protected function validateValue($tags) {
|
||||||
if(!is_array($tags)) {
|
|
||||||
$tags = $this->tagStringToArray($tags);
|
$sanitizer = $this->wire()->sanitizer;
|
||||||
}
|
|
||||||
if(!$this->allowUserTags) {
|
if(!is_array($tags)) $tags = $this->tagStringToArray($tags);
|
||||||
|
|
||||||
|
$allowUserTags = $this->allowUserTags();
|
||||||
|
$isPageField = $this->isPageField();
|
||||||
$validTags = $this->getTagsList();
|
$validTags = $this->getTagsList();
|
||||||
|
$delimiter = $this->delimiter();
|
||||||
|
|
||||||
foreach(array_keys($tags) as $tag) {
|
foreach(array_keys($tags) as $tag) {
|
||||||
if(!isset($validTags[$tag])) {
|
if(!isset($validTags[$tag])) {
|
||||||
|
if(!$allowUserTags && ($isPageField || !$this->tagsUrl)) {
|
||||||
unset($tags[$tag]);
|
unset($tags[$tag]);
|
||||||
$this->error(sprintf($this->_('Removed invalid tag value: %s'), $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
|
* Get or set delimiter
|
||||||
*
|
*
|
||||||
* @param bool $getName
|
* @param bool $getName Get delimiter name rather than character?
|
||||||
* @return string
|
* @return string
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected function delimiter($getName = false) {
|
protected function delimiter($getName = false) {
|
||||||
$ds = array('s' => ' ', 'c' => ',', 'p' => '|');
|
$ds = array('s' => ' ', 'c' => ',', 'p' => '|');
|
||||||
$d = $this->delimiter;
|
// $d = $this->delimiter;
|
||||||
|
$d = $this->isPageField() ? 'p' : $this->delimiter;
|
||||||
if($getName) return $d;
|
if($getName) return $d;
|
||||||
if(isset($ds[$d])) return $ds[$d];
|
if(isset($ds[$d])) return $ds[$d];
|
||||||
if(in_array($d, $ds)) return $d;
|
if(in_array($d, $ds)) return $d;
|
||||||
return ' ';
|
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
|
* Config
|
||||||
*
|
*
|
||||||
@@ -726,8 +797,9 @@ class InputfieldTextTags extends Inputfield
|
|||||||
|
|
||||||
$moduleInfo = self::getModuleInfo();
|
$moduleInfo = self::getModuleInfo();
|
||||||
$modules = $this->wire()->modules;
|
$modules = $this->wire()->modules;
|
||||||
$inputfields = $this->hasFieldtype ? new InputfieldWrapper() : parent::___getConfigInputfields();
|
$inputfields = parent::___getConfigInputfields();
|
||||||
$languages = $this->wire()->languages;
|
$languages = $this->wire()->languages;
|
||||||
|
$isTextField = $this->isTextField();
|
||||||
|
|
||||||
/** @var InputfieldFieldset $fieldset */
|
/** @var InputfieldFieldset $fieldset */
|
||||||
$fieldset = $modules->get('InputfieldFieldset');
|
$fieldset = $modules->get('InputfieldFieldset');
|
||||||
@@ -736,15 +808,11 @@ class InputfieldTextTags extends Inputfield
|
|||||||
$fieldset->icon = 'tags';
|
$fieldset->icon = 'tags';
|
||||||
$inputfields->prepend($fieldset);
|
$inputfields->prepend($fieldset);
|
||||||
|
|
||||||
if($this->hasFieldtype && "$this->hasFieldtype" !== 'FieldtypeText') {
|
if($isTextField) {
|
||||||
$fieldset->description = $this->_('There are currently no configurable settings.');
|
|
||||||
return $inputfields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var InputfieldTextarea $f */
|
/** @var InputfieldTextarea $f */
|
||||||
$f = $modules->get('InputfieldTextarea');
|
$f = $modules->get('InputfieldTextarea');
|
||||||
$f->attr('name', 'tagsList');
|
$f->attr('name', 'tagsList');
|
||||||
$f->label = $this->label = $this->_('Predefined tags');
|
$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->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->notes = $this->_('Tags may not contain the delimiter selected below but labels can.');
|
||||||
$f->val($this->tagsListArrayToString($this->tagsList));
|
$f->val($this->tagsListArrayToString($this->tagsList));
|
||||||
@@ -757,22 +825,69 @@ class InputfieldTextTags extends Inputfield
|
|||||||
}
|
}
|
||||||
$fieldset->add($f);
|
$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 =
|
||||||
|
"<p class='description'>" . sprintf($this->_('URL handler example in %s for URL: %s'), '<u>/site/init.php</u>', "<u>$exampleUrl</u>") . '</p>' .
|
||||||
|
"<pre style='margin:0'><code>" .
|
||||||
|
'$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});" .
|
||||||
|
"</code></pre>" .
|
||||||
|
"<p class='description'>" . $this->_('This example finds titles from pages to use as tags, but you may use whatever data source you want.') . "</p>";
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($isTextField) {
|
||||||
/** @var InputfieldToggle $f */
|
/** @var InputfieldToggle $f */
|
||||||
$f = $modules->get('InputfieldToggle');
|
$f = $modules->get('InputfieldToggle');
|
||||||
$f->attr('name', 'allowUserTags');
|
$f->attr('name', 'allowUserTags');
|
||||||
$f->label = $this->_('Allow user to enter their own tags?');
|
$f->label = $this->_('Allow user to enter their own tags?');
|
||||||
$f->val($this->allowUserTags);
|
$f->val($this->allowUserTags);
|
||||||
$f->columnWidth = 33;
|
|
||||||
$fieldset->add($f);
|
$fieldset->add($f);
|
||||||
|
} else {
|
||||||
|
/** @var InputfieldHidden $f */
|
||||||
|
$f = $modules->get('InputfieldHidden');
|
||||||
|
$f->attr('name', 'allowUserTags');
|
||||||
|
$f->val('0');
|
||||||
|
$fieldset->add($f);
|
||||||
|
}
|
||||||
|
|
||||||
/** @var InputfieldToggle $f */
|
/** @var InputfieldToggle $f */
|
||||||
$f = $modules->get('InputfieldToggle');
|
$f = $modules->get('InputfieldToggle');
|
||||||
$f->attr('name', 'closeAfterSelect');
|
$f->attr('name', 'closeAfterSelect');
|
||||||
$f->label = $this->_('Close dropdown after each selection is made');
|
$f->label = $this->_('Close dropdown after each selection is made?');
|
||||||
$f->columnWidth = 34;
|
|
||||||
$f->val($this->closeAfterSelect);
|
$f->val($this->closeAfterSelect);
|
||||||
$fieldset->add($f);
|
$fieldset->add($f);
|
||||||
|
|
||||||
|
if($isTextField) {
|
||||||
/** @var InputfieldRadios $f */
|
/** @var InputfieldRadios $f */
|
||||||
$f = $modules->get('InputfieldRadios');
|
$f = $modules->get('InputfieldRadios');
|
||||||
$f->attr('name', 'delimiter');
|
$f->attr('name', 'delimiter');
|
||||||
@@ -781,9 +896,9 @@ class InputfieldTextTags extends Inputfield
|
|||||||
$f->addOption('c', $this->_('Comma'));
|
$f->addOption('c', $this->_('Comma'));
|
||||||
$f->addOption('p', $this->_('Pipe'));
|
$f->addOption('p', $this->_('Pipe'));
|
||||||
$f->optionColumns = 1;
|
$f->optionColumns = 1;
|
||||||
$f->columnWidth = 33;
|
|
||||||
$f->val($this->delimiter(true));
|
$f->val($this->delimiter(true));
|
||||||
$fieldset->add($f);
|
$fieldset->add($f);
|
||||||
|
}
|
||||||
|
|
||||||
return $inputfields;
|
return $inputfields;
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,9 @@ $tag-border-colors: #b1c3d4 #cbd7e3 #cbd7e3 #cbd7e3;
|
|||||||
input.InputfieldTextTagsInput:not(.selectized) {
|
input.InputfieldTextTagsInput:not(.selectized) {
|
||||||
color: $tag-background-color;
|
color: $tag-background-color;
|
||||||
}
|
}
|
||||||
|
.InputfieldPageAdd {
|
||||||
|
display: none; // handled internally
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Inputfield {
|
.Inputfield {
|
||||||
|
Reference in New Issue
Block a user