Files
ave-cms/adminx/modules/Rubrics/FieldEditors/SimpleFieldEditor.php
T
2026-07-27 12:58:44 +03:00

640 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/*
|--------------------------------------------------------------------------------------
| AVE.cms
|--------------------------------------------------------------------------------------
| @package AVE.cms
| @file adminx/modules/Rubrics/FieldEditors/SimpleFieldEditor.php
| @author AVE.cms <support@ave-cms.ru>
| @copyright 2007-2026 (c) AVE.cms
| @link https://ave-cms.ru
| @version 3.3
*/
namespace App\Adminx\Rubrics\FieldEditors;
defined('BASEPATH') || die('Direct access to this location is not allowed.');
use App\Helpers\Json;
class SimpleFieldEditor extends AbstractFieldEditor
{
public function parseValue($type, $value)
{
$type = (string) $type;
$value = (string) $value;
if ($type === 'checkbox') {
return array('raw' => $value, 'checked' => (int) $value === 1);
}
if ($type === 'date') {
return array(
'raw' => $value,
'timestamp' => preg_match('/^\d+$/', $value) ? (int) $value : 0,
'date' => preg_match('/^\d+$/', $value) && (int) $value > 0 ? date('Y-m-d', (int) $value) : '',
);
}
if ($type === 'date_time') {
$timestamp = \App\Content\Fields\Types\DateTimeValue::normalize($value);
$timestamp = $timestamp === '' ? 0 : (int) $timestamp;
return array(
'raw' => $value,
'timestamp' => $timestamp,
'date' => $timestamp > 0 ? date('Y-m-d', $timestamp) : '',
'datetime' => $timestamp > 0 ? date('Y-m-d\TH:i', $timestamp) : '',
);
}
if ($type === 'period') {
$period = \App\Content\Fields\Types\PeriodValue::parseValue($value);
return array('raw' => $value, 'start' => $period['start'], 'end' => $period['end']);
}
if ($type === 'range') {
$range = \App\Content\Fields\Types\RangeValue::parseValue($value);
return array('raw' => $value, 'min' => $range['min'], 'max' => $range['max']);
}
if ($type === 'dimensions') {
$dimensions = \App\Content\Fields\Types\DimensionsValue::parseValue($value);
return array('raw' => $value) + $dimensions;
}
if ($type === 'address') {
$address = \App\Content\Fields\Types\AddressValue::parseValue($value);
return array('raw' => $value) + $address;
}
if (in_array($type, array('single_line_numeric_two', 'single_line_numeric_three'), true)) {
return array('raw' => $value, 'parts' => $this->cleanItems(explode('|', $value)));
}
if ($type === 'single_line_numeric') {
return array('raw' => $value, 'number' => $value === '' ? '' : 0 + $value);
}
if ($type === 'number') {
return array('raw' => $value, 'number' => $value);
}
return parent::parseValue($type, $value);
}
public function serializeValue($type, $value)
{
$type = (string) $type;
if (!is_array($value)) {
return (string) $value;
}
if ($type === 'checkbox') {
return !empty($value['checked']) ? '1' : '';
}
if ($type === 'date') {
if (!empty($value['timestamp'])) {
return (string) (int) $value['timestamp'];
}
if (!empty($value['date']) && preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) $value['date'])) {
$time = strtotime($value['date'] . ' 00:00:00');
return $time !== false ? (string) $time : '';
}
return '';
}
if ($type === 'date_time') {
$input = isset($value['datetime']) ? (string) $value['datetime'] : (isset($value['date']) ? (string) $value['date'] : '');
return \App\Content\Fields\Types\DateTimeValue::normalize($input);
}
if ($type === 'period') {
return \App\Content\Fields\Types\PeriodValue::encodeValue($value);
}
if ($type === 'range') {
$range = \App\Content\Fields\Types\RangeValue::parseValue($value);
return ($range['min'] === '' && $range['max'] === '')
? ''
: Json::encode($range, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
if ($type === 'dimensions') {
$dimensions = \App\Content\Fields\Types\DimensionsValue::parseValue($value);
return count(array_filter($dimensions, 'strlen')) === 0
? ''
: Json::encode($dimensions, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
if ($type === 'address') {
return \App\Content\Fields\Types\AddressValue::encodeValue($value);
}
if (in_array($type, array('single_line_numeric_two', 'single_line_numeric_three'), true)) {
$parts = isset($value['parts']) && is_array($value['parts']) ? $value['parts'] : array();
return implode('|', $this->cleanItems($parts));
}
if ($type === 'single_line_numeric') {
return isset($value['number']) && $value['number'] !== '' ? (string) (0 + $value['number']) : '';
}
if ($type === 'number') {
return isset($value['number']) ? trim((string) $value['number']) : '';
}
return parent::serializeValue($type, $value);
}
public function normalizeDefault($type, $value)
{
$type = (string) $type;
$value = (string) $value;
if ($type === 'checkbox') {
$normalized = trim(function_exists('mb_strtolower') ? mb_strtolower($value, 'UTF-8') : strtolower($value));
return in_array($normalized, array('1', 'true', 'yes', 'on', 'да', 'вкл', 'включено'), true) ? '1' : '0';
}
if ($type === 'number') {
return \App\Content\Fields\Types\NumberValue::normalize($value);
}
if ($type === 'color') {
return \App\Content\Fields\Types\ColorValue::normalize($value);
}
if ($type === 'range') {
$range = \App\Content\Fields\Types\RangeValue::parseValue($value);
return ($range['min'] === '' && $range['max'] === '')
? ''
: Json::encode($range, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
if ($type === 'dimensions') {
$dimensions = \App\Content\Fields\Types\DimensionsValue::parseValue($value);
return count(array_filter($dimensions, 'strlen')) === 0
? ''
: Json::encode($dimensions, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
if ($type === 'address') {
return \App\Content\Fields\Types\AddressValue::encodeValue($value);
}
if (in_array($type, array('single_line_numeric', 'doc_from_rub'), true)) {
$value = trim($value);
return $value === '' ? '' : (string) (0 + $value);
}
if (in_array($type, array('single_line_numeric_two', 'single_line_numeric_three'), true)) {
$parts = preg_split('/[|,\r\n]+/', $value);
$out = array();
foreach ($parts ?: array() as $part) {
$part = trim((string) $part);
if ($part !== '') {
$out[] = (string) (0 + $part);
}
}
return implode('|', $out);
}
if ($type === 'date') {
$value = trim($value);
if ($value === '' || preg_match('/^\d+$/', $value)) {
return $value;
}
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
$time = strtotime($value . ' 00:00:00');
return $time !== false ? (string) $time : '';
}
}
if ($type === 'date_time') {
return \App\Content\Fields\Types\DateTimeValue::normalize($value);
}
if ($type === 'period') {
return \App\Content\Fields\Types\PeriodValue::encodeValue($value);
}
return $value;
}
public function renderEdit(array $field)
{
$kind = isset($field['editor_kind']) ? (string) $field['editor_kind'] : 'text';
$type = isset($field['rubric_field_type']) ? (string) $field['rubric_field_type'] : '';
$parsed = $this->value($field);
$settings = isset($field['settings']) && is_array($field['settings']) ? $field['settings'] : array();
$editorHeight = $this->editorHeight($settings, $kind === 'code' ? 280 : 300);
if ($type === 'multi_line' && isset($settings['editor']) && (string) $settings['editor'] === 'plain') {
$kind = 'textarea';
}
if ($type === 'content') {
$mode = isset($settings['mode']) ? (string) $settings['mode'] : 'rich';
$kind = $mode === 'code' ? 'code' : ($mode === 'plain' ? 'textarea' : 'rich');
}
if ($type === 'date_time') {
$mode = isset($settings['mode']) ? (string) $settings['mode'] : 'date';
$timestamp = isset($field['field_value']) ? (int) $field['field_value'] : 0;
$isDate = $mode !== 'datetime';
return '<input class="input" type="' . ($isDate ? 'date' : 'datetime-local') . '" name="'
. $this->e($this->fname($field, '[datetime]')) . '" value="'
. $this->e($timestamp > 0 ? date($isDate ? 'Y-m-d' : 'Y-m-d\TH:i', $timestamp) : '') . '">';
}
if ($type === 'period') {
$period = \App\Content\Fields\Types\PeriodValue::parseValue(isset($field['field_value']) ? $field['field_value'] : '');
$dateOnly = !isset($settings['mode']) || (string) $settings['mode'] !== 'datetime';
$inputType = $dateOnly ? 'date' : 'datetime-local';
$inputFormat = $dateOnly ? 'Y-m-d' : 'Y-m-d\TH:i';
$name = $this->e($this->fname($field, ''));
$html = '<div class="documents-period-control">';
foreach (array('start' => 'Начало', 'end' => 'Окончание') as $key => $label) {
$timestamp = isset($period[$key]) ? (int) $period[$key] : 0;
$html .= '<label class="field"><span class="field-label">' . $this->e($label) . '</span>'
. '<input class="input" type="' . $inputType . '" name="' . $name . '[' . $key . ']" value="'
. $this->e($timestamp > 0 ? date($inputFormat, $timestamp) : '') . '"></label>';
}
return $html . '</div>';
}
if ($type === 'contact') {
$mode = isset($settings['mode']) ? (string) $settings['mode'] : 'email';
$inputType = $mode === 'phone' ? 'tel' : ($mode === 'url' ? 'url' : 'email');
$placeholder = $mode === 'phone' ? '+7 999 000-00-00' : ($mode === 'url' ? 'https://example.com' : 'name@example.com');
return '<input class="input" type="' . $inputType . '" autocomplete="off" placeholder="' . $this->e($placeholder)
. '" name="' . $this->e($this->fname($field, '[raw]')) . '" value="' . $this->e($parsed) . '">';
}
if ($type === 'color') {
$value = \App\Content\Fields\Types\ColorValue::normalize($parsed);
return '<div class="documents-color-control" data-document-color-control>'
. '<input type="color" value="' . $this->e($value !== '' ? $value : '#000000') . '" data-document-color-picker aria-label="Выбрать цвет">'
. '<input class="input mono" type="text" placeholder="#rrggbb" name="' . $this->e($this->fname($field, '[raw]'))
. '" value="' . $this->e($value) . '" data-document-color-value></div>';
}
if ($type === 'range') {
$range = \App\Content\Fields\Types\RangeValue::parseValue(isset($field['field_value']) ? $field['field_value'] : '');
$unit = isset($settings['unit']) ? trim((string) $settings['unit']) : '';
$minLabel = isset($settings['min_label']) && trim((string) $settings['min_label']) !== '' ? (string) $settings['min_label'] : 'От';
$maxLabel = isset($settings['max_label']) && trim((string) $settings['max_label']) !== '' ? (string) $settings['max_label'] : 'До';
$html = '<div class="documents-range-control">';
foreach (array('min' => $minLabel, 'max' => $maxLabel) as $key => $label) {
$html .= '<label class="field"><span class="field-label">' . $this->e($label) . '</span><span class="input-group">'
. '<input class="input" type="text" inputmode="decimal" name="' . $this->e($this->fname($field, '[' . $key . ']'))
. '" value="' . $this->e($range[$key]) . '">'
. ($unit !== '' ? '<span class="input-addon">' . $this->e($unit) . '</span>' : '') . '</span></label>';
}
return $html . '</div>';
}
if ($type === 'dimensions') {
$dimensions = \App\Content\Fields\Types\DimensionsValue::parseValue(isset($field['field_value']) ? $field['field_value'] : '');
$units = array('mm' => 'мм', 'cm' => 'см', 'm' => 'м');
$unitKey = isset($settings['unit']) ? (string) $settings['unit'] : 'cm';
$unit = isset($units[$unitKey]) ? $units[$unitKey] : $units['cm'];
$html = '<div class="documents-dimensions-control">';
foreach (array('length' => 'Длина', 'width' => 'Ширина', 'height' => 'Высота') as $key => $label) {
$html .= '<label class="field"><span class="field-label">' . $this->e($label) . '</span><span class="input-group">'
. '<input class="input" type="text" inputmode="decimal" name="' . $this->e($this->fname($field, '[' . $key . ']'))
. '" value="' . $this->e($dimensions[$key]) . '"><span class="input-addon">' . $this->e($unit) . '</span></span></label>';
}
return $html . '</div>';
}
if ($type === 'address') {
$address = \App\Content\Fields\Types\AddressValue::parseValue(isset($field['field_value']) ? $field['field_value'] : '');
$name = $this->e($this->fname($field, ''));
$html = '<div class="documents-address-control">';
foreach (array(
'postal_code' => 'Индекс',
'region' => 'Регион',
'city' => 'Город / населённый пункт',
'street' => 'Улица',
'building' => 'Дом / строение',
'unit' => 'Квартира / офис',
) as $key => $label) {
$html .= '<label class="field documents-address-' . $key . '"><span class="field-label">' . $this->e($label) . '</span>'
. '<input class="input" type="text" name="' . $name . '[' . $key . ']" value="' . $this->e($address[$key]) . '"></label>';
}
if (!empty($settings['coordinates'])) {
foreach (array('latitude' => 'Широта', 'longitude' => 'Долгота') as $key => $label) {
$html .= '<label class="field documents-address-' . $key . '"><span class="field-label">' . $this->e($label) . '</span>'
. '<input class="input" type="text" inputmode="decimal" name="' . $name . '[' . $key . ']" value="' . $this->e($address[$key]) . '"></label>';
}
} else {
$html .= '<input type="hidden" name="' . $name . '[latitude]" value="' . $this->e($address['latitude']) . '">'
. '<input type="hidden" name="' . $name . '[longitude]" value="' . $this->e($address['longitude']) . '">';
}
return $html . '</div>';
}
if ($kind === 'boolean') {
$on = (string) $parsed === '1';
$name = $this->e($this->fname($field, '[checked]'));
return '<div class="documents-boolean-control">'
. '<input type="hidden" name="' . $name . '" value="0">'
. '<label class="switch"><input type="checkbox" name="' . $name . '" value="1" data-document-boolean'
. ($on ? ' checked' : '') . '><span data-document-boolean-label>' . ($on ? 'Включено' : 'Выключено') . '</span></label>'
. '</div>';
}
if ($kind === 'date') {
return '<input class="input" type="date" name="' . $this->e($this->fname($field, '[date]'))
. '" value="' . $this->e($parsed) . '">';
}
if ($kind === 'number') {
return '<input class="input" type="text" inputmode="decimal" pattern="[+\\-]?[0-9]*([.,][0-9]*)?" spellcheck="false" name="'
. $this->e($this->fname($field, '[number]')) . '" value="' . $this->e($parsed) . '">';
}
if ($kind === 'numeric_parts') {
$count = $type === 'single_line_numeric_three' ? 3 : 2;
$parts = is_array($parsed) ? $parsed : array();
$html = '<div class="documents-inline-grid documents-numeric-parts" style="--parts-count:' . $count . '">';
for ($i = 0; $i < $count; $i++) {
$val = isset($parts[$i]) ? $parts[$i] : '';
$html .= '<label class="field"><span class="field-label">Значение ' . ($i + 1) . '</span>'
. '<input class="input" type="text" inputmode="decimal" pattern="[+\\-]?[0-9]*([.,][0-9]*)?" spellcheck="false" name="'
. $this->e($this->fname($field, '[parts][' . $i . ']')) . '" value="' . $this->e($val) . '"></label>';
}
return $html . '</div>';
}
if ($kind === 'url') {
return '<input class="input" type="url" name="' . $this->e($this->fname($field, '[raw]'))
. '" value="' . $this->e($parsed) . '">';
}
if ($kind === 'code') {
$language = isset($settings['language']) ? (string) $settings['language'] : 'html';
return '<textarea class="textarea mono" name="' . $this->e($this->fname($field, '[raw]'))
. '" rows="8" data-code-editor data-mode="' . $this->e($language) . '" data-height="' . $editorHeight
. '" spellcheck="false">' . $this->e($parsed) . '</textarea>';
}
if ($kind === 'rich' || $kind === 'rich_simple' || $kind === 'rich_slim') {
$preset = $kind === 'rich' ? 'full' : ($kind === 'rich_simple' ? 'simple' : 'slim');
return '<textarea class="textarea documents-rich-editor" name="' . $this->e($this->fname($field, '[raw]'))
. '" data-rich-editor data-rich-preset="' . $preset . '" data-editor-height="' . $editorHeight
. '" spellcheck="true">' . $this->e($parsed) . '</textarea>';
}
if ($kind === 'textarea') {
return '<textarea class="textarea" name="' . $this->e($this->fname($field, '[raw]'))
. '" rows="6" style="height:' . $editorHeight . 'px">' . $this->e($parsed) . '</textarea>';
}
if ($kind === 'text') {
$options = isset($field['options']) && is_array($field['options']) ? $field['options'] : array();
if (!empty($options)) {
$html = '<select class="select" name="' . $this->e($this->fname($field, '[raw]')) . '">'
. '<option value="">Не выбрано</option>';
foreach ($options as $option) {
$ov = isset($option['value']) ? (string) $option['value'] : '';
$ol = isset($option['label']) ? (string) $option['label'] : $ov;
$html .= '<option value="' . $this->e($ov) . '"' . ((string) $parsed === $ov ? ' selected' : '') . '>' . $this->e($ol) . '</option>';
}
return $html . '</select>';
}
return '<input class="input" type="text" name="' . $this->e($this->fname($field, '[raw]'))
. '" value="' . $this->e($parsed) . '">';
}
return $this->renderGeneric($field);
}
protected function definitions()
{
return array(
'single_line' => array(
'title' => 'Строка',
'icon' => 'forms',
'summary' => 'Обычное текстовое поле для короткого значения.',
'default_label' => 'Текст по умолчанию',
'default_hint' => 'Можно оставить пустым. Значение будет подставлено при создании документа.',
'default_kind' => 'text',
'template_hint' => 'HTML/PHP-шаблон вывода значения поля на сайте.',
'entity_fields' => array('value'),
),
'single_line_numeric' => array(
'title' => 'Число',
'icon' => 'number',
'summary' => 'Одно числовое значение.',
'default_label' => 'Число по умолчанию',
'default_hint' => 'Можно оставить пустым или указать число.',
'default_kind' => 'number',
'entity_fields' => array('number'),
),
'number' => array(
'title' => 'Числовое значение',
'icon' => 'number',
'summary' => 'Число с настраиваемым публичным форматом: сумма, процент, величина или рейтинг.',
'default_label' => 'Число по умолчанию',
'default_hint' => 'Можно использовать точку или запятую. В хранилище значение нормализуется автоматически.',
'default_kind' => 'number',
'entity_fields' => array('number'),
),
'date_time' => array(
'title' => 'Дата и время',
'icon' => 'calendar-time',
'summary' => 'Дата или дата со временем в одном типе с настраиваемым публичным форматом.',
'default_label' => 'Дата по умолчанию',
'default_hint' => 'Хранится как Unix timestamp и редактируется нативным контролом браузера.',
'default_kind' => 'datetime',
'entity_fields' => array('timestamp'),
),
'period' => array(
'title' => 'Период',
'icon' => 'calendar-event',
'summary' => 'Дата начала и окончания для событий, публикаций и временных блоков.',
'default_label' => 'Период по умолчанию',
'default_hint' => 'Формат: начало|окончание. Поддерживаются YYYY-MM-DD и дата со временем.',
'default_kind' => 'period',
'entity_fields' => array('start', 'end'),
),
'single_line_numeric_two' => array(
'title' => 'Два числа',
'icon' => 'numbers',
'summary' => 'Две числовые части, которые legacy хранит через вертикальную черту.',
'default_label' => 'Числа по умолчанию',
'default_hint' => 'Формат: значение1|значение2.',
'default_kind' => 'pipe',
'entity_fields' => array('parts[]'),
),
'single_line_numeric_three' => array(
'title' => 'Три числа',
'icon' => 'numbers',
'summary' => 'Три числовые части, которые legacy хранит через вертикальную черту.',
'default_label' => 'Числа по умолчанию',
'default_hint' => 'Формат: значение1|значение2|значение3.',
'default_kind' => 'pipe',
'entity_fields' => array('parts[]'),
),
'multi_line' => array(
'title' => 'Многострочный текст',
'icon' => 'align-left',
'summary' => 'Полноценный визуальный HTML-редактор.',
'default_label' => 'Текст по умолчанию',
'default_hint' => 'Сохраняется как HTML, совместимый с legacy multi_line.',
'default_kind' => 'rich',
'entity_fields' => array('value'),
),
'content' => array(
'title' => 'Текстовое содержимое',
'icon' => 'file-text',
'summary' => 'Единое поле с выбором обычного текста, Tiptap или CodeMirror.',
'default_label' => 'Содержимое по умолчанию',
'default_hint' => 'Режим редактора и публичного вывода задаётся в настройках типа.',
'default_kind' => 'rich',
'entity_fields' => array('value'),
),
'richtext' => array(
'title' => 'Форматированный текст',
'icon' => 'text-format',
'summary' => 'Визуальный HTML-редактор с полным набором инструментов.',
'default_label' => 'Текст по умолчанию',
'default_hint' => 'Сохраняется как HTML и редактируется через общий rich-text редактор.',
'default_kind' => 'rich',
'entity_fields' => array('value'),
),
'multi_line_simple' => array(
'title' => 'Простой многострочный текст',
'icon' => 'align-left',
'summary' => 'Визуальный HTML-редактор средней высоты.',
'default_label' => 'Текст по умолчанию',
'default_kind' => 'rich_simple',
'entity_fields' => array('value'),
),
'multi_line_slim' => array(
'title' => 'Компактный многострочный текст',
'icon' => 'align-left',
'summary' => 'Компактный визуальный HTML-редактор.',
'default_label' => 'Текст по умолчанию',
'default_kind' => 'rich_slim',
'entity_fields' => array('value'),
),
'checkbox' => array(
'title' => 'Флажок',
'icon' => 'checkbox',
'summary' => 'Булево значение: включено или выключено.',
'default_label' => 'Значение по умолчанию',
'default_hint' => 'Используйте 1 для включенного состояния или 0 для выключенного.',
'default_kind' => 'boolean',
'entity_fields' => array('checked'),
),
'date' => array(
'title' => 'Дата',
'icon' => 'calendar',
'summary' => 'Дата или дата-время в документе.',
'default_label' => 'Дата по умолчанию',
'default_hint' => 'Поддерживаются YYYY-MM-DD, timestamp или пустое значение.',
'default_kind' => 'date',
'entity_fields' => array('timestamp', 'date'),
),
'link' => array(
'title' => 'Ссылка',
'icon' => 'link',
'summary' => 'URL или относительная ссылка.',
'default_label' => 'Ссылка по умолчанию',
'default_hint' => 'Например /catalog или https://example.com.',
'default_kind' => 'url',
'entity_fields' => array('value'),
),
'contact' => array(
'title' => 'Контакт',
'icon' => 'address-book',
'summary' => 'Email, телефон или URL с режимной валидацией и безопасной публичной ссылкой.',
'default_label' => 'Значение по умолчанию',
'default_hint' => 'Формат значения определяется настройкой типа контакта.',
'default_kind' => 'text',
'entity_fields' => array('value'),
),
'color' => array(
'title' => 'Цвет',
'icon' => 'palette',
'summary' => 'HEX-цвет с нативным picker и синхронным ручным вводом.',
'default_label' => 'Цвет по умолчанию',
'default_hint' => 'Формат #rgb или #rrggbb.',
'default_kind' => 'text',
'entity_fields' => array('value'),
),
'range' => array(
'title' => 'Числовой диапазон',
'icon' => 'arrows-horizontal',
'summary' => 'Нижняя и верхняя граница с единицей измерения и JSON-хранением.',
'default_label' => 'Диапазон по умолчанию',
'default_hint' => 'Формат: минимум|максимум. Одну границу можно оставить пустой.',
'default_kind' => 'range',
'entity_fields' => array('min', 'max'),
),
'dimensions' => array(
'title' => 'Габариты',
'icon' => 'box-model-2',
'summary' => 'Длина, ширина и высота с единицей измерения и JSON-хранением.',
'default_label' => 'Габариты по умолчанию',
'default_hint' => 'Формат: длина|ширина|высота.',
'default_kind' => 'dimensions',
'entity_fields' => array('length', 'width', 'height'),
),
'address' => array(
'title' => 'Адрес',
'icon' => 'map-pin',
'summary' => 'Структурированный почтовый адрес с необязательными координатами.',
'default_label' => 'Адрес по умолчанию',
'default_hint' => 'Формат: индекс|регион|город|улица|дом|помещение|широта|долгота.',
'default_kind' => 'address',
'entity_fields' => array('postal_code', 'region', 'city', 'street', 'building', 'unit', 'latitude', 'longitude'),
),
'code' => array(
'title' => 'Код',
'icon' => 'code-dots',
'summary' => 'Редактор исходного кода.',
'default_label' => 'Код по умолчанию',
'default_kind' => 'code',
'entity_fields' => array('value'),
),
);
}
protected function cleanItems(array $items)
{
$out = array();
foreach ($items as $item) {
$item = trim((string) $item);
if ($item !== '') {
$out[] = $item;
}
}
return array_values($out);
}
protected function editorHeight(array $settings, $default)
{
$height = isset($settings['height']) ? (int) $settings['height'] : (int) $default;
return max(140, min(900, $height));
}
}