1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-18 04:22:10 +02:00

Various minor Inputfield code and phpdoc improvements

This commit is contained in:
Ryan Cramer
2022-01-05 10:08:43 -05:00
parent 49de9483ce
commit d1a6e2303d
13 changed files with 349 additions and 103 deletions

View File

@@ -3,6 +3,9 @@
/** /**
* An Inputfield for handling "button" buttons * An Inputfield for handling "button" buttons
* *
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
* @property string $href URL to link to * @property string $href URL to link to
* @property string $aclass Optional class name(s) for <a> element (if href is used). * @property string $aclass Optional class name(s) for <a> element (if href is used).
* @property string $target Link target * @property string $target Link target
@@ -17,7 +20,7 @@ class InputfieldButton extends InputfieldSubmit {
'summary' => __('Form button element that you can optionally pass an href attribute to.', __FILE__), // Module Summary 'summary' => __('Form button element that you can optionally pass an href attribute to.', __FILE__), // Module Summary
'version' => 100, 'version' => 100,
'permanent' => true, 'permanent' => true,
); );
} }
public function init() { public function init() {

View File

@@ -3,7 +3,7 @@
/** /**
* An Inputfield for handling email addresses * An Inputfield for handling email addresses
* *
* ProcessWire 3.x, Copyright 2020 by Ryan Cramer * ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
* @property int $confirm Specify 1 to make it include a second input for confirmation * @property int $confirm Specify 1 to make it include a second input for confirmation

View File

@@ -1,16 +1,41 @@
<?php namespace ProcessWire; <?php namespace ProcessWire;
/**
* Fieldset Inputfield
*
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
* @property string|int $defaultValue
* @property array|string $options Get or set options, array of [value => label], or use options string.
* @property array $optionAttributes
* @property bool $valueAddOption If value attr set from API (only) that is not an option, add it as an option? (default=false) 3.0.171+
*
*/
class InputfieldFieldset extends InputfieldWrapper { class InputfieldFieldset extends InputfieldWrapper {
/**
* Get module info
*
* @return array
*
*/
public static function getModuleInfo() { public static function getModuleInfo() {
return array( return array(
'title' => __('Fieldset', __FILE__), // Module Title 'title' => __('Fieldset', __FILE__), // Module Title
'summary' => __('Groups one or more fields together in a container', __FILE__), // Module Summary 'summary' => __('Groups one or more fields together in a container', __FILE__), // Module Summary
'version' => 101, 'version' => 101,
'permanent' => true, 'permanent' => true,
); );
} }
/**
* Render
*
* @return string
*
*/
public function ___render() { public function ___render() {
// Note the extra "\n" is required in order to prevent InputfieldWrapper from // Note the extra "\n" is required in order to prevent InputfieldWrapper from
// skipping over an empty fieldset. Empty fieldsets are used by InputfieldRepeater // skipping over an empty fieldset. Empty fieldsets are used by InputfieldRepeater

View File

@@ -3,7 +3,7 @@
/** /**
* Inputfield for floating point numbers * Inputfield for floating point numbers
* *
* ProcessWire 3.x, Copyright 2020 by Ryan Cramer * ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
* @property int $precision Decimals precision * @property int $precision Decimals precision

View File

@@ -3,7 +3,7 @@
/** /**
* InputfieldForm: An Inputfield for containing form elements * InputfieldForm: An Inputfield for containing form elements
* *
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer * ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
* @property string $prependMarkup Optional markup to prepend to the form output * @property string $prependMarkup Optional markup to prepend to the form output
@@ -75,7 +75,7 @@ class InputfieldForm extends InputfieldWrapper {
'summary' => __('Contains one or more fields in a form', __FILE__), // Module Summary 'summary' => __('Contains one or more fields in a form', __FILE__), // Module Summary
'version' => 107, 'version' => 107,
'permanent' => true, 'permanent' => true,
); );
} }
const debug = false; // set to true to enable debug mode for field dependencies const debug = false; // set to true to enable debug mode for field dependencies
@@ -233,6 +233,8 @@ class InputfieldForm extends InputfieldWrapper {
} }
if(self::debug) $this->debugNote("showIf selector: $selectorString"); if(self::debug) $this->debugNote("showIf selector: $selectorString");
/** @var Selectors $selectors */
$selectors = $this->wire(new Selectors($selectorString)); $selectors = $this->wire(new Selectors($selectorString));
// whether we should process $child now or not // whether we should process $child now or not
@@ -388,6 +390,7 @@ class InputfieldForm extends InputfieldWrapper {
if(strlen($selectorString)) { if(strlen($selectorString)) {
if(self::debug) $this->debugNote("requiredIf selector: $selectorString"); if(self::debug) $this->debugNote("requiredIf selector: $selectorString");
/** @var Selectors $selectors */
$selectors = $this->wire(new Selectors($selectorString)); $selectors = $this->wire(new Selectors($selectorString));
foreach($selectors as $selector) { foreach($selectors as $selector) {

View File

@@ -3,6 +3,9 @@
/** /**
* An Inputfield for handling HTML “hidden” form inputs * An Inputfield for handling HTML “hidden” form inputs
* *
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer
* https://processwire.com
*
* @property bool $renderValueAsInput Render the hidden input, even when in renderValue mode (default=false) * @property bool $renderValueAsInput Render the hidden input, even when in renderValue mode (default=false)
* @property string $initValue Initial populated value if value attribute not separately populated (default='') * @property string $initValue Initial populated value if value attribute not separately populated (default='')
* *
@@ -18,6 +21,10 @@ class InputfieldHidden extends Inputfield {
); );
} }
/**
* Construct
*
*/
public function __construct() { public function __construct() {
parent::__construct(); parent::__construct();
$this->setAttribute('type', 'hidden'); $this->setAttribute('type', 'hidden');
@@ -25,10 +32,22 @@ class InputfieldHidden extends Inputfield {
$this->initValue = ''; $this->initValue = '';
} }
/**
* Render
*
* @return string
*
*/
public function ___render() { public function ___render() {
return "<input " . $this->getAttributesString() . " />"; return "<input " . $this->getAttributesString() . " />";
} }
/**
* Render value
*
* @return string
*
*/
public function ___renderValue() { public function ___renderValue() {
if($this->renderValueAsInput) { if($this->renderValueAsInput) {
return $this->render(); return $this->render();
@@ -37,12 +56,24 @@ class InputfieldHidden extends Inputfield {
} }
} }
/**
* Get attributes
*
* @return array
*
*/
public function getAttributes() { public function getAttributes() {
$attrs = parent::getAttributes(); $attrs = parent::getAttributes();
if(!strlen("$attrs[value]") && $this->initValue) $attrs['value'] = (string) $this->initValue; if(!strlen("$attrs[value]") && $this->initValue) $attrs['value'] = (string) $this->initValue;
return $attrs; return $attrs;
} }
/**
* Configure Inputfield
*
* @return InputfieldWrapper
*
*/
public function ___getConfigInputfields() { public function ___getConfigInputfields() {
$inputfields = parent::___getConfigInputfields(); $inputfields = parent::___getConfigInputfields();
@@ -54,7 +85,7 @@ class InputfieldHidden extends Inputfield {
$inputfields->remove($f); $inputfields->remove($f);
/** @var InputfieldText $field */ /** @var InputfieldText $field */
$field = $this->modules->get('InputfieldText'); $field = $this->wire()->modules->get('InputfieldText');
$field->setAttribute('name', 'initValue'); $field->setAttribute('name', 'initValue');
$field->label = $this->_('Value'); $field->label = $this->_('Value');
$field->description = $this->_('Value to be populated in this hidden field.'); $field->description = $this->_('Value to be populated in this hidden field.');

View File

@@ -3,7 +3,7 @@
/** /**
* Integer Inputfield * Integer Inputfield
* *
* ProcessWire 3.x, Copyright 2020 by Ryan Cramer * ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
* @property string $inputType Input type to use, one of "text" or "number" * @property string $inputType Input type to use, one of "text" or "number"

View File

@@ -3,6 +3,9 @@
/** /**
* Intended just for outputting markup as help or commentary among other Inputfields * Intended just for outputting markup as help or commentary among other Inputfields
* *
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer
* https://processwire.com
*
* @property callable|string|null $markupFunction * @property callable|string|null $markupFunction
* @property array $textformatters * @property array $textformatters
* @property string $markupText * @property string $markupText
@@ -11,17 +14,33 @@
class InputfieldMarkup extends InputfieldWrapper { class InputfieldMarkup extends InputfieldWrapper {
/**
* Get module info
*
* @return array
*
*/
public static function getModuleInfo() { public static function getModuleInfo() {
return array( return array(
'title' => __('Markup', __FILE__), 'title' => __('Markup', __FILE__),
'summary' => __('Contains any other markup and optionally child Inputfields', __FILE__), 'summary' => __('Contains any other markup and optionally child Inputfields', __FILE__),
'version' => 102, 'version' => 102,
'permanent' => true, 'permanent' => true,
); );
} }
/**
* Whether render() has been called from renderValue()
*
* @var bool
*
*/
protected $renderValueMode = false; protected $renderValueMode = false;
/**
* Init
*
*/
public function init() { public function init() {
$this->set('markupText', ''); $this->set('markupText', '');
$this->set('markupFunction', null); // closure or name of function that returns markup, receives $this as arg0. $this->set('markupFunction', null); // closure or name of function that returns markup, receives $this as arg0.
@@ -30,6 +49,14 @@ class InputfieldMarkup extends InputfieldWrapper {
parent::init(); parent::init();
} }
/**
* Render ready
*
* @param Inputfield|null $parent
* @param bool $renderValueMode
* @return bool
*
*/
public function renderReady(Inputfield $parent = null, $renderValueMode = false) { public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
$label = $this->getSetting('label'); $label = $this->getSetting('label');
@@ -39,8 +66,15 @@ class InputfieldMarkup extends InputfieldWrapper {
return parent::renderReady($parent, $renderValueMode); return parent::renderReady($parent, $renderValueMode);
} }
/**
* Render
*
* @return string
*
*/
public function ___render() { public function ___render() {
$modules = $this->wire()->modules;
$out = ''; $out = '';
$value = (string) $this->attr('value'); $value = (string) $this->attr('value');
@@ -63,7 +97,8 @@ class InputfieldMarkup extends InputfieldWrapper {
if(wireCount($textformatters)) { if(wireCount($textformatters)) {
foreach($textformatters as $className) { foreach($textformatters as $className) {
$t = $this->wire()->modules->get($className); /** @var Textformatter $t */
$t = $modules->get($className);
if(!$t) continue; if(!$t) continue;
$t->formatValue($this->wire()->page, $this->wire(new Field()), $out); $t->formatValue($this->wire()->page, $this->wire(new Field()), $out);
} }
@@ -74,7 +109,7 @@ class InputfieldMarkup extends InputfieldWrapper {
if($this->getSetting('entityEncodeText') !== false && $textFormat != Inputfield::textFormatNone) { if($this->getSetting('entityEncodeText') !== false && $textFormat != Inputfield::textFormatNone) {
if($textFormat == Inputfield::textFormatBasic) { if($textFormat == Inputfield::textFormatBasic) {
$description = $this->entityEncode($description, Inputfield::textFormatBasic); $description = $this->entityEncode($description, Inputfield::textFormatBasic);
$out = "<p class='description'>{$description}</p>$out"; $out = "<p class='description'>$description</p>$out";
} else if($textFormat == Inputfield::textFormatMarkdown) { } else if($textFormat == Inputfield::textFormatMarkdown) {
$out = "<div class='description'>" . $this->entityEncode($description, Inputfield::textFormatMarkdown) . "</div>$out"; $out = "<div class='description'>" . $this->entityEncode($description, Inputfield::textFormatMarkdown) . "</div>$out";
} }
@@ -93,6 +128,12 @@ class InputfieldMarkup extends InputfieldWrapper {
return $out; return $out;
} }
/**
* Render value
*
* @return string
*
*/
public function ___renderValue() { public function ___renderValue() {
$this->renderValueMode = true; $this->renderValueMode = true;
$out = $this->render(); $out = $this->render();
@@ -100,6 +141,12 @@ class InputfieldMarkup extends InputfieldWrapper {
return $out; return $out;
} }
/**
* Configure Inputfield
*
* @return InputfieldWrapper
*
*/
public function ___getConfigInputfields() { public function ___getConfigInputfields() {
$modules = $this->wire()->modules; $modules = $this->wire()->modules;
@@ -119,13 +166,13 @@ class InputfieldMarkup extends InputfieldWrapper {
$f->attr('id+name', 'textformatters'); $f->attr('id+name', 'textformatters');
$f->label = $this->_('Text Formatters'); $f->label = $this->_('Text Formatters');
foreach($modules->find("className^=Textformatter") as $textformatter) { foreach($modules->findByPrefix('Textformatter') as $moduleName) {
$info = $textformatter->getModuleInfo(); $info = $modules->getModuleInfo($moduleName);
$f->addOption($textformatter->className(), "$info[title]"); $title = $info['title'] ? $info['title'] : $moduleName;
$f->addOption($moduleName, $title);
} }
$f->attr('value', $this->textformatters); $f->attr('value', $this->textformatters);
$f->description = $this->_('Select the format that your Markup Text is in, or the formatters that you want to be applied to it, in the order you want them applied.'); $f->description = $this->_('Select the format that your Markup Text is in, or the formatters that you want to be applied to it, in the order you want them applied.');
$f->notes = $this->_('If your Markup Text is plain HTML, you may not want to select any Text Formatters.'); $f->notes = $this->_('If your Markup Text is plain HTML, you may not want to select any Text Formatters.');
$inputfields->add($f); $inputfields->add($f);

View File

@@ -3,6 +3,9 @@
/** /**
* An Inputfield for handling ProcessWire "name" fields * An Inputfield for handling ProcessWire "name" fields
* *
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer
* https://processwire.com
*
* @property string $sanitizeMethod * @property string $sanitizeMethod
* *
*/ */
@@ -14,7 +17,7 @@ class InputfieldName extends InputfieldText {
'version' => 100, 'version' => 100,
'summary' => __('Text input validated as a ProcessWire name field', __FILE__), // Module Summary 'summary' => __('Text input validated as a ProcessWire name field', __FILE__), // Module Summary
'permanent' => true, 'permanent' => true,
); );
} }
public function __construct() { public function __construct() {
@@ -37,9 +40,11 @@ class InputfieldName extends InputfieldText {
protected function setAttributeValue($value) { protected function setAttributeValue($value) {
$sanitizeMethod = $this->sanitizeMethod; $sanitizeMethod = $this->sanitizeMethod;
if($this->isWired()) { if($this->isWired()) {
$value = call_user_func(array($this->wire('sanitizer'), $sanitizeMethod), $value); $sanitizer = $this->wire()->sanitizer;
$value = call_user_func(array($sanitizer, $sanitizeMethod), $value);
} else { } else {
$value = wire('sanitizer')->$sanitizeMethod($value); $sanitizer = wire()->sanitizer;
$value = $sanitizer->$sanitizeMethod($value);
} }
return $value; return $value;
} }

View File

@@ -8,7 +8,7 @@
* Sublcasses will want to override the render method, but it's not necessary to override processInput(). * Sublcasses will want to override the render method, but it's not necessary to override processInput().
* Subclasses that select multiple values should implement the InputfieldHasArrayValue interface. * Subclasses that select multiple values should implement the InputfieldHasArrayValue interface.
* *
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer * ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
* @property string|int $defaultValue * @property string|int $defaultValue
@@ -164,8 +164,10 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
* *
*/ */
public function optionLanguageLabel($language, $key = null, $label = null) { public function optionLanguageLabel($language, $key = null, $label = null) {
$languages = $this->wire()->languages;
if(!$languages) return $this;
if(is_string($language) && !ctype_digit("$language")) { if(is_string($language) && !ctype_digit("$language")) {
$language = $this->wire('languages')->get($language); $language = $languages->get($language);
} }
$languageID = (int) "$language"; // converts Page or string to id $languageID = (int) "$language"; // converts Page or string to id
if(!isset($this->optionLanguageLabels[$languageID])) { if(!isset($this->optionLanguageLabels[$languageID])) {
@@ -676,17 +678,20 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
* *
*/ */
public function renderReady(Inputfield $parent = null, $renderValueMode = false) { public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
if(!empty($this->optionLanguageLabels) && $this->wire('languages') && $this->hasFieldtype === false) { if(!empty($this->optionLanguageLabels) && $this->hasFieldtype === false) {
// make option labels use use language where available $languages = $this->wire()->languages;
$language = $this->wire('user')->language; if($languages) {
$defaultLanguage = $this->wire('languages')->getDefault(); // make option labels use use language where available
if(!empty($this->optionLanguageLabels[$language->id])) { $language = $this->wire()->user->language;
$labels = $this->optionLanguageLabels[$language->id]; $defaultLanguage = $languages->getDefault();
foreach($this->options as $key => $defaultLabel) { if(!empty($this->optionLanguageLabels[$language->id])) {
if(empty($labels[$key])) continue; $labels = $this->optionLanguageLabels[$language->id];
$this->options[$key] = $labels[$key]; foreach($this->options as $key => $defaultLabel) {
if($language->id != $defaultLanguage->id) { if(empty($labels[$key])) continue;
$this->optionLanguageLabel($defaultLanguage, $key, $defaultLabel); $this->options[$key] = $labels[$key];
if($language->id != $defaultLanguage->id) {
$this->optionLanguageLabel($defaultLanguage, $key, $defaultLabel);
}
} }
} }
} }
@@ -705,12 +710,10 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
$attrs = $this->getAttributes(); $attrs = $this->getAttributes();
unset($attrs['value']); unset($attrs['value']);
$out = return
"<select " . $this->getAttributesString($attrs) . ">" . "<select " . $this->getAttributesString($attrs) . ">" .
$this->renderOptions($this->options) . $this->renderOptions($this->options) .
"</select>"; "</select>";
return $out;
} }
/** /**
@@ -722,6 +725,7 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
public function ___renderValue() { public function ___renderValue() {
$out = ''; $out = '';
$sanitizer = $this->wire()->sanitizer;
foreach($this->options as $value => $label) { foreach($this->options as $value => $label) {
@@ -738,11 +742,13 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
} }
if(strlen($o)) { if(strlen($o)) {
$out .= "<li>" . $this->wire('sanitizer')->entities($o) . "</li>"; $out .= "<li>" . $sanitizer->entities($o) . "</li>";
} }
} }
if(strlen($out)) $out = "<ul class='pw-bullets'>$out</ul>"; if(strlen($out)) {
$out = "<ul class='pw-bullets'>$out</ul>";
}
return $out; return $out;
} }
@@ -779,10 +785,10 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
if($this instanceof InputfieldHasArrayValue) { if($this instanceof InputfieldHasArrayValue) {
/** @var InputfieldSelect $this */ /** @var InputfieldSelect $this */
if(!is_array($value)) $value = array();
foreach($value as $k => $v) { foreach($value as $k => $v) {
if(!$this->isOption($v)) { if(!$this->isOption($v)) {
// $this->message("Removing invalid option: " . wire('sanitizer')->entities($value[$k]), Notice::debug); unset($value[$k]); // remove invalid option
unset($value[$k]);
} }
} }
@@ -805,8 +811,8 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
* *
*/ */
public function get($key) { public function get($key) {
if($key == 'options') return $this->options; if($key === 'options') return $this->options;
if($key == 'optionAttributes') return $this->optionAttributes; if($key === 'optionAttributes') return $this->optionAttributes;
return parent::get($key); return parent::get($key);
} }
@@ -874,7 +880,7 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
} }
} }
} else { } else {
if(strlen($value) && !$this->isOption($value)) { if(strlen("$value") && !$this->isOption($value)) {
$this->addOption($value); $this->addOption($value);
} }
} }
@@ -901,7 +907,7 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
} else if($value === null || $value === false) { } else if($value === null || $value === false) {
return true; return true;
} else if($value == "0") { } else if("$value" === "0") {
if(!array_key_exists("$value", $this->options)) return true; if(!array_key_exists("$value", $this->options)) return true;
} else { } else {
@@ -920,12 +926,15 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
public function ___getConfigInputfields() { public function ___getConfigInputfields() {
$inputfields = parent::___getConfigInputfields(); $inputfields = parent::___getConfigInputfields();
$modules = $this->wire()->modules;
if($this instanceof InputfieldHasArrayValue) { if($this instanceof InputfieldHasArrayValue) {
$f = $this->wire('modules')->get('InputfieldTextarea'); /** @var InputfieldTextarea $f */
$f = $modules->get('InputfieldTextarea');
$f->description = $this->_('To have pre-selected default value(s), enter the option values (one per line) below.'); $f->description = $this->_('To have pre-selected default value(s), enter the option values (one per line) below.');
} else { } else {
$f = $this->wire('modules')->get('InputfieldText'); /** @var InputfieldText $f */
$f = $modules->get('InputfieldText');
$f->description = $this->_('To have a pre-selected default value, enter the option value below.'); $f->description = $this->_('To have a pre-selected default value, enter the option value below.');
} }
$f->attr('name', 'defaultValue'); $f->attr('name', 'defaultValue');
@@ -943,10 +952,10 @@ class InputfieldSelect extends Inputfield implements InputfieldHasSelectableOpti
// the following configuration specific to non-Fieldtype use of single/multi-selects // the following configuration specific to non-Fieldtype use of single/multi-selects
$isInputfieldSelect = $this->className() == 'InputfieldSelect'; $isInputfieldSelect = $this->className() == 'InputfieldSelect';
/** @var Languages|null $languages */ $languages = $this->wire()->languages;
$languages = $this->wire('languages');
$f = $this->wire('modules')->get('InputfieldTextarea'); /** @var InputfieldTextarea $f */
$f = $modules->get('InputfieldTextarea');
$f->attr('name', 'options'); $f->attr('name', 'options');
$f->label = $this->_('Options'); $f->label = $this->_('Options');
$value = ''; $value = '';

View File

@@ -1,18 +1,45 @@
<?php namespace ProcessWire; <?php namespace ProcessWire;
/**
* Select Multiple Inputfield
*
* An Inputfield for handling multiple selection using HTML `<select multiple>`.
* Also a base type for other multiple selection types (checkboxes, asmSelect, etc.)
*
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
* @property int $size
*
*/
class InputfieldSelectMultiple extends InputfieldSelect implements InputfieldHasArrayValue { class InputfieldSelectMultiple extends InputfieldSelect implements InputfieldHasArrayValue {
/**
* Default 'size' attribute value
*
*/
const defaultSize = 10; const defaultSize = 10;
/**
* Get module info
*
* @return array
*
*/
public static function getModuleInfo() { public static function getModuleInfo() {
return array( return array(
'title' => __('Select Multiple', __FILE__), // Module Title 'title' => __('Select Multiple', __FILE__), // Module Title
'summary' => __('Select multiple items from a list', __FILE__), // Module Summary 'summary' => __('Select multiple items from a list', __FILE__), // Module Summary
'version' => 101, 'version' => 101,
'permanent' => true, 'permanent' => true,
); );
} }
/**
* Construct
*
*/
public function __construct() { public function __construct() {
parent::__construct(); parent::__construct();
$this->setAttribute('multiple', 'multiple'); $this->setAttribute('multiple', 'multiple');
@@ -24,17 +51,29 @@ class InputfieldSelectMultiple extends InputfieldSelect implements InputfieldHas
* *
* We don't need blank options in a select multiple since the unselected state involves no selected options * We don't need blank options in a select multiple since the unselected state involves no selected options
* *
* @param string|int $value
* @param string|null $label
* @param array|null $attributes
* @return InputfieldSelect|InputfieldSelectMultiple|self
*
*/ */
public function addOption($value, $label = null, array $attributes = null) { public function addOption($value, $label = null, array $attributes = null) {
if(is_null($value) || (is_string($value) && !strlen($value))) return $this; if(is_null($value) || (is_string($value) && !strlen($value))) return $this;
return parent::addOption($value, $label, $attributes); return parent::addOption($value, $label, $attributes);
} }
/**
* Configure Inputfield
*
* @return InputfieldWrapper
*
*/
public function ___getConfigInputfields() { public function ___getConfigInputfields() {
$inputfields = parent::___getConfigInputfields(); $inputfields = parent::___getConfigInputfields();
if($this->className() == 'InputfieldSelectMultiple') { if($this->className() === 'InputfieldSelectMultiple') {
// descending classes may null out the 'size' attribute if they don't need it // descending classes may null out the 'size' attribute if they don't need it
$f = $this->wire('modules')->get('InputfieldInteger'); /** @var InputfieldInteger $f */
$f = $this->wire()->modules->get('InputfieldInteger');
$f->label = $this->_('Size: number of rows visible at once in the select multiple'); $f->label = $this->_('Size: number of rows visible at once in the select multiple');
$f->attr('name', 'size'); $f->attr('name', 'size');
$f->attr('value', (int) $this->attr('size')); $f->attr('value', (int) $this->attr('size'));
@@ -42,6 +81,4 @@ class InputfieldSelectMultiple extends InputfieldSelect implements InputfieldHas
} }
return $inputfields; return $inputfields;
} }
} }

View File

@@ -3,14 +3,27 @@
/** /**
* An Inputfield for handling "textarea" form inputs * An Inputfield for handling "textarea" form inputs
* *
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
*
* @property int $rows Number of rows for textarea (default=5) * @property int $rows Number of rows for textarea (default=5)
* @property int $contentType Content type, applicable when used with FieldtypeTextarea. See FieldtypeTextarea contentType constants (default=contentTypeUnknown) * @property int $contentType Content type, applicable when used with FieldtypeTextarea. See FieldtypeTextarea contentType constants (default=contentTypeUnknown)
* *
*/ */
class InputfieldTextarea extends InputfieldText { class InputfieldTextarea extends InputfieldText {
/**
* Default value for rows attribute
*
*/
const defaultRows = 5; const defaultRows = 5;
/**
* Get module info
*
* @return array
*
*/
public static function getModuleInfo() { public static function getModuleInfo() {
return array( return array(
'title' => __('Textarea', __FILE__), // Module Title 'title' => __('Textarea', __FILE__), // Module Title
@@ -20,7 +33,10 @@ class InputfieldTextarea extends InputfieldText {
); );
} }
/**
* Init Inputfield
*
*/
public function init() { public function init() {
parent::init(); parent::init();
$this->setAttribute('rows', self::defaultRows); $this->setAttribute('rows', self::defaultRows);
@@ -70,10 +86,8 @@ class InputfieldTextarea extends InputfieldText {
* *
*/ */
protected function getValueLength($value, $countWords = false) { protected function getValueLength($value, $countWords = false) {
if(in_array($this->contentType, array(FieldtypeTextarea::contentTypeHTML, FieldtypeTextarea::contentTypeImageHTML))) { if($this->isContentTypeHTML()) $value = strip_tags($value);
$value = strip_tags($value); $value = $this->wire()->sanitizer->textarea($value, array('stripTags' => false));
}
$value = $this->wire('sanitizer')->textarea($value, array('stripTags' => false));
return parent::getValueLength($value, $countWords); return parent::getValueLength($value, $countWords);
} }
@@ -89,11 +103,10 @@ class InputfieldTextarea extends InputfieldText {
$value = $attrs['value']; $value = $attrs['value'];
unset($attrs['value'], $attrs['size'], $attrs['type']); unset($attrs['value'], $attrs['size'], $attrs['type']);
$out = return
"<textarea " . $this->getAttributesString($attrs) . ">" . "<textarea " . $this->getAttributesString($attrs) . ">" .
htmlspecialchars($value, ENT_QUOTES, "UTF-8") . htmlspecialchars($value, ENT_QUOTES, "UTF-8") .
"</textarea>"; "</textarea>";
return $out;
} }
/** /**
@@ -105,8 +118,10 @@ class InputfieldTextarea extends InputfieldText {
* *
*/ */
protected function setAttributeValue($value) { protected function setAttributeValue($value) {
$maxlength = $this->attr('maxlength'); $maxlength = $this->attr('maxlength');
$value = (string) $value; $value = (string) $value;
if($maxlength > 0 && $this->hasFieldtype === false) { if($maxlength > 0 && $this->hasFieldtype === false) {
$value = $this->wire()->sanitizer->textarea($value, array( $value = $this->wire()->sanitizer->textarea($value, array(
'maxLength' => $maxlength, 'maxLength' => $maxlength,
@@ -119,7 +134,9 @@ class InputfieldTextarea extends InputfieldText {
$value = str_replace("\r\n", "\n", $value); $value = str_replace("\r\n", "\n", $value);
} }
} }
if($this->stripTags) $value = strip_tags($value); if($this->stripTags) $value = strip_tags($value);
return $this->noTrim ? $value : trim($value); return $this->noTrim ? $value : trim($value);
} }
@@ -133,6 +150,7 @@ class InputfieldTextarea extends InputfieldText {
public function ___processInput(WireInputData $input) { public function ___processInput(WireInputData $input) {
$maxlength = $this->attr('maxlength'); $maxlength = $this->attr('maxlength');
if($this->hasFieldtype !== false && $maxlength > 0) { if($this->hasFieldtype !== false && $maxlength > 0) {
// we want to apply our own maxlength logic that doesn't truncate // we want to apply our own maxlength logic that doesn't truncate
$this->attr('maxlength', 0); $this->attr('maxlength', 0);
@@ -161,26 +179,49 @@ class InputfieldTextarea extends InputfieldText {
* *
*/ */
public function ___renderValue() { public function ___renderValue() {
if($this->contentType == FieldtypeTextarea::contentTypeHTML) { if($this->isContentTypeHTML()) {
$out = "<div class='InputfieldTextareaContentTypeHTML'>" . $out =
$this->wire('sanitizer')->purify($this->attr('value')) . "</div>"; "<div class='InputfieldTextareaContentTypeHTML'>" .
$this->wire()->sanitizer->purify($this->val()) .
"</div>";
} else { } else {
$out = nl2br(htmlentities($this->attr('value'), ENT_QUOTES, "UTF-8")); $out = nl2br(htmlentities($this->val(), ENT_QUOTES, "UTF-8"));
} }
return $out; return $out;
} }
/**
* Is current content-type an HTML content-type?
*
* @return bool
*
*/
public function isContentTypeHTML() {
$contentTypes = array(
FieldtypeTextarea::contentTypeHTML,
FieldtypeTextarea::contentTypeImageHTML
);
return in_array($this->contentType, $contentTypes);
}
/**
* Configure Inputfield
*
* @return InputfieldWrapper
*
*/
public function ___getConfigInputfields() { public function ___getConfigInputfields() {
$inputfields = parent::___getConfigInputfields(); $inputfields = parent::___getConfigInputfields();
$removes = array('size', 'pattern'); $removes = array('size', 'pattern');
foreach($removes as $name) { foreach($removes as $name) {
$f = $inputfields->getChildByName($name); $f = $inputfields->getChildByName($name);
if($f) $inputfields->remove($f); if($f) $inputfields->remove($f);
} }
/** @var InputfieldInteger $field */ /** @var InputfieldInteger $field */
$field = $this->modules->get('InputfieldInteger'); $field = $this->wire()->modules->get('InputfieldInteger');
$field->setAttribute('name', 'rows'); $field->setAttribute('name', 'rows');
$field->label = $this->_('Rows'); $field->label = $this->_('Rows');
$field->setAttribute('value', $this->attr('rows') > 0 ? $this->attr('rows') : self::defaultRows); $field->setAttribute('value', $this->attr('rows') > 0 ? $this->attr('rows') : self::defaultRows);
@@ -192,6 +233,13 @@ class InputfieldTextarea extends InputfieldText {
return $inputfields; return $inputfields;
} }
/**
* Get config fields allowed for context
*
* @param Field $field
* @return array
*
*/
public function ___getConfigAllowContext($field) { public function ___getConfigAllowContext($field) {
return array_merge(parent::___getConfigAllowContext($field), array('rows')); return array_merge(parent::___getConfigAllowContext($field), array('rows'));
} }

View File

@@ -1,9 +1,12 @@
<?php namespace ProcessWire; <?php namespace ProcessWire;
/** /**
* Class InputfieldURL * URL Inputfield Module
* *
* An Inputfield for handling input of URLs * An Inputfield for handling input of URLs in ProcessWire forms.
*
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
* https://processwire.com
* *
* @property int|bool $noRelative Whether relative URLs are disabled * @property int|bool $noRelative Whether relative URLs are disabled
* @property int|bool $addRoot Whether to prepend root path * @property int|bool $addRoot Whether to prepend root path
@@ -17,10 +20,14 @@ class InputfieldURL extends InputfieldText {
return array( return array(
'title' => __('URL', __FILE__), // Module Title 'title' => __('URL', __FILE__), // Module Title
'summary' => __('URL in valid format', __FILE__), // Module Summary 'summary' => __('URL in valid format', __FILE__), // Module Summary
'version' => 102, 'version' => 103,
); );
} }
/**
* Construct
*
*/
public function __construct() { public function __construct() {
parent::__construct(); parent::__construct();
$this->setAttribute('type', 'text'); $this->setAttribute('type', 'text');
@@ -34,44 +41,75 @@ class InputfieldURL extends InputfieldText {
$this->set('allowQuotes', 0); // whether to allow quote characters in URLs $this->set('allowQuotes', 0); // whether to allow quote characters in URLs
} }
/**
* Render Inputfield
*
* @return string
*
*/
public function ___render() { public function ___render() {
$rootUrl = $this->config->urls->root; $rootUrl = $this->wire()->config->urls->root;
if($this->addRoot && !$this->noRelative && !$this->notes && strlen($rootUrl) > 1) { if($this->addRoot && !$this->noRelative && !$this->notes && strlen($rootUrl) > 1) {
$this->notes = sprintf($this->_("Start local URLs with \"/\" and leave off the \"%s\" part."), $rootUrl); // Instruction for local URLs displayed when site is running from a subdirectory $this->notes = sprintf(
$this->_("Start local URLs with “/” and leave off the “%s” part."), // Instruction for local URLs displayed when site is running from a subdirectory
$rootUrl
);
} }
return parent::___render(); return parent::___render();
} }
/**
* Set value attribute
*
* @param string $value
* @return string
*
*/
protected function setAttributeValue($value) { protected function setAttributeValue($value) {
if(strlen($value)) { $value = trim((string) $value);
$value = trim($value); $dirty = $value;
$unsanitized = $value; $label = $this->label ? $this->label : $this->name;
$value = $this->wire('sanitizer')->url($value, array(
'allowRelative' => $this->noRelative ? false : true,
'allowIDN' => $this->allowIDN ? true : false,
'stripQuotes' => $this->allowQuotes ? false : true,
));
if(!$value || $unsanitized != $value && "http://$unsanitized" != $value) { if(!strlen($dirty)) return '';
$error = true;
if($value && strpos($unsanitized, '%') !== false) {
$test = rawurldecode($unsanitized);
if($value == $test || $value == "http://$test") $error = false;
}
if($error) {
$this->error($this->name . ': ' . $this->_("Error found - please check that it is a valid URL")); // Error message when invalid URL found
}
} else if($value != $unsanitized && $value == "http://$unsanitized") { $value = $this->wire()->sanitizer->url($dirty, array(
$this->message($this->name . ': ' . $this->_("Note that \"http://\" was added")); // Message displayed when http scheme was automatically added to the URL 'allowRelative' => $this->noRelative ? false : true,
'allowIDN' => $this->allowIDN ? true : false,
'stripQuotes' => $this->allowQuotes ? false : true,
));
if(empty($value) || ($dirty !== $value && "http://$dirty" !== $value && "https://$dirty" !== $value)) {
$error = true;
if(strpos($dirty, '%') !== false) {
$test = rawurldecode($dirty);
if($value === $test || $value === "http://$test" || $value === "https://$test") $error = false;
}
if($error) {
$this->error("$label - " . $this->_("Error found - please check that it is a valid URL")); // Error message when invalid URL found
} }
} else $value = ''; } else if($value !== $dirty) {
$scheme = '';
foreach(array('http', 'https') as $s) {
if($value !== "$s://$dirty") continue;
$scheme = "$s://";
break;
}
if($scheme) {
$this->message("$label - " . sprintf($this->_("Note that %s was added"), $scheme)); // Message displayed when scheme was automatically added to the URL
}
}
return $value; return $value;
} }
/**
* Configure Inputfield
*
* @return InputfieldWrapper
*
*/
public function ___getConfigInputfields() { public function ___getConfigInputfields() {
$inputfields = parent::___getConfigInputfields(); $inputfields = parent::___getConfigInputfields();
$f = $inputfields->get('stripTags'); $f = $inputfields->get('stripTags');