1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
namespace DigitalWand\AdminHelper\Widget; |
4
|
|
|
|
5
|
|
|
use Bitrix\Main\Localization\Loc; |
6
|
|
|
use DigitalWand\AdminHelper\Helper\AdminBaseHelper; |
7
|
|
|
use DigitalWand\AdminHelper\Helper\AdminEditHelper; |
8
|
|
|
use DigitalWand\AdminHelper\Helper\AdminListHelper; |
9
|
|
|
use Bitrix\Main\Entity\DataManager; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Виджет - класс, отвечающий за внешний вид отдельно взятого поля сущности. Один виджет отвечает за: |
13
|
|
|
* <ul> |
14
|
|
|
* <li>Отображение поля на странице редактирования</li> |
15
|
|
|
* <li>Отображение ячейки поля в таблице списка - при просмотре и редактировании</li> |
16
|
|
|
* <li>Отображение фильтра по данному полю</li> |
17
|
|
|
* <li>Валидацию значения поля</li> |
18
|
|
|
* </ul> |
19
|
|
|
* |
20
|
|
|
* Также виджетами осуществляется предварительная обработка данных: |
21
|
|
|
* <ul> |
22
|
|
|
* <li>Перед сохранением значения поля в БД</li> |
23
|
|
|
* <li>После получения значения поля из БД</li> |
24
|
|
|
* <li>Модификация запроса перед фильтрацией</li> |
25
|
|
|
* <li>Модификация пуеДшые перед выборкой данных</li> |
26
|
|
|
* </ul> |
27
|
|
|
* |
28
|
|
|
* Для получения минимальной функциональности достаточно переопределить основные методы, отвечающие за отображение |
29
|
|
|
* виджета в списке и на детальной. |
30
|
|
|
* |
31
|
|
|
* Каждый виджет имеет ряд специфических настроек, некоторые из которых обязательны для заполнения. Подробную |
32
|
|
|
* документацию по настройкам стоит искать в документации к конкретному виджету. Настройки могут быть переданы в |
33
|
|
|
* виджет как при описании всего интерфейса в файле Interface.php, так и непосредственно во время исполнения, |
34
|
|
|
* внутри Helper-классов. |
35
|
|
|
* |
36
|
|
|
* При указании настроек типа "да"/"нет", нельзя использовать строковые обозначения "Y"/"N": |
37
|
|
|
* для этого есть булевы true и false. |
38
|
|
|
* |
39
|
|
|
* Настройки базового класса: |
40
|
|
|
* <ul> |
41
|
|
|
* <li><b>HIDE_WHEN_CREATE</b> - скрывает поле в форме редактирования, если создаётся новый элемент, а не открыт |
42
|
|
|
* существующий на редактирование.</li> |
43
|
|
|
* <li><b>TITLE</b> - название поля. Если не задано то возьмется значение title из DataManager::getMap() |
44
|
|
|
* через getField($code)->getTitle(). Будет использовано в фильтре, заголовке таблицы и в качестве подписи поля |
45
|
|
|
* на |
46
|
|
|
* странице редактирования.</li> |
47
|
|
|
* <li><b>REQUIRED</b> - является ли поле обязательным.</li> |
48
|
|
|
* <li><b>READONLY</b> - поле нельзя редактировать, предназначено только для чтения</li> |
49
|
|
|
* <li><b>FILTER</b> - позволяет указать способ фильтрации по полю. В базовом классе возможен только вариант "BETWEEN" |
50
|
|
|
* или "><". И в том и в другом случае это будет означать фильтрацию по диапазону значений. Количество возможных |
51
|
|
|
* вариантов этого параметра может быть расширено в наследуемых классах</li> |
52
|
|
|
* <li><b>UNIQUE</b> - поле должно содержать только уникальные значения</li> |
53
|
|
|
* <li><b>VIRTUAL</b> - особая настройка, отражается как на поведении виджета, так и на поведении хэлперов. Поле, |
54
|
|
|
* объявленное виртуальным, отображается в графическом интерфейче, однако не участвоует в запросах к БД. Опция |
55
|
|
|
* может быть полезной при реализации нестандартной логики, когда, к примеру, под именем одного поля могут |
56
|
|
|
* выводиться данные из нескольких полей сразу. </li> |
57
|
|
|
* <li><b>EDIT_IN_LIST</b> - параметр не обрабатывается непосредственно виджетом, однако используется хэлпером. |
58
|
|
|
* Указывает, можно ли редактировать данное поле в спискке</li> |
59
|
|
|
* <li><b>MULTIPLE</b> - bool является ли поле множественным</li> |
60
|
|
|
* <li><b>MULTIPLE_FIELDS</b> - bool поля используемые в хранилище множественных значений и их алиасы</li> |
61
|
|
|
* <li><b>LIST</b> - отображать ли поле в списке доступных в настройках столбцов таблицы (по-умолчанию true)</li> |
62
|
|
|
* <li><b>HEADER</b> - является ли столбец отображаемым по-умолчанию, если вывод столбцов таблицы не настроен (по-умолчанию true)</li> |
63
|
|
|
* </ul> |
64
|
|
|
* |
65
|
|
|
* Как сделать виджет множественным? |
66
|
|
|
* <ul> |
67
|
|
|
* <li>Реализуйте метод genMultipleEditHTML(). Метод должен выводить множественную форму ввода. Для реализации формы |
68
|
|
|
* ввода есть JS хелпер HelperWidget::jsHelper()</li> |
69
|
|
|
* <li>Опишите поля, которые будут переданы связи в EntityManager. Поля описываются в настройке "MULTIPLE_FIELDS" |
70
|
|
|
* виджета. По умолчанию множественный виджет использует поля ID, ENTITY_ID, VALUE</li> |
71
|
|
|
* <li>Полученные от виджета данные будут переданы в EntityManager и сохранены как связанные данные</li> |
72
|
|
|
* </ul> |
73
|
|
|
* Пример реализации можно увидеть в виджете StringWidget |
74
|
|
|
* |
75
|
|
|
* Как использовать множественный виджет? |
76
|
|
|
* <ul> |
77
|
|
|
* <li> |
78
|
|
|
* Создайте таблицу и модель, которая будет хранить данные поля |
79
|
|
|
* - Таблица обязательно должна иметь поля, которые требует виджет. |
80
|
|
|
* Обязательные поля виджета по умолчанию описаны в: HelperWidget::$settings['MULTIPLE_FIELDS'] |
81
|
|
|
* Если у виджета нестандартный набор полей, то они хранятся в: SomeWidget::$settings['MULTIPLE_FIELDS'] |
82
|
|
|
* - Если поля, которые требует виджет есть в вашей таблице, но они имеют другие названия, |
83
|
|
|
* можно настроить виджет для работы с вашими полями. |
84
|
|
|
* Для этого переопределите настройку MULTIPLE_FIELDS при объявлении поля в интерфейсе следующим способом: |
85
|
|
|
* ``` |
86
|
|
|
* 'RELATED_LINKS' => array( |
87
|
|
|
* 'WIDGET' => new StringWidget(), |
88
|
|
|
* 'TITLE' => 'Ссылки', |
89
|
|
|
* 'MULTIPLE' => true, |
90
|
|
|
* // Обратите внимание, именно тут переопределяются поля виджета |
91
|
|
|
* 'MULTIPLE_FIELDS' => array( |
92
|
|
|
* 'ID', // Должны быть прописаны все поля, даже те, которые не нужно переопределять |
93
|
|
|
* 'ENTITY_ID' => 'NEWS_ID', // ENTITY_ID - поле, которое требует виджет, NEWS_ID - пример поля, которое |
94
|
|
|
* будет использоваться вместо ENTITY_ID |
95
|
|
|
* 'VALUE' => 'LINK', // VALUE - поле, которое требует виджет, LINK - пример поля, которое будет |
96
|
|
|
* использоваться вместо VALUE |
97
|
|
|
* ) |
98
|
|
|
* ), |
99
|
|
|
* ``` |
100
|
|
|
* </li> |
101
|
|
|
* |
102
|
|
|
* <li> |
103
|
|
|
* Далее в основной модели (та, которая указана в AdminBaseHelper::$model) нужно прописать связь с моделью, |
104
|
|
|
* в которой вы хотите хранить данные поля |
105
|
|
|
* Пример объявления связи: |
106
|
|
|
* ``` |
107
|
|
|
* new Entity\ReferenceField( |
108
|
|
|
* 'RELATED_LINKS', |
109
|
|
|
* 'namespace\NewsLinksTable', |
110
|
|
|
* array('=this.ID' => 'ref.NEWS_ID'), |
111
|
|
|
* // Условия FIELD и ENTITY не обязательны, подробности смотрите в комментариях к классу @see EntityManager |
112
|
|
|
* 'ref.FIELD' => new DB\SqlExpression('?s', 'NEWS_LINKS'), |
113
|
|
|
* 'ref.ENTITY' => new DB\SqlExpression('?s', 'news'), |
114
|
|
|
* ), |
115
|
|
|
* ``` |
116
|
|
|
* </li> |
117
|
|
|
* |
118
|
|
|
* <li> |
119
|
|
|
* Что бы виджет работал во множественном режиме, нужно при описании интерфейса поля указать параметр MULTIPLE => true |
120
|
|
|
* ``` |
121
|
|
|
* 'RELATED_LINKS' => array( |
122
|
|
|
* 'WIDGET' => new StringWidget(), |
123
|
|
|
* 'TITLE' => 'Ссылки', |
124
|
|
|
* // Включаем режим множественного ввода |
125
|
|
|
* 'MULTIPLE' => true, |
126
|
|
|
* ) |
127
|
|
|
* ``` |
128
|
|
|
* </li> |
129
|
|
|
* |
130
|
|
|
* <li> |
131
|
|
|
* Готово :) |
132
|
|
|
* </li> |
133
|
|
|
* </ul> |
134
|
|
|
* |
135
|
|
|
* О том как сохраняются данные множественных виджетов можно узнать из комментариев |
136
|
|
|
* класса \DigitalWand\AdminHelper\EntityManager. |
137
|
|
|
* |
138
|
|
|
* @see EntityManager |
139
|
|
|
* @see HelperWidget::getEditHtml() |
140
|
|
|
* @see HelperWidget::generateRow() |
141
|
|
|
* @see showFilterHtml::showFilterHTML() |
142
|
|
|
* @see HelperWidget::setSetting() |
143
|
|
|
* |
144
|
|
|
* @author Nik Samokhvalov <[email protected]> |
145
|
|
|
* @author Dmitriy Baibuhtin <[email protected]> |
146
|
|
|
*/ |
147
|
|
|
abstract class HelperWidget |
|
|
|
|
148
|
|
|
{ |
149
|
|
|
const LIST_HELPER = 1; |
150
|
|
|
const EDIT_HELPER = 2; |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @var string Код поля. |
154
|
|
|
*/ |
155
|
|
|
protected $code; |
156
|
|
|
/** |
157
|
|
|
* @var array $settings Настройки виджета для данной модели. |
158
|
|
|
*/ |
159
|
|
|
protected $settings = array( |
160
|
|
|
// Поля множественного виджета по умолчанию (array('ОРИГИНАЛЬНОЕ НАЗВАНИЕ', 'ОРИГИНАЛЬНОЕ НАЗВАНИЕ' => 'АЛИАС')) |
|
|
|
|
161
|
|
|
'MULTIPLE_FIELDS' => array('ID', 'VALUE', 'ENTITY_ID') |
162
|
|
|
); |
163
|
|
|
/** |
164
|
|
|
* @var array Настройки "по-умолчанию" для модели. |
165
|
|
|
*/ |
166
|
|
|
static protected $defaults; |
167
|
|
|
/** |
168
|
|
|
* @var DataManager Название класса модели. |
169
|
|
|
*/ |
170
|
|
|
protected $entityName; |
171
|
|
|
/** |
172
|
|
|
* @var array Данные модели. |
173
|
|
|
*/ |
174
|
|
|
protected $data; |
175
|
|
|
/** @var AdminBaseHelper|AdminListHelper|AdminEditHelper $helper Экземпляр хэлпера, вызывающий данный виджет. |
176
|
|
|
*/ |
177
|
|
|
protected $helper; |
178
|
|
|
/** |
179
|
|
|
* @var bool Статус отображения JS хелпера. Используется для исключения дублирования JS-кода. |
180
|
|
|
*/ |
181
|
|
|
protected $jsHelper = false; |
182
|
|
|
/** |
183
|
|
|
* @var array $validationErrors Ошибки валидации поля. |
184
|
|
|
*/ |
185
|
|
|
protected $validationErrors = array(); |
186
|
|
|
/** |
187
|
|
|
* @var string Строка, добавляемая к полю name полей фильтра. |
188
|
|
|
*/ |
189
|
|
|
protected $filterFieldPrefix = 'find_'; |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Эксемпляр виджета создаётся всего один раз, при описании настроек интерфейса. При создании есть возможность |
193
|
|
|
* сразу указать для него необходимые настройки. |
194
|
|
|
* |
195
|
|
|
* @param array $settings |
196
|
|
|
*/ |
197
|
|
|
public function __construct(array $settings = array()) |
198
|
|
|
{ |
199
|
|
|
Loc::loadMessages(__FILE__); |
200
|
|
|
|
201
|
|
|
$this->settings = $settings; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Генерирует HTML для редактирования поля. |
206
|
|
|
* |
207
|
|
|
* @return string |
208
|
|
|
* |
209
|
|
|
* @api |
210
|
|
|
*/ |
211
|
|
|
abstract protected function getEditHtml(); |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Генерирует HTML для редактирования поля в мульти-режиме. |
215
|
|
|
* |
216
|
|
|
* @return string |
217
|
|
|
* |
218
|
|
|
* @api |
219
|
|
|
*/ |
220
|
|
|
protected function getMultipleEditHtml() |
221
|
|
|
{ |
222
|
|
|
return Loc::getMessage('DIGITALWAND_AH_MULTI_NOT_SUPPORT'); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Оборачивает поле в HTML код, который в большинстве случаев менять не придется. Далее вызывается |
227
|
|
|
* кастомизируемая часть. |
228
|
|
|
* |
229
|
|
|
* @param bool $isPKField Является ли поле первичным ключом модели. |
230
|
|
|
* |
231
|
|
|
* @see HelperWidget::getEditHtml(); |
232
|
|
|
*/ |
233
|
|
|
public function showBasicEditField($isPKField) |
234
|
|
|
{ |
235
|
|
|
if ($this->getSettings('HIDE_WHEN_CREATE') AND !isset($this->data['ID'])) { |
|
|
|
|
236
|
|
|
return; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
// JS хелперы |
240
|
|
|
$this->jsHelper(); |
241
|
|
|
|
242
|
|
|
if ($this->getSettings('USE_BX_API')) { |
243
|
|
|
$this->getEditHtml(); |
244
|
|
|
} else { |
245
|
|
|
print '<tr>'; |
246
|
|
|
$title = $this->getSettings('TITLE'); |
247
|
|
|
if ($this->getSettings('REQUIRED') === true) { |
248
|
|
|
$title = '<b>' . $title . '</b>'; |
249
|
|
|
} |
250
|
|
|
print '<td width="40%" style="vertical-align: top;">' . $title . ':</td>'; |
251
|
|
|
|
252
|
|
|
$field = $this->getValue(); |
253
|
|
|
|
254
|
|
|
if (is_null($field)) { |
255
|
|
|
$field = ''; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$readOnly = $this->getSettings('READONLY'); |
259
|
|
|
|
260
|
|
|
if (!$readOnly AND !$isPKField) { |
|
|
|
|
261
|
|
|
if ($this->getSettings('MULTIPLE')) { |
262
|
|
|
$field = $this->getMultipleEditHtml(); |
263
|
|
|
} else { |
264
|
|
|
$field = $this->getEditHtml(); |
265
|
|
|
} |
266
|
|
|
} else { |
267
|
|
|
if ($readOnly) { |
268
|
|
|
if ($this->getSettings('MULTIPLE')) { |
269
|
|
|
$field = $this->getMultipleValueReadonly(); |
270
|
|
|
} else { |
271
|
|
|
$field = $this->getValueReadonly(); |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
print '<td width="60%">' . $field . '</td>'; |
277
|
|
|
print '</tr>'; |
278
|
|
|
} |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Возвращает значение поля в форме "только для чтения" для не множественных свойств. |
283
|
|
|
* |
284
|
|
|
* @return mixed |
285
|
|
|
*/ |
286
|
|
|
protected function getValueReadonly() |
287
|
|
|
{ |
288
|
|
|
return static::prepareToOutput($this->getValue()); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Возвращает значения множественного поля. |
293
|
|
|
* |
294
|
|
|
* @return array |
295
|
|
|
*/ |
296
|
|
|
protected function getMultipleValue() |
297
|
|
|
{ |
298
|
|
|
$rsEntityData = null; |
|
|
|
|
299
|
|
|
$values = array(); |
300
|
|
|
if (!empty($this->data['ID'])) { |
301
|
|
|
$entityName = $this->entityName; |
302
|
|
|
$rsEntityData = $entityName::getList(array( |
303
|
|
|
'select' => array('REFERENCE_' => $this->getCode() . '.*'), |
304
|
|
|
'filter' => array('=ID' => $this->data['ID']) |
305
|
|
|
)); |
306
|
|
|
|
307
|
|
|
if ($rsEntityData) { |
308
|
|
|
while ($referenceData = $rsEntityData->fetch()) { |
309
|
|
|
if (empty($referenceData['REFERENCE_' . $this->getMultipleField('ID')])) { |
310
|
|
|
continue; |
311
|
|
|
} |
312
|
|
|
$values[] = $referenceData['REFERENCE_' . $this->getMultipleField('VALUE')]; |
313
|
|
|
} |
314
|
|
|
} |
315
|
|
|
} else { |
316
|
|
|
if ($this->data[$this->code]) { |
317
|
|
|
$values = $this->data[$this->code]; |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
return $values; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* Возвращает значение поля в форме "только для чтения" для множественных свойств. |
326
|
|
|
* |
327
|
|
|
* @return string |
328
|
|
|
*/ |
329
|
|
|
protected function getMultipleValueReadonly() |
330
|
|
|
{ |
331
|
|
|
$values = $this->getMultipleValue(); |
332
|
|
|
|
333
|
|
|
foreach ($values as &$value) { |
334
|
|
|
$value = static::prepareToOutput($value); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
return join('<br/>', $values); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Обработка строки для безопасного отображения. Если нужно отобразить текст как аттрибут тега, |
342
|
|
|
* используйте static::prepareToTag(). |
343
|
|
|
* |
344
|
|
|
* @param string $string |
345
|
|
|
* @param bool $hideTags Скрыть теги: |
346
|
|
|
* |
347
|
|
|
* - true - вырезать теги оставив содержимое. Результат обработки: <b>text</b> = text |
348
|
|
|
* |
349
|
|
|
* - false - отобразаить теги в виде текста. Результат обработки: <b>text</b> = <b>text</b> |
350
|
|
|
* |
351
|
|
|
* @return string |
352
|
|
|
*/ |
353
|
|
|
public static function prepareToOutput($string, $hideTags = true) |
354
|
|
|
{ |
355
|
|
|
if ($hideTags) { |
356
|
|
|
return preg_replace('/<.+>/mU', '', $string); |
357
|
|
|
} else { |
358
|
|
|
return htmlspecialchars($string, ENT_QUOTES, SITE_CHARSET); |
359
|
|
|
} |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* Подготовка строки для использования в аттрибутах тегов. Например: |
364
|
|
|
* ``` |
365
|
|
|
* <input name="test" value="<?= HelperWidget::prepareToTagAttr($value) ?>"/> |
366
|
|
|
* ``` |
367
|
|
|
* |
368
|
|
|
* @param string $string |
369
|
|
|
* |
370
|
|
|
* @return string |
371
|
|
|
*/ |
372
|
|
|
public static function prepareToTagAttr($string) |
373
|
|
|
{ |
374
|
|
|
// Не используйте addcslashes в этом методе, иначе в тегах будут дубли обратных слешей |
375
|
|
|
return htmlspecialchars($string, ENT_QUOTES, SITE_CHARSET); |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* Подготовка строки для использования в JS. |
380
|
|
|
* |
381
|
|
|
* @param string $string |
382
|
|
|
* |
383
|
|
|
* @return string |
384
|
|
|
*/ |
385
|
|
|
public static function prepareToJs($string) |
386
|
|
|
{ |
387
|
|
|
$string = htmlspecialchars($string, ENT_QUOTES, SITE_CHARSET); |
388
|
|
|
$string = addcslashes($string, "\r\n\"\\"); |
389
|
|
|
|
390
|
|
|
return $string; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
/** |
394
|
|
|
* Генерирует HTML для поля в списке. |
395
|
|
|
* |
396
|
|
|
* @param \CAdminListRow $row |
397
|
|
|
* @param array $data Данные текущей строки. |
398
|
|
|
* |
399
|
|
|
* @return void |
400
|
|
|
* |
401
|
|
|
* @see AdminListHelper::addRowCell() |
402
|
|
|
* |
403
|
|
|
* @api |
404
|
|
|
*/ |
405
|
|
|
abstract public function generateRow(&$row, $data); |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Генерирует HTML для поля фильтрации. |
409
|
|
|
* |
410
|
|
|
* @return void |
411
|
|
|
* |
412
|
|
|
* @see AdminListHelper::createFilterForm() |
413
|
|
|
* |
414
|
|
|
* @api |
415
|
|
|
*/ |
416
|
|
|
abstract public function showFilterHtml(); |
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* Возвращает массив настроек данного виджета, либо значение отдельного параметра, если указано его имя. |
420
|
|
|
* |
421
|
|
|
* @param string $name Название конкретного параметра. |
422
|
|
|
* |
423
|
|
|
* @return array|mixed |
424
|
|
|
* |
425
|
|
|
* @api |
426
|
|
|
*/ |
427
|
|
|
public function getSettings($name = '') |
428
|
|
|
{ |
429
|
|
|
if (empty($name)) { |
430
|
|
|
return $this->settings; |
431
|
|
|
} else { |
432
|
|
|
if (isset($this->settings[$name])) { |
433
|
|
|
return $this->settings[$name]; |
434
|
|
|
} else { |
435
|
|
|
if (isset(static::$defaults[$name])) { |
436
|
|
|
return static::$defaults[$name]; |
437
|
|
|
} else { |
438
|
|
|
return false; |
439
|
|
|
} |
440
|
|
|
} |
441
|
|
|
} |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* Передаёт в виджет ссылку на вызывающий его объект. |
446
|
|
|
* |
447
|
|
|
* @param AdminBaseHelper $helper |
448
|
|
|
*/ |
449
|
|
|
public function setHelper(&$helper) |
450
|
|
|
{ |
451
|
|
|
$this->helper = $helper; |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
/** |
455
|
|
|
* Возвращает текукщее значение поля фильтрации (спец. символы экранированы). |
456
|
|
|
* |
457
|
|
|
* @return bool|string |
458
|
|
|
*/ |
459
|
|
|
protected function getCurrentFilterValue() |
|
|
|
|
460
|
|
|
{ |
461
|
|
|
if (isset($GLOBALS[$this->filterFieldPrefix . $this->code])) { |
462
|
|
|
return htmlspecialcharsbx($GLOBALS[$this->filterFieldPrefix . $this->code]); |
463
|
|
|
} else { |
464
|
|
|
return false; |
465
|
|
|
} |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* Проверяет корректность введенных в фильтр значений |
470
|
|
|
* |
471
|
|
|
* @param string $operationType тип операции |
472
|
|
|
* @param mixed $value значение фильтра |
473
|
|
|
* |
474
|
|
|
* @see AdminListHelper::checkFilter(); |
475
|
|
|
* @return bool |
476
|
|
|
*/ |
477
|
|
|
public function checkFilter($operationType, $value) |
|
|
|
|
478
|
|
|
{ |
479
|
|
|
return true; |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* Позволяет модифицировать опции, передаваемые в getList, непосредственно перед выборкой. |
484
|
|
|
* Если в настройках явно указан способ фильтрации, до добавляет соответствующий префикс в $arFilter. |
485
|
|
|
* Если фильтр BETWEEN, то формирует сложную логику фильтрации. |
486
|
|
|
* |
487
|
|
|
* @param array $filter $arFilter целиком |
488
|
|
|
* @param array $select |
489
|
|
|
* @param $sort |
490
|
|
|
* @param array $raw $arSelect, $arFilter, $arSort до примененных к ним преобразований. |
491
|
|
|
* |
492
|
|
|
* @see AdlinListHelper::getData(); |
493
|
|
|
*/ |
494
|
|
|
public function changeGetListOptions(&$filter, &$select, &$sort, $raw) |
|
|
|
|
495
|
|
|
{ |
496
|
|
|
if ($this->isFilterBetween()) { |
497
|
|
|
$field = $this->getCode(); |
498
|
|
|
$from = $to = false; |
499
|
|
|
|
500
|
|
|
if (isset($_REQUEST['find_' . $field . '_from'])) { |
501
|
|
|
$from = $_REQUEST['find_' . $field . '_from']; |
502
|
|
|
if (is_a($this, 'DateWidget')) { |
503
|
|
|
$from = date('Y-m-d H:i:s', strtotime($from)); |
504
|
|
|
} |
505
|
|
|
} |
506
|
|
|
if (isset($_REQUEST['find_' . $field . '_to'])) { |
507
|
|
|
$to = $_REQUEST['find_' . $field . '_to']; |
508
|
|
|
if (is_a($this, 'DateWidget')) { |
509
|
|
|
$to = date('Y-m-d 23:59:59', strtotime($to)); |
510
|
|
|
} else if ( |
511
|
|
|
is_a($this, '\DigitalWand\AdminHelper\Widget\DateTimeWidget') && |
512
|
|
|
!preg_match('/\d{2}:\d{2}:\d{2}/', $to) |
513
|
|
|
) { |
514
|
|
|
$to = date('d.m.Y 23:59:59', strtotime($to)); |
515
|
|
|
} |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
if ($from !== false AND $to !== false) { |
|
|
|
|
519
|
|
|
$filter['><' . $field] = array($from, $to); |
520
|
|
|
} else { |
521
|
|
|
if ($from !== false) { |
522
|
|
|
$filter['>' . $field] = $from; |
523
|
|
|
} else { |
524
|
|
|
if ($to !== false) { |
525
|
|
|
$filter['<' . $field] = $to; |
526
|
|
|
} |
527
|
|
|
} |
528
|
|
|
} |
529
|
|
|
} else { |
530
|
|
|
if ($filterPrefix = $this->getSettings('FILTER') AND $filterPrefix !== true AND isset($filter[$this->getCode()])) { |
|
|
|
|
531
|
|
|
$filter[$filterPrefix . $this->getCode()] = $filter[$this->getCode()]; |
532
|
|
|
unset($filter[$this->getCode()]); |
533
|
|
|
} |
534
|
|
|
} |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
/** |
538
|
|
|
* Проверяет оператор фильтрации. |
539
|
|
|
* |
540
|
|
|
* @return bool |
541
|
|
|
*/ |
542
|
|
|
protected function isFilterBetween() |
543
|
|
|
{ |
544
|
|
|
return $this->getSettings('FILTER') === '><' OR $this->getSettings('FILTER') === 'BETWEEN'; |
|
|
|
|
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
/** |
548
|
|
|
* Действия, выполняемые над полем в процессе редактирования элемента, до его сохранения. |
549
|
|
|
* По-умолчанию выполняется проверка обязательных полей и уникальности. |
550
|
|
|
* |
551
|
|
|
* @see AdminEditHelper::editAction(); |
552
|
|
|
* @see AdminListHelper::editAction(); |
553
|
|
|
*/ |
554
|
|
|
public function processEditAction() |
555
|
|
|
{ |
556
|
|
|
if (!$this->checkRequired()) { |
557
|
|
|
$this->addError('DIGITALWAND_AH_REQUIRED_FIELD_ERROR'); |
558
|
|
|
} |
559
|
|
|
if ($this->getSettings('UNIQUE') && !$this->isUnique()) { |
560
|
|
|
$this->addError('DIGITALWAND_AH_DUPLICATE_FIELD_ERROR'); |
561
|
|
|
} |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* В совсем экзотических случаях может потребоваться моджифицировать значение поля уже после его сохраненния в БД - |
566
|
|
|
* для последующей обработки каким-либо другим классом. |
567
|
|
|
*/ |
568
|
|
|
public function processAfterSaveAction() |
569
|
|
|
{ |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* Добавляет строку ошибки в массив ошибок. |
574
|
|
|
* |
575
|
|
|
* @param string $messageId Код сообщения об ошибке из лэнг-файла. Плейсхолдер #FIELD# будет заменён на значение |
576
|
|
|
* параметра TITLE. |
577
|
|
|
* @param array $replace Данные для замены. |
578
|
|
|
* |
579
|
|
|
* @see Loc::getMessage() |
580
|
|
|
*/ |
581
|
|
|
protected function addError($messageId, $replace = array()) |
582
|
|
|
{ |
583
|
|
|
$this->validationErrors[$this->getCode()] = Loc::getMessage( |
584
|
|
|
$messageId, |
585
|
|
|
array_merge(array('#FIELD#' => $this->getSettings('TITLE')), $replace) |
586
|
|
|
); |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* Проверка заполненности обязательных полей. |
591
|
|
|
* Не должны быть null или содержать пустую строку. |
592
|
|
|
* |
593
|
|
|
* @return bool |
594
|
|
|
*/ |
595
|
|
View Code Duplication |
public function checkRequired() |
|
|
|
|
596
|
|
|
{ |
597
|
|
|
if ($this->getSettings('REQUIRED') == true) { |
598
|
|
|
$value = $this->getValue(); |
599
|
|
|
|
600
|
|
|
return !is_null($value) && !empty($value); |
601
|
|
|
} else { |
602
|
|
|
return true; |
603
|
|
|
} |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
/** |
607
|
|
|
* Выставляет код для данного виджета при инициализации. Перегружает настройки. |
608
|
|
|
* |
609
|
|
|
* @param string $code |
610
|
|
|
*/ |
611
|
|
|
public function setCode($code) |
612
|
|
|
{ |
613
|
|
|
$this->code = $code; |
614
|
|
|
$this->loadSettings(); |
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
/** |
618
|
|
|
* @return mixed |
619
|
|
|
*/ |
620
|
|
|
public function getCode() |
621
|
|
|
{ |
622
|
|
|
return $this->code; |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
/** |
626
|
|
|
* Устанавливает настройки интерфейса для текущего поля. |
627
|
|
|
* |
628
|
|
|
* @param string $code |
629
|
|
|
* |
630
|
|
|
* @return bool |
631
|
|
|
* |
632
|
|
|
* @see AdminBaseHelper::getInterfaceSettings() |
633
|
|
|
* @see AdminBaseHelper::setFields() |
634
|
|
|
*/ |
635
|
|
|
public function loadSettings($code = null) |
636
|
|
|
{ |
637
|
|
|
$interface = $this->helper->getInterfaceSettings(); |
638
|
|
|
|
639
|
|
|
$code = is_null($code) ? $this->code : $code; |
640
|
|
|
|
641
|
|
|
if (!isset($interface['FIELDS'][$code])) { |
642
|
|
|
return false; |
643
|
|
|
} |
644
|
|
|
unset($interface['FIELDS'][$code]['WIDGET']); |
645
|
|
|
$this->settings = array_merge($this->settings, $interface['FIELDS'][$code]); |
646
|
|
|
$this->setDefaultValue(); |
647
|
|
|
|
648
|
|
|
return true; |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
/** |
652
|
|
|
* Возвращает название сущности данной модели. |
653
|
|
|
* |
654
|
|
|
* @return string|DataManager |
655
|
|
|
*/ |
656
|
|
|
public function getEntityName() |
657
|
|
|
{ |
658
|
|
|
return $this->entityName; |
659
|
|
|
} |
660
|
|
|
|
661
|
|
|
/** |
662
|
|
|
* @param string $entityName |
663
|
|
|
*/ |
664
|
|
|
public function setEntityName($entityName) |
665
|
|
|
{ |
666
|
|
|
$this->entityName = $entityName; |
|
|
|
|
667
|
|
|
$this->setDefaultValue(); |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
/** |
671
|
|
|
* Устанавливает значение по-умолчанию для данного поля |
672
|
|
|
*/ |
673
|
|
|
public function setDefaultValue() |
674
|
|
|
{ |
675
|
|
|
if (isset($this->settings['DEFAULT']) && is_null($this->getValue())) { |
676
|
|
|
$this->setValue($this->settings['DEFAULT']); |
677
|
|
|
} |
678
|
|
|
} |
679
|
|
|
|
680
|
|
|
/** |
681
|
|
|
* Передает ссылку на данные сущности в виджет |
682
|
|
|
* |
683
|
|
|
* @param $data |
684
|
|
|
*/ |
685
|
|
|
public function setData(&$data) |
686
|
|
|
{ |
687
|
|
|
$this->data = &$data; |
688
|
|
|
//FIXME: нелепый оверхэд ради того, чтобы можно было централизованно преобразовывать значение при записи |
689
|
|
|
$this->setValue($data[$this->getCode()]); |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
/** |
693
|
|
|
* Возвращает текущее значение, хранимое в поле виджета |
694
|
|
|
* Если такого поля нет, возвращает null |
695
|
|
|
* |
696
|
|
|
* @return mixed|null |
697
|
|
|
*/ |
698
|
|
|
public function getValue() |
699
|
|
|
{ |
700
|
|
|
$code = $this->getCode(); |
701
|
|
|
|
702
|
|
|
return isset($this->data[$code]) ? $this->data[$code] : null; |
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
/** |
706
|
|
|
* Устанавливает значение поля |
707
|
|
|
* |
708
|
|
|
* @param $value |
709
|
|
|
* |
710
|
|
|
* @return bool |
711
|
|
|
*/ |
712
|
|
|
protected function setValue($value) |
713
|
|
|
{ |
714
|
|
|
$code = $this->getCode(); |
715
|
|
|
$this->data[$code] = $value; |
716
|
|
|
|
717
|
|
|
return true; |
718
|
|
|
} |
719
|
|
|
|
720
|
|
|
/** |
721
|
|
|
* Получения названия поля таблицы, в которой хранятся множественные данные этого виджета |
722
|
|
|
* |
723
|
|
|
* @param string $fieldName Название поля |
724
|
|
|
* |
725
|
|
|
* @return bool|string |
726
|
|
|
*/ |
727
|
|
|
public function getMultipleField($fieldName) |
728
|
|
|
{ |
729
|
|
|
$fields = $this->getSettings('MULTIPLE_FIELDS'); |
730
|
|
|
if (empty($fields)) { |
731
|
|
|
return $fieldName; |
732
|
|
|
} |
733
|
|
|
|
734
|
|
|
// Поиск алиаса названия поля |
735
|
|
|
if (isset($fields[$fieldName])) { |
736
|
|
|
return $fields[$fieldName]; |
737
|
|
|
} |
738
|
|
|
|
739
|
|
|
// Поиск оригинального названия поля |
740
|
|
|
$fieldsFlip = array_flip($fields); |
741
|
|
|
|
742
|
|
|
if (isset($fieldsFlip[$fieldName])) { |
743
|
|
|
return $fieldsFlip[$fieldName]; |
744
|
|
|
} |
745
|
|
|
|
746
|
|
|
return $fieldName; |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
/** |
750
|
|
|
* Выставляет значение отдельной настройки |
751
|
|
|
* |
752
|
|
|
* @param string $name |
753
|
|
|
* @param mixed $value |
754
|
|
|
*/ |
755
|
|
|
public function setSetting($name, $value) |
756
|
|
|
{ |
757
|
|
|
$this->settings[$name] = $value; |
758
|
|
|
} |
759
|
|
|
|
760
|
|
|
/** |
761
|
|
|
* Возвращает собранные ошибки валидации |
762
|
|
|
* @return array |
763
|
|
|
*/ |
764
|
|
|
public function getValidationErrors() |
765
|
|
|
{ |
766
|
|
|
return $this->validationErrors; |
767
|
|
|
} |
768
|
|
|
|
769
|
|
|
/** |
770
|
|
|
* Возвращает имена для атрибута name полей фильтра. |
771
|
|
|
* Если это фильтр BETWEEN, то вернёт массив с вариантами from и to. |
772
|
|
|
* |
773
|
|
|
* @return array|string |
774
|
|
|
*/ |
775
|
|
|
protected function getFilterInputName() |
776
|
|
|
{ |
777
|
|
|
if ($this->isFilterBetween()) { |
778
|
|
|
$baseName = $this->filterFieldPrefix . $this->code;; |
779
|
|
|
$inputNameFrom = $baseName . '_from'; |
780
|
|
|
$inputNameTo = $baseName . '_to'; |
781
|
|
|
|
782
|
|
|
return array($inputNameFrom, $inputNameTo); |
783
|
|
|
} else { |
784
|
|
|
return $this->filterFieldPrefix . $this->code; |
785
|
|
|
} |
786
|
|
|
} |
787
|
|
|
|
788
|
|
|
/** |
789
|
|
|
* Возвращает текст для атрибута name инпута редактирования. |
790
|
|
|
* |
791
|
|
|
* @param null $suffix опциональное дополнение к названию поля |
792
|
|
|
* |
793
|
|
|
* @return string |
794
|
|
|
*/ |
795
|
|
|
protected function getEditInputName($suffix = null) |
796
|
|
|
{ |
797
|
|
|
return 'FIELDS[' . $this->getCode() . $suffix . ']'; |
798
|
|
|
} |
799
|
|
|
|
800
|
|
|
/** |
801
|
|
|
* Уникальный ID для DOM HTML |
802
|
|
|
* @return string |
803
|
|
|
*/ |
804
|
|
|
protected function getEditInputHtmlId() |
805
|
|
|
{ |
806
|
|
|
$htmlId = end(explode('\\', $this->entityName)) . '-' . $this->getCode(); |
|
|
|
|
807
|
|
|
|
808
|
|
|
return strtolower(preg_replace('/[^A-z-]/', '-', $htmlId)); |
809
|
|
|
} |
810
|
|
|
|
811
|
|
|
/** |
812
|
|
|
* Возвращает текст для атрибута name инпута редактирования поля в списке |
813
|
|
|
* @return string |
814
|
|
|
*/ |
815
|
|
|
protected function getEditableListInputName() |
816
|
|
|
{ |
817
|
|
|
$id = $this->data['ID']; |
818
|
|
|
|
819
|
|
|
return 'FIELDS[' . $id . '][' . $this->getCode() . ']'; |
820
|
|
|
} |
821
|
|
|
|
822
|
|
|
/** |
823
|
|
|
* Определяет тип вызывающего хэлпера, от чего может зависить поведение виджета. |
824
|
|
|
* |
825
|
|
|
* @return bool|int |
826
|
|
|
* @see HelperWidget::EDIT_HELPER |
827
|
|
|
* @see HelperWidget::LIST_HELPER |
828
|
|
|
*/ |
829
|
|
|
protected function getCurrentViewType() |
830
|
|
|
{ |
831
|
|
|
if (is_a($this->helper, 'DigitalWand\AdminHelper\Helper\AdminListHelper')) { |
832
|
|
|
return self::LIST_HELPER; |
833
|
|
|
} else { |
834
|
|
|
if (is_a($this->helper, 'DigitalWand\AdminHelper\Helper\AdminEditHelper')) { |
835
|
|
|
return self::EDIT_HELPER; |
836
|
|
|
} |
837
|
|
|
} |
838
|
|
|
|
839
|
|
|
return false; |
840
|
|
|
} |
841
|
|
|
|
842
|
|
|
/** |
843
|
|
|
* Проверяет значение поля на уникальность |
844
|
|
|
* @return bool |
845
|
|
|
*/ |
846
|
|
|
private function isUnique() |
847
|
|
|
{ |
848
|
|
|
if ($this->getSettings('VIRTUAL')) { |
849
|
|
|
return true; |
850
|
|
|
} |
851
|
|
|
|
852
|
|
|
$value = $this->getValue(); |
853
|
|
|
if (empty($value)) { |
854
|
|
|
return true; |
855
|
|
|
} |
856
|
|
|
|
857
|
|
|
/** @var DataManager $class */ |
858
|
|
|
$class = $this->entityName; |
859
|
|
|
$field = $this->getCode(); |
860
|
|
|
$idField = 'ID'; |
861
|
|
|
$id = $this->data[$idField]; |
862
|
|
|
|
863
|
|
|
$filter = array( |
864
|
|
|
$field => $value, |
865
|
|
|
); |
866
|
|
|
|
867
|
|
|
if (!empty($id)) { |
868
|
|
|
$filter["!=" . $idField] = $id; |
869
|
|
|
} |
870
|
|
|
|
871
|
|
|
$count = $class::getCount($filter); |
872
|
|
|
|
873
|
|
|
if (!$count) { |
874
|
|
|
return true; |
875
|
|
|
} |
876
|
|
|
|
877
|
|
|
return false; |
878
|
|
|
} |
879
|
|
|
|
880
|
|
|
/** |
881
|
|
|
* Проверяет, не является ли текущий запрос попыткой выгрузить данные в Excel |
882
|
|
|
* @return bool |
883
|
|
|
*/ |
884
|
|
|
protected function isExcelView() |
|
|
|
|
885
|
|
|
{ |
886
|
|
|
if (isset($_REQUEST['mode']) && $_REQUEST['mode'] == 'excel') { |
887
|
|
|
return true; |
888
|
|
|
} |
889
|
|
|
|
890
|
|
|
return false; |
891
|
|
|
} |
892
|
|
|
|
893
|
|
|
/** |
894
|
|
|
* @todo Вынести в ресурс (\CJSCore::Init()). |
895
|
|
|
* @todo Описать. |
896
|
|
|
*/ |
897
|
|
|
protected function jsHelper() |
898
|
|
|
{ |
899
|
|
|
if ($this->jsHelper == true) { |
|
|
|
|
900
|
|
|
return true; |
901
|
|
|
} |
902
|
|
|
|
903
|
|
|
$this->jsHelper = true; |
904
|
|
|
\CJSCore::Init(array("jquery")); |
905
|
|
|
?> |
906
|
|
|
<script> |
907
|
|
|
/** |
908
|
|
|
* Менеджер множественных полей |
909
|
|
|
* Позволяет добавлять и удалять любой HTML код с возможность подстановки динамических данных |
910
|
|
|
* Инструкция: |
911
|
|
|
* - создайте контейнер, где будут хранится отображаться код |
912
|
|
|
* - создайте экземпляр MultipleWidgetHelper |
913
|
|
|
* Например: var multiple = MultipleWidgetHelper(селектор контейнера, шаблон) |
914
|
|
|
* шаблон - это HTML код, который можно будет добавлять и удалять в интерфейсе |
915
|
|
|
* В шаблон можно добавлять переменные, их нужно обрамлять фигурными скобками. Например {{entity_id}} |
916
|
|
|
* Если в шаблоне несколько полей, переменная {{field_id}} обязательна |
917
|
|
|
* Например <input type="text" name="image[{{field_id}}][SRC]"><input type="text" name="image[{{field_id}}][DESCRIPTION]"> |
918
|
|
|
* Если добавляемые поле не новое, то обязательно передавайте в addField переменную field_id с ID записи, |
919
|
|
|
* для новосозданных полей переменная заполнится автоматически |
920
|
|
|
*/ |
921
|
|
|
function MultipleWidgetHelper(container, fieldTemplate) { |
922
|
|
|
this.$container = $(container); |
923
|
|
|
if (this.$container.size() == 0) { |
924
|
|
|
throw 'Главный контейнер полей не найден (' + container + ')'; |
925
|
|
|
} |
926
|
|
|
if (!fieldTemplate) { |
927
|
|
|
throw 'Не передан обязательный параметр fieldTemplate'; |
928
|
|
|
} |
929
|
|
|
this.fieldTemplate = fieldTemplate; |
930
|
|
|
this._init(); |
931
|
|
|
} |
932
|
|
|
|
933
|
|
|
MultipleWidgetHelper.prototype = { |
934
|
|
|
/** |
935
|
|
|
* Основной контейнер |
936
|
|
|
*/ |
937
|
|
|
$container: null, |
938
|
|
|
/** |
939
|
|
|
* Контейнер полей |
940
|
|
|
*/ |
941
|
|
|
$fieldsContainer: null, |
942
|
|
|
/** |
943
|
|
|
* Шаблон поля |
944
|
|
|
*/ |
945
|
|
|
fieldTemplate: null, |
946
|
|
|
/** |
947
|
|
|
* Счетчик добавлений полей |
948
|
|
|
*/ |
949
|
|
|
fieldsCounter: 0, |
950
|
|
|
/** |
951
|
|
|
* Добавления поля |
952
|
|
|
* @param data object Данные для шаблона в виде ключ: значение |
953
|
|
|
*/ |
954
|
|
|
addField: function (data) { |
955
|
|
|
// console.log('Добавление поля'); |
956
|
|
|
this.addFieldHtml(this.fieldTemplate, data); |
957
|
|
|
}, |
958
|
|
|
addFieldHtml: function (fieldTemplate, data) { |
959
|
|
|
this.fieldsCounter++; |
960
|
|
|
this.$fieldsContainer.append(this._generateFieldContent(fieldTemplate, data)); |
961
|
|
|
}, |
962
|
|
|
/** |
963
|
|
|
* Удаление поля |
964
|
|
|
* @param field string|object Селектор или jQuery объект |
965
|
|
|
*/ |
966
|
|
|
deleteField: function (field) { |
967
|
|
|
// console.log('Удаление поля'); |
968
|
|
|
$(field).remove(); |
969
|
|
|
if (this.$fieldsContainer.find('> *').size() == 0) { |
970
|
|
|
this.addField(); |
971
|
|
|
} |
972
|
|
|
}, |
973
|
|
|
_init: function () { |
974
|
|
|
this.$container.append('<div class="fields-container"></div>'); |
975
|
|
|
this.$fieldsContainer = this.$container.find('.fields-container'); |
976
|
|
|
this.$container.append(this._getAddButton()); |
977
|
|
|
|
978
|
|
|
this._trackEvents(); |
979
|
|
|
}, |
980
|
|
|
/** |
981
|
|
|
* Генерация контента контейнера поля |
982
|
|
|
* @param data |
983
|
|
|
* @returns {string} |
984
|
|
|
* @private |
985
|
|
|
*/ |
986
|
|
|
_generateFieldContent: function (fieldTemplate, data) { |
987
|
|
|
return '<div class="field-container" style="margin-bottom: 5px;">' |
988
|
|
|
+ this._generateFieldTemplate(fieldTemplate, data) + this._getDeleteButton() |
989
|
|
|
+ '</div>'; |
990
|
|
|
}, |
991
|
|
|
/** |
992
|
|
|
* Генерация шаблона поля |
993
|
|
|
* @param data object Данные для подстановки |
994
|
|
|
* @returns {null} |
995
|
|
|
* @private |
996
|
|
|
*/ |
997
|
|
|
_generateFieldTemplate: function (fieldTemplate, data) { |
998
|
|
|
if (!data) { |
999
|
|
|
data = {}; |
1000
|
|
|
} |
1001
|
|
|
|
1002
|
|
|
if (typeof data.field_id == 'undefined') { |
1003
|
|
|
data.field_id = 'new_' + this.fieldsCounter; |
1004
|
|
|
} |
1005
|
|
|
|
1006
|
|
|
$.each(data, function (key, value) { |
1007
|
|
|
// Подставление значений переменных |
1008
|
|
|
fieldTemplate = fieldTemplate.replace(new RegExp('\{\{' + key + '\}\}', ['g']), value); |
1009
|
|
|
}); |
1010
|
|
|
|
1011
|
|
|
// Удаление из шаблона необработанных переменных |
1012
|
|
|
fieldTemplate = fieldTemplate.replace(/\{\{.+?\}\}/g, ''); |
1013
|
|
|
|
1014
|
|
|
return fieldTemplate; |
1015
|
|
|
}, |
1016
|
|
|
/** |
1017
|
|
|
* Кнопка удаления |
1018
|
|
|
* @returns {string} |
1019
|
|
|
* @private |
1020
|
|
|
*/ |
1021
|
|
|
_getDeleteButton: function () { |
1022
|
|
|
return '<input type="button" value="-" class="delete-field-button" style="margin-left: 5px;">'; |
1023
|
|
|
}, |
1024
|
|
|
/** |
1025
|
|
|
* Кнопка добавления |
1026
|
|
|
* @returns {string} |
1027
|
|
|
* @private |
1028
|
|
|
*/ |
1029
|
|
|
_getAddButton: function () { |
1030
|
|
|
return '<input type="button" value="Добавить..." class="add-field-button">'; |
1031
|
|
|
}, |
1032
|
|
|
/** |
1033
|
|
|
* Отслеживание событий |
1034
|
|
|
* @private |
1035
|
|
|
*/ |
1036
|
|
|
_trackEvents: function () { |
1037
|
|
|
var context = this; |
1038
|
|
|
// Добавление поля |
1039
|
|
|
this.$container.find('.add-field-button').on('click', function () { |
1040
|
|
|
context.addField(); |
1041
|
|
|
}); |
1042
|
|
|
// Удаление поля |
1043
|
|
|
this.$container.on('click', '.delete-field-button', function () { |
1044
|
|
|
context.deleteField($(this).parents('.field-container')); |
1045
|
|
|
}); |
1046
|
|
|
} |
1047
|
|
|
}; |
1048
|
|
|
</script> |
1049
|
|
|
<? |
|
|
|
|
1050
|
|
|
} |
1051
|
|
|
} |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.