1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
hook.php
См. документацию.
1<?php
2namespace Bitrix\Landing;
3
4use Bitrix\Landing\Hook\Page;
5use \Bitrix\Landing\Internals\HookDataTable as HookData;
6use \Bitrix\Main\Event;
7use \Bitrix\Main\EventResult;
8use CPHPCache;
9
10class Hook
11{
16 protected static $editMode = false;
17
21 const ENTITY_TYPE_SITE = 'S';
22
27
31 const HOOKS_PAGE_DIR = '/bitrix/modules/landing/lib/hook/page';
32
36 const HOOKS_NAMESPACE = '\\Hook\\Page\\';
37
41 protected const HOOKS_ON_COPY_HANDLER = 'onCopy';
42
47 'METAOG_IMAGE',
48 'BACKGROUND_PICTURE'
49 ];
50
55 'BACKGROUND_USE',
56 'BACKGROUND_PICTURE',
57 'BACKGROUND_POSITION',
58 'BACKGROUND_COLOR',
59 'FONTS_CODE',
60 'THEME_CODE',
61 'THEME_USE',
62 'THEME_COLOR',
63 'THEMEFONTS_USE',
64 'THEMEFONTS_CODE_H',
65 'THEMEFONTS_CODE',
66 'THEMEFONTS_SIZE',
67 'THEMEFONTS_COLOR',
68 'THEMEFONTS_COLOR_H',
69 'THEMEFONTS_LINE_HEIGHT',
70 'THEMEFONTS_FONT_WEIGHT',
71 'THEMEFONTS_FONT_WEIGHT_H',
72 ];
73
74 private const CACHE_TIME = 2592000; // 30 days
75 private const CACHE_DIR = '/landing/hook/';
76
82 protected static function getClassesFromDir($dir)
83 {
84 $classes = array();
85
87 if (($handle = opendir($path)))
88 {
89 while ((($entry = readdir($handle)) !== false))
90 {
91 if ($entry != '.' && $entry != '..')
92 {
93 $classes[] = mb_strtoupper(pathinfo($entry, PATHINFO_FILENAME));
94 }
95 }
96 }
97
98 return $classes;
99 }
100
108 public static function getData($id, $type, $asIs = false): array
109 {
110 $data = [];
111 $id = (int)$id;
112
113 if (!is_string($type))
114 {
115 return $data;
116 }
117
118 $isEditMode = self::$editMode ? 'N' : 'Y';
119
120 $cacheId = self::getCacheId($id, $type, $isEditMode, $asIs);
121 $data = self::getDataFromCache($cacheId);
122
123 if (empty($data))
124 {
125 $data = self::getDataFromDatabase($id, $type, $isEditMode, $asIs);
126 self::saveDataToCache($cacheId, $data);
127 }
128
129 return $data;
130 }
131
139 private static function getDataFromCache(string $cacheId): array
140 {
141 $data = [];
142
143 $cache = new CPHPCache();
144 if ($cache->InitCache(self::CACHE_TIME, $cacheId, self::CACHE_DIR))
145 {
146 $vars = $cache->GetVars();
147 if (isset($vars[0]))
148 {
149 $data = $vars[0];
150 }
151 }
152
153 return $data;
154 }
155
166 private static function getDataFromDatabase(int $id, string $type, string $isEditMode, bool $asIs): array
167 {
168 $data = [];
169
170 $res = HookData::getList([
171 'select' => [
172 'ID', 'HOOK', 'CODE', 'VALUE'
173 ],
174 'filter' => [
175 'ENTITY_ID' => $id,
176 '=ENTITY_TYPE' => $type,
177 '=PUBLIC' => $isEditMode,
178 ],
179 'order' => [
180 'ID' => 'asc'
181 ],
182 ]);
183
184 foreach ($res->fetchAll() as $row)
185 {
186 if (!isset($data[$row['HOOK']]))
187 {
188 $data[$row['HOOK']] = [];
189 }
190 if (mb_strpos($row['VALUE'], 'serialized#') === 0)
191 {
192 $row['VALUE'] = unserialize(mb_substr($row['VALUE'], 11), ['allowed_classes' => false]);
193 }
194 $data[$row['HOOK']][$row['CODE']] = $asIs ? $row : $row['VALUE'];
195 }
196
197 return $data;
198 }
199
206 private static function saveDataToCache(string $cacheId, array $data): void
207 {
208 $cache = new CPHPCache();
209 if (!$cache->InitCache(self::CACHE_TIME, $cacheId, self::CACHE_DIR))
210 {
211 $cache->StartDataCache();
212 $cache->EndDataCache([$data]);
213 }
214 }
215
223 protected static function getList($id, $type, array $data = array())
224 {
225 $hooks = array();
226 $classDir = self::HOOKS_PAGE_DIR;
227 $classNamespace = self::HOOKS_NAMESPACE;
229
230 // first read all hooks in base dir
231 foreach (self::getClassesFromDir($classDir) as $class)
232 {
233 if (in_array($class, $excludedHooks))
234 {
235 continue;
236 }
237 $classFull = __NAMESPACE__ . $classNamespace . $class;
238 if (class_exists($classFull))
239 {
240 $hooks[$class] = new $classFull(
241 self::$editMode,
242 !($type == self::ENTITY_TYPE_SITE)
243 );
244 }
245 }
246
247 // sort hooks
248 uasort($hooks, function($a, $b)
249 {
250 if ($a->getSort() == $b->getSort())
251 {
252 return 0;
253 }
254 return ($a->getSort() < $b->getSort()) ? -1 : 1;
255 });
256
257 // check custom exec
258 $event = new Event('landing', 'onHookExec');
259 $event->send();
260 foreach ($event->getResults() as $result)
261 {
262 if ($result->getType() != EventResult::ERROR)
263 {
264 if ($customExec = $result->getModified())
265 {
266 foreach ((array)$customExec as $code => $itemExec)
267 {
268 $code = mb_strtoupper($code);
269 if (isset($hooks[$code]) && is_callable($itemExec))
270 {
271 $hooks[$code]->setCustomExec($itemExec);
272 }
273 }
274 unset($code, $itemExec);
275 }
276 unset($customExec);
277 }
278 }
279 unset($event, $result);
280
281 // then fill hook with data
282 if (!empty($hooks) && $id > 0)
283 {
284 if (empty($data))
285 {
286 $data = self::getData($id, $type);
287 }
288 foreach ($hooks as $code => $hook)
289 {
290 if (isset($data[$code]))
291 {
292 $hook->setData($data[$code]);
293 }
294 }
295 }
296
297 return $hooks;
298 }
299
305 public static function setEditMode(bool $mode = true): void
306 {
307 self::$editMode = $mode;
308 }
309
314 public static function getEditMode(): bool
315 {
316 return self::$editMode;
317 }
318
324 public static function getForSite($id)
325 {
326 if (!Landing::getEditMode())
327 {
328 static $hooks = [];
329 }
330 else
331 {
332 $hooks = [];
333 }
334
335 if (!array_key_exists($id, $hooks))
336 {
337 $hooks[$id] = self::getList($id, self::ENTITY_TYPE_SITE);
338 }
339
340 return $hooks[$id];
341 }
342
348 public static function getForLanding($id)
349 {
350 if (!Landing::getEditMode())
351 {
352 static $hooks = [];
353 }
354 else
355 {
356 $hooks = [];
357 }
358
359 if (!array_key_exists($id, $hooks))
360 {
361 $hooks[$id] = self::getList($id, self::ENTITY_TYPE_LANDING);
362 }
363
364 return $hooks[$id];
365 }
366
372 public static function getForLandingRow($id)
373 {
374 return self::getData($id, self::ENTITY_TYPE_LANDING);
375 }
376
385 protected static function copy($from, $to, $type, $publication = false)
386 {
387 $from = (int)$from;
388 $to = (int)$to;
389 $data = self::getData($from, $type);
390 $existData = [];
391
392 $classDir = self::HOOKS_PAGE_DIR;
393 $classNamespace = self::HOOKS_NAMESPACE;
395
396 // first read all hooks in base dir
397 foreach (self::getClassesFromDir($classDir) as $class)
398 {
399 if (in_array($class, $excludedHooks, true))
400 {
401 continue;
402 }
403 $classFull = __NAMESPACE__ . $classNamespace . $class;
404 if (
405 isset($data[$class])
406 && method_exists($classFull, self::HOOKS_ON_COPY_HANDLER)
407 )
408 {
409 $handler = self::HOOKS_ON_COPY_HANDLER;
410 if ($preparedData = $classFull::$handler($data[$class], $from, $type, $publication))
411 {
412 $data[$class] = $preparedData;
413 }
414 }
415 }
416
417 // collect exist data
418 if ($data)
419 {
420 $res = HookData::getList([
421 'select' => [
422 'ID', 'HOOK', 'CODE'
423 ],
424 'filter' => [
425 'ENTITY_ID' => $to,
426 '=ENTITY_TYPE' => $type,
427 '=PUBLIC' => $publication ? 'Y' : 'N'
428 ]
429 ]);
430 while ($row = $res->fetch())
431 {
432 $existData[$row['HOOK'] . '_' . $row['CODE']] = $row['ID'];
433 }
434 }
435
436 // update existing keys or add new
437 foreach ($data as $hookCode => $items)
438 {
439 foreach ($items as $code => $value)
440 {
441 $existKey = $hookCode . '_' . $code;
442 if (is_array($value))
443 {
444 $value = 'serialized#' . serialize($value);
445 }
446 if (array_key_exists($existKey, $existData))
447 {
448 HookData::update($existData[$existKey], [
449 'VALUE' => $value
450 ]);
451 unset($existData[$existKey]);
452 }
453 else
454 {
455 HookData::add([
456 'ENTITY_ID' => $to,
457 'ENTITY_TYPE' => $type,
458 'HOOK' => $hookCode,
459 'CODE' => $code,
460 'VALUE' => $value,
461 'PUBLIC' => $publication ? 'Y' : 'N'
462 ]);
463 }
464 }
465 }
466
467 // delete unused data
468 if ($existData)
469 {
470 foreach ($existData as $delId)
471 {
472 HookData::delete($delId);
473 }
474 }
475
476 self::clearCache();
477 }
478
485 public static function copySite($from, $to)
486 {
487 $originalEditMode = self::$editMode;
488 if (!self::$editMode)
489 {
490 self::$editMode = true;
491 }
492 self::copy($from, $to, self::ENTITY_TYPE_SITE);
493 self::$editMode = $originalEditMode;
494 }
495
502 public static function copyLanding($from, $to)
503 {
504 $originalEditMode = self::$editMode;
505 if (!self::$editMode)
506 {
507 self::$editMode = true;
508 }
509 self::copy($from, $to, self::ENTITY_TYPE_LANDING);
510 self::$editMode = $originalEditMode;
511 }
512
518 public static function publicationSite($siteId)
519 {
520 self::copy($siteId, $siteId, self::ENTITY_TYPE_SITE, true);
521 }
522
528 public static function publicationLanding($lid)
529 {
530 self::copy($lid, $lid, self::ENTITY_TYPE_LANDING, true);
531 }
532
539 {
540 self::publicationWithSkipNeededPublication($siteId, self::ENTITY_TYPE_SITE);
541 }
542
548 public static function publicationLandingWithSkipNeededPublication($landingId): void
549 {
550 self::publicationWithSkipNeededPublication($landingId, self::ENTITY_TYPE_LANDING);
551 }
552
553 protected static function publicationWithSkipNeededPublication($id, $type): void
554 {
555 $editModeBack = self::$editMode;
556 self::$editMode = false;
557 $publicData = self::getData($id, $type, true);
558 self::$editMode = $editModeBack;
559
560 if ($type === self::ENTITY_TYPE_SITE)
561 {
562 self::publicationSite($id);
563 }
564 if ($type === self::ENTITY_TYPE_LANDING)
565 {
566 self::publicationLanding($id);
567 }
568 $data = self::getData($id, $type, true);
569
570 // return previously public values
571 $needClearCache = false;
572 foreach (self::getList($id, $type) as $hook)
573 {
574 if ($hook->isNeedPublication())
575 {
576 $fieldsToDelete = [];
577 if (isset($publicData[$hook->getCode()]))
578 {
579 foreach ($data[$hook->getCode()] as $fieldCode => $field)
580 {
581 if (!isset($publicData[$hook->getCode()][$fieldCode]))
582 {
583 $fieldsToDelete[$fieldCode] = $field;
584 }
585 elseif ($publicData[$hook->getCode()][$fieldCode]['VALUE'] !== $field['VALUE'])
586 {
587 $needClearCache = true;
588 HookData::update($field['ID'],
589 [
590 'VALUE' => $field['VALUE'],
591 ]
592 );
593 }
594 }
595 }
596 else
597 {
598 $fieldsToDelete = $data[$hook->getCode()] ?? [];
599 }
600
601 // del if not exists in public
602 if (!empty($fieldsToDelete))
603 {
604 $needClearCache = true;
605 foreach ($fieldsToDelete as $fieldCode => $field)
606 {
607 $res = HookData::getList([
608 'select' => ['ID'],
609 'filter' => [
610 'ENTITY_ID' => $id,
611 '=ENTITY_TYPE' => $type,
612 '=HOOK' => $hook->getCode(),
613 '=CODE' => $fieldCode,
614 '=PUBLIC' => 'Y'
615 ]
616 ]);
617 if ($row = $res->fetch())
618 {
619 HookData::delete($row['ID']);
620 }
621 }
622 }
623 }
624 }
625
626 // drop public cache
627 if ($needClearCache)
628 {
629 if ($type === self::ENTITY_TYPE_SITE)
630 {
631 $landings = Landing::getList([
632 'select' => ['ID'],
633 'filter' => [
634 'SITE_ID' => $id,
635 '=PUBLIC' => 'Y',
636 '=DELETED' => 'N',
637 ],
638 ]);
639 while ($landing = $landings->fetch())
640 {
641 Landing::update($landing['ID'], ['PUBLIC' => 'N']);
642 }
643 }
644 if ($type === self::ENTITY_TYPE_LANDING)
645 {
646 Landing::update($id, ['PUBLIC' => 'N']);
647 }
648 }
649
650 self::clearCache();
651 }
652
658 protected static function prepareData(array $data)
659 {
660 $newData = array();
661
662 foreach ($data as $code => $val)
663 {
664 if (mb_strpos($code, '_') !== false)
665 {
666 $codeHook = mb_substr($code, 0, mb_strpos($code, '_'));
667 $codeVal = mb_substr($code, mb_strpos($code, '_') + 1);
668 if (!isset($newData[$codeHook]))
669 {
670 $newData[$codeHook] = array();
671 }
672 $newData[$codeHook][$codeVal] = $val;
673 }
674 }
675
676 return $newData;
677 }
678
686 protected static function saveData($id, $type, array $data)
687 {
688 $data = self::prepareData($data);
689 $hooks = self::getList($id, $type, $data);
690 $dataSave = self::getData($id, $type, true);
691
692 // get hooks with new new data (not saved yet)
693 foreach ($hooks as $hook)
694 {
695 $hookLocked = $hook->isLocked();
696 $codeHook = $hook->getCode();
697 // modify $dataSave ...
698 foreach ($hook->getFields() as $field)
699 {
700 $codeVal = $field->getCode();
701 if ($hookLocked && !$field->isEmptyValue())
702 {
703 continue;
704 }
705 if (!isset($data[$codeHook][$codeVal]))
706 {
707 continue;
708 }
709 // ... for changed
710 if (isset($dataSave[$codeHook][$codeVal]))
711 {
712 $dataSave[$codeHook][$codeVal]['CHANGED'] = true;
713 $dataSave[$codeHook][$codeVal]['VALUE'] = $field->getValue();
714 }
715 // ... and new fields
716 else
717 {
718 if (!isset($dataSave[$codeHook]))
719 {
720 $dataSave[$codeHook] = array();
721 }
722 $dataSave[$codeHook][$codeVal] = array(
723 'HOOK' => $codeHook,
724 'CODE' => $codeVal,
725 'VALUE' => $field->getValue()
726 );
727 }
728 if (is_array($dataSave[$codeHook][$codeVal]['VALUE']))
729 {
730 $dataSave[$codeHook][$codeVal]['VALUE'] = 'serialized#' . serialize(
731 $dataSave[$codeHook][$codeVal]['VALUE']
732 );
733 }
734 }
735 }
736
737 // now save the data
738 foreach ($dataSave as $codeHook => $dataHook)
739 {
740 foreach ($dataHook as $code => $row)
741 {
742 if (
743 is_array($row['VALUE']) && empty($row['VALUE'])
744 ||
745 !is_array($row['VALUE']) && trim($row['VALUE']) == ''
746 )
747 {
748 if (isset($row['ID']))
749 {
750 HookData::delete($row['ID']);
751 }
752 }
753 else
754 {
755 if (!isset($row['ID']))
756 {
757 $row['ENTITY_ID'] = $id;
758 $row['ENTITY_TYPE'] = $type;
759 HookData::add($row);
760 }
761 elseif (isset($row['CHANGED']) && $row['CHANGED'])
762 {
763 $updId = $row['ID'];
764 unset($row['ID'], $row['CHANGED']);
765 HookData::update($updId, $row);
766 }
767 }
768 }
769 }
770
771 self::clearCache();
772 }
773
780 protected static function indexContent($id, $type)
781 {
782 $id = intval($id);
783
784 if ($type == self::ENTITY_TYPE_LANDING)
785 {
786 $class = '\Bitrix\Landing\Landing';
787 }
788
789 if (!isset($class))
790 {
791 return;
792 }
793
794 // base fields
795 $searchContent = $class::getList([
796 'select' => [
797 'TITLE', 'DESCRIPTION'
798 ],
799 'filter' => [
800 'ID' => $id,
801 '=DELETED' => ['Y', 'N'],
802 '=SITE.DELETED' => ['Y', 'N']
803 ]
804 ])->fetch();
805 if (!$searchContent)
806 {
807 return;
808 }
809
810 $searchContent = array_values($searchContent);
811
812 // hook fields
813 foreach (self::getList($id, $type) as $hook)
814 {
815 foreach ($hook->getFields() as $field)
816 {
817 if ($field->isSearchable())
818 {
819 $searchContent[] = $field->getValue();
820 }
821 }
822 }
823
824 $searchContent = array_unique($searchContent);
825 $searchContent = $searchContent ? implode(' ', $searchContent) : '';
826 $searchContent = trim($searchContent);
827
828 if ($searchContent)
829 {
830 $res = $class::update($id, [
831 'SEARCH_CONTENT' => $searchContent
832 ]);
833 $res->isSuccess();
834 }
835 }
836
843 public static function saveForSite(int $id, array $data): void
844 {
845 $check = Site::getList([
846 'select' => [
847 'ID'
848 ],
849 'filter' => [
850 'ID' => $id
851 ]
852 ])->fetch();
853 if ($check)
854 {
855 $editModeBack = self::$editMode;
856 self::$editMode = true;
857 self::saveData($id, self::ENTITY_TYPE_SITE, $data);
858 if (Manager::getOption('public_hook_on_save') === 'Y')
859 {
860 self::publicationSiteWithSkipNeededPublication($id);
861 }
862 self::$editMode = $editModeBack;
863 }
864 }
865
872 public static function saveForLanding(int $id, array $data): void
873 {
874 $check = Landing::getList([
875 'select' => [
876 'ID'
877 ],
878 'filter' => [
879 'ID' => $id
880 ]
881 ])->fetch();
882 if ($check)
883 {
884 $editModeBack = self::$editMode;
885 self::$editMode = true;
886 self::saveData($id, self::ENTITY_TYPE_LANDING, $data);
887 self::indexContent($id, self::ENTITY_TYPE_LANDING);
888 if (Manager::getOption('public_hook_on_save') === 'Y')
889 {
890 self::publicationLandingWithSkipNeededPublication($id);
891 }
892 self::$editMode = $editModeBack;
893 }
894 }
895
901 public static function indexLanding($id)
902 {
903 self::indexContent($id, self::ENTITY_TYPE_LANDING);
904 }
905
912 protected static function deleteData($id, $type)
913 {
914 $id = intval($id);
915
916 $res = HookData::getList(array(
917 'select' => array(
918 'ID'
919 ),
920 'filter' => array(
921 'ENTITY_ID' => $id,
922 '=ENTITY_TYPE' => $type
923 )
924 ));
925 while ($row = $res->fetch())
926 {
927 HookData::delete($row['ID']);
928 }
929
930 self::clearCache();
931 }
932
938 public static function deleteForSite($id)
939 {
940 self::deleteData($id, self::ENTITY_TYPE_SITE);
941 }
942
948 public static function deleteForLanding($id)
949 {
950 self::deleteData($id, self::ENTITY_TYPE_LANDING);
951 }
952
953 private static function clearCache(): void
954 {
955 (new CPHPCache())->CleanDir(self::CACHE_DIR);
956 }
957
958 private static function getCacheId(int $entityId, string $entityType, string $isPublic, bool $asIs): string
959 {
960 return "hook_data_{$entityId}_{$entityType}_{$isPublic}_" . (int)$asIs;
961 }
962}
$path
Определения access_edit.php:21
$type
Определения options.php:106
static copySite($from, $to)
Определения hook.php:485
static getEditMode()
Определения hook.php:314
static publicationSite($siteId)
Определения hook.php:518
static saveData($id, $type, array $data)
Определения hook.php:686
static getClassesFromDir($dir)
Определения hook.php:82
static getForLanding($id)
Определения hook.php:348
const HOOKS_CODES_FILES
Определения hook.php:46
const HOOKS_ON_COPY_HANDLER
Определения hook.php:41
static deleteForSite($id)
Определения hook.php:938
static publicationLanding($lid)
Определения hook.php:528
static copyLanding($from, $to)
Определения hook.php:502
static getList($id, $type, array $data=array())
Определения hook.php:223
static indexContent($id, $type)
Определения hook.php:780
static getData($id, $type, $asIs=false)
Определения hook.php:108
static getForLandingRow($id)
Определения hook.php:372
const ENTITY_TYPE_SITE
Определения hook.php:21
const HOOKS_CODES_DESIGN
Определения hook.php:54
const HOOKS_PAGE_DIR
Определения hook.php:31
static publicationWithSkipNeededPublication($id, $type)
Определения hook.php:553
static getForSite($id)
Определения hook.php:324
static publicationLandingWithSkipNeededPublication($landingId)
Определения hook.php:548
static setEditMode(bool $mode=true)
Определения hook.php:305
static saveForSite(int $id, array $data)
Определения hook.php:843
static saveForLanding(int $id, array $data)
Определения hook.php:872
static deleteData($id, $type)
Определения hook.php:912
const ENTITY_TYPE_LANDING
Определения hook.php:26
static indexLanding($id)
Определения hook.php:901
static prepareData(array $data)
Определения hook.php:658
static publicationSiteWithSkipNeededPublication($siteId)
Определения hook.php:538
static copy($from, $to, $type, $publication=false)
Определения hook.php:385
static $editMode
Определения hook.php:16
static deleteForLanding($id)
Определения hook.php:948
const HOOKS_NAMESPACE
Определения hook.php:36
static getOption($code, $default=null)
Определения manager.php:160
static getDocRoot()
Определения manager.php:180
static getExcludedHooks()
Определения type.php:218
static update($primary, array $data)
Определения file.php:228
static getList(array $parameters=array())
Определения datamanager.php:431
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$handle
Определения include.php:55
$result
Определения get_property_values.php:14
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$siteId
Определения ajax.php:8
Определения page.php:2
Определения buffer.php:3
$entityId
Определения payment.php:4
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$dir
Определения quickway.php:303
$items
Определения template.php:224
else $a
Определения template.php:137
$val
Определения options.php:1793