Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
dialog.php
1<?
2
4
8
9class Dialog implements \JsonSerializable
10{
11 protected $id;
12
14 protected $itemCollection;
15
17 protected $tabs = [];
18
20 protected $entities = [];
21
23 protected $recentItems;
24
27
30
32 protected $context;
33
35 protected $header;
36
38 protected $headerOptions;
39
41 protected $footer;
42
44 protected $footerOptions;
45
47 protected $clearUnavailableItems = false;
48
49 public function __construct(array $options)
50 {
51 if (isset($options['entities']) && is_array($options['entities']))
52 {
53 foreach ($options['entities'] as $entityOptions)
54 {
55 if (is_array($entityOptions))
56 {
57 $entity = Entity::create($entityOptions);
58 if ($entity)
59 {
60 $this->addEntity($entity);
61 }
62 }
63 }
64 }
65
66 if (isset($options['id']) && is_string($options['id']))
67 {
68 $this->id = $options['id'];
69 }
70
71 if (isset($options['context']) && is_string($options['context']) && strlen($options['context']) > 0)
72 {
73 $this->context = $options['context'];
74 }
75
76 if (isset($options['clearUnavailableItems']) && is_bool($options['clearUnavailableItems']))
77 {
78 $this->clearUnavailableItems = $options['clearUnavailableItems'];
79 }
80
81 $this->itemCollection = new ItemCollection();
82 $this->recentItems = new RecentCollection();
83 $this->globalRecentItems = new RecentCollection();
84 $this->preselectedItems = new PreselectedCollection();
85
86 if (isset($options['preselectedItems']) && is_array($options['preselectedItems']))
87 {
88 $this->setPreselectedItems($options['preselectedItems']);
89 }
90 }
91
92 public function getId(): ?string
93 {
94 return $this->id;
95 }
96
97 public function getContext(): ?string
98 {
99 return $this->context;
100 }
101
103 {
105 }
106
107 public function getCurrentUserId(): int
108 {
109 return is_object($GLOBALS['USER']) ? $GLOBALS['USER']->getId() : 0;
110 }
111
112 public function addItem(Item $item)
113 {
114 $success = $this->getItemCollection()->add($item);
115 if ($success)
116 {
117 $this->handleItemAdd($item);
118 }
119 }
120
121 public function addItems(array $items)
122 {
123 foreach ($items as $item)
124 {
125 $this->addItem($item);
126 }
127 }
128
129 public function addRecentItem(Item $item)
130 {
131 $this->addItem($item);
132
133 $recentItem = $this->getRecentItems()->getByItem($item);
134 if (!$recentItem && $item->isAvailableInRecentTab())
135 {
136 $this->getRecentItems()->add(
137 new RecentItem(
138 [
139 'id' => $item->getId(),
140 'entityId' => $item->getEntityId(),
141 'loaded' => true,
142 ]
143 )
144 );
145 }
146 }
147
148 public function addRecentItems(array $items)
149 {
150 foreach ($items as $item)
151 {
152 $this->addRecentItem($item);
153 }
154 }
155
156 public function setHeader(string $header, array $options = [])
157 {
158 if (strlen($header) > 0)
159 {
160 $this->header = $header;
161 $this->headerOptions = $options;
162 }
163 }
164
165 public function getHeader(): ?string
166 {
167 return $this->header;
168 }
169
170 public function getHeaderOptions(): ?array
171 {
173 }
174
175 public function setFooter(string $footer, array $options = [])
176 {
177 if (strlen($footer) > 0)
178 {
179 $this->footer = $footer;
180 $this->footerOptions = $options;
181 }
182 }
183
184 public function getFooter(): ?string
185 {
186 return $this->footer;
187 }
188
189 public function getFooterOptions(): ?array
190 {
192 }
193
194 public function handleItemAdd(Item $item): void
195 {
196 $item->setDialog($this);
197
198 $recentItem = $this->getRecentItems()->getByItem($item);
199 if ($recentItem)
200 {
201 $recentItem->setLoaded(true);
202 $recentItem->setAvailable($item->isAvailableInRecentTab());
203 $item->setContextSort($recentItem->getLastUseDate());
204 }
205
206 $globalRecentItem = $this->getGlobalRecentItems()->getByItem($item);
207 if ($globalRecentItem)
208 {
209 $globalRecentItem->setLoaded(true);
210 $item->setGlobalSort($globalRecentItem->getLastUseDate());
211 }
212
213 $preselectedItem = $this->getPreselectedCollection()->getByItem($item);
214 if ($preselectedItem && !$preselectedItem->getItem())
215 {
216 $preselectedItem->setItem($item);
217 }
218
219 foreach ($item->getChildren() as $childItem)
220 {
221 $this->handleItemAdd($childItem);
222 }
223 }
224
226 {
227 return $this->recentItems;
228 }
229
231 {
233 }
234
235 public function addTab(Tab $tab): void
236 {
237 if (!empty($tab->getId()))
238 {
239 $this->tabs[$tab->getId()] = $tab;
240 }
241 }
242
246 public function getTabs(): array
247 {
248 return $this->tabs;
249 }
250
251 public function getTab(string $tabId): ?Tab
252 {
253 return $this->tabs[$tabId] ?? null;
254 }
255
256 public function addEntity(Entity $entity)
257 {
258 if (!empty($entity->getId()))
259 {
260 $this->entities[$entity->getId()] = $entity;
261 }
262 }
263
267 public function getEntities(): array
268 {
269 return $this->entities;
270 }
271
277 public function getEntity(string $entityId): ?Entity
278 {
279 return $this->entities[$entityId] ?? null;
280 }
281
285 public function load(): void
286 {
287 $entities = [];
288 foreach ($this->getEntities() as $entity)
289 {
290 if ($entity->hasDynamicLoad())
291 {
292 $entities[] = $entity->getId();
293 }
294 }
295
296 if (empty($entities))
297 {
298 return;
299 }
300
301 $this->fillRecentItems($entities);
302 if ($this->getContext() !== null)
303 {
304 $this->fillGlobalRecentItems($entities);
305 }
306
307 foreach ($entities as $entityId)
308 {
309 $this->getEntity($entityId)->getProvider()->fillDialog($this);
310 }
311
312 $this->loadRecentItems();
313 $this->loadPreselectedItems();
314 }
315
320 public function doSearch(SearchQuery $searchQuery)
321 {
322 if (empty($searchQuery->getQueryWords()))
323 {
324 return;
325 }
326
327 $entities = [];
328 foreach ($this->getEntities() as $entity)
329 {
330 $hasDynamicSearch =
331 $entity->isSearchable() &&
332 ($entity->hasDynamicSearch() || $searchQuery->hasDynamicSearchEntity($entity->getId()))
333 ;
334
335 if ($hasDynamicSearch)
336 {
337 $entities[] = $entity->getId();
338 }
339 }
340
341 if ($this->getContext() !== null)
342 {
343 $this->fillRecentItems($entities);
344 }
345
346 $this->fillGlobalRecentItems($entities);
347 foreach ($entities as $entityId)
348 {
349 $this->getEntity($entityId)->getProvider()->doSearch($searchQuery, $this);
350 }
351 }
352
357 public function getChildren(Item $parentItem)
358 {
359 $entities = [];
360 foreach ($this->getEntities() as $entity)
361 {
362 if ($entity->hasDynamicLoad())
363 {
364 $entities[] = $entity->getId();
365 }
366 }
367
368 $entity = $this->getEntity($parentItem->getEntityId());
369 if ($entity && $entity->hasDynamicLoad())
370 {
371 $this->fillGlobalRecentItems($entities);
372 $entity->getProvider()->getChildren($parentItem, $this);
373 }
374 }
375
377 {
378 $this->preselectedItems->load($preselectedItems);
379 }
380
385
389 public function loadPreselectedItems($preselectedMode = true): void
390 {
391 if ($this->getPreselectedCollection()->count() < 1)
392 {
393 return;
394 }
395
396 foreach ($this->getPreselectedCollection()->getItems() as $entityId => $preselectedItems)
397 {
398 $unloadedIds = [];
399 $entity = $this->getEntity($entityId) ?? Entity::create(['id' => $entityId]);
400 foreach ($preselectedItems as $preselectedItem)
401 {
402 // Entity doesn't exist
403 if (!$entity && $preselectedMode)
404 {
405 $this->addItem(self::createHiddenItem($preselectedItem->getId(), $entityId));
406 }
407 else if (!$preselectedItem->isLoaded())
408 {
409 $unloadedIds[] = $preselectedItem->getId();
410 }
411 }
412
413 if ($entity && !empty($unloadedIds))
414 {
415 $availableItems = [];
416 $items =
417 $preselectedMode
418 ? $entity->getProvider()->getPreselectedItems($unloadedIds)
419 : $entity->getProvider()->getItems($unloadedIds)
420 ;
421
422 foreach ($items as $item)
423 {
424 $availableItems[$item->getId()] = $item;
425 }
426
427 foreach ($unloadedIds as $unloadedId)
428 {
429 $item = $availableItems[$unloadedId] ?? null;
430 if ($item)
431 {
432 $this->addItem($item);
433 }
434 else if ($preselectedMode)
435 {
436 $this->addItem(self::createHiddenItem($unloadedId, $entityId));
437 }
438 }
439 }
440 }
441 }
442
443 public function shouldClearUnavailableItems(): bool
444 {
446 }
447
448 public static function createHiddenItem($id, $entityId): Item
449 {
450 return new Item([
451 'id' => $id,
452 'entityId' => $entityId,
453 'title' => Loc::getMessage("UI_SELECTOR_HIDDEN_ITEM_TITLE"),
454 'hidden' => true,
455 'deselectable' => false,
456 'searchable' => false,
457 'saveable' => false,
458 'link' => '',
459 'avatar' => '',
460 'availableInRecentTab' => false
461 ]);
462 }
463
468 public static function getSelectedItems(array $ids, array $options = []): ItemCollection
469 {
470 return self::getItemsInternal($ids, $options, true);
471 }
472
473 public static function getPreselectedItems(array $ids, array $options = []): ItemCollection
474 {
475 return self::getItemsInternal($ids, $options, true);
476 }
477
478 public static function getItems(array $ids, array $options = []): ItemCollection
479 {
480 return self::getItemsInternal($ids, $options, false);
481 }
482
483 private static function getItemsInternal(array $ids, array $options = [], $preselectedMode = true): ItemCollection
484 {
485 $isAssocArray = array_keys($options) !== range(0, count($options) - 1);
486 $dialogOptions = $isAssocArray ? $options : ['entities' => $options];
487
488 $dialog = new self($dialogOptions);
489 $dialog->setPreselectedItems($ids);
490 $dialog->loadPreselectedItems($preselectedMode);
491 $dialog->applyFilters();
492
493 return $dialog->getItemCollection();
494 }
495
496 public function saveRecentItems(array $recentItems)
497 {
498 if ($this->getContext() === null)
499 {
500 return;
501 }
502
503 foreach ($recentItems as $recentItemOptions)
504 {
505 if (!is_array($recentItemOptions))
506 {
507 continue;
508 }
509
510 $recentItem = new Item($recentItemOptions);
511 $entity = $this->getEntity($recentItem->getEntityId());
512
513 if ($entity)
514 {
515 $entity->getProvider()->handleBeforeItemSave($recentItem);
516 if ($recentItem->isSaveable())
517 {
518 EntityUsageTable::merge([
519 'USER_ID' => $GLOBALS['USER']->getId(),
520 'CONTEXT' => $this->getContext(),
521 'ENTITY_ID' => $recentItem->getEntityId(),
522 'ITEM_ID' => $recentItem->getId()
523 ]);
524 }
525 }
526 }
527 }
528
529 private function fillRecentItems(array $entities)
530 {
531 if (empty($entities))
532 {
533 return;
534 }
535
536 if ($this->getContext() === null)
537 {
538 $usages = $this->getGlobalUsages($entities, 50);
539 while ($usage = $usages->fetch())
540 {
541 $this->getRecentItems()->add(
542 new RecentItem(
543 [
544 'id' => $usage['ITEM_ID'],
545 'entityId' => $usage['ENTITY_ID'],
546 'lastUseDate' => $usage['MAX_LAST_USE_DATE']->getTimestamp()
547 ]
548 )
549 );
550 }
551 }
552 else
553 {
554 $usages = $this->getContextUsages($entities);
555 foreach ($usages as $usage)
556 {
557 $this->getRecentItems()->add(
558 new RecentItem(
559 [
560 'id' => $usage->getItemId(),
561 'entityId' => $usage->getEntityId(),
562 'lastUseDate' => $usage->getLastUseDate()->getTimestamp()
563 ]
564 )
565 );
566 }
567 }
568 }
569
570 private function fillGlobalRecentItems(array $entities)
571 {
572 if (empty($entities))
573 {
574 return;
575 }
576
577 $usages = $this->getGlobalUsages($entities);
578 while ($usage = $usages->fetch())
579 {
580 $this->getGlobalRecentItems()->add(
581 new RecentItem(
582 [
583 'id' => $usage['ITEM_ID'],
584 'entityId' => $usage['ENTITY_ID'],
585 'lastUseDate' => $usage['MAX_LAST_USE_DATE']->getTimestamp()
586 ]
587 )
588 );
589 }
590 }
591
592 private function getContextUsages(array $entities)
593 {
594 return EntityUsageTable::getList(
595 [
596 'select' => ['*'],
597 'filter' => [
598 '=USER_ID' => $this->getCurrentUserId(),
599 '=CONTEXT' => $this->getContext(),
600 '@ENTITY_ID' => $entities
601 ],
602 'limit' => 50,
603 'order' => [
604 'LAST_USE_DATE' => 'DESC'
605 ]
606 ]
607 )->fetchCollection();
608 }
609
610 private function getGlobalUsages(array $entities, int $limit = 200)
611 {
612 $query = EntityUsageTable::query();
613 $query->setSelect(['ENTITY_ID', 'ITEM_ID', 'MAX_LAST_USE_DATE']);
614 $query->setGroup(['ENTITY_ID', 'ITEM_ID']);
615 $query->where('USER_ID', $this->getCurrentUserId());
616 $query->whereIn('ENTITY_ID', $entities);
617
618 if ($this->getContext() !== null)
619 {
620 $query->whereNot('CONTEXT', $this->getContext());
621 }
622
623 $query->registerRuntimeField(new ExpressionField('MAX_LAST_USE_DATE', 'MAX(%s)', 'LAST_USE_DATE'));
624 $query->setOrder(['MAX_LAST_USE_DATE' => 'desc']);
625 $query->setLimit($limit);
626
627 return $query->exec();
628 }
629
630 private function loadRecentItems()
631 {
632 foreach ($this->getEntities() as $entity)
633 {
634 $unloadedIds = [];
635 $unavailableIds = [];
636 $recentItems = $this->getRecentItems()->getEntityItems($entity->getId());
637 foreach ($recentItems as $recentItem)
638 {
639 if (!$recentItem->isAvailable())
640 {
641 $unavailableIds[] = $recentItem->getId();
642 }
643 else if (!$recentItem->isLoaded())
644 {
645 $unloadedIds[] = $recentItem->getId();
646 }
647 }
648
649 if (!empty($unloadedIds))
650 {
651 $availableItems = [];
652 $items = $entity->getProvider()->getItems($unloadedIds);
653 foreach ($items as $item)
654 {
655 if ($item instanceof Item)
656 {
657 $availableItems[$item->getId()] = $item;
658 }
659 }
660
661 foreach ($unloadedIds as $unloadedId)
662 {
663 $item = $availableItems[$unloadedId] ?? null;
664 if ($item && $item->isAvailableInRecentTab())
665 {
666 $this->addRecentItem($item);
667 }
668 else
669 {
670 $unavailableIds[] = $unloadedId;
671 }
672 }
673 }
674
675 if ($this->getContext() !== null && $this->shouldClearUnavailableItems() && !empty($unavailableIds))
676 {
677 EntityUsageTable::deleteByFilter([
678 '=USER_ID' => $this->getCurrentUserId(),
679 '=CONTEXT' => $this->getContext(),
680 '=ENTITY_ID' => $entity->getId(),
681 '@ITEM_ID' => $unavailableIds
682 ]);
683 }
684 }
685 }
686
687 public function applyFilters(): void
688 {
689 foreach ($this->getEntities() as $entity)
690 {
691 $items = $this->getItemCollection()->getEntityItems($entity->getId());
692 if (empty($items))
693 {
694 continue;
695 }
696
697 $filters = $entity->getFilters();
698 foreach ($filters as $filter)
699 {
700 $filter->apply($items, $this);
701 }
702 }
703 }
704
708 public function getAjaxData(): array
709 {
710 $this->applyFilters();
711
712 return $this->jsonSerialize();
713 }
714
715 public function jsonSerialize()
716 {
717 $json = [
718 'id' => $this->getId(),
719 'items' => $this->getItemCollection(),
720 'tabs' => array_values($this->getTabs()),
721 'entities' => array_values($this->getEntities()),
722 ];
723
724 if ($this->getHeader())
725 {
726 $json['header'] = $this->getHeader();
727 $json['headerOptions'] = $this->getHeaderOptions();
728 }
729
730 if ($this->getFooter())
731 {
732 $json['footer'] = $this->getFooter();
733 $json['footerOptions'] = $this->getFooterOptions();
734 }
735
736 if ($this->getRecentItems()->count() > 0)
737 {
738 $json['recentItems'] = $this->getRecentItems();
739 }
740
741 if ($this->getPreselectedCollection()->count() > 0)
742 {
743 $json['preselectedItems'] = $this->getPreselectedCollection();
744 }
745
746 return $json;
747 }
748}
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
__construct(array $options)
Definition dialog.php:49
getChildren(Item $parentItem)
Definition dialog.php:357
loadPreselectedItems($preselectedMode=true)
Definition dialog.php:389
setPreselectedItems(array $preselectedItems)
Definition dialog.php:376
setFooter(string $footer, array $options=[])
Definition dialog.php:175
static getSelectedItems(array $ids, array $options=[])
Definition dialog.php:468
getEntity(string $entityId)
Definition dialog.php:277
static getItems(array $ids, array $options=[])
Definition dialog.php:478
doSearch(SearchQuery $searchQuery)
Definition dialog.php:320
setHeader(string $header, array $options=[])
Definition dialog.php:156
static createHiddenItem($id, $entityId)
Definition dialog.php:448
static getPreselectedItems(array $ids, array $options=[])
Definition dialog.php:473
saveRecentItems(array $recentItems)
Definition dialog.php:496
static create(array $entityOptions)
Definition entity.php:49
setDialog(Dialog $dialog)
Definition item.php:651