1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
namespace DigitalWand\AdminHelper\Helper; |
4
|
|
|
|
5
|
|
|
use Bitrix\Main\Context; |
6
|
|
|
use Bitrix\Main\HttpRequest; |
7
|
|
|
use Bitrix\Main\Localization\Loc; |
8
|
|
|
use Bitrix\Main\Entity\DataManager; |
9
|
|
|
use Bitrix\Main\DB\Result; |
10
|
|
|
use DigitalWand\AdminHelper\EntityManager; |
11
|
|
|
|
12
|
|
|
Loc::loadMessages(__FILE__); |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Базовый класс для реализации страницы списка админки. |
16
|
|
|
* При создании своего класса необходимо переопределить следующие переменные: |
17
|
|
|
* <ul> |
18
|
|
|
* <li> static protected $model </Li> |
19
|
|
|
* </ul> |
20
|
|
|
* |
21
|
|
|
* Этого будет дастаточно для получения минимальной функциональности |
22
|
|
|
* Также данный класс может использоваться для отображения всплывающих окон с возможностью выбора элемента из списка |
23
|
|
|
* |
24
|
|
|
* @see AdminBaseHelper::$model |
25
|
|
|
* @see AdminBaseHelper::$module |
26
|
|
|
* @see AdminBaseHelper::$editViewName |
27
|
|
|
* @see AdminBaseHelper::$viewName |
28
|
|
|
* @package AdminHelper |
29
|
|
|
* |
30
|
|
|
* @author Nik Samokhvalov <[email protected]> |
31
|
|
|
* @author Artem Yarygin <[email protected]> |
32
|
|
|
*/ |
33
|
|
|
abstract class AdminListHelper extends AdminBaseHelper |
34
|
|
|
{ |
35
|
|
|
const OP_GROUP_ACTION = 'AdminListHelper::__construct_groupAction'; |
36
|
|
|
const OP_ADMIN_VARIABLES_FILTER = 'AdminListHelper::prepareAdminVariables_filter'; |
37
|
|
|
const OP_ADMIN_VARIABLES_HEADER = 'AdminListHelper::prepareAdminVariables_header'; |
38
|
|
|
const OP_GET_DATA_BEFORE = 'AdminListHelper::getData_before'; |
39
|
|
|
const OP_ADD_ROW_CELL = 'AdminListHelper::addRowCell'; |
40
|
|
|
const OP_CREATE_FILTER_FORM = 'AdminListHelper::createFilterForm'; |
41
|
|
|
const OP_CHECK_FILTER = 'AdminListHelper::checkFilter'; |
42
|
|
|
const OP_EDIT_ACTION = 'AdminListHelper::editAction'; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @var bool |
46
|
|
|
* Выводить кнопку экспорта в Excel |
47
|
|
|
* @api |
48
|
|
|
*/ |
49
|
|
|
protected $exportExcel = true; |
50
|
|
|
/** |
51
|
|
|
* @var bool |
52
|
|
|
* Выводить в списке кол-во элементов пункт Все |
53
|
|
|
*/ |
54
|
|
|
protected $showAll = true; |
55
|
|
|
/** |
56
|
|
|
* @var bool |
57
|
|
|
* Является ли список всплывающим окном для выбора элементов из списка. |
58
|
|
|
* В этой версии не должно быть операций удаления/перехода к редактированию. |
59
|
|
|
*/ |
60
|
|
|
protected $isPopup = false; |
61
|
|
|
/** |
62
|
|
|
* @var string |
63
|
|
|
* Название поля, в котором хранится результат выбора во всплывающем окне |
64
|
|
|
*/ |
65
|
|
|
protected $fieldPopupResultName = ''; |
66
|
|
|
/** |
67
|
|
|
* @var string |
68
|
|
|
* Уникальный индекс поля, в котором хранится результат выбора во всплывающем окне |
69
|
|
|
*/ |
70
|
|
|
protected $fieldPopupResultIndex = ''; |
71
|
|
|
protected $sectionFields = array(); |
72
|
|
|
/** |
73
|
|
|
* @var string |
74
|
|
|
* Название столбца, в котором хранится название элемента |
75
|
|
|
*/ |
76
|
|
|
protected $fieldPopupResultElTitle = ''; |
77
|
|
|
/** |
78
|
|
|
* @var string |
79
|
|
|
* Название функции, вызываемой при даблклике на строке списка, в случае, если список выводится в режиме |
80
|
|
|
* всплывающего окна |
81
|
|
|
*/ |
82
|
|
|
protected $popupClickFunctionName = 'selectRow'; |
83
|
|
|
/** |
84
|
|
|
* @var string |
85
|
|
|
* Код функции, вызываемой при клике на строке списка |
86
|
|
|
* @see AdminListHelper::genPipupActionJS() |
87
|
|
|
*/ |
88
|
|
|
protected $popupClickFunctionCode; |
89
|
|
|
/** |
90
|
|
|
* @var array |
91
|
|
|
* Массив с заголовками таблицы |
92
|
|
|
* @see \CAdminList::AddHeaders() |
93
|
|
|
*/ |
94
|
|
|
protected $arHeader = array(); |
95
|
|
|
/** |
96
|
|
|
* @var array |
97
|
|
|
* параметры фильтрации списка в классическим битриксовом формате |
98
|
|
|
*/ |
99
|
|
|
protected $arFilter = array(); |
100
|
|
|
/** |
101
|
|
|
* @var array |
102
|
|
|
* Массив, хранящий тип фильтра для данного поля. Позволяет избежать лишнего парсинга строк. |
103
|
|
|
*/ |
104
|
|
|
protected $filterTypes = array(); |
105
|
|
|
/** |
106
|
|
|
* @var array |
107
|
|
|
* Поля, предназначенные для фильтрации |
108
|
|
|
* @see \CAdminList::InitFilter(); |
109
|
|
|
*/ |
110
|
|
|
protected $arFilterFields = array(); |
111
|
|
|
/** |
112
|
|
|
* Список полей, для которых доступна фильтрация |
113
|
|
|
* @var array |
114
|
|
|
* @see \CAdminFilter::__construct(); |
115
|
|
|
*/ |
116
|
|
|
protected $arFilterOpts = array(); |
117
|
|
|
/** |
118
|
|
|
* @var \CAdminList |
119
|
|
|
*/ |
120
|
|
|
protected $list; |
121
|
|
|
/** |
122
|
|
|
* @var string |
123
|
|
|
* Префикс таблицы. Нужен, чтобы обеспечить уникальность относительно других админ. интерфейсов. |
124
|
|
|
* Без его добавления к конструктору таблицы повычается вероятность, что возникнет конфликт с таблицей из другого |
125
|
|
|
* административного интерфейса, в результате чего неправильно будет работать паджинация, фильтрация. Вероятны |
126
|
|
|
* ошибки запросов к БД. |
127
|
|
|
*/ |
128
|
|
|
static protected $tablePrefix = "digitalwand_admin_helper_"; |
129
|
|
|
/** |
130
|
|
|
* @var array |
131
|
|
|
* @see \CAdminList::AddGroupActionTable() |
132
|
|
|
*/ |
133
|
|
|
protected $groupActionsParams = array(); |
134
|
|
|
/** |
135
|
|
|
* Текущие параметры пагинации, |
136
|
|
|
* требуются для составления смешанного списка разделов и элементов |
137
|
|
|
* @var array |
138
|
|
|
*/ |
139
|
|
|
protected $navParams = array(); |
140
|
|
|
/** |
141
|
|
|
* Количество элементов смешанном списке |
142
|
|
|
* @see AdminListHelper::CustomNavStart |
143
|
|
|
* @var int |
144
|
|
|
*/ |
145
|
|
|
protected $totalRowsCount = 0; |
146
|
|
|
/** |
147
|
|
|
* Массив для слияния столбцов элементов и разделов |
148
|
|
|
* @var array |
149
|
|
|
*/ |
150
|
|
|
protected $tableColumnsMap = array(); |
151
|
|
|
/** |
152
|
|
|
* @var string |
153
|
|
|
* HTML верхней части таблицы |
154
|
|
|
* @api |
155
|
|
|
*/ |
156
|
|
|
public $prologHtml; |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @var string |
160
|
|
|
* HTML нижней части таблицы |
161
|
|
|
* @api |
162
|
|
|
*/ |
163
|
|
|
public $epilogHtml; |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Производится инициализация переменных, обработка запросов на редактирование |
167
|
|
|
* |
168
|
|
|
* @param array $fields |
169
|
|
|
* @param bool $isPopup |
170
|
|
|
* @throws \Bitrix\Main\ArgumentException |
171
|
|
|
*/ |
172
|
|
|
public function __construct(array $fields, $isPopup = false) |
|
|
|
|
173
|
|
|
{ |
174
|
|
|
$this->isPopup = $isPopup; |
175
|
|
|
|
176
|
|
|
if ($this->isPopup) { |
177
|
|
|
$this->fieldPopupResultName = preg_replace("/[^a-zA-Z0-9_:\\[\\]]/", "", $_REQUEST['n']); |
178
|
|
|
$this->fieldPopupResultIndex = preg_replace("/[^a-zA-Z0-9_:]/", "", $_REQUEST['k']); |
179
|
|
|
$this->fieldPopupResultElTitle = $_REQUEST['eltitle']; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
parent::__construct($fields); |
183
|
|
|
|
184
|
|
|
$this->restoreLastGetQuery(); |
185
|
|
|
$this->prepareAdminVariables(); |
186
|
|
|
|
187
|
|
|
$className = static::getModel(); |
188
|
|
|
$oSort = $this->initSortingParameters(Context::getCurrent()->getRequest()); |
189
|
|
|
$this->list = new \CAdminList($this->getListTableID(), $oSort); |
190
|
|
|
$this->list->InitFilter($this->arFilterFields); |
191
|
|
|
|
192
|
|
|
if ($this->list->EditAction() AND $this->hasWriteRights()) { |
|
|
|
|
193
|
|
|
global $FIELDS; |
|
|
|
|
194
|
|
|
foreach ($FIELDS as $id => $fields) { |
195
|
|
|
if (!$this->list->IsUpdated($id)) { |
196
|
|
|
continue; |
197
|
|
|
} |
198
|
|
|
$this->editAction($id, $fields); |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
if ($IDs = $this->list->GroupAction() AND $this->hasWriteRights()) { |
|
|
|
|
202
|
|
|
if ($_REQUEST['action_target'] == 'selected') { |
203
|
|
|
$this->setContext(AdminListHelper::OP_GROUP_ACTION); |
204
|
|
|
$IDs = array(); |
205
|
|
|
|
206
|
|
|
//Текущий фильтр должен быть модифицирован виждтами |
207
|
|
|
//для соответствия результатов фильтрации тому, что видит пользователь в интерфейсе. |
208
|
|
|
$raw = array( |
209
|
|
|
'SELECT' => $this->pk(), |
210
|
|
|
'FILTER' => $this->arFilter, |
211
|
|
|
'SORT' => array() |
212
|
|
|
); |
213
|
|
|
|
214
|
|
|
foreach ($this->fields as $code => $settings) { |
215
|
|
|
$widget = $this->createWidgetForField($code); |
216
|
|
|
$widget->changeGetListOptions($this->arFilter, $raw['SELECT'], $raw['SORT'], $raw); |
|
|
|
|
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
$res = $className::getList(array( |
220
|
|
|
'filter' => $this->arFilter, |
221
|
|
|
'select' => array($this->pk()), |
222
|
|
|
)); |
223
|
|
|
|
224
|
|
|
while ($el = $res->Fetch()) { |
225
|
|
|
$IDs[] = $el[$this->pk()]; |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
$filteredIDs = array(); |
230
|
|
|
|
231
|
|
|
foreach ($IDs as $id) { |
232
|
|
|
if (strlen($id) <= 0) { |
233
|
|
|
continue; |
234
|
|
|
} |
235
|
|
|
$filteredIDs[] = IntVal($id); |
236
|
|
|
} |
237
|
|
|
$this->groupActions($IDs, $_REQUEST['action']); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
if (isset($_REQUEST['action']) || isset($_REQUEST['action_button']) && count($this->getErrors()) == 0) { |
241
|
|
|
$listHelperClass = $this->getHelperClass(AdminListHelper::className()); |
242
|
|
|
$id = isset($_GET['ID']) ? $_GET['ID'] : null; |
243
|
|
|
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : $_REQUEST['action_button']; |
244
|
|
|
if ($action != 'edit' && $_REQUEST['cancel'] != 'Y') { |
245
|
|
|
$params = $_GET; |
246
|
|
|
unset($params['action']); |
247
|
|
|
unset($params['action_button']); |
248
|
|
|
$this->customActions($action, $id); |
|
|
|
|
249
|
|
|
LocalRedirect($listHelperClass::getUrl($params)); |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
if ($this->isPopup()) { |
254
|
|
|
$this->genPopupActionJS(); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
// Получаем параметры навигации |
258
|
|
|
$navUniqSettings = array('sNavID' => $this->getListTableID()); |
259
|
|
|
$this->navParams = array( |
260
|
|
|
'nPageSize' => \CAdminResult::GetNavSize($this->getListTableID()), |
261
|
|
|
'navParams' => \CAdminResult::GetNavParams($navUniqSettings) |
262
|
|
|
); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* Инициализирует параметры сортировки на основании запроса |
267
|
|
|
* @return \CAdminSorting |
268
|
|
|
*/ |
269
|
|
|
protected function initSortingParameters(HttpRequest $request) |
270
|
|
|
{ |
271
|
|
|
$sortByParameter = 'by'; |
272
|
|
|
$sortOrderParameter = 'order'; |
273
|
|
|
|
274
|
|
|
$sortBy = $request->get($sortByParameter); |
275
|
|
|
$sortBy = $sortBy ?: static::pk(); |
276
|
|
|
|
277
|
|
|
$sortOrder = $request->get($sortOrderParameter); |
278
|
|
|
$sortOrder = $sortOrder ?: 'desc'; |
279
|
|
|
|
280
|
|
|
return new \CAdminSorting($this->getListTableID(), $sortBy, $sortOrder, $sortByParameter, $sortOrderParameter); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* Подготавливает переменные, используемые для инициализации списка. |
285
|
|
|
* |
286
|
|
|
* - добавляет поля в список фильтра только если FILTER не задано false по умолчанию для виджета и поле не является |
287
|
|
|
* полем связи сущностью разделов |
288
|
|
|
*/ |
289
|
|
|
protected function prepareAdminVariables() |
|
|
|
|
290
|
|
|
{ |
291
|
|
|
$this->arHeader = array(); |
292
|
|
|
$this->arFilter = array(); |
293
|
|
|
$this->arFilterFields = array(); |
294
|
|
|
$arFilter = array(); |
295
|
|
|
$this->filterTypes = array(); |
296
|
|
|
$this->arFilterOpts = array(); |
297
|
|
|
|
298
|
|
|
$sectionField = static::getSectionField(); |
299
|
|
|
|
300
|
|
|
foreach ($this->fields as $code => $settings) { |
301
|
|
|
$widget = $this->createWidgetForField($code); |
302
|
|
|
|
303
|
|
|
if ( |
304
|
|
|
($sectionField != $code && $widget->getSettings('FILTER') !==false) |
305
|
|
|
&& |
306
|
|
|
((isset($settings['FILTER']) AND $settings['FILTER'] != false) OR !isset($settings['FILTER'])) |
|
|
|
|
307
|
|
|
) { |
308
|
|
|
|
309
|
|
|
$this->setContext(AdminListHelper::OP_ADMIN_VARIABLES_FILTER); |
310
|
|
|
$filterVarName = 'find_' . $code; |
311
|
|
|
$this->arFilterFields[] = $filterVarName; |
312
|
|
|
$filterType = ''; |
313
|
|
|
|
314
|
|
|
if (is_string($settings['FILTER'])) { |
315
|
|
|
$filterType = $settings['FILTER']; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
if (isset($_REQUEST[$filterVarName]) |
319
|
|
|
AND !isset($_REQUEST['del_filter']) |
|
|
|
|
320
|
|
|
AND $_REQUEST['del_filter'] != 'Y' |
|
|
|
|
321
|
|
|
) { |
322
|
|
|
$arFilter[$filterType . $code] = $_REQUEST[$filterVarName]; |
323
|
|
|
$this->filterTypes[$code] = $filterType; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
$this->arFilterOpts[$code] = $widget->getSettings('TITLE'); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
if (!isset($settings['LIST']) || $settings['LIST'] === true) { |
330
|
|
|
$this->setContext(AdminListHelper::OP_ADMIN_VARIABLES_HEADER); |
331
|
|
|
$mergedColumn = false; |
332
|
|
|
// проверяем есть ли столбец раздела с таким названием |
333
|
|
|
if ($widget->getSettings('LIST_TITLE')) { |
334
|
|
|
$sectionHeader = $this->getSectionsHeader(); |
335
|
|
|
foreach ($sectionHeader as $sectionColumn) { |
336
|
|
|
if ($sectionColumn['content'] == $widget->getSettings('LIST_TITLE')) { |
337
|
|
|
// добавляем столбец элементов в карту столбцов |
338
|
|
|
$this->tableColumnsMap[$code] = $sectionColumn['id']; |
339
|
|
|
$mergedColumn = true; |
340
|
|
|
break; |
341
|
|
|
} |
342
|
|
|
} |
343
|
|
|
} |
344
|
|
|
if (!$mergedColumn) { |
345
|
|
|
$this->arHeader[] = array( |
346
|
|
|
"id" => $code, |
347
|
|
|
"content" => $widget->getSettings('LIST_TITLE') ? $widget->getSettings('LIST_TITLE') : $widget->getSettings('TITLE'), |
348
|
|
|
"sort" => $code, |
349
|
|
|
"default" => !isset($settings['HEADER']) || $settings['HEADER'] === true, |
350
|
|
|
'admin_list_helper_sort' => $widget->getSettings('LIST_COLUMN_SORT') ? $widget->getSettings('LIST_COLUMN_SORT') : 100 |
351
|
|
|
); |
352
|
|
|
} |
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
if ($this->checkFilter($arFilter)) { |
357
|
|
|
$this->arFilter = $arFilter; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
if (static::getHelperClass(AdminSectionEditHelper::className())) { |
361
|
|
|
$this->arFilter[static::getSectionField()] = $_GET['ID']; |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* Возвращает список столбцов для разделов |
367
|
|
|
* @return array |
368
|
|
|
*/ |
369
|
|
|
public function getSectionsHeader() |
370
|
|
|
{ |
371
|
|
|
$arSectionsHeaders = array(); |
372
|
|
|
$sectionHelper = static::getHelperClass(AdminSectionEditHelper::className()); |
373
|
|
|
$sectionsInterfaceSettings = static::getInterfaceSettings($sectionHelper::getViewName()); |
374
|
|
|
$this->sectionFields = $sectionsInterfaceSettings['FIELDS']; |
375
|
|
|
|
376
|
|
|
foreach ($sectionsInterfaceSettings['FIELDS'] as $code => $settings) { |
377
|
|
|
|
378
|
|
|
if (!isset($settings['LIST']) || $settings['LIST'] === true) { |
379
|
|
|
$arSectionsHeaders[] = array( |
380
|
|
|
"id" => $code, |
381
|
|
|
"content" => isset($settings['LIST_TITLE']) ? $settings['LIST_TITLE'] : $settings['TITLE'], |
382
|
|
|
"sort" => $code, |
383
|
|
|
"default" => !isset($settings['HEADER']) || $settings['HEADER'] === true, |
384
|
|
|
'admin_list_helper_sort' => isset($settings['LIST_COLUMN_SORT']) ? $settings['LIST_COLUMN_SORT'] : 100 |
385
|
|
|
); |
386
|
|
|
} |
387
|
|
|
unset($settings['WIDGET']); |
388
|
|
|
|
389
|
|
|
foreach ($settings as $c => $v) { |
390
|
|
|
$sectionsInterfaceSettings['FIELDS'][$code]['WIDGET']->setSetting($c, $v); |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
return $arSectionsHeaders; |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
/** |
398
|
|
|
* Производит проверку корректности данных (в массиве $_REQUEST), переданных в фильтр |
399
|
|
|
* @TODO: нужно сделать вывод сообщений об ошибке фильтрации. |
400
|
|
|
* @param $arFilter |
401
|
|
|
* @return bool |
402
|
|
|
*/ |
403
|
|
|
protected function checkFilter($arFilter) |
404
|
|
|
{ |
405
|
|
|
$this->setContext(AdminListHelper::OP_CHECK_FILTER); |
406
|
|
|
$filterValidationErrors = array(); |
407
|
|
|
foreach ($this->filterTypes as $code => $type) { |
408
|
|
|
$widget = $this->createWidgetForField($code); |
409
|
|
|
$value = $arFilter[$type . $code]; |
410
|
|
|
if (!$widget->checkFilter($type, $value)) { |
411
|
|
|
$filterValidationErrors = array_merge($filterValidationErrors, |
412
|
|
|
$widget->getValidationErrors()); |
413
|
|
|
} |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
return empty($filterValidationErrors); |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
/** |
420
|
|
|
* Подготавливает массив с настройками контекстного меню. По-умолчанию добавлена кнопка "создать элемент". |
421
|
|
|
* |
422
|
|
|
* @see $contextMenu |
423
|
|
|
* |
424
|
|
|
* @api |
425
|
|
|
*/ |
426
|
|
|
protected function getContextMenu() |
|
|
|
|
427
|
|
|
{ |
428
|
|
|
$contextMenu = array(); |
429
|
|
|
$sectionEditHelper = static::getHelperClass(AdminSectionEditHelper::className()); |
430
|
|
|
if ($sectionEditHelper) { |
431
|
|
|
$this->additionalUrlParams['SECTION_ID'] = $_GET['ID']; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
/** |
435
|
|
|
* Если задан для разделов добавляем кнопку создать раздел и |
436
|
|
|
* кнопку на уровень вверх если это не корневой раздел |
437
|
|
|
*/ |
438
|
|
|
if ($sectionEditHelper && isset($_GET['ID'])) { |
439
|
|
|
if ($_GET['ID']) { |
440
|
|
|
$params = $this->additionalUrlParams; |
441
|
|
|
$sectionModel = $sectionEditHelper::getModel(); |
442
|
|
|
$sectionField = $sectionEditHelper::getSectionField(); |
443
|
|
|
$section = $sectionModel::getById( |
444
|
|
|
$this->getCommonPrimaryFilterById($sectionModel, null, $_GET['ID']) |
445
|
|
|
)->Fetch(); |
446
|
|
|
if ($this->isPopup()) { |
447
|
|
|
$params = array_merge($_GET); |
448
|
|
|
} |
449
|
|
|
if ($section[$sectionField]) { |
450
|
|
|
$params['ID'] = $section[$sectionField]; |
451
|
|
|
} |
452
|
|
|
else { |
453
|
|
|
unset($params['ID']); |
454
|
|
|
} |
455
|
|
|
unset($params['SECTION_ID']); |
456
|
|
|
$contextMenu[] = $this->getButton('LIST_SECTION_UP', array( |
457
|
|
|
'LINK' => static::getUrl($params), |
458
|
|
|
'ICON' => 'btn_list' |
459
|
|
|
)); |
460
|
|
|
} |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
/** |
464
|
|
|
* Добавляем кнопку создать элемент и создать раздел |
465
|
|
|
*/ |
466
|
|
|
if (!$this->isPopup() && $this->hasWriteRights()) { |
467
|
|
|
$editHelperClass = static::getHelperClass(AdminEditHelper::className()); |
468
|
|
View Code Duplication |
if ($editHelperClass) { |
|
|
|
|
469
|
|
|
$contextMenu[] = $this->getButton('LIST_CREATE_NEW', array( |
470
|
|
|
'LINK' => $editHelperClass::getUrl($this->additionalUrlParams), |
471
|
|
|
'ICON' => 'btn_new' |
472
|
|
|
)); |
473
|
|
|
} |
474
|
|
|
$sectionsHelperClass = static::getHelperClass(AdminSectionEditHelper::className()); |
475
|
|
View Code Duplication |
if ($sectionsHelperClass) { |
|
|
|
|
476
|
|
|
$contextMenu[] = $this->getButton('LIST_CREATE_NEW_SECTION', array( |
477
|
|
|
'LINK' => $sectionsHelperClass::getUrl($this->additionalUrlParams), |
478
|
|
|
'ICON' => 'btn_new' |
479
|
|
|
)); |
480
|
|
|
} |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
return $contextMenu; |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* Возвращает массив с настройками групповых действий над списком. |
488
|
|
|
* |
489
|
|
|
* @return array |
490
|
|
|
* |
491
|
|
|
* @api |
492
|
|
|
*/ |
493
|
|
|
protected function getGroupActions() |
494
|
|
|
{ |
495
|
|
|
$result = array(); |
496
|
|
|
|
497
|
|
|
if (!$this->isPopup()) { |
498
|
|
|
if ($this->hasDeleteRights()) { |
499
|
|
|
$result = array('delete' => Loc::getMessage("DIGITALWAND_ADMIN_HELPER_LIST_DELETE")); |
500
|
|
|
} |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
return $result; |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* Обработчик групповых операций. По-умолчанию прописаны операции активации / деактивации и удаления. |
508
|
|
|
* |
509
|
|
|
* @param array $IDs |
510
|
|
|
* @param string $action |
511
|
|
|
* |
512
|
|
|
* @api |
513
|
|
|
*/ |
514
|
|
|
protected function groupActions($IDs, $action) |
|
|
|
|
515
|
|
|
{ |
516
|
|
|
$sectionEditHelperClass = $this->getHelperClass(AdminSectionEditHelper::className()); |
517
|
|
|
$listHelperClass = $this->getHelperClass(AdminListHelper::className()); |
518
|
|
|
|
519
|
|
|
$className = static::getModel(); |
520
|
|
|
if (isset($_REQUEST['model'])) { |
521
|
|
|
$className = $_REQUEST['model']; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
View Code Duplication |
if ($sectionEditHelperClass && !isset($_REQUEST['model-section'])) { |
|
|
|
|
525
|
|
|
$sectionClassName = $sectionEditHelperClass::getModel(); |
526
|
|
|
} |
527
|
|
|
else { |
528
|
|
|
$sectionClassName = $_REQUEST['model-section']; |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
if ($action == 'delete') { |
532
|
|
|
if ($this->hasDeleteRights()) { |
533
|
|
|
$complexPrimaryKey = is_array($className::getEntity()->getPrimary()); |
534
|
|
|
if ($complexPrimaryKey) { |
535
|
|
|
$IDs = $this->getIds(); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
// ищем правильный урл для перехода |
539
|
|
|
if (!empty($IDs[0])) { |
540
|
|
|
|
541
|
|
|
$id = $complexPrimaryKey ? $IDs[0][$this->pk()] : $IDs[0]; |
542
|
|
|
$model = $className; |
543
|
|
|
|
544
|
|
|
if (strpos($id, 's') === 0) { |
545
|
|
|
$model = $sectionClassName; |
546
|
|
|
$listHelper = $this->getHelperClass(AdminSectionListHelper::className()); |
547
|
|
|
if (!$listHelper) { |
548
|
|
|
$this->addErrors(Loc::getMessage('DIGITALWAND_ADMIN_HELPER_LIST_SECTION_HELPER_NOT_FOUND')); |
549
|
|
|
unset($_GET['ID']); |
550
|
|
|
return; |
551
|
|
|
} |
552
|
|
|
$id = substr($id, 1); |
553
|
|
|
} else { |
554
|
|
|
$listHelper = $listHelperClass; |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
if ($listHelper) { |
558
|
|
|
$id = $this->getCommonPrimaryFilterById($model, null, $id); |
559
|
|
|
$element = $model::getById($id)->Fetch(); |
560
|
|
|
$sectionField = $listHelper::getSectionField(); |
561
|
|
View Code Duplication |
if ($element[$sectionField]) { |
|
|
|
|
562
|
|
|
$_GET[$this->pk()] = $element[$sectionField]; |
563
|
|
|
} else { |
564
|
|
|
unset($_GET['ID']); |
565
|
|
|
} |
566
|
|
|
} |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
foreach ($IDs as $id) { |
570
|
|
|
$model = $className; |
571
|
|
|
if (strpos($id[$this->pk()], 's') === 0) { |
572
|
|
|
$model = $sectionClassName; |
573
|
|
|
$id[$this->pk()] = substr($id[$this->pk()], 1); |
574
|
|
|
} |
575
|
|
|
/** @var EntityManager $entityManager */ |
576
|
|
|
$entityManager = new static::$entityManager($model, empty($this->data) ? [] : $this->data, $id, |
|
|
|
|
577
|
|
|
$this); |
578
|
|
|
$result = $entityManager->delete(); |
579
|
|
|
$this->addNotes($entityManager->getNotes()); |
580
|
|
|
if (!$result->isSuccess()) { |
581
|
|
|
$this->addErrors($result->getErrorMessages()); |
582
|
|
|
break; |
583
|
|
|
} |
584
|
|
|
} |
585
|
|
|
} |
586
|
|
|
else { |
587
|
|
|
$this->addErrors(Loc::getMessage('DIGITALWAND_ADMIN_HELPER_LIST_DELETE_FORBIDDEN')); |
588
|
|
|
} |
589
|
|
|
} |
590
|
|
|
|
591
|
|
|
if ($action == 'delete-section') { |
592
|
|
|
if ($this->hasDeleteRights()) { |
593
|
|
|
|
594
|
|
|
// ищем правильный урл для перехода |
595
|
|
|
if (!empty($IDs[0])) { |
596
|
|
|
$id = $this->getCommonPrimaryFilterById($sectionClassName, null, $IDs[0]); |
597
|
|
|
$sectionListHelperClass = $this->getHelperClass(AdminSectionListHelper::className()); |
598
|
|
|
if ($sectionListHelperClass) { |
599
|
|
|
$element = $sectionClassName::getById($id)->Fetch(); |
600
|
|
|
$sectionField = $sectionListHelperClass::getSectionField(); |
601
|
|
View Code Duplication |
if ($element[$sectionField]) { |
|
|
|
|
602
|
|
|
$_GET[$this->pk()] = $element[$sectionField]; |
603
|
|
|
} else { |
604
|
|
|
unset($_GET['ID']); |
605
|
|
|
} |
606
|
|
|
} else { |
607
|
|
|
unset($_GET['ID']); |
608
|
|
|
$this->addErrors(Loc::getMessage('DIGITALWAND_ADMIN_HELPER_LIST_SECTION_HELPER_NOT_FOUND')); |
609
|
|
|
return; |
610
|
|
|
} |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
foreach ($IDs as $id) { |
614
|
|
|
$id = $this->getCommonPrimaryFilterById($sectionClassName, null, $id); |
615
|
|
|
$entityManager = new static::$entityManager($sectionClassName, [], $id, $this); |
616
|
|
|
$result = $entityManager->delete(); |
617
|
|
|
$this->addNotes($entityManager->getNotes()); |
618
|
|
|
if(!$result->isSuccess()){ |
619
|
|
|
$this->addErrors($result->getErrorMessages()); |
620
|
|
|
break; |
621
|
|
|
} |
622
|
|
|
} |
623
|
|
|
} |
624
|
|
|
else { |
625
|
|
|
$this->addErrors(Loc::getMessage('DIGITALWAND_ADMIN_HELPER_LIST_DELETE_FORBIDDEN')); |
626
|
|
|
} |
627
|
|
|
} |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
/** |
631
|
|
|
* Сохранение полей для отной записи, отредактированной в списке. |
632
|
|
|
* Этапы: |
633
|
|
|
* <ul> |
634
|
|
|
* <li> Выборка элемента по ID, чтобы удостовериться, что он существует. В противном случае возвращается |
635
|
|
|
* ошибка</li> |
636
|
|
|
* <li> Создание виджета для каждой ячейки, валидация значений поля</li> |
637
|
|
|
* <li> TODO: вывод ошибок валидации</li> |
638
|
|
|
* <li> Сохранение записи</li> |
639
|
|
|
* <li> Вывод ошибок сохранения, если таковые появились</li> |
640
|
|
|
* <li> Модификация данных сроки виджетами.</li> |
641
|
|
|
* </ul> |
642
|
|
|
* |
643
|
|
|
* @param int $id ID записи в БД |
644
|
|
|
* @param array $fields Поля с изменениями |
645
|
|
|
* |
646
|
|
|
* @see HelperWidget::processEditAction(); |
647
|
|
|
* @see HelperWidget::processAfterSaveAction(); |
648
|
|
|
*/ |
649
|
|
|
protected function editAction($id, $fields) |
|
|
|
|
650
|
|
|
{ |
651
|
|
|
$this->setContext(AdminListHelper::OP_EDIT_ACTION); |
652
|
|
|
if(strpos($id, 's')===0){ // для раделов другой класс модели |
653
|
|
|
$editHelperClass = $this->getHelperClass(AdminSectionEditHelper::className()); |
654
|
|
|
$sectionsInterfaceSettings = static::getInterfaceSettings($editHelperClass::getViewName()); |
655
|
|
|
$className = $editHelperClass::getModel(); |
656
|
|
|
$id = str_replace('s','',$id); |
657
|
|
|
}else{ |
658
|
|
|
$className = static::getModel(); |
659
|
|
|
$sectionsInterfaceSettings = false; |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
$idForLog = $id; |
663
|
|
|
$complexPrimaryKey = is_array($className::getEntity()->getPrimary()); |
664
|
|
|
if ($complexPrimaryKey) { |
665
|
|
|
$oldRequest = $_REQUEST; |
666
|
|
|
$_REQUEST = [$this->pk() => $id]; |
667
|
|
|
$id = $this->getCommonPrimaryFilterById($className, null, $id); |
668
|
|
|
$idForLog = json_encode($id); |
669
|
|
|
$_REQUEST = $oldRequest; |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
$el = $className::getById($id); |
673
|
|
|
if ($el->getSelectedRowsCount() == 0) { |
674
|
|
|
$this->list->AddGroupError(Loc::getMessage("MAIN_ADMIN_SAVE_ERROR"), $idForLog); |
675
|
|
|
return; |
676
|
|
|
} |
677
|
|
|
|
678
|
|
|
// замена кодов для столбцов элементов соединенных со столбцами разделов |
679
|
|
|
if($sectionsInterfaceSettings==false){ |
680
|
|
|
$tableColumnsMap = array_flip($this->tableColumnsMap); |
681
|
|
|
$replacedFields = array(); |
682
|
|
|
foreach($fields as $key => $value){ |
683
|
|
|
if(!empty($tableColumnsMap[$key])) { |
684
|
|
|
$key = $tableColumnsMap[$key]; |
685
|
|
|
} |
686
|
|
|
$replacedFields[$key] = $value; |
687
|
|
|
} |
688
|
|
|
$fields = $replacedFields; |
689
|
|
|
} |
690
|
|
|
|
691
|
|
|
$allWidgets = array(); |
692
|
|
|
foreach ($fields as $key => $value) { |
693
|
|
|
if($sectionsInterfaceSettings!==false){ // для разделов свои виджеты |
694
|
|
|
$widget = $sectionsInterfaceSettings['FIELDS'][$key]['WIDGET']; |
695
|
|
|
}else{ |
696
|
|
|
$widget = $this->createWidgetForField($key, $fields); // для элементов свои |
697
|
|
|
} |
698
|
|
|
|
699
|
|
|
$widget->processEditAction(); |
700
|
|
|
$this->validationErrors = array_merge($this->validationErrors, $widget->getValidationErrors()); |
701
|
|
|
$allWidgets[] = $widget; |
702
|
|
|
} |
703
|
|
|
//FIXME: может, надо добавить вывод ошибок ДО сохранения?.. |
704
|
|
|
$this->addErrors($this->validationErrors); |
705
|
|
|
|
706
|
|
|
$result = $className::update($id, $fields); |
707
|
|
|
$errors = $result->getErrorMessages(); |
708
|
|
|
if (empty($this->validationErrors) AND !empty($errors)) { |
|
|
|
|
709
|
|
|
$fieldList = implode("\n", $errors); |
710
|
|
|
$this->list->AddGroupError(Loc::getMessage("MAIN_ADMIN_SAVE_ERROR") . " " . $fieldList, $idForLog); |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
if (!empty($errors)) { |
714
|
|
|
foreach ($allWidgets as $widget) { |
715
|
|
|
/** @var \DigitalWand\AdminHelper\Widget\HelperWidget $widget */ |
716
|
|
|
$widget->setData($fields); |
717
|
|
|
$widget->processAfterSaveAction(); |
718
|
|
|
} |
719
|
|
|
} |
720
|
|
|
} |
721
|
|
|
|
722
|
|
|
/** |
723
|
|
|
* Является ли список всплывающим окном для выбора элементов из списка. |
724
|
|
|
* В этой версии не должно быть операций удаления/перехода к редактированию. |
725
|
|
|
* |
726
|
|
|
* @return boolean |
727
|
|
|
*/ |
728
|
|
|
public function isPopup() |
729
|
|
|
{ |
730
|
|
|
return $this->isPopup; |
731
|
|
|
} |
732
|
|
|
|
733
|
|
|
/** |
734
|
|
|
* Функция определяет js-функцию для двойонго клика по строке. |
735
|
|
|
* Вызывается в том случае, если окно открыто в режиме попапа. |
736
|
|
|
* |
737
|
|
|
* @api |
738
|
|
|
*/ |
739
|
|
|
protected function genPopupActionJS() |
740
|
|
|
{ |
741
|
|
|
$this->popupClickFunctionCode = '<script> |
742
|
|
|
function ' . $this->popupClickFunctionName . '(data){ |
743
|
|
|
var input = window.opener.document.getElementById("' . $this->fieldPopupResultName . '[' . $this->fieldPopupResultIndex . ']"); |
744
|
|
|
if(!input) |
745
|
|
|
input = window.opener.document.getElementById("' . $this->fieldPopupResultName . '"); |
746
|
|
|
if(input) |
747
|
|
|
{ |
748
|
|
|
input.value = data.ID; |
749
|
|
|
if (window.opener.BX) |
750
|
|
|
window.opener.BX.fireEvent(input, "change"); |
751
|
|
|
} |
752
|
|
|
var span = window.opener.document.getElementById("sp_' . md5($this->fieldPopupResultName) . '_' . $this->fieldPopupResultIndex . '"); |
753
|
|
|
if(!span) |
754
|
|
|
span = window.opener.document.getElementById("sp_' . $this->fieldPopupResultName . '"); |
755
|
|
|
if(!span) |
756
|
|
|
span = window.opener.document.getElementById("' . $this->fieldPopupResultName . '_link"); |
757
|
|
|
if(span) |
758
|
|
|
span.innerHTML = data["' . $this->fieldPopupResultElTitle . '"]; |
759
|
|
|
window.close(); |
760
|
|
|
} |
761
|
|
|
</script>'; |
762
|
|
|
} |
763
|
|
|
|
764
|
|
|
/** |
765
|
|
|
* Основной цикл отображения списка. Этапы: |
766
|
|
|
* <ul> |
767
|
|
|
* <li> Вывод заголовков страницы </li> |
768
|
|
|
* <li> Определение списка видимых колонок и колонок, участвующих в выборке. </li> |
769
|
|
|
* <li> Создание виджета для каждого поля выборки </li> |
770
|
|
|
* <li> Модификация параметров запроса каждым из виджетов </li> |
771
|
|
|
* <li> Выборка данных </li> |
772
|
|
|
* <li> Вывод строк таблицы. Во время итерации по строкам возможна модификация данных строки. </li> |
773
|
|
|
* <li> Отрисовка футера таблицы, добавление контекстного меню </li> |
774
|
|
|
* </ul> |
775
|
|
|
* |
776
|
|
|
* @param array $sort Настройки сортировки. |
777
|
|
|
* |
778
|
|
|
* @see AdminListHelper::getList(); |
779
|
|
|
* @see AdminListHelper::getMixedData(); |
780
|
|
|
* @see AdminListHelper::modifyRowData(); |
781
|
|
|
* @see AdminListHelper::addRowCell(); |
782
|
|
|
* @see AdminListHelper::addRow(); |
783
|
|
|
* @see HelperWidget::changeGetListOptions(); |
784
|
|
|
*/ |
785
|
|
|
public function buildList($sort) |
|
|
|
|
786
|
|
|
{ |
787
|
|
|
$this->setContext(AdminListHelper::OP_GET_DATA_BEFORE); |
788
|
|
|
|
789
|
|
|
$headers = $this->arHeader; |
790
|
|
|
|
791
|
|
|
$sectionEditHelper = static::getHelperClass(AdminSectionEditHelper::className()); |
792
|
|
|
|
793
|
|
|
if ($sectionEditHelper) { // если есть реализация класса AdminSectionEditHelper, значит используются разделы |
794
|
|
|
$sectionHeaders = $this->getSectionsHeader(); |
795
|
|
|
foreach ($sectionHeaders as $sectionHeader) { |
796
|
|
|
$found = false; |
797
|
|
|
foreach ($headers as $i => $elementHeader) { |
798
|
|
|
if ($sectionHeader['content'] == $elementHeader['content'] || $sectionHeader['id'] == $elementHeader['id']) { |
799
|
|
|
if (!$elementHeader['default'] && $sectionHeader['default']) { |
800
|
|
|
$headers[$i] = $sectionHeader; |
801
|
|
|
} else { |
802
|
|
|
$found = true; |
803
|
|
|
} |
804
|
|
|
break; |
805
|
|
|
} |
806
|
|
|
} |
807
|
|
|
if (!$found) { |
808
|
|
|
$headers[] = $sectionHeader; |
809
|
|
|
} |
810
|
|
|
} |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
// сортировка столбцов с сохранением исходной позиции в |
814
|
|
|
// массиве для развнозначных элементов |
815
|
|
|
// массив $headers модифицируется |
816
|
|
|
$this->mergeSortHeader($headers); |
817
|
|
|
|
818
|
|
|
$this->list->AddHeaders($headers); |
819
|
|
|
$visibleColumns = $this->list->GetVisibleHeaderColumns(); |
820
|
|
|
|
821
|
|
|
$modelClass = $this->getModel(); |
822
|
|
|
$elementFields = array_keys($modelClass::getEntity()->getFields()); |
823
|
|
|
|
824
|
|
|
if ($sectionEditHelper) { |
825
|
|
|
$sectionsVisibleColumns = array(); |
826
|
|
|
foreach ($visibleColumns as $k => $v) { |
827
|
|
|
if (isset($this->sectionFields[$v])) { |
828
|
|
|
if(!in_array($v, $elementFields)){ |
829
|
|
|
unset($visibleColumns[$k]); |
830
|
|
|
} |
831
|
|
|
if (!isset($this->sectionFields[$v]['LIST']) || $this->sectionFields[$v]['LIST'] !== false) { |
832
|
|
|
$sectionsVisibleColumns[] = $v; |
833
|
|
|
} |
834
|
|
|
} |
835
|
|
|
} |
836
|
|
|
$visibleColumns = array_values($visibleColumns); |
837
|
|
|
$visibleColumns = array_merge($visibleColumns, array_keys($this->tableColumnsMap)); |
838
|
|
|
} |
839
|
|
|
|
840
|
|
|
$className = static::getModel(); |
841
|
|
|
$visibleColumns[] = $this->pk(); |
842
|
|
|
$sectionsVisibleColumns[] = $this->sectionPk(); |
|
|
|
|
843
|
|
|
|
844
|
|
|
$raw = array( |
845
|
|
|
'SELECT' => $visibleColumns, |
846
|
|
|
'FILTER' => $this->arFilter, |
847
|
|
|
'SORT' => $sort |
848
|
|
|
); |
849
|
|
|
|
850
|
|
|
foreach ($this->fields as $name => $settings) { |
851
|
|
|
$key = array_search($name, $visibleColumns); |
852
|
|
|
if ((isset($settings['VIRTUAL']) AND $settings['VIRTUAL'] == true)) { |
|
|
|
|
853
|
|
|
unset($visibleColumns[$key]); |
854
|
|
|
unset($this->arFilter[$name]); |
855
|
|
|
unset($sort[$name]); |
856
|
|
|
} |
857
|
|
View Code Duplication |
if (isset($settings['LIST']) && $settings['LIST'] === false) { |
|
|
|
|
858
|
|
|
unset($visibleColumns[$key]); |
859
|
|
|
} |
860
|
|
View Code Duplication |
if (isset($settings['FORCE_SELECT']) AND $settings['FORCE_SELECT'] == true) { |
|
|
|
|
861
|
|
|
$visibleColumns[] = $name; |
862
|
|
|
} |
863
|
|
|
} |
864
|
|
|
|
865
|
|
|
$visibleColumns = array_unique($visibleColumns); |
866
|
|
|
$sectionsVisibleColumns = array_unique($sectionsVisibleColumns); |
867
|
|
|
|
868
|
|
|
// Поля для селекта (перевернутый массив) |
869
|
|
|
$listSelect = array_flip($visibleColumns); |
870
|
|
|
foreach ($this->fields as $code => $settings) { |
871
|
|
|
$widget = $this->createWidgetForField($code); |
872
|
|
|
$widget->changeGetListOptions($this->arFilter, $visibleColumns, $sort, $raw); |
873
|
|
|
// Множественные поля не должны быть в селекте |
874
|
|
|
if (!empty($settings['MULTIPLE'])) { |
875
|
|
|
unset($listSelect[$code]); |
876
|
|
|
} |
877
|
|
|
} |
878
|
|
|
// Поля для селекта (множественные поля отфильтрованы) |
879
|
|
|
$listSelect = array_flip($listSelect); |
880
|
|
|
|
881
|
|
|
if ($sectionEditHelper) // Вывод разделов и элементов в одном списке |
882
|
|
|
{ |
883
|
|
|
$mixedData = $this->getMixedData($sectionsVisibleColumns, $visibleColumns, $sort, $raw); |
884
|
|
|
$res = new \CDbResult; |
885
|
|
|
$res->InitFromArray($mixedData); |
886
|
|
|
$res = new \CAdminResult($res, $this->getListTableID()); |
887
|
|
|
$res->nSelectedCount = $this->totalRowsCount; |
888
|
|
|
// используем кастомный NavStart что бы определить правильное количество страниц и элементов в списке |
889
|
|
|
$this->customNavStart($res); |
890
|
|
|
$this->list->NavText($res->GetNavPrint(Loc::getMessage("PAGES"))); |
891
|
|
|
while ($data = $res->NavNext(false)) { |
892
|
|
|
$this->modifyRowData($data); |
893
|
|
|
if ($data['IS_SECTION']) // для разделов своя обработка |
894
|
|
|
{ |
895
|
|
|
list($link, $name) = $this->getRow($data, $this->getHelperClass(AdminSectionEditHelper::className())); |
896
|
|
|
$row = $this->list->AddRow('s' . $data[$this->pk()], $data, $link, $name); |
897
|
|
|
foreach ($this->sectionFields as $code => $settings) { |
898
|
|
|
if (in_array($code, $sectionsVisibleColumns)) { |
899
|
|
|
$this->addRowSectionCell($row, $code, $data); |
900
|
|
|
} |
901
|
|
|
} |
902
|
|
|
$row->AddActions($this->getRowActions($data, true)); |
903
|
|
|
} |
904
|
|
|
else // для элементов своя |
905
|
|
|
{ |
906
|
|
|
$this->modifyRowData($data); |
907
|
|
|
list($link, $name) = $this->getRow($data); |
908
|
|
|
// объединение полей элемента с полями раздела |
909
|
|
|
foreach ($this->tableColumnsMap as $elementCode => $sectionCode) { |
910
|
|
|
if (isset($data[$elementCode])) { |
911
|
|
|
$data[$sectionCode] = $data[$elementCode]; |
912
|
|
|
} |
913
|
|
|
} |
914
|
|
|
$row = $this->list->AddRow($data[$this->pk()], $data, $link, $name); |
915
|
|
|
foreach ($this->fields as $code => $settings) { |
916
|
|
|
if(in_array($code, $listSelect)) { |
917
|
|
|
$this->addRowCell($row, $code, $data, |
918
|
|
|
isset($this->tableColumnsMap[$code]) ? $this->tableColumnsMap[$code] : false); |
919
|
|
|
} |
920
|
|
|
} |
921
|
|
|
$row->AddActions($this->getRowActions($data)); |
922
|
|
|
} |
923
|
|
|
} |
924
|
|
|
} |
925
|
|
|
else // Обычный вывод элементов без использования разделов |
926
|
|
|
{ |
927
|
|
|
$this->totalRowsCount = $className::getCount($this->getElementsFilter($this->arFilter)); |
928
|
|
|
$res = $this->getData($className, $this->arFilter, $listSelect, $sort, $raw); |
|
|
|
|
929
|
|
|
$res = new \CAdminResult($res, $this->getListTableID()); |
930
|
|
|
$this->customNavStart($res); |
931
|
|
|
// отключаем отображение всех элементов, если установлено св-во |
932
|
|
|
$res->bShowAll = $this->showAll; |
933
|
|
|
$this->list->NavText($res->GetNavPrint(Loc::getMessage("PAGES"))); |
934
|
|
|
while ($data = $res->NavNext(false)) { |
935
|
|
|
$this->modifyRowData($data); |
936
|
|
|
list($link, $name) = $this->getRow($data); |
937
|
|
|
$row = $this->list->AddRow($data[$this->pk()], $data, $link, $name); |
938
|
|
|
foreach ($this->fields as $code => $settings) { |
939
|
|
|
if(in_array($code, $listSelect)) { |
940
|
|
|
$this->addRowCell($row, $code, $data); |
941
|
|
|
} |
942
|
|
|
} |
943
|
|
|
$row->AddActions($this->getRowActions($data)); |
944
|
|
|
} |
945
|
|
|
} |
946
|
|
|
|
947
|
|
|
$this->list->AddFooter($this->getFooter($res)); |
948
|
|
|
$this->list->AddGroupActionTable($this->getGroupActions(), $this->groupActionsParams); |
949
|
|
|
$this->list->AddAdminContextMenu($this->getContextMenu(), $this->exportExcel); |
950
|
|
|
|
951
|
|
|
$this->list->BeginPrologContent(); |
952
|
|
|
echo $this->prologHtml; |
953
|
|
|
$this->list->EndPrologContent(); |
954
|
|
|
|
955
|
|
|
$this->list->BeginEpilogContent(); |
956
|
|
|
echo $this->epilogHtml; |
957
|
|
|
$this->list->EndEpilogContent(); |
958
|
|
|
|
959
|
|
|
// добавляем ошибки в CAdminList для режимов list и frame |
960
|
|
|
$errors = $this->getErrors(); |
961
|
|
|
if(in_array($_GET['mode'], array('list','frame')) && is_array($errors)) { |
962
|
|
|
foreach($errors as $error) { |
963
|
|
|
$this->list->addGroupError($error); |
964
|
|
|
} |
965
|
|
|
} |
966
|
|
|
|
967
|
|
|
$this->list->CheckListMode(); |
968
|
|
|
} |
969
|
|
|
|
970
|
|
|
/** |
971
|
|
|
* Функция сортировки столбцов c сохранением порядка равнозначных элементов |
972
|
|
|
* @param $array |
973
|
|
|
*/ |
974
|
|
|
protected function mergeSortHeader(&$array) |
975
|
|
|
{ |
976
|
|
|
// для сортировки нужно хотя бы 2 элемента |
977
|
|
|
if (count($array) < 2) return; |
978
|
|
|
|
979
|
|
|
// делим массив пополам |
980
|
|
|
$halfway = count($array) / 2; |
981
|
|
|
$array1 = array_slice($array, 0, $halfway); |
982
|
|
|
$array2 = array_slice($array, $halfway); |
983
|
|
|
|
984
|
|
|
// реукрсивно сортируем каждую половину |
985
|
|
|
$this->mergeSortHeader($array1); |
986
|
|
|
$this->mergeSortHeader($array2); |
987
|
|
|
|
988
|
|
|
// если последний элемент первой половины меньше или равен первому элементу |
989
|
|
|
// второй половины, то просто соединяем массивы |
990
|
|
|
if ($this->mergeSortHeaderCompare(end($array1), $array2[0]) < 1) { |
991
|
|
|
$array = array_merge($array1, $array2); |
992
|
|
|
return; |
993
|
|
|
} |
994
|
|
|
|
995
|
|
|
// соединяем 2 отсортированных половины в один отсортированный массив |
996
|
|
|
$array = array(); |
997
|
|
|
$ptr1 = $ptr2 = 0; |
998
|
|
|
while ($ptr1 < count($array1) && $ptr2 < count($array2)) { |
999
|
|
|
// собираем в 1 массив последовательную цепочку |
1000
|
|
|
// элементов из 2-х отсортированных половинок |
1001
|
|
|
if ($this->mergeSortHeaderCompare($array1[$ptr1], $array2[$ptr2]) < 1) { |
1002
|
|
|
$array[] = $array1[$ptr1++]; |
1003
|
|
|
} |
1004
|
|
|
else { |
1005
|
|
|
$array[] = $array2[$ptr2++]; |
1006
|
|
|
} |
1007
|
|
|
} |
1008
|
|
|
|
1009
|
|
|
// если в исходных массивах что-то осталось забираем в основной массив |
1010
|
|
|
while ($ptr1 < count($array1)) $array[] = $array1[$ptr1++]; |
1011
|
|
|
while ($ptr2 < count($array2)) $array[] = $array2[$ptr2++]; |
1012
|
|
|
|
1013
|
|
|
return; |
1014
|
|
|
} |
1015
|
|
|
|
1016
|
|
|
/** |
1017
|
|
|
* Функция сравнения столбцов по их весу в сортировке |
1018
|
|
|
* @param $a |
1019
|
|
|
* @param $b |
1020
|
|
|
* @return int |
1021
|
|
|
*/ |
1022
|
|
|
public function mergeSortHeaderCompare($a, $b) |
1023
|
|
|
{ |
1024
|
|
|
$a = $a['admin_list_helper_sort']; |
1025
|
|
|
$b = $b['admin_list_helper_sort']; |
1026
|
|
|
if ($a == $b) { |
1027
|
|
|
return 0; |
1028
|
|
|
} |
1029
|
|
|
|
1030
|
|
|
return ($a < $b) ? -1 : 1; |
1031
|
|
|
} |
1032
|
|
|
|
1033
|
|
|
/** |
1034
|
|
|
* Получение смешанного списка из разделов и элементов. |
1035
|
|
|
* |
1036
|
|
|
* @param $sectionsVisibleColumns |
1037
|
|
|
* @param $elementVisibleColumns |
1038
|
|
|
* @param $sort |
1039
|
|
|
* @param $raw |
1040
|
|
|
* @return array |
1041
|
|
|
*/ |
1042
|
|
|
protected function getMixedData($sectionsVisibleColumns, $elementVisibleColumns, $sort, $raw) |
|
|
|
|
1043
|
|
|
{ |
1044
|
|
|
$sectionEditHelperClass = $this->getHelperClass(AdminSectionEditHelper::className()); |
1045
|
|
|
$elementEditHelperClass = $this->getHelperClass(AdminEditHelper::className()); |
1046
|
|
|
$sectionField = $sectionEditHelperClass::getSectionField(); |
1047
|
|
|
$sectionId = $_GET['SECTION_ID'] ? $_GET['SECTION_ID'] : $_GET['ID']; |
1048
|
|
|
$returnData = array(); |
1049
|
|
|
/** |
1050
|
|
|
* @var DataManager $sectionModel |
1051
|
|
|
*/ |
1052
|
|
|
$sectionModel = $sectionEditHelperClass::getModel(); |
1053
|
|
|
$sectionFilter = array(); |
1054
|
|
|
|
1055
|
|
|
// добавляем из фильтра те поля которые есть у разделов |
1056
|
|
|
foreach ($this->arFilter as $field => $value) { |
1057
|
|
|
$fieldName = $this->escapeFilterFieldName($field); |
1058
|
|
|
|
1059
|
|
|
if(!empty($this->tableColumnsMap[$fieldName])) { |
1060
|
|
|
$field = str_replace($fieldName, $this->tableColumnsMap[$fieldName], $field); |
1061
|
|
|
$fieldName = $this->tableColumnsMap[$fieldName]; |
1062
|
|
|
} |
1063
|
|
|
|
1064
|
|
|
if (in_array($fieldName, $sectionsVisibleColumns)) { |
1065
|
|
|
$sectionFilter[$field] = $value; |
1066
|
|
|
} |
1067
|
|
|
} |
1068
|
|
|
|
1069
|
|
|
$sectionFilter[$sectionField] = $sectionId; |
1070
|
|
|
|
1071
|
|
|
$raw['SELECT'] = array_unique($raw['SELECT']); |
1072
|
|
|
|
1073
|
|
|
// при использовании в качестве popup окна исключаем раздел из выборке |
1074
|
|
|
// что бы не было возможности сделать раздел родителем самого себя |
1075
|
|
|
if (!empty($_REQUEST['self_id'])) { |
1076
|
|
|
$sectionFilter['!' . $this->sectionPk()] = $_REQUEST['self_id']; |
1077
|
|
|
} |
1078
|
|
|
|
1079
|
|
|
$sectionSort = array(); |
1080
|
|
|
$limitData = $this->getLimits(); |
1081
|
|
|
// добавляем к общему количеству элементов количество разделов |
1082
|
|
|
$this->totalRowsCount = $sectionModel::getCount($this->getSectionsFilter($sectionFilter)); |
1083
|
|
|
foreach ($sort as $field => $direction) { |
1084
|
|
|
if (in_array($field, $sectionsVisibleColumns)) { |
1085
|
|
|
$sectionSort[$field] = $direction; |
1086
|
|
|
} |
1087
|
|
|
} |
1088
|
|
|
// добавляем к выборке разделы |
1089
|
|
|
$rsSections = $sectionModel::getList(array( |
1090
|
|
|
'filter' => $this->getSectionsFilter($sectionFilter), |
1091
|
|
|
'select' => $sectionsVisibleColumns, |
1092
|
|
|
'order' => $sectionSort, |
1093
|
|
|
'limit' => $limitData[1], |
1094
|
|
|
'offset' => $limitData[0], |
1095
|
|
|
)); |
1096
|
|
|
|
1097
|
|
|
while ($section = $rsSections->fetch()) { |
1098
|
|
|
$section['IS_SECTION'] = true; |
1099
|
|
|
$returnData[] = $section; |
1100
|
|
|
} |
1101
|
|
|
|
1102
|
|
|
// расчитываем offset и limit для элементов |
1103
|
|
|
if (count($returnData) > 0) { |
1104
|
|
|
$elementOffset = 0; |
1105
|
|
|
} |
1106
|
|
|
else { |
1107
|
|
|
$elementOffset = $limitData[0] - $this->totalRowsCount; |
1108
|
|
|
} |
1109
|
|
|
|
1110
|
|
|
// для списка разделов элементы не нужны |
1111
|
|
|
if (static::getHelperClass(AdminSectionListHelper::className()) == static::className()) { |
1112
|
|
|
return $returnData; |
1113
|
|
|
} |
1114
|
|
|
|
1115
|
|
|
$elementLimit = $limitData[1] - count($returnData); |
1116
|
|
|
$elementModel = static::$model; |
1117
|
|
|
$elementFilter = $this->arFilter; |
1118
|
|
|
$elementFilter[$elementEditHelperClass::getSectionField()] = $_GET['ID']; |
1119
|
|
|
// добавляем к общему количеству элементов количество элементов |
1120
|
|
|
$this->totalRowsCount += $elementModel::getCount($this->getElementsFilter($elementFilter)); |
1121
|
|
|
|
1122
|
|
|
// возвращае данные без элементов если разделы занимают всю страницу выборки |
1123
|
|
|
if (!empty($returnData) && $limitData[0] == 0 && $limitData[1] == $this->totalRowsCount) { |
1124
|
|
|
return $returnData; |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
$elementSort = array(); |
1128
|
|
|
foreach ($sort as $field => $direction) { |
1129
|
|
|
if (in_array($field, $elementVisibleColumns)) { |
1130
|
|
|
$elementSort[$field] = $direction; |
1131
|
|
|
} |
1132
|
|
|
} |
1133
|
|
|
|
1134
|
|
|
$elementParams = array( |
1135
|
|
|
'filter' => $this->getElementsFilter($elementFilter), |
1136
|
|
|
'select' => $elementVisibleColumns, |
1137
|
|
|
'order' => $elementSort, |
1138
|
|
|
); |
1139
|
|
|
if ($elementLimit > 0 && $elementOffset >= 0) { |
1140
|
|
|
$elementParams['limit'] = $elementLimit; |
1141
|
|
|
$elementParams['offset'] = $elementOffset; |
1142
|
|
|
// добавляем к выборке элементы |
1143
|
|
|
$rsSections = $elementModel::getList($elementParams); |
1144
|
|
|
|
1145
|
|
|
while ($element = $rsSections->fetch()) { |
1146
|
|
|
$element['IS_SECTION'] = false; |
1147
|
|
|
$returnData[] = $element; |
1148
|
|
|
} |
1149
|
|
|
} |
1150
|
|
|
|
1151
|
|
|
return $returnData; |
1152
|
|
|
} |
1153
|
|
|
|
1154
|
|
|
/** |
1155
|
|
|
* Огранчения выборки из CAdminResult |
1156
|
|
|
* @return array |
1157
|
|
|
*/ |
1158
|
|
|
protected function getLimits() |
1159
|
|
|
{ |
1160
|
|
|
if ($this->navParams['navParams']['SHOW_ALL']) { |
1161
|
|
|
return array(); |
1162
|
|
|
} |
1163
|
|
|
else { |
1164
|
|
|
if (!intval($this->navParams['navParams']['PAGEN']) OR !isset($this->navParams['navParams']['PAGEN'])) { |
|
|
|
|
1165
|
|
|
$this->navParams['navParams']['PAGEN'] = 1; |
1166
|
|
|
} |
1167
|
|
|
$from = $this->navParams['nPageSize'] * ((int)$this->navParams['navParams']['PAGEN'] - 1); |
1168
|
|
|
|
1169
|
|
|
return array($from, $this->navParams['nPageSize']); |
1170
|
|
|
} |
1171
|
|
|
} |
1172
|
|
|
|
1173
|
|
|
/** |
1174
|
|
|
* Очищает название поля от операторов фильтра |
1175
|
|
|
* @param string $fieldName названия поля из фильтра |
1176
|
|
|
* @return string название поля без без операторов фильтра |
1177
|
|
|
*/ |
1178
|
|
|
protected function escapeFilterFieldName($fieldName) |
1179
|
|
|
{ |
1180
|
|
|
return str_replace(array('!','<', '<=', '>', '>=', '><', '=', '%'), '', $fieldName); |
1181
|
|
|
} |
1182
|
|
|
|
1183
|
|
|
/** |
1184
|
|
|
* Выполняет CDBResult::NavNext с той разницей, что общее количество элементов берется не из count($arResult), |
1185
|
|
|
* а из нашего параметра, полученного из SQL-запроса. |
1186
|
|
|
* array_slice также не делается. |
1187
|
|
|
* |
1188
|
|
|
* @param \CAdminResult $res |
1189
|
|
|
*/ |
1190
|
|
|
protected function customNavStart(&$res) |
|
|
|
|
1191
|
|
|
{ |
1192
|
|
|
$res->NavStart($this->navParams['nPageSize'], |
1193
|
|
|
$this->navParams['navParams']['SHOW_ALL'], |
1194
|
|
|
(int)$this->navParams['navParams']['PAGEN'] |
1195
|
|
|
); |
1196
|
|
|
// отключаем отображение всех элементов |
1197
|
|
|
$res->bShowAll = $this->showAll; |
1198
|
|
|
|
1199
|
|
|
$res->NavRecordCount = $this->totalRowsCount; |
1200
|
|
|
if ($res->NavRecordCount < 1) |
1201
|
|
|
return; |
1202
|
|
|
|
1203
|
|
|
if ($res->NavShowAll) |
1204
|
|
|
$res->NavPageSize = $res->NavRecordCount; |
1205
|
|
|
|
1206
|
|
|
$res->NavPageCount = floor($res->NavRecordCount / $res->NavPageSize); |
1207
|
|
|
if ($res->NavRecordCount % $res->NavPageSize > 0) |
1208
|
|
|
$res->NavPageCount++; |
1209
|
|
|
|
1210
|
|
|
$res->NavPageNomer = |
1211
|
|
|
($res->PAGEN < 1 || $res->PAGEN > $res->NavPageCount |
1212
|
|
|
? |
1213
|
|
|
(\CPageOption::GetOptionString("main", "nav_page_in_session", "Y") != "Y" |
1214
|
|
|
|| $_SESSION[$res->SESS_PAGEN] < 1 |
1215
|
|
|
|| $_SESSION[$res->SESS_PAGEN] > $res->NavPageCount |
1216
|
|
|
? |
1217
|
|
|
1 |
1218
|
|
|
: |
1219
|
|
|
$_SESSION[$res->SESS_PAGEN] |
1220
|
|
|
) |
1221
|
|
|
: |
1222
|
|
|
$res->PAGEN |
1223
|
|
|
); |
1224
|
|
|
} |
1225
|
|
|
|
1226
|
|
|
/** |
1227
|
|
|
* Преобразует данные строки, перед тем как добавлять их в список. |
1228
|
|
|
* |
1229
|
|
|
* @param $data |
1230
|
|
|
* |
1231
|
|
|
* @see AdminListHelper::getList() |
1232
|
|
|
* |
1233
|
|
|
* @api |
1234
|
|
|
*/ |
1235
|
|
|
protected function modifyRowData(&$data) |
|
|
|
|
1236
|
|
|
{ |
1237
|
|
|
} |
1238
|
|
|
|
1239
|
|
|
/** |
1240
|
|
|
* Настройки строки таблицы. |
1241
|
|
|
* |
1242
|
|
|
* @param array $data Данные текущей строки БД. |
1243
|
|
|
* @param bool|string $class Класс хелпера через метод getUrl которого идет получение ссылки. |
1244
|
|
|
* |
1245
|
|
|
* @return array Возвращает ссылку на детальную страницу и её название. |
1246
|
|
|
* |
1247
|
|
|
* @api |
1248
|
|
|
*/ |
1249
|
|
|
protected function getRow($data, $class = false) |
1250
|
|
|
{ |
1251
|
|
|
if (empty($class)) { |
1252
|
|
|
$class = static::getHelperClass(AdminEditHelper::className()); |
1253
|
|
|
} |
1254
|
|
|
if ($this->isPopup()) { |
1255
|
|
|
return array(); |
1256
|
|
|
} |
1257
|
|
|
else { |
1258
|
|
|
$query = array_merge($this->additionalUrlParams, array( |
1259
|
|
|
'lang' => LANGUAGE_ID, |
1260
|
|
|
$this->pk() => $data[$this->pk()] |
1261
|
|
|
)); |
1262
|
|
|
|
1263
|
|
|
return array($class::getUrl($query)); |
1264
|
|
|
} |
1265
|
|
|
} |
1266
|
|
|
|
1267
|
|
|
/** |
1268
|
|
|
* Для каждой ячейки(раздела) таблицы создаёт виджет соответствующего типа. |
1269
|
|
|
* Виджет подготавливает необходимый HTML для списка. |
1270
|
|
|
* |
1271
|
|
|
* @param \CAdminListRow $row |
1272
|
|
|
* @param $code Сивольный код поля. |
1273
|
|
|
* @param $data Данные текущей строки. |
1274
|
|
|
* |
1275
|
|
|
* @throws Exception |
1276
|
|
|
* |
1277
|
|
|
* @see HelperWidget::generateRow() |
1278
|
|
|
*/ |
1279
|
|
|
protected function addRowSectionCell($row, $code, $data) |
1280
|
|
|
{ |
1281
|
|
|
$sectionEditHelper = $this->getHelperClass(AdminSectionEditHelper::className()); |
1282
|
|
View Code Duplication |
if (!isset($this->sectionFields[$code]['WIDGET'])) { |
|
|
|
|
1283
|
|
|
$error = str_replace('#CODE#', $code, 'Can\'t create widget for the code "#CODE#"'); |
1284
|
|
|
throw new Exception($error, Exception::CODE_NO_WIDGET); |
1285
|
|
|
} |
1286
|
|
|
|
1287
|
|
|
/** |
1288
|
|
|
* @var \DigitalWand\AdminHelper\Widget\HelperWidget $widget |
1289
|
|
|
*/ |
1290
|
|
|
$widget = $this->sectionFields[$code]['WIDGET']; |
1291
|
|
|
|
1292
|
|
|
$widget->setHelper($this); |
1293
|
|
|
$widget->setCode($code); |
1294
|
|
|
$widget->setData($data); |
1295
|
|
|
$widget->setEntityName($sectionEditHelper::getModel()); |
1296
|
|
|
|
1297
|
|
|
$this->setContext(AdminListHelper::OP_ADD_ROW_CELL); |
1298
|
|
|
$widget->generateRow($row, $data); |
1299
|
|
|
} |
1300
|
|
|
|
1301
|
|
|
/** |
1302
|
|
|
* Возвращает массив со списком действий при клике правой клавишей мыши на строке таблицы |
1303
|
|
|
* По-умолчанию: |
1304
|
|
|
* <ul> |
1305
|
|
|
* <li> Редактировать элемент </li> |
1306
|
|
|
* <li> Удалить элемент </li> |
1307
|
|
|
* <li> Если это всплывающее окно - запустить кастомную JS-функцию. </li> |
1308
|
|
|
* </ul> |
1309
|
|
|
* |
1310
|
|
|
* @param $data Данные текущей строки. |
1311
|
|
|
* @param $section Признак списка для раздела. |
1312
|
|
|
* |
1313
|
|
|
* @return array |
1314
|
|
|
* |
1315
|
|
|
* @see CAdminListRow::AddActions |
1316
|
|
|
* |
1317
|
|
|
* @api |
1318
|
|
|
*/ |
1319
|
|
|
protected function getRowActions($data, $section = false) |
1320
|
|
|
{ |
1321
|
|
|
$actions = array(); |
1322
|
|
|
|
1323
|
|
|
if ($this->isPopup()) { |
1324
|
|
|
$jsData = \CUtil::PhpToJSObject($data); |
1325
|
|
|
$actions['select'] = array( |
1326
|
|
|
'ICON' => 'select', |
1327
|
|
|
'DEFAULT' => true, |
1328
|
|
|
'TEXT' => Loc::getMessage('DIGITALWAND_ADMIN_HELPER_LIST_SELECT'), |
1329
|
|
|
"ACTION" => 'javascript:' . $this->popupClickFunctionName . '(' . $jsData . ')' |
1330
|
|
|
); |
1331
|
|
|
} |
1332
|
|
|
else { |
1333
|
|
|
$viewQueryString = 'module=' . static::getModule() . '&view=' . static::getViewName() . '&entity=' . static::getEntityCode(); |
1334
|
|
|
$query = array_merge($this->additionalUrlParams, |
1335
|
|
|
array($this->pk() => $data[$this->pk()])); |
1336
|
|
|
if ($this->hasWriteRights()) { |
1337
|
|
|
$sectionHelperClass = static::getHelperClass(AdminSectionEditHelper::className()); |
1338
|
|
|
$editHelperClass = static::getHelperClass(AdminEditHelper::className()); |
1339
|
|
|
|
1340
|
|
|
$actions['edit'] = array( |
1341
|
|
|
'ICON' => 'edit', |
1342
|
|
|
'DEFAULT' => true, |
1343
|
|
|
'TEXT' => Loc::getMessage('DIGITALWAND_ADMIN_HELPER_LIST_EDIT'), |
1344
|
|
|
'ACTION' => $this->list->ActionRedirect($section ? $sectionHelperClass::getUrl($query) : $editHelperClass::getUrl($query)) |
1345
|
|
|
); |
1346
|
|
|
} |
1347
|
|
|
if ($this->hasDeleteRights()) { |
1348
|
|
|
$actions['delete'] = array( |
1349
|
|
|
'ICON' => 'delete', |
1350
|
|
|
'TEXT' => Loc::getMessage("DIGITALWAND_ADMIN_HELPER_LIST_DELETE"), |
1351
|
|
|
'ACTION' => "if(confirm('" . Loc::getMessage('DIGITALWAND_ADMIN_HELPER_LIST_DELETE_CONFIRM') . "')) " . $this->list->ActionDoGroup($data[$this->pk()], |
1352
|
|
|
$section ? "delete-section" : "delete", $viewQueryString) |
1353
|
|
|
); |
1354
|
|
|
} |
1355
|
|
|
} |
1356
|
|
|
|
1357
|
|
|
return $actions; |
1358
|
|
|
} |
1359
|
|
|
|
1360
|
|
|
/** |
1361
|
|
|
* Для каждой ячейки таблицы создаёт виджет соответствующего типа. Виджет подготавливает необходимый HTML-код |
1362
|
|
|
* для списка. |
1363
|
|
|
* |
1364
|
|
|
* @param \CAdminListRow $row Объект строки списка записей. |
1365
|
|
|
* @param string $code Сивольный код поля. |
1366
|
|
|
* @param array $data Данные текущей строки. |
1367
|
|
|
* @param bool $virtualCode |
1368
|
|
|
* |
1369
|
|
|
* @throws Exception |
1370
|
|
|
* |
1371
|
|
|
* @see HelperWidget::generateRow() |
1372
|
|
|
*/ |
1373
|
|
|
protected function addRowCell($row, $code, $data, $virtualCode = false) |
1374
|
|
|
{ |
1375
|
|
|
$widget = $this->createWidgetForField($code, $data); |
1376
|
|
|
$this->setContext(AdminListHelper::OP_ADD_ROW_CELL); |
1377
|
|
|
|
1378
|
|
|
// устанавливаем виртуальный код ячейки, используется при слиянии столбцов |
1379
|
|
|
if ($virtualCode) { |
1380
|
|
|
$widget->setCode($virtualCode); |
|
|
|
|
1381
|
|
|
} |
1382
|
|
|
|
1383
|
|
|
$widget->generateRow($row, $data); |
1384
|
|
|
|
1385
|
|
|
if ($virtualCode) { |
1386
|
|
|
$widget->setCode($code); |
1387
|
|
|
} |
1388
|
|
|
} |
1389
|
|
|
|
1390
|
|
|
/** |
1391
|
|
|
* Производит выборку данных. Функцию стоит переопределить в случае, если необходима своя логика, и её нельзя |
1392
|
|
|
* вынести в класс модели. |
1393
|
|
|
* |
1394
|
|
|
* @param DataManager $className |
1395
|
|
|
* @param array $filter |
1396
|
|
|
* @param array $select |
1397
|
|
|
* @param array $sort |
1398
|
|
|
* @param array $raw |
1399
|
|
|
* |
1400
|
|
|
* @return Result |
1401
|
|
|
* |
1402
|
|
|
* @api |
1403
|
|
|
*/ |
1404
|
|
|
protected function getData($className, $filter, $select, $sort, $raw) |
|
|
|
|
1405
|
|
|
{ |
1406
|
|
|
$limits = $this->getLimits(); |
1407
|
|
|
$parameters = array( |
1408
|
|
|
'filter' => $this->getElementsFilter($filter), |
1409
|
|
|
'select' => $select, |
1410
|
|
|
'order' => $sort, |
1411
|
|
|
'offset' => $limits[0], |
1412
|
|
|
'limit' => $limits[1], |
1413
|
|
|
); |
1414
|
|
|
|
1415
|
|
|
/** @var Result $res */ |
1416
|
|
|
$res = $className::getList($parameters); |
1417
|
|
|
|
1418
|
|
|
return $res; |
1419
|
|
|
} |
1420
|
|
|
|
1421
|
|
|
/** |
1422
|
|
|
* Подготавливает массив с настройками футера таблицы Bitrix |
1423
|
|
|
* @param \CAdminResult $res - результат выборки данных |
1424
|
|
|
* @see \CAdminList::AddFooter() |
1425
|
|
|
* @return array[] |
1426
|
|
|
*/ |
1427
|
|
|
protected function getFooter($res) |
1428
|
|
|
{ |
1429
|
|
|
return array( |
1430
|
|
|
$this->getButton('MAIN_ADMIN_LIST_SELECTED', array("value" => $res->SelectedRowsCount())), |
1431
|
|
|
$this->getButton('MAIN_ADMIN_LIST_CHECKED', array("value" => $res->SelectedRowsCount()), array( |
1432
|
|
|
"counter" => true, |
1433
|
|
|
"value" => "0", |
1434
|
|
|
)), |
1435
|
|
|
); |
1436
|
|
|
} |
1437
|
|
|
|
1438
|
|
|
/** |
1439
|
|
|
* Выводит форму фильтрации списка |
1440
|
|
|
*/ |
1441
|
|
|
public function createFilterForm() |
1442
|
|
|
{ |
1443
|
|
|
//нужно пробрасывать параметр popup в форму, если она является таковой |
1444
|
|
|
if($this->isPopup()) |
1445
|
|
|
{ |
1446
|
|
|
$this->additionalUrlParams['popup'] = 'Y'; |
1447
|
|
|
} |
1448
|
|
|
|
1449
|
|
|
$this->setContext(AdminListHelper::OP_CREATE_FILTER_FORM); |
1450
|
|
|
print ' <form name="find_form" method="GET" action="' . static::getUrl($this->additionalUrlParams) . '?">'; |
1451
|
|
|
|
1452
|
|
|
$sectionHelper = $this->getHelperClass(AdminSectionEditHelper::className()); |
1453
|
|
|
if($sectionHelper) { |
1454
|
|
|
$sectionsInterfaceSettings = static::getInterfaceSettings($sectionHelper::getViewName()); |
1455
|
|
|
foreach($this->arFilterOpts as $code => &$name) { |
1456
|
|
|
if(!empty($this->tableColumnsMap[$code])) { |
1457
|
|
|
$name = $sectionsInterfaceSettings['FIELDS'][$this->tableColumnsMap[$code]]['WIDGET']->getSettings('TITLE'); |
1458
|
|
|
} |
1459
|
|
|
} |
1460
|
|
|
} |
1461
|
|
|
|
1462
|
|
|
$oFilter = new \CAdminFilter($this->getListTableID() . '_filter', $this->arFilterOpts); |
1463
|
|
|
$oFilter->Begin(); |
1464
|
|
|
|
1465
|
|
|
foreach ($this->arFilterOpts as $code => $name) { |
1466
|
|
|
$widget = $this->createWidgetForField($code); |
1467
|
|
|
if($widget->getSettings('TITLE') != $this->arFilterOpts[$code]) { |
1468
|
|
|
$widget->setSetting('TITLE', $this->arFilterOpts[$code]); |
1469
|
|
|
} |
1470
|
|
|
$widget->showFilterHtml(); |
1471
|
|
|
} |
1472
|
|
|
|
1473
|
|
|
$oFilter->Buttons(array( |
1474
|
|
|
"table_id" => $this->getListTableID(), |
1475
|
|
|
"url" => static::getUrl($this->additionalUrlParams), |
1476
|
|
|
"form" => "find_form", |
1477
|
|
|
)); |
1478
|
|
|
$oFilter->End(); |
1479
|
|
|
|
1480
|
|
|
print '</form>'; |
1481
|
|
|
} |
1482
|
|
|
|
1483
|
|
|
/** |
1484
|
|
|
* Возвращает ID таблицы, который не должен конфликтовать с ID в других разделах админки, а также нормально |
1485
|
|
|
* парситься в JS |
1486
|
|
|
* |
1487
|
|
|
* @return string |
1488
|
|
|
*/ |
1489
|
|
|
protected function getListTableID() |
1490
|
|
|
{ |
1491
|
|
|
return str_replace('.', '', static::$tablePrefix . $this->table()); |
1492
|
|
|
} |
1493
|
|
|
|
1494
|
|
|
/** |
1495
|
|
|
* Выводит сформированный список. |
1496
|
|
|
* Сохраняет обработанный GET-запрос в сессию |
1497
|
|
|
*/ |
1498
|
|
|
public function show() |
1499
|
|
|
{ |
1500
|
|
|
if (!$this->hasReadRights()) { |
1501
|
|
|
$this->addErrors(Loc::getMessage('DIGITALWAND_ADMIN_HELPER_ACCESS_FORBIDDEN')); |
1502
|
|
|
$this->showMessages(); |
1503
|
|
|
|
1504
|
|
|
return false; |
1505
|
|
|
} |
1506
|
|
|
$this->showMessages(); |
1507
|
|
|
$this->list->DisplayList(); |
1508
|
|
|
|
1509
|
|
|
if ($this->isPopup()) { |
1510
|
|
|
print $this->popupClickFunctionCode; |
1511
|
|
|
} |
1512
|
|
|
|
1513
|
|
|
$this->saveGetQuery(); |
1514
|
|
|
} |
1515
|
|
|
|
1516
|
|
|
/** |
1517
|
|
|
* Сохраняет параметры запроса для поторного использования после возврата с других страниц (к примеру, после |
1518
|
|
|
* перехода с детальной обратно в список - чтобы вернуться в точности в тот раздел, с которого ранее ушли) |
1519
|
|
|
*/ |
1520
|
|
|
private function saveGetQuery() |
|
|
|
|
1521
|
|
|
{ |
1522
|
|
|
$_SESSION['LAST_GET_QUERY'][get_called_class()] = $_GET; |
1523
|
|
|
} |
1524
|
|
|
|
1525
|
|
|
/** |
1526
|
|
|
* Восстанавливает последний GET-запрос, если в текущем задан параметр restore_query=Y |
1527
|
|
|
*/ |
1528
|
|
|
private function restoreLastGetQuery() |
|
|
|
|
1529
|
|
|
{ |
1530
|
|
|
if (!isset($_SESSION['LAST_GET_QUERY'][get_called_class()]) |
1531
|
|
|
OR !isset($_REQUEST['restore_query']) |
|
|
|
|
1532
|
|
|
OR $_REQUEST['restore_query'] != 'Y' |
|
|
|
|
1533
|
|
|
) { |
1534
|
|
|
return; |
1535
|
|
|
} |
1536
|
|
|
|
1537
|
|
|
$_GET = array_merge($_GET, $_SESSION['LAST_GET_QUERY'][get_called_class()]); |
1538
|
|
|
$_REQUEST = array_merge($_REQUEST, $_SESSION['LAST_GET_QUERY'][get_called_class()]); |
1539
|
|
|
} |
1540
|
|
|
|
1541
|
|
|
/** |
1542
|
|
|
* @inheritdoc |
1543
|
|
|
*/ |
1544
|
|
|
public static function getUrl(array $params = array()) |
1545
|
|
|
{ |
1546
|
|
|
return static::getViewURL(static::getViewName(), static::$listPageUrl, $params); |
1547
|
|
|
} |
1548
|
|
|
|
1549
|
|
|
/** |
1550
|
|
|
* Кастомизация фильтра разделов |
1551
|
|
|
* @param $filter |
1552
|
|
|
* @return mixed |
1553
|
|
|
*/ |
1554
|
|
|
protected function getSectionsFilter(array $filter) |
1555
|
|
|
{ |
1556
|
|
|
return $filter; |
1557
|
|
|
} |
1558
|
|
|
|
1559
|
|
|
/** |
1560
|
|
|
* Кастомизация фильтра элементов |
1561
|
|
|
* @param $filter |
1562
|
|
|
* @return mixed |
1563
|
|
|
*/ |
1564
|
|
|
protected function getElementsFilter($filter) |
1565
|
|
|
{ |
1566
|
|
|
return $filter; |
1567
|
|
|
} |
1568
|
|
|
|
1569
|
|
|
/** |
1570
|
|
|
* Список идентификаторов для групповых операций |
1571
|
|
|
* |
1572
|
|
|
* @return array |
1573
|
|
|
*/ |
1574
|
|
|
protected function getIds() |
|
|
|
|
1575
|
|
|
{ |
1576
|
|
|
$className = static::getModel(); |
1577
|
|
|
if (isset($_REQUEST['model'])) { |
1578
|
|
|
$className = $_REQUEST['model']; |
1579
|
|
|
} |
1580
|
|
|
|
1581
|
|
|
$sectionEditHelperClass = $this->getHelperClass(AdminSectionEditHelper::className()); |
1582
|
|
View Code Duplication |
if ($sectionEditHelperClass && !isset($_REQUEST['model-section'])) { |
|
|
|
|
1583
|
|
|
$sectionClassName = $sectionEditHelperClass::getModel(); |
1584
|
|
|
} |
1585
|
|
|
else { |
1586
|
|
|
$sectionClassName = $_REQUEST['model-section']; |
1587
|
|
|
} |
1588
|
|
|
|
1589
|
|
|
if (isset($this->getPk()[$this->pk()]) && is_array($this->getPk()[$this->pk()])) { |
1590
|
|
|
foreach ($this->getPk()[$this->pk()] as $id) { |
1591
|
|
|
$class = strpos($id, 's') === 0 ? $sectionClassName : $className; |
1592
|
|
|
$ids[] = $this->getCommonPrimaryFilterById($class, null, $id); |
|
|
|
|
1593
|
|
|
} |
1594
|
|
|
} else { |
1595
|
|
|
$ids = [$this->getPk()]; |
1596
|
|
|
} |
1597
|
|
|
|
1598
|
|
|
return $ids; |
|
|
|
|
1599
|
|
|
} |
1600
|
|
|
|
1601
|
|
|
/** |
1602
|
|
|
* Получить оставшуюся часть составного первичного ключа |
1603
|
|
|
* |
1604
|
|
|
* @param $className |
1605
|
|
|
* @param null $sectionClassName |
1606
|
|
|
* @param $id |
1607
|
|
|
* @return array |
1608
|
|
|
*/ |
1609
|
|
|
protected function getCommonPrimaryFilterById($className, $sectionClassName = null, $id) |
1610
|
|
|
{ |
1611
|
|
|
$class = $this->getHelperClass($sectionClassName); |
1612
|
|
|
if (!empty($class) && strpos($id, 's') === 0) { |
1613
|
|
|
$primary = $sectionClassName::getEntity()->getPrimary(); |
1614
|
|
|
} else { |
1615
|
|
|
$primary = $className::getEntity()->getPrimary(); |
1616
|
|
|
} |
1617
|
|
|
|
1618
|
|
|
if (count($primary) === 1) { |
1619
|
|
|
return [$this->pk() => $id]; |
1620
|
|
|
} |
1621
|
|
|
|
1622
|
|
|
$key = $this->getPk(); |
1623
|
|
|
$key[$this->pk()] = $id; |
1624
|
|
|
|
1625
|
|
|
return $key; |
1626
|
|
|
} |
1627
|
|
|
} |
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.