<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Framework\Data\Form\Element;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Data\Form;
use Magento\Framework\Data\Form\AbstractForm;
use Magento\Framework\Data\Form\Element\Renderer\RendererInterface;
use Magento\Framework\Escaper;
use Magento\Framework\Math\Random;
use Magento\Framework\View\Helper\SecureHtmlRenderer;
/**
* Data form abstract class
*
* phpcs:disable Magento2.Classes.AbstractApi
* @api
* @author Magento Core Team <core@magentocommerce.com>
* @SuppressWarnings(PHPMD.NumberOfChildren)
* @since 100.0.2
*/
abstract class AbstractElement extends AbstractForm
{
/**
* @var string|int
*/
protected $_id;
/**
* @var string
*/
protected $_type;
/**
* @var Form
*/
protected $_form;
/**
* @var array
*/
protected $_elements;
/**
* @var RendererInterface
*/
protected $_renderer;
/**
* Shows whether current element belongs to Basic or Advanced form layout
*
* @var bool
*/
protected $_advanced = false;
/**
* @var Escaper
*/
protected $_escaper;
/**
* @var string
*/
private $lockHtmlAttribute = 'data-locked';
/**
* @var SecureHtmlRenderer
*/
private $secureRenderer;
/**
* @var Random
*/
private $random;
/**
* @param Factory $factoryElement
* @param CollectionFactory $factoryCollection
* @param Escaper $escaper
* @param array $data
* @param SecureHtmlRenderer|null $secureRenderer
* @param Random|null $random
*/
public function __construct(
Factory $factoryElement,
CollectionFactory $factoryCollection,
Escaper $escaper,
$data = [],
?SecureHtmlRenderer $secureRenderer = null,
?Random $random = null
) {
$this->_escaper = $escaper;
parent::__construct($factoryElement, $factoryCollection, $data);
$this->_renderer = \Magento\Framework\Data\Form::getElementRenderer();
$this->secureRenderer = $secureRenderer ?? ObjectManager::getInstance()->get(SecureHtmlRenderer::class);
$this->random = $random ?? ObjectManager::getInstance()->get(Random::class);
}
/**
* Generate this element's ID.
*
* @return string
*/
private function generateElementId(): string
{
if (!$this->hasData('formelementhookid')) {
$this->setData('formelementhookid', 'elemId' .$this->random->getRandomString(10));
}
return $this->getData('formelementhookid');
}
/**
* Add form element
*
* @param AbstractElement $element
* @param bool $after
* @return Form
*/
public function addElement(AbstractElement $element, $after = false)
{
if ($this->getForm()) {
$this->getForm()->checkElementId($element->getId());
$this->getForm()->addElementToCollection($element);
}
parent::addElement($element, $after);
return $this;
}
/**
* Shows whether current element belongs to Basic or Advanced form layout
*
* @return bool
*/
public function isAdvanced()
{
return $this->_advanced;
}
/**
* Set _advanced layout property
*
* @param bool $advanced
* @return $this
*/
public function setAdvanced($advanced)
{
$this->_advanced = $advanced;
return $this;
}
/**
* Get id.
*
* @return string|int
*/
public function getId()
{
return $this->_id;
}
/**
* Get type.
*
* @return string
*/
public function getType()
{
return $this->_type;
}
/**
* Get form
*
* @return Form
*/
public function getForm()
{
return $this->_form;
}
/**
* Set the Id.
*
* @param string|int $id
* @return $this
*/
public function setId($id)
{
$this->_id = $id;
$this->setData('html_id', $id);
return $this;
}
/**
* Get the Html Id.
*
* @return string
*/
public function getHtmlId()
{
return $this->_escaper->escapeHtml(
$this->getForm()->getHtmlIdPrefix() .
$this->getData('html_id') .
$this->getForm()->getHtmlIdSuffix()
);
}
/**
* Get the name.
*
* @return mixed
*/
public function getName()
{
$name = $this->_escaper->escapeHtml($this->getData('name'));
if ($suffix = $this->getForm()->getFieldNameSuffix()) {
$name = $this->getForm()->addSuffixToName($name, $suffix);
}
return $name;
}
/**
* Set the type.
*
* @param string $type
* @return $this
*/
public function setType($type)
{
$this->_type = $type;
$this->setData('type', $type);
return $this;
}
/**
* Set form.
*
* @param AbstractForm $form
* @return $this
*/
public function setForm($form)
{
$this->_form = $form;
return $this;
}
/**
* Remove field
*
* @param string $elementId
* @return AbstractForm
*/
public function removeField($elementId)
{
$this->getForm()->removeField($elementId);
return parent::removeField($elementId);
}
/**
* Return the attributes for Html.
*
* @return string[]
*/
public function getHtmlAttributes()
{
return [
'type',
'title',
'class',
'style',
'onclick',
'onchange',
'disabled',
'readonly',
'autocomplete',
'tabindex',
'placeholder',
'data-form-part',
'data-role',
'data-action',
'checked',
];
}
/**
* Add a class.
*
* @param string $class
* @return $this
*/
public function addClass($class)
{
$oldClass = $this->getClass();
$this->setClass($oldClass . ' ' . $class);
return $this;
}
/**
* Remove CSS class
*
* @param string $class
* @return $this
*/
public function removeClass($class)
{
$classes = array_unique(explode(' ', $this->getClass() ?? ''));
if (false !== ($key = array_search($class, $classes))) {
unset($classes[$key]);
}
$this->setClass(implode(' ', $classes));
return $this;
}
/**
* Escape a string's contents.
*
* @param string $string
* @return string
*/
protected function _escape($string)
{
return $this->_escaper->escapeHtml($string);
}
/**
* Return the escaped value of the element specified by the given index.
*
* @param null|int|string $index
* @return string
*/
public function getEscapedValue($index = null)
{
$value = $this->getValue($index);
if ($filter = $this->getValueFilter()) {
$value = $filter->filter($value);
}
return $this->_escape($value);
}
/**
* Set the renderer.
*
* @param RendererInterface $renderer
* @return $this
*/
public function setRenderer(RendererInterface $renderer)
{
$this->_renderer = $renderer;
return $this;
}
/**
* Get the renderer.
*
* @return RendererInterface
*/
public function getRenderer()
{
return $this->_renderer;
}
/**
* Get Ui Id.
*
* @param null|string $suffix
* @return string
*/
protected function _getUiId($suffix = null)
{
if ($this->_renderer instanceof \Magento\Framework\View\Element\AbstractBlock) {
return $this->_renderer->getUiId($this->getType(), $this->getName(), $suffix);
} else {
return ' data-ui-id="form-element-' . $this->_escaper->escapeHtml($this->getName()) . ($suffix ?: '') . '"';
}
}
/**
* Get the Html for the element.
*
* @return string
*/
public function getElementHtml()
{
$html = '';
$htmlId = $this->getHtmlId();
$beforeElementHtml = $this->getBeforeElementHtml();
if ($beforeElementHtml) {
$html .= '<label class="addbefore" for="' . $htmlId . '">' . $beforeElementHtml . '</label>';
}
if (is_array($this->getValue())) {
foreach ($this->getValue() as $value) {
$html .= $this->getHtmlForInputByValue($this->_escape($value));
}
} else {
$html .= $this->getHtmlForInputByValue($this->getEscapedValue());
}
$afterElementJs = $this->getAfterElementJs();
if ($afterElementJs) {
$html .= $afterElementJs;
}
$afterElementHtml = $this->getAfterElementHtml();
if ($afterElementHtml) {
$html .= '<label class="addafter" for="' . $htmlId . '">' . $afterElementHtml . '</label>';
}
return $html;
}
/**
* Get the before element html.
*
* @return mixed
*/
public function getBeforeElementHtml()
{
return $this->getData('before_element_html');
}
/**
* Generate HTML to replace unsecure attributes.
*
* @return string
*/
private function generateAttributesSubstitute(): string
{
$html = '';
//Rendering element's style as separate tag.
if ($this->getStyle()) {
$selector = "*[formelementhookid='{$this->generateElementId()}']";
if ($id = $this->getHtmlId()) {
$selector = "#{$id}";
}
$html .= $this->secureRenderer->renderStyleAsTag($this->getStyle(), $selector);
}
//Rendering each event listener as a separate script tag.
$events = array_filter(
$this->getHtmlAttributes(),
function (string $attribute): bool {
return mb_strpos($attribute, 'on') === 0;
}
);
foreach ($events as $event) {
$eventShort = mb_substr($event, 2);
$methodName = 'getOn' . $eventShort;
if ($eventListener = $this->$methodName()) {
$html .= $this->secureRenderer->renderEventListenerAsTag(
$event,
$eventListener,
"*[formelementhookid='{$this->generateElementId()}']"
);
}
}
return $html;
}
/**
* Get the after element html.
*
* @return mixed
*/
public function getAfterElementHtml()
{
return $this->getData('after_element_html') . $this->generateAttributesSubstitute();
}
/**
* Get the after element Javascript.
*
* @return mixed
*/
public function getAfterElementJs()
{
return $this->getData('after_element_js');
}
/**
* Render HTML for element's label
*
* @param string $idSuffix
* @param string $scopeLabel
* @return string
*/
public function getLabelHtml($idSuffix = '', $scopeLabel = '')
{
$scopeLabel = $scopeLabel ? ' data-config-scope="' . $scopeLabel . '"' : '';
if ($this->getLabel() !== null) {
$html = '<label class="label admin__field-label" for="' .
$this->getHtmlId() . $idSuffix . '"' . $this->_getUiId(
'label'
) . '><span' . $scopeLabel . '>' . $this->_escape(
$this->getLabel()
) . '</span></label>' . "\n";
} else {
$html = '';
}
return $html;
}
/**
* Get the default html.
*
* @return mixed
*/
public function getDefaultHtml()
{
$html = $this->getData('default_html');
if ($html === null) {
$html = $this->getNoSpan() === true ? '' : '<div class="admin__field">' . "\n";
$html .= $this->getLabelHtml();
$html .= $this->getElementHtml();
$html .= $this->getNoSpan() === true ? '' : '</div>' . "\n";
}
return $html;
}
/**
* Get the html.
*
* @return mixed
*/
public function getHtml()
{
if ($this->getRequired()) {
$this->addClass('required-entry _required');
}
if ($this->_renderer) {
$html = $this->_renderer->render($this);
} else {
$html = $this->getDefaultHtml();
}
return $html;
}
/**
* Get the html.
*
* @return mixed
*/
public function toHtml()
{
return $this->getHtml();
}
/**
* Serialize the element.
*
* @param string[] $attributes
* @param string $valueSeparator
* @param string $fieldSeparator
* @param string $quote
* @return string
*/
public function serialize($attributes = [], $valueSeparator = '=', $fieldSeparator = ' ', $quote = '"')
{
if ($this->isLocked() && !empty($attributes)) {
$attributes[] = $this->lockHtmlAttribute;
}
if (in_array('disabled', $attributes) && !empty($this->_data['disabled'])) {
$this->_data['disabled'] = 'disabled';
} else {
unset($this->_data['disabled']);
}
if (in_array('checked', $attributes) && !empty($this->_data['checked'])) {
$this->_data['checked'] = 'checked';
} else {
unset($this->_data['checked']);
}
$attributes[] = 'formelementhookid';
$this->generateElementId();
//Unset attributes that are to be rendered as separate tags
$attributes = array_filter(
$attributes,
function (string $attribute): bool {
return $attribute !== 'style' && mb_strpos($attribute, 'on') !== 0;
}
);
return parent::serialize($attributes, $valueSeparator, $fieldSeparator, $quote);
}
/**
* Indicates the elements readonly status.
*
* @return mixed
*/
public function getReadonly()
{
if ($this->hasData('readonly_disabled')) {
return $this->_getData('readonly_disabled');
}
return $this->_getData('readonly');
}
/**
* Get the container Id.
*
* @return mixed
*/
public function getHtmlContainerId()
{
if ($this->hasData('container_id')) {
return $this->getData('container_id');
} elseif ($idPrefix = $this->getForm()->getFieldContainerIdPrefix()) {
return $idPrefix . $this->getId();
}
return '';
}
/**
* Add specified values to element values
*
* @param string|int|array $values
* @param bool $overwrite
* @return $this
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function addElementValues($values, $overwrite = false)
{
if (empty($values) || is_string($values) && trim($values) == '') {
return $this;
}
if (!is_array($values)) {
$values = $this->_escaper->escapeHtml(trim($values));
$values = [$values => $values];
}
$elementValues = $this->getValues();
if (!empty($elementValues)) {
foreach ($values as $key => $value) {
if (isset($elementValues[$key]) && $overwrite || !isset($elementValues[$key])) {
$elementValues[$key] = $this->_escaper->escapeHtml($value);
}
}
$values = $elementValues;
}
$this->setValues($values);
return $this;
}
/**
* Lock element
*
* @return void
*/
public function lock()
{
$this->setData($this->lockHtmlAttribute, 1);
}
/**
* Is element locked
*
* @return bool
*/
public function isLocked()
{
return $this->getData($this->lockHtmlAttribute) == 1;
}
/**
* Get input html by sting value.
*
* @param string|null $value
*
* @return string
*/
private function getHtmlForInputByValue($value)
{
return '<input id="' . $this->getHtmlId() . '" name="' . $this->getName() . '" ' . $this->_getUiId()
. ' value="' . $value . '" ' . $this->serialize($this->getHtmlAttributes()) . '/>';
}
}
|