1
|
|
|
<?php |
|
|
|
|
2
|
|
|
namespace DigitalWand\AdminHelper\Widget; |
3
|
|
|
|
4
|
|
|
use DigitalWand\AdminHelper\Helper\AdminBaseHelper; |
5
|
|
|
use Bitrix\Main\Entity\DataManager; |
6
|
|
|
use Bitrix\Main\Entity\EntityError; |
7
|
|
|
use Bitrix\Main\Entity\Result; |
8
|
|
|
use Bitrix\Highloadblock as HL; |
9
|
|
|
use Bitrix\Main\Localization\Loc; |
10
|
|
|
use DigitalWand\AdminHelper\Helper\AdminEditHelper; |
11
|
|
|
use DigitalWand\AdminHelper\Helper\AdminListHelper; |
12
|
|
|
|
13
|
|
|
Loc::loadMessages(__FILE__); |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Виджет, отображающий стандартные поля, создаваемые в HL-инфоблоке в админке. |
17
|
|
|
* |
18
|
|
|
* Настройки: |
19
|
|
|
* <ul> |
20
|
|
|
* <li><b>MODEL</b> - Название модели, из которой будет производиться выборка данных. По-умолчанию - модель текущего |
21
|
|
|
* хэлпера</li> |
22
|
|
|
* </ul> |
23
|
|
|
* Class HLIBlockFieldWidget |
24
|
|
|
* @package DigitalWand\AdminHelper\Widget |
25
|
|
|
*/ |
26
|
|
|
class HLIBlockFieldWidget extends HelperWidget |
27
|
|
|
{ |
28
|
|
|
static protected $userFieldsCache = array(); |
29
|
|
|
static protected $defaults = array( |
30
|
|
|
'USE_BX_API' => true |
31
|
|
|
); |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Генерирует HTML для редактирования поля |
35
|
|
|
* |
36
|
|
|
* @see \CAdminForm::ShowUserFieldsWithReadyData |
37
|
|
|
* @return mixed |
38
|
|
|
*/ |
39
|
|
|
protected function getEditHtml() |
|
|
|
|
40
|
|
|
{ |
41
|
|
|
$info = $this->getUserFieldInfo(); |
42
|
|
|
if ($info) { |
43
|
|
|
|
44
|
|
|
/** @var \CAllUserTypeManager $USER_FIELD_MANAGER */ |
45
|
|
|
global $USER_FIELD_MANAGER; |
|
|
|
|
46
|
|
|
$GLOBALS[$this->getCode()] = isset($GLOBALS[$this->getCode()]) ? $GLOBALS[$this->getCode()] : $this->data[$this->getCode()]; |
47
|
|
|
$bVarsFromForm = false; |
48
|
|
|
|
49
|
|
|
$info["VALUE_ID"] = intval($this->data['ID']); |
50
|
|
|
$info['EDIT_FORM_LABEL'] = $this->getSettings('TITLE'); |
51
|
|
|
|
52
|
|
|
if (isset($_REQUEST['def_' . $this->getCode()])) { |
53
|
|
|
$info['SETTINGS']['DEFAULT_VALUE'] = $_REQUEST['def_' . $this->getCode()]; |
54
|
|
|
} |
55
|
|
|
print $USER_FIELD_MANAGER->GetEditFormHTML($bVarsFromForm, $GLOBALS[$this->getCode()], $info); |
56
|
|
|
|
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Конвертирует данные при сохранении так, как это делали бы пользовательские свойства битрикса. |
62
|
|
|
* Выполняет валидацию с помощью CheckFields() пользовательских полей. |
63
|
|
|
* |
64
|
|
|
* @see Bitrix\Highloadblock\DataManager |
65
|
|
|
* @see /bitrix/modules/highloadblock/admin/highloadblock_row_edit.php |
66
|
|
|
* |
67
|
|
|
* @throws \Bitrix\Main\ArgumentException |
68
|
|
|
* @throws \Bitrix\Main\SystemException |
69
|
|
|
*/ |
70
|
|
|
public function processEditAction() |
|
|
|
|
71
|
|
|
{ |
72
|
|
|
/** @var \CAllUserTypeManager $USER_FIELD_MANAGER */ |
73
|
|
|
global $USER_FIELD_MANAGER; |
|
|
|
|
74
|
|
|
$iblockId = 'HLBLOCK_' . $this->getHLId(); |
75
|
|
|
|
76
|
|
|
//Чтобы не терялись старые данные |
77
|
|
|
if (!isset($this->data[$this->getCode()]) AND isset($_REQUEST[$this->getCode() . '_old_id'])) { |
|
|
|
|
78
|
|
|
$this->data[$this->getCode()] = $_REQUEST[$this->getCode() . '_old_id']; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
//Функция работает для всех полей, так что запускаем её только один раз, результат кешируем. |
82
|
|
|
static $data = array(); |
83
|
|
|
if (empty($data)) { |
84
|
|
|
$data = $this->data; |
85
|
|
|
$USER_FIELD_MANAGER->EditFormAddFields($iblockId, $data); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
$value = $data[$this->getCode()]; |
89
|
|
|
|
90
|
|
|
$entity_data_class = AdminBaseHelper::getHLEntity($this->getSettings('MODEL')); |
91
|
|
|
|
92
|
|
|
$oldData = $this->getOldFieldData($entity_data_class); |
93
|
|
|
$fieldsInfo = $USER_FIELD_MANAGER->getUserFieldsWithReadyData($iblockId, $oldData, LANGUAGE_ID, false, 'ID'); |
94
|
|
|
$fieldInfo = $fieldsInfo[$this->getCode()]; |
95
|
|
|
|
96
|
|
|
$className = $fieldInfo['USER_TYPE']['CLASS_NAME']; |
97
|
|
|
if (is_callable(array($className, 'CheckFields'))) { |
98
|
|
|
$errors = $className::CheckFields($fieldInfo, $value); |
99
|
|
|
if (!empty($errors)) { |
100
|
|
|
$this->addError($errors); |
101
|
|
|
return; |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
// use save modifiers |
106
|
|
|
$field = $entity_data_class::getEntity()->getField($this->getCode()); |
107
|
|
|
$value = $field->modifyValueBeforeSave($value, $data); |
108
|
|
|
|
109
|
|
|
//Типоспецифичные хаки |
110
|
|
|
if ($unserialized = unserialize($value)) { |
111
|
|
|
//Список значений прилетает сериализованным |
112
|
|
|
$this->data[$this->getCode()] = $unserialized; |
113
|
|
|
|
114
|
|
|
} else if ($className == 'CUserTypeFile' AND !is_array($value)) { |
|
|
|
|
115
|
|
|
//Если не сделать intval, то при сохранении с ранее добавленным файлом будет выскакивать ошибка |
116
|
|
|
$this->data[$this->getCode()] = intval($value); |
117
|
|
|
|
118
|
|
|
} else { |
119
|
|
|
//Все остальные поля - сохраняем как есть. |
120
|
|
|
$this->data[$this->getCode()] = $value; |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Битриксу надо получить поля, кторые сохранены в базе для этого пользовательского свойства. |
126
|
|
|
* Иначе множественные свойства он затрёт. |
127
|
|
|
* Проблема в том, что пользовательские свойства могут браться из связанной сущности. |
128
|
|
|
* @param HL\DataManager $entity_data_class |
129
|
|
|
* |
130
|
|
|
* @return mixed |
131
|
|
|
*/ |
132
|
|
|
protected function getOldFieldData($entity_data_class) |
133
|
|
|
{ |
134
|
|
|
if (is_null($this->data) OR !isset($this->data[$this->helper->pk()])) return false; |
|
|
|
|
135
|
|
|
return $entity_data_class::getByPrimary($this->data[$this->helper->pk()])->fetch(); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Если запрашивается модель, и если модель явно не указана, то берется модель текущего хэлпера, сохраняется для |
140
|
|
|
* последующего использования и возарвщвется пользователю. |
141
|
|
|
* |
142
|
|
|
* @param string $name |
143
|
|
|
* @return array|\Bitrix\Main\Entity\DataManager|mixed|string |
144
|
|
|
*/ |
145
|
|
|
public function getSettings($name = '') |
146
|
|
|
{ |
147
|
|
|
$value = parent::getSettings($name); |
148
|
|
|
if (!$value) { |
149
|
|
|
if ($name == 'MODEL') { |
150
|
|
|
$value = $this->helper->getModel(); |
151
|
|
|
$this->setSetting($name, $value); |
152
|
|
|
|
153
|
|
|
} else if ($name == 'TITLE') { |
154
|
|
|
|
155
|
|
|
$context = $this->helper->getContext(); |
156
|
|
|
$info = $this->getUserFieldInfo(); |
157
|
|
|
|
158
|
|
|
if (($context == AdminListHelper::OP_ADMIN_VARIABLES_FILTER OR $context == AdminListHelper::OP_CREATE_FILTER_FORM) |
|
|
|
|
159
|
|
|
AND (isset($info['LIST_FILTER_LABEL']) AND !empty($info['LIST_FILTER_LABEL'])) |
|
|
|
|
160
|
|
|
) { |
161
|
|
|
$value = $info['LIST_FILTER_LABEL']; |
162
|
|
|
|
163
|
|
|
} else if ($context == AdminListHelper::OP_ADMIN_VARIABLES_HEADER |
164
|
|
|
AND isset($info['LIST_COLUMN_LABEL']) |
|
|
|
|
165
|
|
|
AND !empty($info['LIST_COLUMN_LABEL']) |
|
|
|
|
166
|
|
|
) { |
167
|
|
|
$value = $info['LIST_COLUMN_LABEL']; |
168
|
|
|
|
169
|
|
|
} else if ($context == AdminEditHelper::OP_SHOW_TAB_ELEMENTS |
170
|
|
|
AND isset($info['EDIT_FORM_LABEL']) |
|
|
|
|
171
|
|
|
AND !empty($info['EDIT_FORM_LABEL']) |
|
|
|
|
172
|
|
|
) { |
173
|
|
|
$value = $info['EDIT_FORM_LABEL']; |
174
|
|
|
|
175
|
|
|
} else { |
176
|
|
|
$value = $info['FIELD_NAME']; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
return $value; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Генерирует HTML для поля в списке |
187
|
|
|
* Копипаст из API Битрикса, бессмысленного и беспощадного... |
188
|
|
|
* |
189
|
|
|
* @see AdminListHelper::addRowCell(); |
190
|
|
|
* |
191
|
|
|
* @param \CAdminListRow $row |
192
|
|
|
* @param array $data - данные текущей строки |
193
|
|
|
* |
194
|
|
|
* @return mixed |
195
|
|
|
*/ |
196
|
|
|
public function generateRow(&$row, $data) |
|
|
|
|
197
|
|
|
{ |
198
|
|
|
$info = $this->getUserFieldInfo(); |
199
|
|
|
if ($info) { |
200
|
|
|
|
201
|
|
|
/** @var \CAllUserTypeManager $USER_FIELD_MANAGER */ |
202
|
|
|
global $USER_FIELD_MANAGER; |
|
|
|
|
203
|
|
|
$FIELD_NAME = $this->getCode(); |
204
|
|
|
$GLOBALS[$FIELD_NAME] = isset($GLOBALS[$FIELD_NAME]) ? $GLOBALS[$FIELD_NAME] : $this->data[$this->getCode()]; |
205
|
|
|
|
206
|
|
|
$info["VALUE_ID"] = intval($this->data['ID']); |
207
|
|
|
|
208
|
|
|
if (isset($_REQUEST['def_' . $FIELD_NAME])) { |
209
|
|
|
$info['SETTINGS']['DEFAULT_VALUE'] = $_REQUEST['def_' . $FIELD_NAME]; |
210
|
|
|
} |
211
|
|
|
$USER_FIELD_MANAGER->AddUserField($info, $data[$this->getCode()], $row); |
212
|
|
|
|
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Генерирует HTML для поля фильтрации |
218
|
|
|
* |
219
|
|
|
* @see AdminListHelper::createFilterForm(); |
220
|
|
|
* @return mixed |
221
|
|
|
*/ |
222
|
|
|
public function showFilterHtml() |
|
|
|
|
223
|
|
|
{ |
224
|
|
|
$info = $this->getUserFieldInfo(); |
225
|
|
|
if ($info) { |
226
|
|
|
/** @var \CAllUserTypeManager $USER_FIELD_MANAGER */ |
227
|
|
|
global $USER_FIELD_MANAGER; |
|
|
|
|
228
|
|
|
$FIELD_NAME = $this->getCode(); |
229
|
|
|
$GLOBALS[$FIELD_NAME] = isset($GLOBALS[$FIELD_NAME]) ? $GLOBALS[$FIELD_NAME] : $this->data[$this->getCode()]; |
230
|
|
|
|
231
|
|
|
$info["VALUE_ID"] = intval($this->data['ID']); |
232
|
|
|
$info['LIST_FILTER_LABEL'] = $this->getSettings('TITLE'); |
233
|
|
|
|
234
|
|
|
print $USER_FIELD_MANAGER->GetFilterHTML($info, $this->getFilterInputName(), $this->getCurrentFilterValue()); |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
public function getUserFieldInfo() |
239
|
|
|
{ |
240
|
|
|
$id = $this->getHLId(); |
241
|
|
|
$fields = static::getUserFields($id, $this->data); |
242
|
|
|
if (isset($fields[$this->getCode()])) { |
243
|
|
|
return $fields[$this->getCode()]; |
244
|
|
|
} |
245
|
|
|
return false; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Получаем ID HL-инфоблока по имени его класса |
250
|
|
|
* @return mixed |
251
|
|
|
*/ |
252
|
|
|
protected function getHLId() |
253
|
|
|
{ |
254
|
|
|
static $id = false; |
255
|
|
|
|
256
|
|
|
if ($id === false) { |
257
|
|
|
$model = $this->getSettings('MODEL'); |
258
|
|
|
$info = AdminBaseHelper::getHLEntityInfo($model); |
259
|
|
|
if ($info AND isset($info['ID'])) { |
|
|
|
|
260
|
|
|
$id = $info['ID']; |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
return $id; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
static public function getUserFields($iblockId, $data) |
|
|
|
|
268
|
|
|
{ |
269
|
|
|
/** @var \CAllUserTypeManager $USER_FIELD_MANAGER */ |
270
|
|
|
global $USER_FIELD_MANAGER; |
|
|
|
|
271
|
|
|
$iblockId = 'HLBLOCK_' . $iblockId; |
272
|
|
|
if (!isset(static::$userFieldsCache[$iblockId][$data['ID']])) { |
273
|
|
|
$fields = $USER_FIELD_MANAGER->getUserFieldsWithReadyData($iblockId, $data, LANGUAGE_ID, false, 'ID'); |
274
|
|
|
self::$userFieldsCache[$iblockId][$data['ID']] = $fields; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
return self::$userFieldsCache[$iblockId][$data['ID']]; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Заменяем оригинальную функцию, т.к. текст ошибки приходит от битрикса, причем название поля там почему-то не |
282
|
|
|
* проставлено |
283
|
|
|
* |
284
|
|
|
*@param string $messageId |
285
|
|
|
*/ |
286
|
|
|
protected function addError($messageId) |
287
|
|
|
{ |
288
|
|
|
if (is_array($messageId)) { |
289
|
|
|
foreach ($messageId as $key => $error) { |
290
|
|
|
if (isset($error['text'])) { |
291
|
|
|
//FIXME: почему-то битрикс не подхватывает корректное название поля, поэтому запихиваем его сами. |
292
|
|
|
if (isset($error['id']) AND strpos($error['text'], '""')) { |
|
|
|
|
293
|
|
|
$messageId[$key] = str_replace('""', '"' . $this->getSettings('TITLE') . '"', $error['text']); |
294
|
|
|
|
295
|
|
|
} else { |
296
|
|
|
$messageId[$key] = $error['text']; |
297
|
|
|
} |
298
|
|
|
} |
299
|
|
|
} |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
$messageId = implode("\n", $messageId); |
303
|
|
|
$this->validationErrors[$this->getCode()] = $messageId; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
} |
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.