Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
hook.php
1<?php
2namespace Bitrix\Landing;
3
5use \Bitrix\Landing\Internals\HookDataTable as HookData;
6use \Bitrix\Main\Event;
7use \Bitrix\Main\EventResult;
8
9class Hook
10{
15 protected static $editMode = false;
16
20 const ENTITY_TYPE_SITE = 'S';
21
26
30 const HOOKS_PAGE_DIR = '/bitrix/modules/landing/lib/hook/page';
31
35 const HOOKS_NAMESPACE = '\\Hook\\Page\\';
36
40 protected const HOOKS_ON_COPY_HANDLER = 'onCopy';
41
46 'METAOG_IMAGE',
47 'BACKGROUND_PICTURE'
48 ];
49
54 'BACKGROUND_USE',
55 'BACKGROUND_PICTURE',
56 'BACKGROUND_POSITION',
57 'BACKGROUND_COLOR',
58 'FONTS_CODE',
59 'THEME_CODE',
60 'THEME_USE',
61 'THEME_COLOR',
62 'THEMEFONTS_USE',
63 'THEMEFONTS_CODE_H',
64 'THEMEFONTS_CODE',
65 'THEMEFONTS_SIZE',
66 'THEMEFONTS_COLOR',
67 'THEMEFONTS_COLOR_H',
68 'THEMEFONTS_LINE_HEIGHT',
69 'THEMEFONTS_FONT_WEIGHT',
70 'THEMEFONTS_FONT_WEIGHT_H',
71 ];
72
78 protected static function getClassesFromDir($dir)
79 {
80 $classes = array();
81
82 $path = Manager::getDocRoot() . $dir;
83 if (($handle = opendir($path)))
84 {
85 while ((($entry = readdir($handle)) !== false))
86 {
87 if ($entry != '.' && $entry != '..')
88 {
89 $classes[] = mb_strtoupper(pathinfo($entry, PATHINFO_FILENAME));
90 }
91 }
92 }
93
94 return $classes;
95 }
96
104 public static function getData($id, $type, $asIs = false): array
105 {
106 $data = [];
107 $id = (int)$id;
108
109 if (!is_string($type))
110 {
111 return $data;
112 }
113
114 $res = HookData::getList([
115 'select' => [
116 'ID', 'HOOK', 'CODE', 'VALUE'
117 ],
118 'filter' => [
119 'ENTITY_ID' => $id,
120 '=ENTITY_TYPE' => $type,
121 '=PUBLIC' => self::$editMode ? 'N' : 'Y'
122 ],
123 'order' => [
124 'ID' => 'asc'
125 ]
126 ]);
127 while ($row = $res->fetch())
128 {
129 if (!isset($data[$row['HOOK']]))
130 {
131 $data[$row['HOOK']] = [];
132 }
133 if (mb_strpos($row['VALUE'], 'serialized#') === 0)
134 {
135 $row['VALUE'] = unserialize(mb_substr($row['VALUE'], 11), ['allowed_classes' => false]);
136 }
137 $data[$row['HOOK']][$row['CODE']] = $asIs ? $row : $row['VALUE'];
138 }
139
140 return $data;
141 }
142
150 protected static function getList($id, $type, array $data = array())
151 {
152 $hooks = array();
153 $classDir = self::HOOKS_PAGE_DIR;
154 $classNamespace = self::HOOKS_NAMESPACE;
155 $excludedHooks = \Bitrix\Landing\Site\Type::getExcludedHooks();
156
157 // first read all hooks in base dir
158 foreach (self::getClassesFromDir($classDir) as $class)
159 {
160 if (in_array($class, $excludedHooks))
161 {
162 continue;
163 }
164 $classFull = __NAMESPACE__ . $classNamespace . $class;
165 if (class_exists($classFull))
166 {
167 $hooks[$class] = new $classFull(
168 self::$editMode,
169 !($type == self::ENTITY_TYPE_SITE)
170 );
171 }
172 }
173
174 // sort hooks
175 uasort($hooks, function($a, $b)
176 {
177 if ($a->getSort() == $b->getSort())
178 {
179 return 0;
180 }
181 return ($a->getSort() < $b->getSort()) ? -1 : 1;
182 });
183
184 // check custom exec
185 $event = new Event('landing', 'onHookExec');
186 $event->send();
187 foreach ($event->getResults() as $result)
188 {
189 if ($result->getType() != EventResult::ERROR)
190 {
191 if ($customExec = $result->getModified())
192 {
193 foreach ((array)$customExec as $code => $itemExec)
194 {
195 $code = mb_strtoupper($code);
196 if (isset($hooks[$code]) && is_callable($itemExec))
197 {
198 $hooks[$code]->setCustomExec($itemExec);
199 }
200 }
201 unset($code, $itemExec);
202 }
203 unset($customExec);
204 }
205 }
206 unset($event, $result);
207
208 // then fill hook with data
209 if (!empty($hooks) && $id > 0)
210 {
211 if (empty($data))
212 {
213 $data = self::getData($id, $type);
214 }
215 foreach ($hooks as $code => $hook)
216 {
217 if (isset($data[$code]))
218 {
219 $hook->setData($data[$code]);
220 }
221 }
222 }
223
224 return $hooks;
225 }
226
232 public static function setEditMode(bool $mode = true): void
233 {
234 self::$editMode = $mode;
235 }
236
241 public static function getEditMode(): bool
242 {
243 return self::$editMode;
244 }
245
251 public static function getForSite($id)
252 {
253 if (!Landing::getEditMode())
254 {
255 static $hooks = [];
256 }
257 else
258 {
259 $hooks = [];
260 }
261
262 if (!array_key_exists($id, $hooks))
263 {
264 $hooks[$id] = self::getList($id, self::ENTITY_TYPE_SITE);
265 }
266
267 return $hooks[$id];
268 }
269
275 public static function getForLanding($id)
276 {
277 if (!Landing::getEditMode())
278 {
279 static $hooks = [];
280 }
281 else
282 {
283 $hooks = [];
284 }
285
286 if (!array_key_exists($id, $hooks))
287 {
288 $hooks[$id] = self::getList($id, self::ENTITY_TYPE_LANDING);
289 }
290
291 return $hooks[$id];
292 }
293
299 public static function getForLandingRow($id)
300 {
301 return self::getData($id, self::ENTITY_TYPE_LANDING);
302 }
303
312 protected static function copy($from, $to, $type, $publication = false)
313 {
314 $from = (int)$from;
315 $to = (int)$to;
316 $data = self::getData($from, $type);
317 $existData = [];
318
319 $classDir = self::HOOKS_PAGE_DIR;
320 $classNamespace = self::HOOKS_NAMESPACE;
321 $excludedHooks = \Bitrix\Landing\Site\Type::getExcludedHooks();
322
323 // first read all hooks in base dir
324 foreach (self::getClassesFromDir($classDir) as $class)
325 {
326 if (in_array($class, $excludedHooks, true))
327 {
328 continue;
329 }
330 $classFull = __NAMESPACE__ . $classNamespace . $class;
331 if (
332 isset($data[$class])
333 && method_exists($classFull, self::HOOKS_ON_COPY_HANDLER)
334 )
335 {
336 $handler = self::HOOKS_ON_COPY_HANDLER;
337 if ($preparedData = $classFull::$handler($data[$class], $from, $type, $publication))
338 {
339 $data[$class] = $preparedData;
340 }
341 }
342 }
343
344 // collect exist data
345 if ($data)
346 {
347 $res = HookData::getList([
348 'select' => [
349 'ID', 'HOOK', 'CODE'
350 ],
351 'filter' => [
352 'ENTITY_ID' => $to,
353 '=ENTITY_TYPE' => $type,
354 '=PUBLIC' => $publication ? 'Y' : 'N'
355 ]
356 ]);
357 while ($row = $res->fetch())
358 {
359 $existData[$row['HOOK'] . '_' . $row['CODE']] = $row['ID'];
360 }
361 }
362
363 // update existing keys or add new
364 foreach ($data as $hookCode => $items)
365 {
366 foreach ($items as $code => $value)
367 {
368 $existKey = $hookCode . '_' . $code;
369 if (is_array($value))
370 {
371 $value = 'serialized#' . serialize($value);
372 }
373 if (array_key_exists($existKey, $existData))
374 {
375 HookData::update($existData[$existKey], [
376 'VALUE' => $value
377 ]);
378 unset($existData[$existKey]);
379 }
380 else
381 {
382 HookData::add([
383 'ENTITY_ID' => $to,
384 'ENTITY_TYPE' => $type,
385 'HOOK' => $hookCode,
386 'CODE' => $code,
387 'VALUE' => $value,
388 'PUBLIC' => $publication ? 'Y' : 'N'
389 ]);
390 }
391 }
392 }
393
394 // delete unused data
395 if ($existData)
396 {
397 foreach ($existData as $delId)
398 {
399 HookData::delete($delId);
400 }
401 }
402 }
403
410 public static function copySite($from, $to)
411 {
412 $originalEditMode = self::$editMode;
413 if (!self::$editMode)
414 {
415 self::$editMode = true;
416 }
417 self::copy($from, $to, self::ENTITY_TYPE_SITE);
418 self::$editMode = $originalEditMode;
419 }
420
427 public static function copyLanding($from, $to)
428 {
429 $originalEditMode = self::$editMode;
430 if (!self::$editMode)
431 {
432 self::$editMode = true;
433 }
434 self::copy($from, $to, self::ENTITY_TYPE_LANDING);
435 self::$editMode = $originalEditMode;
436 }
437
443 public static function publicationSite($siteId)
444 {
445 self::copy($siteId, $siteId, self::ENTITY_TYPE_SITE, true);
446 }
447
453 public static function publicationLanding($lid)
454 {
455 self::copy($lid, $lid, self::ENTITY_TYPE_LANDING, true);
456 }
457
463 public static function publicationSiteWithSkipNeededPublication($siteId): void
464 {
465 self::publicationWithSkipNeededPublication($siteId, self::ENTITY_TYPE_SITE);
466 }
467
473 public static function publicationLandingWithSkipNeededPublication($landingId): void
474 {
475 self::publicationWithSkipNeededPublication($landingId, self::ENTITY_TYPE_LANDING);
476 }
477
478 protected static function publicationWithSkipNeededPublication($id, $type): void
479 {
480 $editModeBack = self::$editMode;
481 self::$editMode = false;
482 $publicData = self::getData($id, $type, true);
483 self::$editMode = $editModeBack;
484
485 if ($type === self::ENTITY_TYPE_SITE)
486 {
487 self::publicationSite($id);
488 }
489 if ($type === self::ENTITY_TYPE_LANDING)
490 {
491 self::publicationLanding($id);
492 }
493 $data = self::getData($id, $type, true);
494
495 // return previously public values
496 $needClearCache = false;
497 foreach (self::getList($id, $type) as $hook)
498 {
499 if ($hook->isNeedPublication())
500 {
501 $fieldsToDelete = [];
502 if (isset($publicData[$hook->getCode()]))
503 {
504 foreach ($data[$hook->getCode()] as $fieldCode => $field)
505 {
506 if (!isset($publicData[$hook->getCode()][$fieldCode]))
507 {
508 $fieldsToDelete[$fieldCode] = $field;
509 }
510 elseif ($publicData[$hook->getCode()][$fieldCode]['VALUE'] !== $field['VALUE'])
511 {
512 $needClearCache = true;
513 HookData::update($field['ID'],
514 [
515 'VALUE' => $field['VALUE'],
516 ]
517 );
518 }
519 }
520 }
521 else
522 {
523 $fieldsToDelete = $data[$hook->getCode()] ?? [];
524 }
525
526 // del if not exists in public
527 if (!empty($fieldsToDelete))
528 {
529 $needClearCache = true;
530 foreach ($fieldsToDelete as $fieldCode => $field)
531 {
532 $res = HookData::getList([
533 'select' => ['ID'],
534 'filter' => [
535 'ENTITY_ID' => $id,
536 '=ENTITY_TYPE' => $type,
537 '=HOOK' => $hook->getCode(),
538 '=CODE' => $fieldCode,
539 '=PUBLIC' => 'Y'
540 ]
541 ]);
542 if ($row = $res->fetch())
543 {
544 HookData::delete($row['ID']);
545 }
546 }
547 }
548 }
549 }
550
551 // drop public cache
552 if ($needClearCache)
553 {
554 if ($type === self::ENTITY_TYPE_SITE)
555 {
556 $landings = Landing::getList([
557 'select' => ['ID'],
558 'filter' => [
559 'SITE_ID' => $id,
560 '=PUBLIC' => 'Y',
561 '=DELETED' => 'N',
562 ],
563 ]);
564 while ($landing = $landings->fetch())
565 {
566 Landing::update($landing['ID'], ['PUBLIC' => 'N']);
567 }
568 }
569 if ($type === self::ENTITY_TYPE_LANDING)
570 {
571 Landing::update($id, ['PUBLIC' => 'N']);
572 }
573 }
574 }
575
581 protected static function prepareData(array $data)
582 {
583 $newData = array();
584
585 foreach ($data as $code => $val)
586 {
587 if (mb_strpos($code, '_') !== false)
588 {
589 $codeHook = mb_substr($code, 0, mb_strpos($code, '_'));
590 $codeVal = mb_substr($code, mb_strpos($code, '_') + 1);
591 if (!isset($newData[$codeHook]))
592 {
593 $newData[$codeHook] = array();
594 }
595 $newData[$codeHook][$codeVal] = $val;
596 }
597 }
598
599 return $newData;
600 }
601
609 protected static function saveData($id, $type, array $data)
610 {
611 $data = self::prepareData($data);
612 $hooks = self::getList($id, $type, $data);
613 $dataSave = self::getData($id, $type, true);
614
615 // get hooks with new new data (not saved yet)
616 foreach ($hooks as $hook)
617 {
618 $hookLocked = $hook->isLocked();
619 $codeHook = $hook->getCode();
620 // modify $dataSave ...
621 foreach ($hook->getFields() as $field)
622 {
623 $codeVal = $field->getCode();
624 if ($hookLocked && !$field->isEmptyValue())
625 {
626 continue;
627 }
628 if (!isset($data[$codeHook][$codeVal]))
629 {
630 continue;
631 }
632 // ... for changed
633 if (isset($dataSave[$codeHook][$codeVal]))
634 {
635 $dataSave[$codeHook][$codeVal]['CHANGED'] = true;
636 $dataSave[$codeHook][$codeVal]['VALUE'] = $field->getValue();
637 }
638 // ... and new fields
639 else
640 {
641 if (!isset($dataSave[$codeHook]))
642 {
643 $dataSave[$codeHook] = array();
644 }
645 $dataSave[$codeHook][$codeVal] = array(
646 'HOOK' => $codeHook,
647 'CODE' => $codeVal,
648 'VALUE' => $field->getValue()
649 );
650 }
651 if (is_array($dataSave[$codeHook][$codeVal]['VALUE']))
652 {
653 $dataSave[$codeHook][$codeVal]['VALUE'] = 'serialized#' . serialize(
654 $dataSave[$codeHook][$codeVal]['VALUE']
655 );
656 }
657 }
658 }
659
660 // now save the data
661 foreach ($dataSave as $codeHook => $dataHook)
662 {
663 foreach ($dataHook as $code => $row)
664 {
665 if (
666 is_array($row['VALUE']) && empty($row['VALUE'])
667 ||
668 !is_array($row['VALUE']) && trim($row['VALUE']) == ''
669 )
670 {
671 if (isset($row['ID']))
672 {
673 HookData::delete($row['ID']);
674 }
675 }
676 else
677 {
678 if (!isset($row['ID']))
679 {
680 $row['ENTITY_ID'] = $id;
681 $row['ENTITY_TYPE'] = $type;
682 HookData::add($row);
683 }
684 elseif (isset($row['CHANGED']) && $row['CHANGED'])
685 {
686 $updId = $row['ID'];
687 unset($row['ID'], $row['CHANGED']);
688 HookData::update($updId, $row);
689 }
690 }
691 }
692 }
693 }
694
701 protected static function indexContent($id, $type)
702 {
703 $id = intval($id);
704
705 if ($type == self::ENTITY_TYPE_LANDING)
706 {
707 $class = '\Bitrix\Landing\Landing';
708 }
709
710 if (!isset($class))
711 {
712 return;
713 }
714
715 // base fields
716 $searchContent = $class::getList([
717 'select' => [
718 'TITLE', 'DESCRIPTION'
719 ],
720 'filter' => [
721 'ID' => $id,
722 '=DELETED' => ['Y', 'N'],
723 '=SITE.DELETED' => ['Y', 'N']
724 ]
725 ])->fetch();
726 if (!$searchContent)
727 {
728 return;
729 }
730
731 $searchContent = array_values($searchContent);
732
733 // hook fields
734 foreach (self::getList($id, $type) as $hook)
735 {
736 foreach ($hook->getFields() as $field)
737 {
738 if ($field->isSearchable())
739 {
740 $searchContent[] = $field->getValue();
741 }
742 }
743 }
744
745 $searchContent = array_unique($searchContent);
746 $searchContent = $searchContent ? implode(' ', $searchContent) : '';
747 $searchContent = trim($searchContent);
748
749 if ($searchContent)
750 {
751 $res = $class::update($id, [
752 'SEARCH_CONTENT' => $searchContent
753 ]);
754 $res->isSuccess();
755 }
756 }
757
764 public static function saveForSite(int $id, array $data): void
765 {
766 $check = Site::getList([
767 'select' => [
768 'ID'
769 ],
770 'filter' => [
771 'ID' => $id
772 ]
773 ])->fetch();
774 if ($check)
775 {
776 $editModeBack = self::$editMode;
777 self::$editMode = true;
778 self::saveData($id, self::ENTITY_TYPE_SITE, $data);
779 if (Manager::getOption('public_hook_on_save') === 'Y')
780 {
781 self::publicationSiteWithSkipNeededPublication($id);
782 }
783 self::$editMode = $editModeBack;
784 }
785 }
786
793 public static function saveForLanding(int $id, array $data): void
794 {
795 $check = Landing::getList([
796 'select' => [
797 'ID'
798 ],
799 'filter' => [
800 'ID' => $id
801 ]
802 ])->fetch();
803 if ($check)
804 {
805 $editModeBack = self::$editMode;
806 self::$editMode = true;
807 self::saveData($id, self::ENTITY_TYPE_LANDING, $data);
808 self::indexContent($id, self::ENTITY_TYPE_LANDING);
809 if (Manager::getOption('public_hook_on_save') === 'Y')
810 {
811 self::publicationLandingWithSkipNeededPublication($id);
812 }
813 self::$editMode = $editModeBack;
814 }
815 }
816
822 public static function indexLanding($id)
823 {
824 self::indexContent($id, self::ENTITY_TYPE_LANDING);
825 }
826
833 protected static function deleteData($id, $type)
834 {
835 $id = intval($id);
836
837 $res = HookData::getList(array(
838 'select' => array(
839 'ID'
840 ),
841 'filter' => array(
842 'ENTITY_ID' => $id,
843 '=ENTITY_TYPE' => $type
844 )
845 ));
846 while ($row = $res->fetch())
847 {
848 HookData::delete($row['ID']);
849 }
850 }
851
857 public static function deleteForSite($id)
858 {
859 self::deleteData($id, self::ENTITY_TYPE_SITE);
860 }
861
867 public static function deleteForLanding($id)
868 {
869 self::deleteData($id, self::ENTITY_TYPE_LANDING);
870 }
871}
static copySite($from, $to)
Definition hook.php:410
static getEditMode()
Definition hook.php:241
static publicationSite($siteId)
Definition hook.php:443
static saveData($id, $type, array $data)
Definition hook.php:609
static getClassesFromDir($dir)
Definition hook.php:78
static getForLanding($id)
Definition hook.php:275
const HOOKS_CODES_FILES
Definition hook.php:45
const HOOKS_ON_COPY_HANDLER
Definition hook.php:40
static deleteForSite($id)
Definition hook.php:857
static publicationLanding($lid)
Definition hook.php:453
static copyLanding($from, $to)
Definition hook.php:427
static getList($id, $type, array $data=array())
Definition hook.php:150
static indexContent($id, $type)
Definition hook.php:701
static getData($id, $type, $asIs=false)
Definition hook.php:104
static getForLandingRow($id)
Definition hook.php:299
const ENTITY_TYPE_SITE
Definition hook.php:20
const HOOKS_CODES_DESIGN
Definition hook.php:53
const HOOKS_PAGE_DIR
Definition hook.php:30
static publicationWithSkipNeededPublication($id, $type)
Definition hook.php:478
static getForSite($id)
Definition hook.php:251
static publicationLandingWithSkipNeededPublication($landingId)
Definition hook.php:473
static setEditMode(bool $mode=true)
Definition hook.php:232
static saveForSite(int $id, array $data)
Definition hook.php:764
static saveForLanding(int $id, array $data)
Definition hook.php:793
static deleteData($id, $type)
Definition hook.php:833
const ENTITY_TYPE_LANDING
Definition hook.php:25
static indexLanding($id)
Definition hook.php:822
static prepareData(array $data)
Definition hook.php:581
static publicationSiteWithSkipNeededPublication($siteId)
Definition hook.php:463
static copy($from, $to, $type, $publication=false)
Definition hook.php:312
static $editMode
Definition hook.php:15
static deleteForLanding($id)
Definition hook.php:867
const HOOKS_NAMESPACE
Definition hook.php:35
static getOption($code, $default=null)
Definition manager.php:160
static update($primary, array $data)
Definition file.php:228
static getList(array $parameters=array())