Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
block.php
1<?php
2namespace Bitrix\Landing;
3
4use \Bitrix\Main\Page\Asset;
5use \Bitrix\Main\Web\Json;
6use \Bitrix\Main\Web\DOM;
7use \Bitrix\Main\Localization\Loc;
8use \Bitrix\Landing\Connector;
9use \Bitrix\Landing\Controller;
10use \Bitrix\Landing\Internals;
11use \Bitrix\Landing\Assets;
12use \Bitrix\Landing\Block\Cache;
13use \Bitrix\Landing\Restriction;
14use \Bitrix\Landing\Node\Type as NodeType;
15use \Bitrix\Landing\Node\Img;
16use \Bitrix\Landing\PublicAction\Utils as UtilsAction;
17
18Loc::loadMessages(__FILE__);
19
21{
25 const BLOCKS_DIR = 'blocks';
26
30 const BLOCKS_TAG = 'landing_blocks';
31
35 const PREVIEW_FILE_NAME = 'preview.jpg';
36
40 const CSS_FILE_NAME = 'style.css';
41
45 const JS_FILE_NAME = 'script.js';
46
50 const REPO_MASK = '/^repo_([\d]+)$/';
51
55 const NEW_BLOCK_LT = 1209600;//86400 * 14
56
60 const ACCESS_A = 'A';
61
65 const ACCESS_D = 'D';
66
70 const ACCESS_V = 'V';
71
75 const ACCESS_W = 'W';
76
80 const ACCESS_X = 'X';
81
85 const CARD_SYM_CODE = 'card';
86
90 const PRESET_SYM_CODE = 'preset';
91
95 public const DEFAULT_WRAPPER_STYLE = ['block-default'];
96
100 public const FAVOURITE_BLOCKS_LIMIT = 5000;
101
106
111 public static $internalClass = 'BlockTable';
112
117 protected $id = 0;
118
123 protected $lid = 0;
124
129 protected $parentId = 0;
130
135 protected $siteId = 0;
136
141 protected $sort = 0;
142
147 protected $repoId = 0;
148
153 protected $repoInfo = [];
154
159 protected $code = '';
160
165 protected $anchor = '';
166
171 protected $content = '';
172
178
184 protected $access = 'X';
185
190 protected $metaData = array();
191
196 protected $assets = array();
197
202 protected $active = false;
203
208 protected $landingActive = false;
209
214 protected $deleted = false;
215
220 protected $designed = false;
221
226 protected $public = false;
227
232 protected $allowedByTariff = true;
233
238 protected $docRoot = '';
239
244 protected $error = null;
245
250 protected $dynamicParams = [];
251
256 protected $allowedExtensions = [
257 'landing_form',
258 'landing_carousel',
259 'landing_google_maps_new',
260 'landing_map',
261 'landing_countdown',
262 'landing_gallery_cards',
263 'landing_chat'
264 ];
265
272 public function __construct($id, $data = [], array $params = [])
273 {
274 if (empty($data) || !is_array($data))
275 {
276 $data = parent::getList(array(
277 'select' => array(
278 '*',
279 'LANDING_TITLE' => 'LANDING.TITLE',
280 'LANDING_ACTIVE' => 'LANDING.ACTIVE',
281 'LANDING_TPL_CODE' => 'LANDING.TPL_CODE',
282 'SITE_TPL_CODE' => 'LANDING.SITE.TPL_CODE',
283 'SITE_TYPE' => 'LANDING.SITE.TYPE',
284 'SITE_ID' => 'LANDING.SITE_ID'
285 ),
286 'filter' => array(
287 'ID' => (int)$id
288 )
289 ))->fetch();
290 if (!$data)
291 {
292 $id = 0;
293 }
294 }
295
296 // if content is empty, fill from repository
297 if (!isset($data['CONTENT']) || trim($data['CONTENT']) == '')
298 {
299 $data['CONTENT'] = '';
300 }
301
302 $this->id = intval($id);
303 $this->lid = isset($data['LID']) ? intval($data['LID']) : 0;
304 $this->parentId = isset($data['PARENT_ID']) ? intval($data['PARENT_ID']) : 0;
305 $this->siteId = isset($data['SITE_ID']) ? intval($data['SITE_ID']) : 0;
306 $this->sort = isset($data['SORT']) ? intval($data['SORT']) : '';
307 $this->code = isset($data['CODE']) ? trim($data['CODE']) : '';
308 $this->anchor = isset($data['ANCHOR']) ? trim($data['ANCHOR']) : '';
309 $this->active = isset($data['ACTIVE']) && $data['ACTIVE'] == 'Y';
310 $this->landingActive = isset($data['LANDING_ACTIVE']) && $data['LANDING_ACTIVE'] == 'Y';
311 $this->deleted = isset($data['DELETED']) && $data['DELETED'] == 'Y';
312 $this->designed = isset($data['DESIGNED']) && $data['DESIGNED'] == 'Y';
313 $this->public = isset($data['PUBLIC']) && $data['PUBLIC'] == 'Y';
314 $this->content = (!$this->deleted && isset($data['CONTENT'])) ? trim($data['CONTENT']) : '';
315
316 // access
317 if (isset($data['ACCESS']))
318 {
319 $this->access = $data['ACCESS'];
320 }
321
322 // assets
323 if (isset($data['ASSETS']))
324 {
325 $this->assets = $data['ASSETS'];
326 }
327
328 // fill meta data
329 $keys = [
330 'LID', 'FAVORITE_META', 'CREATED_BY_ID', 'DATE_CREATE',
331 'MODIFIED_BY_ID', 'DATE_MODIFY', 'SITE_TYPE'
332 ];
333 foreach ($keys as $key)
334 {
335 if (isset($data[$key]))
336 {
337 $this->metaData[$key] = $data[$key];
338 }
339 }
340 $this->metaData['LANDING_TITLE'] = isset($data['LANDING_TITLE']) ? $data['LANDING_TITLE'] : '';
341 $this->metaData['LANDING_TPL_CODE'] = isset($data['LANDING_TPL_CODE']) ? $data['LANDING_TPL_CODE'] : '';
342 $this->metaData['SITE_TPL_CODE'] = isset($data['SITE_TPL_CODE']) ? $data['SITE_TPL_CODE'] : '';
343 $this->metaData['XML_ID'] = isset($data['XML_ID']) ? $data['XML_ID'] : '';
344 $this->metaData['DESIGNER_MODE'] = isset($params['designer_mode']) && $params['designer_mode'] === true;
345
346 // other data
347 if (preg_match(self::REPO_MASK, $this->code, $matches))
348 {
349 $this->repoId = $matches[1];
350 }
351 if (!$this->content && !$this->deleted)
352 {
353 $this->content = self::getContentFromRepository($this->code);
354 }
355 $this->error = new Error;
356 $this->docRoot = Manager::getDocRoot();
357
358 // dynamic params
359 if (isset($data['SOURCE_PARAMS']))
360 {
361 $this->dynamicParams = (array)$data['SOURCE_PARAMS'];
362 }
363 }
364
372 public static function fillLanding(Landing $landing, $limit = 0, array $params = array())
373 {
374 if ($landing->exist())
375 {
376 $editMode = $landing->getEditMode() || $landing->getPreviewMode();
377 $repo = array();
378 $blocks = array();
379 // get all blocks by filter
380 $filter = array(
381 'LID' => $landing->getId(),
382 '=PUBLIC' => $editMode ? 'N' : 'Y',
383 '=DELETED' => (isset($params['deleted']) && $params['deleted'] === true)
384 ? 'Y'
385 : 'N'
386 );
387 if (isset($params['id']) && $params['id'])
388 {
389 $filter['ID'] = $params['id'];
390 }
391 $res = parent::getList(array(
392 'select' => array(
393 '*',
394 'LANDING_ACTIVE' => 'LANDING.ACTIVE',
395 'LANDING_TPL_CODE' => 'LANDING.TPL_CODE',
396 'SITE_TPL_CODE' => 'LANDING.SITE.TPL_CODE',
397 'SITE_TYPE' => 'LANDING.SITE.TYPE',
398 'SITE_ID' => 'LANDING.SITE_ID'
399 ),
400 'filter' => $filter,
401 'order' => array(
402 'SORT' => 'ASC',
403 'ID' => 'ASC'
404 ),
405 'limit' => $limit
406 ));
407 while ($row = $res->fetch())
408 {
409 $blockParams = [];
410 if (!$landing->canEdit())
411 {
412 $row['ACCESS'] = self::ACCESS_A;
413 }
414 $row['SITE_ID'] = $landing->getSiteId();
415 $block = new self(
416 $row['ID'],
417 $row,
418 $blockParams
419 );
420 if ($block->getRepoId())
421 {
422 $repo[] = $block->getRepoId();
423 }
424 $blocks[$row['ID']] = $block;
425 }
426 unset($row, $res);
427 if (!empty($repo))
428 {
429 $repo = Repo::getAppInfo($repo);
430 }
431 // add blocks to landing
432 foreach ($blocks as $block)
433 {
434 if (
435 isset($repo[$block->getRepoId()]['PAYMENT_ALLOW']) &&
436 $repo[$block->getRepoId()]['PAYMENT_ALLOW'] != 'Y'
437 )
438 {
439 $allowedByTariff = false;
440 }
441 else
442 {
443 $allowedByTariff = true;
444 }
445 if ($editMode)
446 {
447 $block->setAllowedByTariff($allowedByTariff);
448 $landing->addBlockToCollection($block);
449 }
450 elseif ($allowedByTariff)
451 {
452 $landing->addBlockToCollection($block);
453 }
454 }
455 unset($blocks, $block, $repo);
456 return true;
457 }
458
459 return false;
460 }
461
467 public static function cloneForEdit(\Bitrix\Landing\Landing $landing)
468 {
469 if ($landing->exist())
470 {
471 $clone = true;
472 $forClone = array();
473
474 $res = parent::getList(array(
475 'select' => array(
476 'ID', 'LID', 'CODE', 'SORT', 'ACTIVE',
477 'CONTENT', 'PUBLIC', 'ACCESS', 'ANCHOR',
478 'DESIGNED'
479 ),
480 'filter' => array(
481 'LID' => $landing->getId()
482 )
483 ));
484 while ($row = $res->fetch())
485 {
486 if ($row['PUBLIC'] != 'Y')
487 {
488 $clone = false;
489 break;
490 }
491 else
492 {
493 if (!$row['ANCHOR'])
494 {
495 $row['ANCHOR'] = 'b' . $row['ID'];
496 }
497 $row['PUBLIC'] = 'N';
498 $row['PARENT_ID'] = $row['ID'];
499 unset($row['ID']);
500 $forClone[] = $row;
501 }
502 }
503
504 if ($clone)
505 {
506 foreach ($forClone as $row)
507 {
508 parent::add($row);
509 }
510 }
511 }
512 }
513
519 public static function publicationBlocks(\Bitrix\Landing\Landing $landing)
520 {
522 }
523
529 public static function getLandingIdByBlockId($id)
530 {
531 $data = array();
532 $res = parent::getList(array(
533 'select' => array(
534 'ID', 'LID'
535 ),
536 'filter' => array(
537 'ID' => $id
538 )
539 ));
540 while ($row = $res->fetch())
541 {
542 $data[$row['ID']] = $row['LID'];
543 }
544
545 if (is_array($id))
546 {
547 return $data;
548 }
549 elseif (!empty($data))
550 {
551 return array_pop($data);
552 }
553
554 return false;
555 }
556
564 public static function getLandingRowByBlockId($id, array $select = array('ID'))
565 {
566 return self::getRowByBlockId($id, $select);
567 }
568
575 public static function getRowByBlockId($id, array $select = array('ID'))
576 {
577 $data = array();
578 $res = parent::getList(array(
579 'select' => $select,
580 'filter' => array(
581 'ID' => $id
582 )
583 ));
584 while ($row = $res->fetch())
585 {
586 $data[$row['ID']] = $row;
587 }
588
589 if (is_array($id))
590 {
591 return $data;
592 }
593 elseif (!empty($data))
594 {
595 return array_pop($data);
596 }
597
598 return false;
599 }
600
606 protected static function getNormalizedBlock(string $code): ?array
607 {
608 static $cached = [];
609
610 if (isset($cached[$code]))
611 {
612 return $cached[$code];
613 }
614
615 $codeOriginal = $code;
616 [$code, $blockId] = explode('@', $code);
617 $filter = [
618 'LID' => 0,
619 '=DELETED' => 'N',
620 '=CODE' => $code
621 ];
622 if ($blockId)
623 {
624 $filter['ID'] = $blockId;
625 }
626 $res = Internals\BlockTable::getList([
627 'select' => [
628 'ID', 'CODE', 'CONTENT', 'SOURCE_PARAMS', 'DESIGNED'
629 ],
630 'filter' => $filter
631 ]);
632 if ($row = $res->fetch())
633 {
634 $cached[$codeOriginal] = $row;
635 $cached[$codeOriginal]['FILES'] = File::getFilesFromBlockContent($row['ID'], $row['CONTENT']);
636 }
637
638 return $cached[$codeOriginal] ?? null;
639 }
640
647 public static function getContentFromRepository(string $code, string $namespace = null): ?string
648 {
649 if (!is_string($code))
650 {
651 return null;
652 }
653
654 if (strpos($code, '@'))
655 {
656 $normalizedBlock = self::getNormalizedBlock($code);
657 return $normalizedBlock['CONTENT'] ?? null;
658 }
659
660 $content = null;
661
662 // local repo
663 if (preg_match(self::REPO_MASK, $code, $matches))
664 {
665 $repo = Repo::getById($matches[1])->fetch();
666 $content = $repo['CONTENT'];
667 }
668 // files storage
669 elseif ($path = self::getBlockPath($code, $namespace))
670 {
671 $path = Manager::getDocRoot() . $path . '/block.php';
672 if (file_exists($path))
673 {
674 $content = file_get_contents($path);
675 if (preg_match('/MESS\[[^\]]+\]/', $content))
676 {
677 $mess = Loc::loadLanguageFile($path);
678 if ($mess)
679 {
680 $replace = [];
681 foreach ($mess as $key => $title)
682 {
683 $replace['MESS[' . $key . ']'] = $title;
684 }
685 $content = str_replace(
686 array_keys($replace),
687 array_values($replace),
689 );
690 }
691 }
692 }
693 }
694
695 return $content;
696 }
697
705 public static function createFromRepository(Landing $landing, string $code, array $data = array())
706 {
707 // get content and manifest
708 $filesFromContent = [];
709 $sourceParams = [];
710 $codeOriginal = null;
711 $designed = 'N';
712 $content = $data['CONTENT'] ?? self::getContentFromRepository($code);
713 if (isset($data['PREPARE_BLOCK_DATA']['ACTION']))
714 {
715 if (
716 $data['PREPARE_BLOCK_DATA']['ACTION'] === 'changeComponentParams'
717 && isset($data['PREPARE_BLOCK_DATA']['PARAMS'])
718 && is_array($data['PREPARE_BLOCK_DATA']['PARAMS'])
719 )
720 {
721 foreach ($data['PREPARE_BLOCK_DATA']['PARAMS'] as $paramName => $paramValue)
722 {
723 $search = "'" . $paramName . "' => '',";
724 $replace = "'" . $paramName . "' => '". $paramValue . "',";
725 $content = str_replace($search, $replace, $content);
726 }
727 }
728 }
729 if (strpos($code, '@'))
730 {
731 $codeOriginal = $code;
732 $normalizedBlock = self::getNormalizedBlock($code);
733 $designed = $normalizedBlock['DESIGNED'] ?? 'N';
734 $filesFromContent = $normalizedBlock['FILES'] ?? [];
735 $sourceParams = $normalizedBlock['SOURCE_PARAMS'] ?? [];
736 [$code, ] = explode('@', $code);
737 }
738 $manifest = self::getManifestFile($code);
739 // version control
740 if (
741 isset($manifest['block']['version']) &&
742 version_compare(Manager::getVersion(), $manifest['block']['version']) < 0
743 )
744 {
745 $landing->getError()->addError(
746 'BLOCK_WRONG_VERSION',
747 Loc::getMessage('LANDING_BLOCK_WRONG_VERSION')
748 );
749 return false;
750 }
751 // check errors
752 if (!$landing->exist())
753 {
754 $landing->getError()->addError(
755 'LANDING_NOT_EXIST',
756 Loc::getMessage('LANDING_BLOCK_LANDING_NOT_EXIST')
757 );
758 return false;
759 }
760 if ($content == '')
761 {
762 $landing->getError()->addError(
763 'BLOCK_NOT_FOUND',
764 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
765 );
766 return false;
767 }
768 // add
769 $fields = array(
770 'LID' => $landing->getId(),
771 'CODE' => $code,
772 'SOURCE_PARAMS' => $sourceParams,
773 'CONTENT' => $content,
774 'ACTIVE' => 'Y',
775 'DESIGNED' => $designed
776 );
777 $availableReplace = array(
778 'ACTIVE', 'PUBLIC', 'ACCESS', 'SORT',
779 'CONTENT', 'ANCHOR', 'SOURCE_PARAMS',
780 'INITIATOR_APP_CODE', 'XML_ID',
781 'DESIGNED', 'FAVORITE_META'
782 );
783 foreach ($availableReplace as $replace)
784 {
785 if (isset($data[$replace]))
786 {
787 $fields[$replace] = $data[$replace];
788 }
789 }
790 $res = parent::add($fields);
791 if ($res->isSuccess())
792 {
793 $block = new self($res->getId());
794 $manifest = $block->getManifest();
795 if (!$block->getLocalAnchor())
796 {
797 $historyActivity = History::isActive();
799 $block->setAnchor('b' . $block->getId());
800 $historyActivity ? History::activate() : History::deactivate();
801 }
802 Assets\PreProcessing::blockAddProcessing($block);
803 if (
804 isset($manifest['callbacks']['afteradd']) &&
805 is_callable($manifest['callbacks']['afteradd'])
806 )
807 {
808 $manifest['callbacks']['afteradd']($block);
809 }
810 // calling class(es) of block
811 foreach ($block->getClass() as $class)
812 {
813 $classBlock = $block->includeBlockClass($class);
814 $classBlock->beforeAdd($block);
815 }
816 // for set filter
817 if ($fields['SOURCE_PARAMS'])
818 {
819 $block->saveDynamicParams(
820 $fields['SOURCE_PARAMS']
821 );
822 }
823 if (isset($manifest['block']['app_code']))
824 {
825 $block->save([
826 'INITIATOR_APP_CODE' => $manifest['block']['app_code']
827 ]);
828 }
829 else// index search only
830 {
831 $block->save();
832 }
833 // copy references to files from content to new block
834 foreach ($filesFromContent as $fileId)
835 {
836 File::addToBlock($block->getId(), $fileId);
837 }
838 return $block;
839 }
840 else
841 {
842 $landing->getError()->addFromResult($res);
843 return false;
844 }
845 }
846
852 protected static function isNewBlock($block)
853 {
854 static $newBlocks = null;
855
856 if (!is_string($block))
857 {
858 return false;
859 }
860
861 if ($newBlocks === null)
862 {
863 $newBlocks = unserialize(Manager::getOption('new_blocks'), ['allowed_classes' => false]);
864 if (!is_array($newBlocks))
865 {
866 $newBlocks = array();
867 }
868 if (
869 !isset($newBlocks['date']) ||
870 (
871 isset($newBlocks['date']) &&
872 ((time() - $newBlocks['date']) > self::NEW_BLOCK_LT)
873 )
874 )
875 {
876 $newBlocks = array();
877 }
878 if (isset($newBlocks['items']))
879 {
880 $newBlocks = $newBlocks['items'];
881 }
882 }
883
884 return in_array($block, $newBlocks);
885 }
886
891 protected static function getGeneralPaths()
892 {
893 static $paths = null;
894
895 if (!$paths)
896 {
897 $paths = [
898 BX_ROOT . '/' . self::BLOCKS_DIR,
899 \getLocalPath(self::BLOCKS_DIR)
900 ];
901 if ($paths[0] == $paths[1])
902 {
903 unset($paths[1]);
904 }
905 }
906
907 return $paths;
908 }
909
914 public static function clearRepositoryCache()
915 {
916 if (Cache::isCaching())
917 {
918 Manager::getCacheManager()->clearByTag(self::BLOCKS_TAG);
919 }
920 }
921
926 protected static function getNamespaces()
927 {
928 static $namespaces = [];
929
930 if ($namespaces)
931 {
932 return $namespaces;
933 }
934
935 $paths = self::getGeneralPaths();
936 $disableNamespace = (array)Config::get('disable_namespace');
937 $enableNamespace = Config::get('enable_namespace');
938 $enableNamespace = $enableNamespace ? (array) $enableNamespace : array();
939
940 $namespaces = [];
941 foreach ($paths as $path)
942 {
943 if ($path !== false)
944 {
945 $path = Manager::getDocRoot() . $path;
946 // read all subdirs ($namespaces) in block dir
947 if (($handle = opendir($path)))
948 {
949 while ((($entry = readdir($handle)) !== false))
950 {
951 if (!empty($enableNamespace))
952 {
953 if (in_array($entry, $enableNamespace))
954 {
955 $namespaces[] = $entry;
956 }
957 }
958 else if (
959 $entry != '.' && $entry != '..' &&
960 is_dir($path . '/' . $entry) &&
961 !in_array($entry, $disableNamespace)
962 )
963 {
964 $namespaces[] = $entry;
965 }
966 }
967 }
968 }
969 }
970 $namespaces = array_unique($namespaces);
971
972 return $namespaces;
973 }
974
980 public static function getRepository($withManifest = false)
981 {
982 static $blocksCats = array();
983
984 // function for prepare return
985 $returnFunc = function($blocksCats) use($withManifest)
986 {
987 $event = new \Bitrix\Main\Event('landing', 'onBlockGetRepository', array(
988 'blocks' => $blocksCats,
989 'withManifest' => $withManifest
990 ));
991 $event->send();
992 foreach ($event->getResults() as $result)
993 {
994 if ($result->getResultType() != \Bitrix\Main\EventResult::ERROR)
995 {
996 if (($modified = $result->getModified()))
997 {
998 if (isset($modified['blocks']))
999 {
1000 $blocksCats = $modified['blocks'];
1001 }
1002 }
1003 }
1004 }
1005 return $blocksCats;
1006 };
1007
1008 // static cache
1009 if (!$withManifest && !empty($blocksCats))
1010 {
1011 return $returnFunc($blocksCats);
1012 }
1013
1014 // local function for fill last used blocks
1015 $fillLastUsed = function($blocksCats)
1016 {
1017 $blocksCats['last']['items'] = array();
1018 $lastUsed = self::getLastUsed();
1019 if ($lastUsed)
1020 {
1021 foreach ($lastUsed as $code)
1022 {
1023 $blocksCats['last']['items'][$code] = array();
1024 }
1025 foreach ($blocksCats as $catCode => &$cat)
1026 {
1027 foreach ($cat['items'] as $code => &$block)
1028 {
1029 if (
1030 in_array($code, $lastUsed) &&
1031 $catCode != 'last' &&
1032 !empty($block)
1033 )
1034 {
1035 $block['section'][] = 'last';
1036 $blocksCats['last']['items'][$code] = $block;
1037 }
1038 }
1039 unset($block);
1040 }
1041 unset($cat);
1042 // clear last-section
1043 foreach ($blocksCats['last']['items'] as $code => $block)
1044 {
1045 if (!$block)
1046 {
1047 unset($blocksCats['last']['items'][$code]);
1048 }
1049 }
1050 }
1051 return $blocksCats;
1052 };
1053
1054 // config
1055 $disableNamespace = (array)Config::get('disable_namespace');
1056 $enableNamespace = Config::get('enable_namespace');
1057 $enableNamespace = $enableNamespace ? (array) $enableNamespace : array();
1058
1059 // system cache begin
1060 $cache = new \CPHPCache();
1061 $cacheTime = 86400;
1062 $cacheStarted = false;
1063 $cacheId = $withManifest ? 'blocks_manifest' : 'blocks';
1064 $cacheId .= LANGUAGE_ID;
1065 $cacheId .= 'user:' . Manager::getUserId();
1066 $cacheId .= 'version:2';
1067 $cacheId .= 'disable:' . implode(',', $disableNamespace);
1068 $cacheId .= 'enable:' . implode(',', $enableNamespace);
1069 $cachePath = 'landing/blocks';
1070 if ($cache->initCache($cacheTime, $cacheId, $cachePath))
1071 {
1072 $blocksCats = $cache->getVars();
1073 if (is_array($blocksCats) && !empty($blocksCats))
1074 {
1075 $blocksCats = $fillLastUsed($blocksCats);
1076 return $returnFunc($blocksCats);
1077 }
1078 }
1079 if ($cache->startDataCache($cacheTime, $cacheId, $cachePath))
1080 {
1081 $cacheStarted = true;
1082 if (Cache::isCaching())
1083 {
1084 Manager::getCacheManager()->startTagCache($cachePath);
1085 Manager::getCacheManager()->registerTag(self::BLOCKS_TAG);
1086 }
1087 }
1088
1089 // not in cache - init
1090 $blocks = array();
1091 $sections = array();
1092
1093 // general paths and namespaces
1094 $paths = self::getGeneralPaths();
1095 $namespaces = self::getNamespaces();
1096
1097 //get all blocks with description-file
1098 sort($namespaces);
1099 foreach ($namespaces as $subdir)
1100 {
1101 foreach ($paths as $path)
1102 {
1103 $path = Manager::getDocRoot() . $path;
1104 if (
1105 is_dir($path . '/' . $subdir) &&
1106 ($handle = opendir($path . '/' . $subdir))
1107 )
1108 {
1109 // sections
1110 $sectionsPath = $path . '/' . $subdir . '/.sections.php';
1111 if (file_exists($sectionsPath))
1112 {
1113 $sections = array_merge(
1114 $sections,
1115 (array) include $sectionsPath
1116 );
1117 }
1118 if (!isset($sections['last']))
1119 {
1120 $sections['last'] = [
1121 'name' => Loc::getMessage('LD_BLOCK_SECTION_LAST')
1122 ];
1123 }
1124 // blocks
1125 while ((($entry = readdir($handle)) !== false))
1126 {
1127 $descriptionPath = $path . '/' . $subdir . '/' . $entry . '/.description.php';
1128 $previewPathJpg = $path . '/' . $subdir . '/' . $entry . '/' . self::PREVIEW_FILE_NAME;
1129 if ($entry != '.' && $entry != '..' && file_exists($descriptionPath))
1130 {
1131 Loc::loadLanguageFile($descriptionPath);
1132 $description = include $descriptionPath;
1133 if (isset($description['block']['name']))
1134 {
1135 $previewFileName = Manager::getUrlFromFile(
1136 \getLocalPath(
1137 self::BLOCKS_DIR . '/' . $subdir . '/' . $entry . '/' . self::PREVIEW_FILE_NAME
1138 )
1139 );
1140 $blocks[$entry] = array(
1141 'id' => isset($description['block']['id'])
1142 ? (string)$description['block']['id']
1143 : null,
1144 'name' => $description['block']['name'],
1145 'namespace' => $subdir,
1146 'new' => self::isNewBlock($entry),
1147 'version' => isset($description['block']['version'])
1148 ? $description['block']['version']
1149 : null,
1150 'type' => isset($description['block']['type'])
1151 ? $description['block']['type']
1152 : array(),
1153 'section' => isset($description['block']['section'])
1154 ? $description['block']['section']
1155 : 'other',
1156 'description' => isset($description['block']['description'])
1157 ? $description['block']['description']
1158 : '',
1159 'preview' => file_exists($previewPathJpg)
1160 ? $previewFileName
1161 : '',
1162 'restricted' => false,
1163 'repo_id' => false,
1164 'app_code' => false,
1165 'only_for_license' => $description['block']['only_for_license'] ?? '',
1166 );
1167 if ($withManifest)
1168 {
1169 $blocks[$entry]['manifest'] = self::getManifestFile(
1170 $subdir . ':' . $entry
1171 );
1172 $blocks[$entry]['content'] = self::getContentFromRepository(
1173 $entry, $subdir
1174 );
1175 if (isset($blocks[$entry]['manifest']['block']))
1176 {
1177 $blocks[$entry]['manifest']['block']['preview'] = $blocks[$entry]['preview'];
1178 }
1179 // local assets to manifest's assets
1180 if (!isset($blocks[$entry]['manifest']['assets']))
1181 {
1182 $blocks[$entry]['manifest']['assets'] = array();
1183 }
1184 // if css exists
1185 if (file_exists($path . '/' . $subdir . '/' . $entry . '/style.min.css'))
1186 {
1187 if (!isset($blocks[$entry]['manifest']['assets']['css']))
1188 {
1189 $blocks[$entry]['manifest']['assets']['css'] = array();
1190 }
1191 $blocks[$entry]['manifest']['assets']['css'][] = Manager::getUrlFromFile(
1192 \getLocalPath(
1193 self::BLOCKS_DIR . '/' . $subdir . '/' . $entry . '/style.min.css'
1194 )
1195 );
1196 }
1197 // if js exists
1198 if (file_exists($path . '/' . $subdir . '/' . $entry . '/script.min.js' ))
1199 {
1200 if (!isset($blocks[$entry]['manifest']['assets']['js']))
1201 {
1202 $blocks[$entry]['manifest']['assets']['js'] = array();
1203 }
1204 $blocks[$entry]['manifest']['assets']['js'][] = Manager::getUrlFromFile(
1205 \getLocalPath(
1206 self::BLOCKS_DIR . '/' . $subdir . '/' . $entry . '/script.min.js'
1207 )
1208 );
1209 }
1210 if (empty($blocks[$entry]['manifest']['assets']))
1211 {
1212 unset($blocks[$entry]['manifest']['assets']);
1213 }
1214 }
1215 }
1216 }
1217 }
1218 }
1219 }
1220 }
1221
1222 // rest repo
1223 $blocksRepo = Repo::getRepository();
1224 // get apps by blocks
1225 $apps = array();
1226 foreach ($blocksRepo as $block)
1227 {
1228 if ($block['app_code'])
1229 {
1230 $apps[] = $block['app_code'];
1231 }
1232 }
1233 if ($apps)
1234 {
1235 $apps = array_unique($apps);
1236 $apps = Repo::getAppByCode($apps);
1237 // mark repo blocks expired
1238 foreach ($blocksRepo as &$block)
1239 {
1240 if (
1241 $block['app_code'] &&
1242 isset($apps[$block['app_code']]) &&
1243 $apps[$block['app_code']]['PAYMENT_ALLOW'] == 'N'
1244 )
1245 {
1246 $block['app_expired'] = true;
1247 }
1248 }
1249 unset($block);
1250 }
1251 $blocks += $blocksRepo;
1252
1253 // favorites block
1254 $currentUser = Manager::getUserId();
1255 $favoriteBlocks = [];
1256 $favoriteMyBlocks = [];
1257 $res = Internals\BlockTable::getList([
1258 'select' => [
1259 'ID', 'CODE', 'FAVORITE_META', 'CREATED_BY_ID'
1260 ],
1261 'filter' => [
1262 'LID' => 0,
1263 '=DELETED' => 'N'
1264 ],
1265 'order' => [
1266 'ID' => 'desc'
1267 ],
1268 'limit' => self::FAVOURITE_BLOCKS_LIMIT,
1269 ]);
1270 $countFavoriteBlocks = 0;
1271 while ($row = $res->fetch())
1272 {
1273 $countFavoriteBlocks++;
1274 if (isset($blocks[$row['CODE']]))
1275 {
1276 if (!is_array($row['FAVORITE_META']))
1277 {
1278 continue;
1279 }
1280 $meta = $row['FAVORITE_META'];
1281 $meta['preview'] = $meta['preview'] ?? 0;
1282 $meta['favorite'] = true;
1283 $meta['favoriteMy'] = ((int)$row['CREATED_BY_ID'] === $currentUser);
1284 if ($meta['preview'] > 0 && $countFavoriteBlocks < self::FAVOURITE_BLOCKS_LIMIT_WITH_PREVIEW)
1285 {
1286 $meta['preview'] = File::getFilePath($meta['preview']);
1287 }
1288 else
1289 {
1290 unset($meta['preview']);
1291 }
1292 if (isset($meta['section']))
1293 {
1294 $meta['section'] = (array)$meta['section'];
1295 }
1296
1297 $item = array_merge(
1298 $blocks[$row['CODE']],
1299 $meta
1300 );
1301 $code = $row['CODE'] . '@' . $row['ID'];
1302 if ($item['type'] === 'null')
1303 {
1304 $item['type'] = [];
1305 }
1306
1307 $meta['favoriteMy']
1308 ? ($favoriteMyBlocks[$code] = $item)
1309 : ($favoriteBlocks[$code] = $item)
1310 ;
1311 }
1312 }
1313 $blocks = $favoriteMyBlocks + $blocks + $favoriteBlocks;
1314
1315 // create new section in repo
1316 $createNewSection = function($item)
1317 {
1318 return array(
1319 'name' => isset($item['name'])
1320 ? (string) $item['name']
1321 : (string) $item,
1322 'meta' => $item['meta'] ?? [],
1323 'new' => false,
1324 'type' => $item['type'] ?? null,
1325 'separator' => false,
1326 'app_code' => false,
1327 'items' => array()
1328 );
1329 };
1330
1331 // set by sections
1332 $createdSects = [];
1333 foreach ($sections as $code => $item)
1334 {
1335 $title = $item['name'] ?? $item;
1336 $title = (string) $title;
1337 $title = trim($title);
1338 $blocksCats[$code] = $createNewSection($item);
1339 $createdSects[$title] = $code;
1340 }
1341 foreach ($blocks as $key => $block)
1342 {
1343 if (!is_array($block['section']))
1344 {
1345 $block['section'] = array($block['section']);
1346 }
1347 foreach ($block['section'] as $section)
1348 {
1349 $section = trim($section);
1350 if (!$section)
1351 {
1352 $section = 'other';
1353 }
1354 // adding new sections (actual for repo blocks)
1355 if (!isset($blocksCats[$section]))
1356 {
1357 if (isset($createdSects[$section]))
1358 {
1359 $section = $createdSects[$section];
1360 }
1361 else
1362 {
1363 $blocksCats[$section] = $createNewSection($section);
1364 }
1365 }
1366 $blocksCats[$section]['items'][$key] = $block;
1367 if ($block['new'])
1368 {
1369 $blocksCats[$section]['new'] = true;
1370 }
1371 }
1372 }
1373
1374 // add apps sections
1375 if (!empty($blocksRepo) && !empty($apps))
1376 {
1377 $blocksCats['separator_apps'] = array(
1378 'name' => Loc::getMessage('LANDING_BLOCK_SEPARATOR_PARTNER_2'),
1379 'separator' => true,
1380 'items' => array()
1381 );
1382 foreach ($apps as $app)
1383 {
1384 $blocksCats[$app['CODE']] = array(
1385 'name' => $app['APP_NAME'],
1386 'new' => false,
1387 'separator' => false,
1388 'app_code' => $app['CODE'],
1389 'items' => array()
1390 );
1391 }
1392 // add blocks to the app sections
1393 foreach ($blocksRepo as $key => $block)
1394 {
1395 if ($block['app_code'])
1396 {
1397 $blocksCats[$block['app_code']]['items'][$key] = $block;
1398 }
1399 }
1400 }
1401
1402 // sort by id
1403 foreach ($blocksCats as $codeCat => &$blocksCat)
1404 {
1405 $codeCat = mb_strtoupper($codeCat);
1406 uasort($blocksCat['items'], function($item1, $item2) use($codeCat)
1407 {
1408 if ($item1['repo_id'])
1409 {
1410 return 1;
1411 }
1412 if ($item2['repo_id'])
1413 {
1414 return 0;
1415 }
1416 if (
1417 ($item1['id'] && $item2['id']) &&
1418 mb_strpos($item1['id'], 'BX_'.$codeCat.'_') === 0 &&
1419 mb_strpos($item2['id'], 'BX_'.$codeCat.'_') === 0
1420 )
1421 {
1422 return ($item1['id'] > $item2['id']) ? 1 : -1;
1423 }
1424 return 0;
1425 });
1426 }
1427 unset($blocksCat);
1428
1429 // system cache end
1430 if ($cacheStarted)
1431 {
1432 $cache->endDataCache($blocksCats);
1433 if (Cache::isCaching())
1434 {
1435 Manager::getCacheManager()->endTagCache();
1436 }
1437 }
1438
1439 $blocksCats = $fillLastUsed($blocksCats);
1440
1441 return $returnFunc($blocksCats);
1442 }
1443
1449 public static function getLastUsed(int $count = 15): array
1450 {
1451 $blocks = array();
1452
1453 $res = Internals\BlockLastUsedTable::getList([
1454 'select' => [
1455 'CODE'
1456 ],
1457 'filter' => [
1458 'USER_ID' => Manager::getUserId()
1459 ],
1460 'order' => [
1461 'DATE_CREATE' => 'DESC'
1462 ],
1463 'limit' => $count
1464 ]);
1465 while ($row = $res->fetch())
1466 {
1467 $blocks[] = $row['CODE'];
1468 }
1469
1470 return $blocks;
1471 }
1472
1478 public static function markAsUsed(string $blockCode): void
1479 {
1480 $res = Internals\BlockLastUsedTable::getList([
1481 'select' => [
1482 'ID'
1483 ],
1484 'filter' => [
1485 'USER_ID' => Manager::getUserId(),
1486 '=CODE' => $blockCode
1487 ],
1488 'limit' => 1
1489 ]);
1490 if ($row = $res->fetch())
1491 {
1492 Internals\BlockLastUsedTable::update($row['ID'], [
1493 'DATE_CREATE' => new \Bitrix\Main\Type\DateTime
1494 ]);
1495 }
1496 else
1497 {
1498 Internals\BlockLastUsedTable::add([
1499 'CODE' => $blockCode,
1500 'USER_ID' => Manager::getUserId(),
1501 'DATE_CREATE' => new \Bitrix\Main\Type\DateTime
1502 ]);
1503 }
1504 }
1505
1511 public static function removeAsUsed(string $blockCode): void
1512 {
1513 $res = Internals\BlockLastUsedTable::getList([
1514 'select' => [
1515 'ID'
1516 ],
1517 'filter' => [
1518 '=CODE' => $blockCode
1519 ]
1520 ]);
1521 while ($row = $res->fetch())
1522 {
1523 Internals\BlockLastUsedTable::delete($row['ID']);
1524 }
1525 }
1526
1531 public static function getStyle(): array
1532 {
1533 return self::getSpecialManifest('style');
1534 }
1535
1540 public static function getSemantic(): array
1541 {
1542 return self::getSpecialManifest('semantic');
1543 }
1544
1549 public static function getAttrs(): array
1550 {
1551 return self::getSpecialManifest('attrs');
1552 }
1553
1558 protected static function getSpecialManifest(string $type): array
1559 {
1560 static $style = [];
1561
1562 if (array_key_exists($type, $style))
1563 {
1564 return $style[$type];
1565 }
1566
1567 $style[$type] = [];
1568 $paths = self::getGeneralPaths();
1569
1570 // read all subdirs ($namespaces) in block dir
1571 foreach ($paths as $path)
1572 {
1573 $path = Manager::getDocRoot() . $path;
1574 if (($handle = opendir($path)))
1575 {
1576 while ((($entry = readdir($handle)) !== false))
1577 {
1578 if (
1579 $entry != '.' && $entry != '..' &&
1580 is_dir($path . '/' . $entry) &&
1581 file_exists($path . '/' . $entry . '/.' . $type . '.php')
1582 )
1583 {
1584 $style[$type][$entry] = include $path . '/' . $entry . '/.' . $type . '.php';
1585 if (!is_array($style[$type][$entry]))
1586 {
1587 unset($style[$type][$entry]);
1588 }
1589 }
1590 }
1591 }
1592 }
1593
1594 return $style[$type];
1595 }
1596
1604 public static function getBlockContent($id, $editMode = false, array $params = array())
1605 {
1606 if (!isset($params['wrapper_show']))
1607 {
1608 $params['wrapper_show'] = true;
1609 }
1610 if ($editMode)
1611 {
1612 $params['force_unactive'] = true;
1613 }
1614 $params['skip_system_script'] = true;
1615
1616 ob_start();
1617 $id = intval($id);
1618 $block = new self($id);
1619 $extContent = '';
1620 if (($ext = $block->getExt()))
1621 {
1622 $extContent = \CUtil::initJSCore($ext, true);
1623 $extContent = preg_replace(
1624 '#<script type="text/javascript"(\sdata\-skip\-moving\="true")?>.*?</script>#is',
1625 '',
1626 $extContent
1627 );
1628 }
1629 $landing = Landing::createInstance(
1630 $block->getLandingId(),
1631 [
1632 'skip_blocks' => true
1633 ]
1634 );
1635 if ($editMode)
1636 {
1638 }
1639 $block->view(
1640 $editMode,
1641 $landing->exist() ? $landing : null,
1642 $params
1643 );
1644 if ($editMode)
1645 {
1647 }
1648 $content = ob_get_contents();
1649 $content = self::replaceMetaMarkers($content);
1650 if ($landing->exist() && mb_strpos($content, '#crm') !== false)
1651 {
1652 $replace = Connector\Crm::getReplacesForContent($landing->getSiteId(), false);
1653 $content = str_replace(
1654 array_keys($replace),
1655 array_values($replace),
1656 $content
1657 );
1658 }
1659 ob_end_clean();
1660 if ($block->exist())
1661 {
1662 Manager::getApplication()->restartBuffer();
1663 $availableJS = !$editMode || !$block->getRepoId();
1664 $manifest = $block->getManifest();
1665 if (
1666 !isset($manifest['requiredUserAction']) &&
1667 $block->getRuntimeRequiredUserAction()
1668 )
1669 {
1670 $manifest['requiredUserAction'] = $block->getRuntimeRequiredUserAction();
1671 }
1672 $sections = (array)(($manifest['block']['section']) ?? null);
1673 $return = array(
1674 'id' => $id,
1675 'sections' => implode(',', $sections),
1676 'active' => $block->isActive(),
1677 'access' => $block->getAccess(),
1678 'anchor' => $block->getLocalAnchor(),
1679 'php' => mb_strpos($block->getContent(), '<?') !== false,
1680 'designed' => $block->isDesigned(),
1681 'repoId' => $block->repoId ? (int)$block->repoId : null,
1682 'content' => $content,
1683 'content_ext' => $extContent,
1684 'css' => $block->getCSS(),
1685 'js' => $availableJS ? $block->getJS() : array(),
1686 'manifest' => $manifest,
1687 'dynamicParams' => $block->dynamicParams
1688 );
1689 if (
1690 $editMode &&
1691 isset($return['manifest']['requiredUserAction'])
1692 )
1693 {
1694 $return['requiredUserAction'] = $return['manifest']['requiredUserAction'];
1695 }
1696
1697 // add ajax initiated assets to output
1698 $ajaxAssets = self::getAjaxInitiatedAssets();
1699 $return['js'] = array_merge($return['js'], $ajaxAssets['js']);
1700 $return['css'] = array_merge($return['css'], $ajaxAssets['css']);
1701 // todo: what about strings, langs?
1702 // todo: what about core.js in strings. And etc relative extensions, which already init
1703
1704 return $return;
1705 }
1706 else
1707 {
1708 return array();
1709 }
1710 }
1711
1717 public static function getAnchor($id)
1718 {
1719 return 'block' . (int)$id;
1720 }
1721
1727 protected static function getBlockNamespace($code)
1728 {
1729 static $paths = array();
1730 static $namespace = array();
1731
1732 if (!is_string($code))
1733 {
1734 return '';
1735 }
1736
1737 $code = trim($code);
1738
1739 if (isset($paths[$code]))
1740 {
1741 return $paths[$code];
1742 }
1743
1744 $paths[$code] = '';
1745
1746 $namespaces = self::getNamespaces();
1747 $generalPaths = self::getGeneralPaths();
1748
1749 // get first needed block from end
1750 foreach (array_reverse($namespaces) as $subdir)
1751 {
1752 foreach ($generalPaths as $path)
1753 {
1754 $path = Manager::getDocRoot() . $path;
1755 if (file_exists($path . '/' . $subdir . '/' . $code . '/.description.php'))
1756 {
1757 $paths[$code] = $subdir;
1758 break 2;
1759 }
1760 }
1761 }
1762
1763 return $paths[$code];
1764 }
1765
1772 protected static function getBlockPath($code, $namespace = null)
1773 {
1774 if (!is_string($code))
1775 {
1776 return '';
1777 }
1778
1779 if (strpos($code, '@'))
1780 {
1781 [$code, ] = explode('@', $code);
1782 }
1783
1784 if (!$namespace)
1785 {
1786 $namespace = self::getBlockNamespace($code);
1787 }
1788 if ($namespace)
1789 {
1790 return \getLocalPath(
1791 self::BLOCKS_DIR . '/' . $namespace . '/' . $code
1792 );
1793 }
1794
1795 return '';
1796 }
1797
1802 public function exist()
1803 {
1804 return $this->id > 0;
1805 }
1806
1811 public function getId()
1812 {
1813 return $this->id;
1814 }
1815
1820 public function getLandingId()
1821 {
1822 return $this->lid;
1823 }
1824
1829 public function getSiteId()
1830 {
1831 return $this->siteId;
1832 }
1833
1838 public function getCode()
1839 {
1840 return $this->code;
1841 }
1842
1847 public function getLocalAnchor()
1848 {
1849 return $this->anchor;
1850 }
1851
1856 public function getContent()
1857 {
1858 return $this->content;
1859 }
1860
1865 public function getBlockClass()
1866 {
1867 $class = $this->getClass();
1868 $class = !empty($class) ? $class[0] : null;
1869 if ($class !== null)
1870 {
1871 return $this->includeBlockClass($class);
1872 }
1873
1874 return null;
1875 }
1876
1882 public function setAllowedByTariff(bool $mark): void
1883 {
1884 $this->allowedByTariff = $mark;
1885 }
1886
1891 public function resetContent()
1892 {
1893 $data = parent::getList([
1894 'select' => [
1895 'CONTENT'
1896 ],
1897 'filter' => [
1898 'ID' => $this->id
1899 ]
1900 ])->fetch();
1901 if ($data)
1902 {
1903 $this->content = $data['CONTENT'];
1904 }
1905 }
1906
1911 public function isActive()
1912 {
1913 return $this->active;
1914 }
1915
1920 public function isPublic()
1921 {
1922 return $this->public;
1923 }
1924
1929 public function isDesigned(): bool
1930 {
1931 return $this->designed;
1932 }
1933
1938 public function getAccess()
1939 {
1940 return $this->access;
1941 }
1942
1948 public function setAccess($letter)
1949 {
1950 if (is_string($letter))
1951 {
1952 $this->access = $letter;
1953 }
1954 }
1955
1961 public function setActive($active)
1962 {
1963 if ($this->access < $this::ACCESS_W)
1964 {
1965 $this->error->addError(
1966 'ACCESS_DENIED',
1967 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
1968 );
1969 return false;
1970 }
1971 $this->active = (boolean) $active;
1972 return true;
1973 }
1974
1979 public function getRepoId()
1980 {
1981 return $this->repoId;
1982 }
1983
1988 public function getSite()
1989 {
1990 static $site = null;
1991
1992 if (
1993 $site === null &&
1994 $this->siteId
1995 )
1996 {
1997 $site = Site::getList(array(
1998 'filter' => array(
1999 'ID' => $this->siteId
2000 )
2001 ))->fetch();
2002 }
2003
2004 return $site;
2005 }
2006
2011 public function getPreview()
2012 {
2013 $path = self::getBlockPath($this->code);
2014 if ($path && file_exists($this->docRoot . '/' . $path . '/' . self::PREVIEW_FILE_NAME))
2015 {
2016 return $path . '/' . self::PREVIEW_FILE_NAME;
2017 }
2018 return '';
2019 }
2020
2025 public function getError()
2026 {
2027 return $this->error;
2028 }
2029
2039 protected function getTypeClass($type)
2040 {
2041 return Node\Type::getClassName($type);
2042 }
2043
2048 protected function parseManifest(): array
2049 {
2050 static $manifests = [];
2051
2052 if (!$this->id || !$this->designed)
2053 {
2054 return [];
2055 }
2056 if (array_key_exists($this->id, $manifests))
2057 {
2058 return $manifests[$this->id];
2059 }
2060
2061 $manifests[$this->id] = Block\Designer::parseManifest($this->content);
2062
2063 return $manifests[$this->id];
2064 }
2065
2071 protected function checkDesignedManifest(array $manifest): array
2072 {
2073 if (isset($manifest['block']['name']))
2074 {
2075 $designerBlockManifest = $this->parseManifest();
2076 if (!empty($designerBlockManifest['nodes']))
2077 {
2078 foreach ($designerBlockManifest['nodes'] as $keyNode => $node)
2079 {
2080 if (isset($manifest['nodes'][$keyNode]))
2081 {
2082 continue;
2083 }
2084 $node['code'] = $keyNode;
2085 $class = Node\Type::getClassName($node['type']);
2086 if (isset($node['type']) && class_exists($class))
2087 {
2088 $node['handler'] = call_user_func(
2089 [
2090 $class,
2091 'getHandlerJS'
2092 ]
2093 );
2094 $manifest['nodes'][$keyNode] = $node;
2095 }
2096 }
2097 }
2098 if (!empty($designerBlockManifest['style']))
2099 {
2100 $manifest['style']['nodes'] = array_merge(
2101 $designerBlockManifest['style'],
2102 $manifest['style']['nodes']
2103 );
2104 }
2105 }
2106
2107 return $manifest;
2108 }
2109
2117 public function getManifest($extended = false, $missCache = false, array $params = array())
2118 {
2119 static $manifestStore = array();
2120
2121 if (
2122 !$missCache &&
2123 isset($manifestStore[$this->code])
2124 )
2125 {
2126 if (
2127 !isset($manifestStore[$this->code]['disableCache']) ||
2128 $manifestStore[$this->code]['disableCache'] !== true
2129 )
2130 {
2131 return $this->checkDesignedManifest($manifestStore[$this->code]);
2132 }
2133 }
2134
2135 // manifest from market, files, or rest
2136 if ($this->repoId)
2137 {
2138 $manifest = Repo::getBlock($this->repoId);
2139 }
2140 else if ($path = self::getBlockPath($this->code))
2141 {
2142 //isolate variables from .description.php
2143 $includeDesc = function($path)
2144 {
2145 Loc::loadLanguageFile($path . '/.description.php');
2146 $manifest = include $path . '/.description.php';
2147 $manifest['timestamp'] = file_exists($path . '/block.php')
2148 ? filectime($path . '/block.php')
2149 : time();
2150 return $manifest;
2151 };
2152
2153 $manifest = $includeDesc($this->docRoot . $path);
2154 }
2155
2156 // prepare manifest
2157 if (isset($manifest['block']['name']))
2158 {
2159 // prepare by subtype
2160 if (
2161 isset($manifest['block']['subtype'])
2162 && (
2163 !isset($params['miss_subtype'])
2164 || $params['miss_subtype'] !== true
2165 )
2166 )
2167 {
2168 $subtypes = $manifest['block']['subtype'];
2169 if (!is_array($subtypes))
2170 {
2171 $subtypes = [$subtypes];
2172 }
2173
2174 foreach ($subtypes as $subtype)
2175 {
2176 $subtypeClass = '\\Bitrix\\Landing\\Subtype\\';
2177 $subtypeClass .= $subtype;
2178 if (class_exists($subtypeClass))
2179 {
2180 $manifest = $subtypeClass::prepareManifest(
2181 $manifest,
2182 $this,
2183 isset($manifest['block']['subtype_params'])
2184 ? (array)$manifest['block']['subtype_params']
2185 : array()
2186 );
2187 }
2188 }
2189 }
2190 // set empty array if no exists
2191 foreach (['cards', 'nodes', 'attrs', 'menu'] as $code)
2192 {
2193 if (!isset($manifest[$code]) || !is_array($manifest[$code]))
2194 {
2195 $manifest[$code] = array();
2196 }
2197 }
2198 // prepare every node
2199 foreach ($manifest['nodes'] as $keyNode => &$node)
2200 {
2201 if (is_callable($node) && !$this->repoId)
2202 {
2203 $node = $node();
2204 }
2205 $node['code'] = $keyNode;
2206 $class = Node\Type::getClassName($node['type']);
2207 if (isset($node['type']) && class_exists($class))
2208 {
2209 $node['handler'] = call_user_func(array(
2210 $class,
2211 'getHandlerJS'
2212 ));
2213 if (method_exists($class, 'prepareManifest'))
2214 {
2215 $node = call_user_func_array(array(
2216 $class,
2217 'prepareManifest'
2218 ), array(
2219 $this,
2220 $node,
2221 &$manifest
2222 ));
2223 if (!is_array($node))
2224 {
2225 unset($manifest['nodes'][$keyNode]);
2226 }
2227 }
2228 }
2229 else
2230 {
2231 unset($manifest['nodes'][$keyNode]);
2232 }
2233 }
2234 unset($node);
2235 // and attrs
2236 foreach ($manifest['attrs'] as $keyNode => &$node)
2237 {
2238 if (is_callable($node) && !$this->repoId)
2239 {
2240 $node = $node();
2241 }
2242 }
2243 unset($node);
2244 // callbacks
2245 if (isset($manifest['callbacks']) && is_array($manifest['callbacks']))
2246 {
2247 $callbacks = array();
2248 foreach ($manifest['callbacks'] as $code => $callback)
2249 {
2250 $callbacks[mb_strtolower($code)] = $callback;
2251 }
2252 $manifest['callbacks'] = $callbacks;
2253 }
2254 // prepare styles
2255 if (!isset($manifest['namespace']))
2256 {
2257 $manifest['namespace'] = $this->getBlockNamespace($this->code);
2258 }
2259 if (
2260 isset($manifest['style'])
2261 && !(
2262 isset($manifest['style']['block'])
2263 && isset($manifest['style']['nodes'])
2264 && count($manifest['style']) == 2
2265 )
2266 )
2267 {
2268 $manifest['style'] = [
2269 'block' => [],
2270 'nodes' => is_array($manifest['style'])
2271 ? $manifest['style']
2272 : []
2273 ];
2274 }
2275 elseif (
2276 !isset($manifest['style'])
2277 || !is_array($manifest['style'])
2278 )
2279 {
2280 $manifest['style'] = [
2281 'block' => [],
2282 'nodes' => []
2283 ];
2284 }
2285
2286 // default block types
2287 if (
2288 !is_array($manifest['style']['block'])
2289 || empty($manifest['style']['block'])
2290 )
2291 {
2292 $manifest['style']['block'] = ['type' => self::DEFAULT_WRAPPER_STYLE];
2293 }
2294
2295 // fake nodes for images from style
2296 $styleNodes = [];
2297 foreach ($manifest['style']['nodes'] as $selector => $styleNode)
2298 {
2299 if (!isset($manifest['nodes'][$selector]) && isset($styleNode['type']) && is_array($styleNode))
2300 {
2301 $styleNodes[$selector] = is_array($styleNode['type']) ? $styleNode['type'] : [$styleNode['type']];
2302 }
2303 }
2304 $styleNodes['#wrapper'] = is_array($manifest['style']['block']['type'])
2305 ? $manifest['style']['block']['type']
2306 : [$manifest['style']['block']['type']];
2307
2308 foreach ($styleNodes as $selector => $type)
2309 {
2310 if (!empty(array_intersect($type, Node\StyleImg::STYLES_WITH_IMAGE)))
2311 {
2312 $manifest['nodes'][$selector] = [
2313 'type' => Node\Type::STYLE_IMAGE,
2314 'code' => $selector,
2315 ];
2316 }
2317 }
2318
2319 // other
2320 $manifest['code'] = $this->code;
2321 }
2322 else
2323 {
2324 $manifest = array();
2325 }
2326
2327 $manifest['preview'] = $this->getPreview();
2328
2329 if (!$missCache)
2330 {
2331 $manifestStore[$this->code] = $manifest;
2332 }
2333
2334 // localization
2335 if (
2336 isset($manifest['lang']) &&
2337 isset($manifest['lang_original']) &&
2338 is_array($manifest['lang'])
2339 )
2340 {
2341 // detect translated messages
2342 $lang = null;
2343 $langPortal = LANGUAGE_ID;
2344 if (in_array($langPortal, ['ru', 'kz', 'by']))
2345 {
2346 $langPortal = 'ru';
2347 }
2348 $langArray = $manifest['lang'];
2349 $langOrig = $manifest['lang_original'];
2350 if (isset($langArray[$langPortal]))
2351 {
2352 $lang = $langArray[$langPortal];
2353 }
2354 else if (
2355 $langOrig != $langPortal &&
2356 isset($langArray['en'])
2357 )
2358 {
2359 $lang = $langArray['en'];
2360 }
2361 // replace all 'name' keys in manifest
2362 if ($lang)
2363 {
2364 $this->localizationManifest(
2365 $manifest,
2366 $lang
2367 );
2368 }
2369 unset($manifest['lang']);
2370 }
2371
2372 return $this->checkDesignedManifest($manifest);
2373 }
2374
2381 protected function localizationManifest(array &$manifest, array $lang)
2382 {
2383 foreach ($manifest as $key => &$value)
2384 {
2385 if (is_array($value))
2386 {
2387 $this->localizationManifest($value, $lang);
2388 }
2389 if (
2390 $key == 'name' &&
2391 isset($lang[$value])
2392 )
2393 {
2394 $value = $lang[$value];
2395 }
2396 }
2397 }
2398
2404 public static function getManifestFile($code)
2405 {
2406 static $manifests = array();
2407
2408 if (!is_string($code))
2409 {
2410 return [];
2411 }
2412
2413 if (preg_match('/[^a-z0-9_.:-]+/i', $code))
2414 {
2415 return [];
2416 }
2417
2418 if (isset($manifests[$code]))
2419 {
2420 return $manifests[$code];
2421 }
2422
2423 $manifests[$code] = array();
2424 $namespace = null;
2425
2426 if (mb_strpos($code, ':') !== false)
2427 {
2428 [$namespace, $code] = explode(':', $code);
2429 }
2430
2431 if ($path = self::getBlockPath($code ,$namespace))
2432 {
2434 Loc::loadLanguageFile($docRoot . $path . '/.description.php');
2435 $manifests[$code] = include $docRoot . $path . '/.description.php';
2436 }
2437
2438 return $manifests[$code];
2439 }
2440
2446 public function getAsset($type = null)
2447 {
2448 static $asset = array();
2449
2450 if (!is_string($type))
2451 {
2452 return [];
2453 }
2454
2455 if (!isset($asset[$this->code]))
2456 {
2457 $asset[$this->code] = array(
2458 'css' => array(),
2459 'js' => array(),
2460 'ext' => array(),
2461 'class' => array()
2462 );
2463
2464 // additional asset first
2465 if ($this->repoId)
2466 {
2467 $manifest = Repo::getBlock($this->repoId);
2468 }
2469 else if ($path = self::getBlockPath($this->code))
2470 {
2471 $manifest = include $this->docRoot . $path . '/.description.php';
2472 $manifest['timestamp'] = file_exists($this->docRoot . $path . '/.description.php')
2473 ? filectime($this->docRoot . $path . '/.description.php')
2474 : time();
2475 }
2476
2477 if (isset($manifest['block']['namespace']))
2478 {
2479 $classFile = self::BLOCKS_DIR;
2480 $classFile .= '/' . $manifest['block']['namespace'] . '/';
2481 $classFile .= $this->code . '/class.php';
2482 $classFile = \getLocalPath($classFile);
2483 if ($classFile)
2484 {
2485 $asset[$this->code]['class'][] = $this->docRoot . $classFile;
2486 }
2487 }
2488
2489 // prepare by subtype
2490 if (
2491 isset($manifest['block']['subtype']) &&
2492 (
2493 !isset($params['miss_subtype']) ||
2494 $params['miss_subtype'] !== true
2495 )
2496 )
2497 {
2498 $subtypes = $manifest['block']['subtype'];
2499 if (!is_array($subtypes))
2500 {
2501 $subtypes = [$subtypes];
2502 }
2503
2504 foreach ($subtypes as $subtype)
2505 {
2506 $subtypeClass = '\\Bitrix\\Landing\\Subtype\\';
2507 $subtypeClass .= $subtype;
2508 if (class_exists($subtypeClass))
2509 {
2510 $manifest = $subtypeClass::prepareManifest(
2511 $manifest,
2512 $this,
2513 isset($manifest['block']['subtype_params'])
2514 ? (array)$manifest['block']['subtype_params']
2515 : array()
2516 );
2517 }
2518 }
2519 }
2520
2521 foreach (array_keys($asset[$this->code]) as $ass)
2522 {
2523 if (
2524 isset($manifest['assets'][$ass]) &&
2525 !empty($manifest['assets'][$ass])
2526 )
2527 {
2528 foreach ($manifest['assets'][$ass] as $file)
2529 {
2530 if (!is_string($file))
2531 {
2532 continue;
2533 }
2534 if ($ass != 'ext')
2535 {
2536 $asset[$this->code][$ass][] = trim($file);
2537 }
2538 // for rest block allowed only this
2539 else if (
2540 !$this->repoId ||
2541 in_array($file, $this->allowedExtensions)
2542 )
2543 {
2544 $asset[$this->code][$ass][] = trim($file);
2545 }
2546 }
2547 $asset[$this->code][$ass] = array_unique($asset[$this->code][$ass]);
2548 }
2549 }
2550
2551 // next is phis files
2552 if (isset($path) && $path)
2553 {
2554 // base files next
2555 $file = $path . '/' . ($this->metaData['DESIGNER_MODE'] ? 'design_' : '') . self::CSS_FILE_NAME;
2556 if (file_exists($this->docRoot . $file))
2557 {
2558 $asset[$this->code]['css'][] = $file;
2559 }
2560 $file = $path . '/' . self::JS_FILE_NAME;
2561 if (file_exists($this->docRoot . $file))
2562 {
2563 $asset[$this->code]['js'][] = $file;
2564 }
2565 }
2566 }
2567
2568 $designerBlockManifest = $this->parseManifest();
2569 if (!empty($designerBlockManifest['assets']))
2570 {
2571 foreach ($designerBlockManifest['assets'] as $key => $assets)
2572 {
2573 $asset[$this->code][$key] = array_merge($asset[$this->code][$key], $assets);
2574 $asset[$this->code][$key] = array_unique($asset[$this->code][$key]);
2575 }
2576 }
2577
2578 return $asset[$this->code][$type] ?? $asset[$this->code];
2579 }
2580
2585 public function getCSS()
2586 {
2587 return $this->getAsset('css');
2588 }
2589
2594 public function getJS()
2595 {
2596 return $this->getAsset('js');
2597 }
2598
2603 public function getExt()
2604 {
2605 return $this->getAsset('ext');
2606 }
2607
2612 public function getClass()
2613 {
2614 return $this->getAsset('class');
2615 }
2616
2622 protected function includeBlockClass($path)
2623 {
2624 static $classes = [];
2625 static $calledClasses = [];
2626
2627 if (!isset($classes[$path]))
2628 {
2629 // include class
2630 $beforeClasses = get_declared_classes();
2631 $beforeClassesCount = count($beforeClasses);
2632 include_once($path);
2633 $afterClasses = get_declared_classes();
2634 $afterClassesCount = count($afterClasses);
2635
2636 // ... and detect class name
2637 for ($i = $beforeClassesCount; $i < $afterClassesCount; $i++)
2638 {
2639 if (is_subclass_of($afterClasses[$i], '\\Bitrix\\Landing\\LandingBlock'))
2640 {
2641 $classes[$path] = $afterClasses[$i];
2642 }
2643 }
2644 }
2645
2646 $landingId = $this->getLandingId();
2647 $landingPath = $path . '@' . $landingId;
2648
2649 // call init method
2650 if (!isset($calledClasses[$landingPath]))
2651 {
2652 $calledClasses[$landingPath] = new $classes[$path];
2653 $calledClasses[$landingPath]->init([
2654 'site_id' => $this->getSiteId(),
2655 'landing_id' => $this->getLandingId()
2656 ]);
2657 }
2658
2659 return $calledClasses[$landingPath];
2660 }
2661
2668 protected static function getMessageBlock($params, $template = '')
2669 {
2670 ob_start();
2671 Manager::getApplication()->includeComponent(
2672 'bitrix:landing.blocks.message',
2673 $template,
2674 $params,
2675 false
2676 );
2677 $blockMesage = ob_get_contents();
2678 ob_end_clean();
2679
2680 return $blockMesage;
2681 }
2682
2690 public function view($edit = false, \Bitrix\Landing\Landing $landing = null, array $params = array())
2691 {
2692 global $APPLICATION;
2693
2694 if ($this->dynamicParams)
2695 {
2696 $this->setDynamic($edit);
2697 }
2698
2699 if (!isset($params['wrapper_show']))
2700 {
2701 $params['wrapper_show'] = true;
2702 }
2703 if (!isset($params['force_unactive']))
2704 {
2705 $params['force_unactive'] = false;
2706 }
2707 if (!isset($params['skip_system_script']))
2708 {
2709 $params['skip_system_script'] = false;
2710 }
2711 if (
2712 !$edit &&
2713 $params['wrapper_show'] &&
2714 !Config::get('public_wrapper_block')
2715 )
2716 {
2717 $params['wrapper_show'] = false;
2718 }
2719
2720 if ($this->deleted)
2721 {
2722 return;
2723 }
2724
2725 if ($edit || $this->active || $params['force_unactive'])
2726 {
2727 $assets = Assets\Manager::getInstance();
2728 if ($css = $this->getCSS())
2729 {
2730 $assets->addAsset($css, Assets\Location::LOCATION_TEMPLATE);
2731 }
2732 if ($ext = $this->getExt())
2733 {
2734 $assets->addAsset($ext, Assets\Location::LOCATION_TEMPLATE);
2735 }
2736 if (!$edit || !$this->repoId)
2737 {
2738 if ($js = $this->getJS())
2739 {
2740 $assets->addAsset($js, Assets\Location::LOCATION_AFTER_TEMPLATE);
2741 }
2742 }
2743 // calling class(es) of block
2744 foreach ($this->getClass() as $class)
2745 {
2746 $classBlock = $this->includeBlockClass($class);
2747 if ($classBlock->beforeView($this) === false)
2748 {
2749 return;
2750 }
2751 }
2752 }
2753
2754 // get manifest
2755 if ($edit && !$params['skip_system_script'])
2756 {
2757 $manifest = $this->getManifest();
2758 }
2759
2760 // develop mode - rebuild and reset content
2761 if (
2762 $this->id > 0 &&
2763 !$params['skip_system_script'] &&
2764 defined('LANDING_DEVELOPER_MODE') &&
2765 LANDING_DEVELOPER_MODE === true
2766 )
2767 {
2768 if (!isset($manifest))
2769 {
2770 $manifest = $this->getManifest();
2771 }
2772 if (isset($this->metaData['DATE_MODIFY']))
2773 {
2774 $modifyTime = $this->metaData['DATE_MODIFY']->getTimeStamp();
2775 }
2776 else
2777 {
2778 $modifyTime = 0;
2779 }
2780 if ($modifyTime < $manifest['timestamp'])
2781 {
2782 $count = 0;
2783 $limit = 1;
2784 Update\Block::executeStep([
2785 'ID' => $this->id
2786 ], $count, $limit, $paramsUpdater = []);
2787 $this->resetContent();
2788 $this->content = $this->getContent();
2789 }
2790 }
2791
2792 if (!\Bitrix\Main\ModuleManager::isModuleInstalled('bitrix24'))
2793 {
2794 if (mb_strpos($this->content, '/upload/') !== false)
2795 {
2796 $this->content = preg_replace(
2797 '#"//[^\'^"]+/upload/#',
2798 '"/upload/',
2799 $this->content
2800 );
2801 }
2802 if (Manager::getOption('badpicture2x') == 'Y')
2803 {
2804 if (mb_strpos($this->content, 'srcset="') !== false)
2805 {
2806 $this->content = str_replace(
2807 'srcset="',
2808 'data-srcset-bad="',
2809 $this->content
2810 );
2811 }
2812 if (mb_strpos($this->content, '2x)') !== false)
2813 {
2814 $this->content = preg_replace(
2815 "#(, url\‍('[^'^\"]+'\‍) 2x)#",
2816 '',
2817 $this->content
2818 );
2819 }
2820 }
2821 }
2822
2823 // show or not a wrapper of block
2824 if ($params['wrapper_show'])
2825 {
2826 if ($this->id > 0)
2827 {
2828 if ($edit)
2829 {
2830 $anchor = $this->getAnchor($this->id);
2831 }
2832 else
2833 {
2834 $anchor = $this->anchor
2835 ? \htmlspecialcharsbx($this->anchor)
2836 : $this->getAnchor($this->id);
2837 }
2838 }
2839 else
2840 {
2841 $anchor = 'block' . rand(10000, 100000);
2842 }
2843 $classFromCode = 'block-' . $this->code;
2844 $classFromCode = preg_replace('/([^a-z0-9-])/i', '-', $classFromCode);
2845 $classFromCode = ' ' . $classFromCode;
2846 $content = '';
2847 if ($landing && $landing->getPreviewMode())
2848 {
2849 $content .= "<a id=\"editor{$this->getId()}\"></a>";
2850 }
2851 $content .= '<div id="' . $anchor . '" ' .
2852 ($edit ? 'data-id="' . $this->id . '" ' : '') .
2853 (($edit && isset($manifest['block']['subtype'])) ? 'data-subtype="' . $manifest['block']['subtype'] . '" ' : '') .
2854 'class="block-wrapper' .
2855 (!$this->active ? ' landing-block-deactive' : '') .
2856 ($this->metaData['DESIGNER_MODE'] ? ' landing-designer-block-mode' : '') .
2857 $classFromCode .
2858 '">' .
2859 $this->content .
2860 '</div>';
2861 }
2862 else
2863 {
2864 $content = $this->content;
2865 }
2866
2867 // replace in requisite page, marker to company title
2868 if (mb_strpos($this->content, '#requisiteCompanyTitle') !== false)
2869 {
2870 $replace = Connector\Crm::getReplaceRequisiteCompanyNameForContent($landing->getXmlId());
2871 $content = str_replace(
2872 array_keys($replace),
2873 array_values($replace),
2874 $content
2875 );
2876 }
2877
2878 // @tmp bug with setInnerHTML save result
2879 $content = preg_replace('/&amp;([^\s]{1})/is', '&$1', $content);
2880 if ($edit)
2881 {
2882 if ($manifest ?? null)
2883 {
2884 if (
2885 !isset($manifest['requiredUserAction']) &&
2886 $this->runtimeRequiredUserAction
2887 )
2888 {
2889 $manifest['requiredUserAction'] = $this->runtimeRequiredUserAction;
2890 }
2891 $sections = (array)($manifest['block']['section'] ?? null);
2892 $designerRepository = $this->metaData['DESIGNER_MODE'] ? \Bitrix\Landing\Block\Designer::getRepository() : [];
2893 $anchor = $this->anchor;
2894 if (!$anchor)
2895 {
2896 $anchor = $this->parentId
2897 ? 'block' . $this->parentId
2898 : 'b' . $this->id;
2899 }
2900 $autoPublicationEnabled = Site\Type::isPublicScope() && \CUserOptions::getOption('landing', 'auto_publication', 'Y') === 'Y';
2901 echo '<script type="text/javascript">'
2902 . 'BX.ready(function(){'
2903 . 'if (typeof BX.Landing.Block !== "undefined")'
2904 . '{'
2905 . 'new BX.Landing.' . ($this->metaData['DESIGNER_MODE'] ? 'DesignerBlock' : 'Block') . '('
2906 . 'BX("block' . $this->id . '"), '
2907 . '{'
2908 . 'id: ' . $this->id . ', '
2909 . 'lid: ' . $this->lid . ', '
2910 . 'code: "' . $this->code . '", '
2911 . 'sections: "' . implode(',', $sections) . '", '
2912 . 'repoId: ' . ($this->repoId ? (int)$this->repoId : "null") . ', '
2913 . 'php: ' . (mb_strpos($content, '<?') !== false ? 'true' : 'false') . ', '
2914 . 'designed: ' . ($this->designed ? 'true' : 'false') . ', '
2915 . 'active: ' . ($this->active ? 'true' : 'false') . ', '
2916 . 'allowedByTariff: ' . ($this->allowedByTariff ? 'true' : 'false') . ', '
2917 . 'autoPublicationEnabled: ' . ($autoPublicationEnabled ? 'true' : 'false') . ', '
2918 . 'anchor: ' . '"' . \CUtil::jsEscape($anchor) . '"' . ', '
2919 . 'access: ' . '"' . $this->access . '"' . ', '
2920 . 'dynamicParams: ' . Json::encode($this->dynamicParams) . ','
2921 . ($this->metaData['DESIGNER_MODE'] ? 'repository: ' . Json::encode($designerRepository) . ',' : '')
2922 . 'manifest: ' . Json::encode($manifest)
2923 . (
2924 isset($manifest['requiredUserAction'])
2925 ? ', requiredUserAction: ' . Json::encode($manifest['requiredUserAction'])
2926 : ''
2927 )
2928 . '}'
2929 . ');'
2930 . '}'
2931 . '});'
2932 . '</script>';
2933 }
2934 $content = $this::replaceMetaMarkers($content);
2935
2936 $event = new \Bitrix\Main\Event('landing', 'onBlockEditView', [
2937 'block' => $this,
2938 'outputContent' => $content
2939 ]);
2940 $event->send();
2941 foreach ($event->getResults() as $result)
2942 {
2943 $content = $result->getParameters();
2944 }
2945
2946 if ($this->repoId)
2947 {
2948 echo $content;
2949 }
2950 else
2951 {
2952 try
2953 {
2954 eval('?>' . $content . '<?');
2955 }
2956 catch (\ParseError $e)
2957 {
2958 $errMessage = $this::getMessageBlock([
2959 'MESSAGE' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_EVAL')
2960 ]);
2961 if ($params['wrapper_show'])
2962 {
2963 echo '<div id="' . $anchor . '" class="block-wrapper' .
2964 (!$this->active ? ' landing-block-deactive' : '') . '">' .
2965 $errMessage .
2966 '</div>';
2967 }
2968 else
2969 {
2970 echo $errMessage;
2971 }
2972 }
2973 }
2974 }
2975 elseif ($this->active || $params['force_unactive'])
2976 {
2977 // @todo make better
2978 static $sysPagesSites = [];
2979
2980 if (!array_key_exists($this->siteId, $sysPagesSites))
2981 {
2982 $sysPages = array();
2983 foreach (Syspage::get($this->siteId) as $syspage)
2984 {
2985 $sysPages['@#system_' . $syspage['TYPE'] . '@'] = $syspage['LANDING_ID'];
2986 }
2987 // for main page we should get current site main page
2988 if (!isset($sysPages['@#system_mainpage@']))
2989 {
2990 $currentSite = $this->getSite();
2991 if ($currentSite['LANDING_ID_INDEX'])
2992 {
2993 $sysPages['@#system_mainpage@'] = $currentSite['LANDING_ID_INDEX'];
2994 }
2995 }
2996 if (!empty($sysPages))
2997 {
2998 $urls = $landing->getPublicUrl($sysPages);
2999 foreach ($sysPages as $code => $lid)
3000 {
3001 if (isset($urls[$lid]))
3002 {
3003 $sysPages[$code] = \htmlspecialcharsbx($urls[$lid]);
3004 }
3005 else
3006 {
3007 unset($sysPages[$code]);
3008 }
3009 }
3010 }
3011
3012 $sysPagesSites[$this->siteId] = $sysPages;
3013 }
3014
3015 $sysPages = $sysPagesSites[$this->siteId];
3016
3017 if (!Connector\Mobile::isMobileHit())
3018 {
3019 $sysPages['@' . Connector\Disk::FILE_MASK_HREF . '@i'] = str_replace(
3020 '#fileId#', '$2',
3021 Controller\DiskFile::getDownloadLink($this->metaData['SITE_TYPE'], $this->id)
3022 );
3023 }
3024
3025 if (!empty($sysPages))
3026 {
3027 $content = preg_replace(
3028 array_keys($sysPages),
3029 array_values($sysPages),
3030 $content
3031 );
3032 }
3033
3034 $event = new \Bitrix\Main\Event('landing', 'onBlockPublicView', [
3035 'block' => $this,
3036 'outputContent' => $content
3037 ]);
3038 $event->send();
3039 foreach ($event->getResults() as $result)
3040 {
3041 $content = $result->getParameters();
3042 }
3043
3044 if ($this->repoId)
3045 {
3046 echo $content;
3047 }
3048 else
3049 {
3050 try
3051 {
3052 eval('?>' . $content . '<?');
3053 }
3054 catch (\ParseError $e)
3055 {
3056 }
3057 }
3058 }
3059 Assets\PreProcessing::blockViewProcessing($this, $edit);
3060 }
3061
3067 public function saveAssets(array $assets): void
3068 {
3069 if ($this->access < $this::ACCESS_W)
3070 {
3071 $this->error->addError(
3072 'ACCESS_DENIED',
3073 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3074 );
3075 return;
3076 }
3077
3078 foreach (['font', 'icon', 'ext'] as $assetCode)
3079 {
3080 if (isset($this->assets[$assetCode]) && !isset($assets[$assetCode]))
3081 {
3082 $assets[$assetCode] = $this->assets[$assetCode];
3083 }
3084 if (isset($assets[$assetCode]) && !$assets[$assetCode])
3085 {
3086 unset($assets[$assetCode]);
3087 }
3088 }
3089
3090 $this->assets = $assets;
3091 }
3092
3097 public function getAssets(): array
3098 {
3099 return $this->assets;
3100 }
3101
3108 public function saveContent(string $content, $designed = false): void
3109 {
3110 if (!is_string($content))
3111 {
3112 return;
3113 }
3114
3115 if ($this->access < $this::ACCESS_W)
3116 {
3117 $this->error->addError(
3118 'ACCESS_DENIED',
3119 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3120 );
3121 return;
3122 }
3123 if ($designed)
3124 {
3125 $this->designed = true;
3126 }
3127 $this->content = trim($content);
3128 $this->getDom(true);
3129 }
3130
3136 public function save(array $additionalFields = [])
3137 {
3138 if ($this->access == $this::ACCESS_A)
3139 {
3140 $this->error->addError(
3141 'ACCESS_DENIED',
3142 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3143 );
3144 return false;
3145 }
3146
3147 $data = array(
3148 'SORT' => $this->sort,
3149 'ACTIVE' => $this->active ? 'Y' : 'N',
3150 'ANCHOR' => $this->anchor,
3151 'DELETED' => $this->deleted ? 'Y' : 'N',
3152 'DESIGNED' => $this->designed ? 'Y' : 'N',
3153 'ASSETS' => $this->assets ? $this->assets : null
3154 );
3155 if ($additionalFields)
3156 {
3157 $data = array_merge($data, $additionalFields);
3158 }
3159 if ($this->content)
3160 {
3161 $data['CONTENT'] = $this->content;
3162 $data['SEARCH_CONTENT'] = $this->getSearchContent();
3163 }
3164 Cache::clear($this->id);
3165 $res = parent::update($this->id, $data);
3166 $this->error->addFromResult($res);
3167 return $res->isSuccess();
3168 }
3169
3176 public function changeLanding($lid)
3177 {
3178 if ($this->access < $this::ACCESS_W)
3179 {
3180 $this->error->addError(
3181 'ACCESS_DENIED',
3182 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3183 );
3184 return false;
3185 }
3186 $res = parent::update($this->id, array(
3187 'LID' => (int)$lid,
3188 'PARENT_ID' => null,
3189 'PUBLIC' => 'N'
3190 ));
3191 $this->error->addFromResult($res);
3192 return $res->isSuccess();
3193 }
3194
3200 public function changeFavoriteMeta(array $meta): bool
3201 {
3202 $res = parent::update($this->id, [
3203 'TPL_CODE' => $meta['tpl_code'] ?? null,
3204 'FAVORITE_META' => $meta
3205 ]);
3206 $this->error->addFromResult($res);
3207 return $res->isSuccess();
3208 }
3209
3214 public function unlink()
3215 {
3216 if ($this->access < $this::ACCESS_X)
3217 {
3218 $this->error->addError(
3219 'ACCESS_DENIED',
3220 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3221 );
3222 return false;
3223 }
3224
3225 $manifest = $this->getManifest();
3226 $res = self::parentDelete($this->id);
3227 if (!$res->isSuccess())
3228 {
3229 $this->error->addFromResult($res);
3230 }
3231 return $res->isSuccess();
3232 }
3233
3239 public function markDeleted($mark)
3240 {
3241 if ($this->access < $this::ACCESS_X)
3242 {
3243 $this->error->addError(
3244 'ACCESS_DENIED',
3245 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3246 );
3247 return;
3248 }
3249 $this->deleted = (boolean) $mark;
3250 }
3251
3257 public function setSort($sort)
3258 {
3259 $this->sort = $sort;
3260 }
3261
3267 public function setAnchor($anchor)
3268 {
3269 if (!is_string($anchor))
3270 {
3271 return false;
3272 }
3273 $anchor = trim($anchor);
3274 $check = !$anchor || preg_match_all('/^[a-z]{1}[a-z0-9\-\_\.\:]+$/i', $anchor);
3275 if (!$check)
3276 {
3277 $this->error->addError(
3278 'BAD_ANCHOR',
3279 Loc::getMessage('LANDING_BLOCK_BAD_ANCHOR')
3280 );
3281 return false;
3282 }
3283
3284 if (History::isActive())
3285 {
3286 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
3287 $history->push('CHANGE_ANCHOR', [
3288 'block' => $this,
3289 'valueBefore' => $this->anchor,
3290 'valueAfter' => $anchor,
3291 ]);
3292 }
3293
3294 $this->anchor = $anchor;
3295 return true;
3296 }
3297
3303 public function saveSort($sort)
3304 {
3305 if ($this->access < $this::ACCESS_W)
3306 {
3307 $this->error->addError(
3308 'ACCESS_DENIED',
3309 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3310 );
3311 return;
3312 }
3313 $sort = intval($sort);
3314 $this->sort = $sort;
3315 Internals\BlockTable::update($this->id, array(
3316 'SORT' => $sort
3317 ));
3318 }
3319
3324 public function getSort()
3325 {
3326 return $this->sort;
3327 }
3328
3334 public function getDynamicParams($id = null)
3335 {
3336 $params = [];
3337
3338 if ($id !== null)
3339 {
3340 $id = intval($id);
3341 $res = parent::getList([
3342 'select' => [
3343 'SOURCE_PARAMS'
3344 ],
3345 'filter' => [
3346 'ID' => $id
3347 ]
3348 ]);
3349 if ($row = $res->fetch())
3350 {
3351 $params = $row['SOURCE_PARAMS'];
3352 }
3353 unset($row, $res);
3354 }
3355 else
3356 {
3357 $params = $this->dynamicParams;
3358 }
3359
3360 return $params;
3361 }
3362
3368 private function dynamicLinkReplacer(array $data, array $replace)
3369 {
3370 foreach ($data as $key => $value)
3371 {
3372 if (is_array($value))
3373 {
3374 $data[$key] = $this->dynamicLinkReplacer($value, $replace);
3375 }
3376 else
3377 {
3378 $data[$key] = str_replace(
3379 array_keys($replace),
3380 array_values($replace),
3381 $data[$key]
3382 );
3383 }
3384 }
3385 unset($key, $value);
3386
3387 return $data;
3388 }
3389
3396 public function saveDynamicParams(array $sourceParams = [], array $params = [])
3397 {
3398 if ($this->access < $this::ACCESS_W)
3399 {
3400 $this->error->addError(
3401 'ACCESS_DENIED',
3402 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3403 );
3404 return;
3405 }
3406 // replace old link to new in dynamic manifest
3407 if (
3408 isset($params['linkReplace']) &&
3409 is_array($params['linkReplace'])
3410 )
3411 {
3412 $sourceParams = $this->dynamicLinkReplacer(
3413 $sourceParams,
3414 $params['linkReplace']
3415 );
3416 }
3417 // save
3418 $paramsBefore = $this->dynamicParams;
3419 $this->dynamicParams = $sourceParams;
3420 $resUpdate = Internals\BlockTable::update($this->id, [
3421 'SOURCE_PARAMS' => $sourceParams
3422 ]);
3423
3424 if ($resUpdate->isSuccess())
3425 {
3426 if (History::isActive())
3427 {
3428 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
3429 $history->push('UPDATE_DYNAMIC', [
3430 'block' => $this,
3431 'valueBefore' => $paramsBefore,
3432 'valueAfter' => $sourceParams,
3433 ]);
3434 }
3435 }
3436
3437 unset($sourceParams, $params);
3438 }
3439
3445 protected function setDynamic($edit)
3446 {
3447 static $sourceList = null;
3448 static $isDetailDynamic = null;
3449 static $dynamicElementId = null;
3450 static $dynamicFilter = null;
3451
3452 $data = $this->dynamicParams;
3453 $caching = false;
3454 $cache = null;
3455
3456 // check if is true dynamic
3457 if (!$this->active || !$this->content)
3458 {
3459 return;
3460 }
3461 if (!is_array($data) || empty($data))
3462 {
3463 return;
3464 }
3465
3466 // check feature
3467 $availableFeature = Restriction\Manager::isAllowed(
3468 'limit_sites_dynamic_blocks',
3469 ['targetBlockId' => $this->id]
3470 );
3471 if (!$availableFeature)
3472 {
3473 $hackContent = preg_replace(
3474 '/^<([a-z]+)\s/',
3475 '<$1 style="display: none;" ',
3476 $this->content
3477 );
3478
3479 $this->saveContent(
3480 $hackContent .
3481 $this::getMessageBlock([
3482 'HEADER' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_DYNAMIC_LIMIT_TITLE'),
3483 'MESSAGE' => Restriction\Manager::getSystemErrorMessage('limit_sites_dynamic_blocks'),
3484 'BUTTON' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_LIMIT_BUTTON'),
3486 ], 'locked')
3487 );
3488 return;
3489 }
3490
3491 // if is detail page
3492 if ($isDetailDynamic === null)
3493 {
3494 $isDetailDynamic = Landing::isDynamicDetailPage();
3495 }
3496 if ($dynamicElementId === null)
3497 {
3498 $dynamicElementId = Landing::getDynamicElementId();
3499 }
3500 if ($dynamicFilter === null)
3501 {
3502 $dynamicFilter = Landing::getDynamicFilter();
3503 }
3504
3505 if (!$edit && Cache::isCaching())
3506 {
3507 $cache = new \CPHPCache();
3508 $cacheTime = 3600;
3509 $cacheId = 'block_' . $this->id . '_' . $dynamicElementId . '_';
3510 $cacheId .= md5(serialize($dynamicFilter));
3511 $cachePath = '/landing/dynamic/' . $this->id;
3512 if ($cache->initCache($cacheTime, $cacheId, $cachePath))
3513 {
3514 $result = $cache->getVars();
3515 if ($result['title'])
3516 {
3517 Manager::setPageTitle($result['title'], true);
3518 Landing\Seo::changeValue('title', $result['title']);
3519 }
3520 $rememberAccess = $this->access;
3521 $this->access = $this::ACCESS_W;
3522 $this->saveContent($result['content']);
3523 $this->access = $rememberAccess;
3524 header('X-Bitrix24-Page: dynamic');
3525 return;
3526 }
3527 else
3528 {
3529 $caching = true;
3530 $cache->startDataCache($cacheTime, $cacheId, $cachePath);
3531 Manager::getCacheManager()->startTagCache($cachePath);
3532 Cache::register($this->id);
3533 }
3534 }
3535
3536 $updated = false;
3537 // @todo: remove after refactoring
3538 $manifest = $this->getManifest();
3539
3540 // build sources list
3541 if ($sourceList === null)
3542 {
3543 $sourceList = new Source\Selector();
3544 }
3545
3546 // @todo: remove after refactoring
3547 $getDetailPage = function(array $detailPage, $filterId = 0, $elemId = 0)
3548 {
3549 $filterId = intval($filterId);
3550 $elemId = intval($elemId);
3551 $query = [];
3552
3553 if (isset($detailPage['query']))
3554 {
3555 $query = (array) $detailPage['query'];
3556 unset($detailPage['query']);
3557 }
3558
3559 // normalize the array
3560 $detailPage = array_merge(
3561 array_fill_keys(['text', 'href', 'target'], ''),
3562 $detailPage
3563 );
3564 foreach ($detailPage as $key => &$detailPageItem)
3565 {
3566 if (!is_array($detailPageItem))
3567 {
3568 $detailPageItem = trim($detailPageItem);
3569 }
3570 if (empty($detailPageItem))
3571 {
3572 unset($detailPage[$key]);
3573 }
3574 }
3575 unset($detailPageItem);
3576
3577 if ($filterId && $elemId && $detailPage['href'])
3578 {
3579 $detailPage['href'] = str_replace(
3580 '#landing',
3581 '#dynamic',
3582 $detailPage['href']
3583 );
3584 $detailPage['href'] .= '_' . $filterId;
3585 $detailPage['href'] .= '_' . $elemId;
3586 }
3587 else if ($filterId && $elemId)
3588 {
3589 $detailPage['href'] = '#';
3590 }
3591
3592 if ($detailPage['href'] && $query)
3593 {
3594 $detailPage['query'] = http_build_query($query);
3595 }
3596
3597 return $detailPage;
3598 };
3599
3600 // apply for each selector dynamic data from source
3601 $disableUpdate = false;
3602 $pageTitle = '';
3603 foreach ($data as $cardSelector => $item)
3604 {
3605 $update = [];
3606 $itemDetail = $cardSelector == 'wrapper';
3607 if (
3608 !isset($item['source']) ||
3609 !isset($item['settings']) ||
3610 !isset($item['references'])
3611 )
3612 {
3613 continue;
3614 }
3615 // build start params
3616 $sourceId = $item['source'];
3617 $settings = $item['settings'];
3618 $references = (array)$item['references'];
3619 $filterId = isset($item['filterId'])
3620 ? intval($item['filterId'])
3621 : 0;
3622 $detailPage = isset($settings['detailPage'])
3623 ? (array)$settings['detailPage']
3624 : [];
3625 $pagesCount = (
3626 isset($settings['pagesCount']) &&
3627 $settings['pagesCount'] > 0
3628 )
3629 ? (int)$settings['pagesCount']
3630 : 10;
3631 $filter = isset($settings['source']['filter'])
3632 ? (array)$settings['source']['filter']
3633 : [];
3634 $order = isset($settings['source']['sort'])
3635 ? (array)$settings['source']['sort']
3636 : [];
3637 $additional = isset($settings['source']['additional'])
3638 ? (array)$settings['source']['additional']
3639 : [];
3640 $stubs = isset($item['stubs'])
3641 ? (array)$item['stubs']
3642 : [];
3643 // load external filter, if we on detail
3644 if (
3645 $isDetailDynamic && $itemDetail &&
3646 $dynamicFilter['SOURCE_ID'] == $sourceId
3647 )
3648 {
3649 $filter = $dynamicFilter['FILTER'];
3650 }
3651 $sourceParameters = [
3652 'select' => array_values($references),
3653 'filter' => $filter,
3654 'order' => $order,
3655 'limit' => $pagesCount,
3656 'additional' => $additional
3657 ];
3658 // gets list or singleton data
3659 $sourceData = [];
3660 $source = $sourceList->getDataLoader(
3661 $sourceId,
3662 $sourceParameters,
3663 [
3664 'context_filter' => [
3665 'SITE_ID' => $this->siteId,
3666 'LANDING_ID' => $this->lid,
3667 'LANDING_ACTIVE' => $this->landingActive ? 'Y' : ['Y', 'N']
3668 ],
3669 'cache' => $cache,
3670 'block' => $this
3671 ]
3672 );
3673 if (is_object($source))
3674 {
3675 // detail page
3676 if ($isDetailDynamic && $itemDetail)
3677 {
3678 $sourceData = $source->getElementData($dynamicElementId);
3679 if (!$sourceData)
3680 {
3681 $disableUpdate = true;
3682 continue;
3683 }
3684 $pageTitle = $source->getSeoTitle();
3685 Manager::setPageTitle($pageTitle, true);
3686 Landing\Seo::changeValue('title', $pageTitle);
3687 }
3688 // element list
3689 else
3690 {
3691 $sourceData = $source->getElementListData();
3692 $pagesCount = max(1, count($sourceData));
3693 }
3694 }
3695 // apply getting data in block
3696 if (!empty($sourceData) && is_array($sourceData))
3697 {
3698 // collect array for update html
3699 foreach ($references as $selector => $field)
3700 {
3701 if (empty($field) || !is_array($field))
3702 {
3703 continue;
3704 }
3705 if (empty($field['id']))
3706 {
3707 continue;
3708 }
3709 if (mb_strpos($selector, '@') !== false)
3710 {
3711 [$selector,] = explode('@', $selector);
3712 }
3713 if (!isset($update[$selector]))
3714 {
3715 $update[$selector] = [];
3716 }
3717 $fieldCode = $field['id'];
3718 $fieldType = isset($manifest['nodes'][$selector]['type'])
3719 ? $manifest['nodes'][$selector]['type']
3721 // fill ever selector with data, if data exist
3722 $detailPageData = [];
3723 foreach ($sourceData as $dataItem)
3724 {
3725 // set link to the card
3726 // @todo: need refactoring
3727 if (
3728 $fieldType == NodeType::LINK &&
3729 isset($field['action'])
3730 )
3731 {
3732 switch ($field['action'])
3733 {
3734 case 'detail':
3735 {
3736 $detailPage['text'] = isset($field['text'])
3737 ? $field['text']
3738 : '';
3739 $update[$selector][] = $detailPageData[$selector][] = $getDetailPage(
3740 $detailPage,
3741 $filterId,
3742 $dataItem['ID']
3743 );
3744 break;
3745 }
3746 case 'link':
3747 {
3748 if (isset($field['link']))
3749 {
3750 $field['link'] = (array) $field['link'];
3751 if (isset($field['text']))
3752 {
3753 $field['link']['text'] = $field['text'];
3754 }
3755 $update[$selector][] = $getDetailPage(
3756 $field['link']
3757 );
3758 }
3759 break;
3760 }
3761 case 'landing':
3762 {
3763 if (isset($dataItem['LINK']))
3764 {
3765 $update[$selector][] = $detailPageData[$selector][] = $getDetailPage([
3766 'text' => isset($field['text'])
3767 ? $field['text']
3768 : '',
3769 'href' => $dataItem['LINK'],
3770 'target' => '_self',
3771 'query' => isset($dataItem['_GET']) ? $dataItem['_GET'] : []
3772 ]);
3773 }
3774 }
3775 }
3776 }
3777 else// if ($fieldType != NodeType::LINK)
3778 {
3779 $value = isset($dataItem[$fieldCode])
3780 ? $dataItem[$fieldCode]
3781 : '';
3782 $update[$selector][] = $value;
3783 if ($detailPage)
3784 {
3785 $detailPageData[$selector][] = $getDetailPage(
3786 $detailPage,
3787 $filterId,
3788 $dataItem['ID']
3789 );;
3790 }
3791 else if (isset($dataItem['LINK']))
3792 {
3793 $detailPageData[$selector][] = $getDetailPage([
3794 'text' => isset($field['text'])
3795 ? $field['text']
3796 : '',
3797 'href' => $dataItem['LINK'],
3798 'target' => '_self',
3799 'query' => isset($dataItem['_GET']) ? $dataItem['_GET'] : []
3800 ]);
3801 }
3802 }
3803 }
3804 // not touch the selector, if there is no data
3805 if (!$update[$selector])
3806 {
3807 unset($update[$selector]);
3808 }
3809 // set detail url for nodes
3810 // @todo: refactor
3811 else if (
3812 isset($field['link']) &&
3813 (
3814 $fieldType == NodeType::IMAGE ||
3815 $fieldType == NodeType::TEXT
3816 )
3817 )
3818 {
3819 if (!isset($detailPageData[$selector]))
3820 {
3821 continue;
3822 }
3823 foreach ($update[$selector] as $i => &$value)
3824 {
3825 if ($fieldType == NodeType::IMAGE)
3826 {
3827 $value = (array) $value;
3828 }
3829 else
3830 {
3831 $value = [
3832 'text' => (string) $value
3833 ];
3834 }
3835 if (
3836 $detailPageData[$selector][$i] &&
3837 UtilsAction::isTrue($field['link'])
3838 )
3839 {
3840 $detailPageData[$selector][$i]['enabled'] = true;
3841 }
3842 else
3843 {
3844 $detailPageData[$selector][$i]['enabled'] = false;
3845 }
3846 if ($detailPageData[$selector][$i]['enabled'])
3847 {
3848 $value['url'] = $detailPageData[$selector][$i];
3849 }
3850 }
3851 unset($value);
3852 }
3853 }
3854 if (!$itemDetail)
3855 {
3856 $rememberAccess = $this->access;
3857 $this->access = $this::ACCESS_W;
3858 $this->adjustCards(
3859 $cardSelector,
3860 $pagesCount
3861 );
3862 $this->access = $rememberAccess;
3863 }
3864 }
3865
3866 // stubs (common content)
3867 if ($stubs)
3868 {
3869 foreach ($stubs as $selector => $stub)
3870 {
3871 if (mb_strpos($selector, '@') !== false)
3872 {
3873 [$selector,] = explode('@', $selector);
3874 }
3875 $update[$selector] = array_fill(0, $pagesCount, $stub);
3876 }
3877 }
3878
3879 // update dynamic
3880 if ($update)
3881 {
3882 $updated = true;
3883 $rememberAccess = $this->access;
3884 $this->access = $this::ACCESS_W;
3885 $this->updateNodes(
3886 $update,
3887 [
3888 'sanitize' => false,
3889 'skipCheckAffected' => true
3890 ]
3891 );
3892 if(!$edit)
3893 {
3894 Assets\PreProcessing::blockSetDynamicProcessing($this);
3895 }
3896 $this->access = $rememberAccess;
3897
3898 header('X-Bitrix24-Page: dynamic');
3899 if ($caching)
3900 {
3901 $cache->endDataCache([
3902 'title' => $pageTitle,
3903 'content' => $this->content
3904 ]);
3905 Manager::getCacheManager()->endTagCache();
3906 }
3907 }
3908 else if (false)
3909 {
3910 $this->runtimeRequiredUserAction = [
3911 'header' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_NO_DATA_TITLE'),
3912 'description' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_NO_DATA_TEXT')
3913 ];
3914 }
3915 }
3916
3917 if (
3918 $disableUpdate ||
3919 (!$updated && !Landing::getEditMode())
3920 )
3921 {
3922 if ($cache)
3923 {
3924 $cache->abortDataCache();
3925 }
3926 $this->deleted = true;
3927 }
3928 }
3929
3934 public function clearDynamic()
3935 {
3936 $this->saveDynamicParams();
3937 }
3938
3943 public function getRuntimeRequiredUserAction(): array
3944 {
3945 return $this->runtimeRequiredUserAction;
3946 }
3947
3952 public function setRuntimeRequiredUserAction(array $action): void
3953 {
3954 $this->runtimeRequiredUserAction = $action;
3955 }
3956
3962 public function getDom($clear = false)
3963 {
3964 static $doc = array();
3965
3966 if (
3967 $clear &&
3968 isset($doc[$this->id])
3969 )
3970 {
3971 unset($doc[$this->id]);
3972 }
3973
3974 if (!isset($doc[$this->id]))
3975 {
3976 $doc[$this->id] = new DOM\Document;
3977 try
3978 {
3979 $doc[$this->id]->loadHTML($this->content);
3980 }
3981 catch (\Exception $e) {}
3982 }
3983
3984 return $doc[$this->id];
3985 }
3986
3991 public function getMeta()
3992 {
3993 return $this->metaData;
3994 }
3995
4003 public function adjustCards($selector, $count, &$changed = false)
4004 {
4005 if (!is_string($selector))
4006 {
4007 return false;
4008 }
4009
4010 if ($this->access < $this::ACCESS_W)
4011 {
4012 $this->error->addError(
4013 'ACCESS_DENIED',
4014 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4015 );
4016 return false;
4017 }
4018
4019 $manifest = $this->getManifest();
4020 if (isset($manifest['cards'][$selector]))
4021 {
4022 $count = (int)$count;
4023 $doc = $this->getDom();
4024 $resultList = $doc->querySelectorAll($selector);
4025 $resultCount = count($resultList);
4026 if ($count > $resultCount)
4027 {
4028 for ($i = $resultCount; $i < $count; $i++)
4029 {
4030 $changed = true;
4031 $this->cloneCard($selector, $i - 1);
4032 }
4033 }
4034 elseif ($count < $resultCount)
4035 {
4036 for ($i = $resultCount; $i > $count; $i--)
4037 {
4038 $changed = true;
4039 $this->removeCard($selector, $i - 1);
4040 }
4041 }
4042 return true;
4043 }
4044
4045 $this->error->addError(
4046 'CARD_NOT_FOUND',
4047 Loc::getMessage('LANDING_BLOCK_CARD_NOT_FOUND')
4048 );
4049
4050 return false;
4051 }
4052
4060 public function cloneCard($selector, $position, $content = '')
4061 {
4062 if (!is_string($selector))
4063 {
4064 return false;
4065 }
4066
4067 if ($this->access < $this::ACCESS_W)
4068 {
4069 $this->error->addError(
4070 'ACCESS_DENIED',
4071 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4072 );
4073 return false;
4074 }
4075
4076 $manifest = $this->getManifest();
4077 if (isset($manifest['cards'][$selector]))
4078 {
4079 $position = intval($position);
4080 $position = max($position, -1);
4081 $realPosition = max($position, 0);
4082 $doc = $this->getDom();
4083 $resultList = $doc->querySelectorAll($selector);
4084 if (isset($resultList[$realPosition]))
4085 {
4086 $parentNode = $resultList[$realPosition]->getParentNode();
4087 $refChild = isset($resultList[$position + 1])
4088 ? $resultList[$position + 1]
4089 : null;
4090 $haveChild = false;
4091 if ($refChild)
4092 {
4093 foreach ($parentNode->getChildNodes() as $child)
4094 {
4095 if ($child === $refChild)
4096 {
4097 $haveChild = true;
4098 break;
4099 }
4100 }
4101 }
4102 if ($parentNode && (!$refChild || $haveChild))
4103 {
4104 // some dance for set new content ;)
4105 if ($content)
4106 {
4107 $tmpCardName = mb_strtolower('tmpcard'.randString(10));
4108 $newChild = new DOM\Element($tmpCardName);
4109 $newChild->setOwnerDocument($doc);
4110 $newChild->setInnerHTML($content);
4111 }
4112 else
4113 {
4114 $newChild = $resultList[$realPosition];
4115 }
4116 $parentNode->insertBefore(
4117 $newChild,
4118 $refChild,
4119 false
4120 );
4121
4122 // history before save content
4123 if (History::isActive())
4124 {
4125 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
4126 $history->push('ADD_CARD', [
4127 'block' => $this,
4128 'selector' => $selector,
4129 'position' => $position,
4130 'content' => $content,
4131 ]);
4132 }
4133
4134 // cleaning and save
4135 if (isset($tmpCardName))
4136 {
4137 $this->saveContent(
4138 str_replace(
4139 array('<' . $tmpCardName . '>', '</' . $tmpCardName . '>'),
4140 '',
4141 $doc->saveHTML()
4142 )
4143 );
4144 }
4145 else
4146 {
4147 $this->saveContent($doc->saveHTML());
4148 }
4149 }
4150
4151 return true;
4152 }
4153
4154 }
4155
4156 $this->error->addError(
4157 'CARD_NOT_FOUND',
4158 Loc::getMessage('LANDING_BLOCK_CARD_NOT_FOUND')
4159 );
4160 return false;
4161 }
4162
4170 public function setCardContent($selector, $position, $content)
4171 {
4172 if (!is_string($selector))
4173 {
4174 return false;
4175 }
4176
4177 if ($this->access < $this::ACCESS_W)
4178 {
4179 $this->error->addError(
4180 'ACCESS_DENIED',
4181 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4182 );
4183 return false;
4184 }
4185
4186 $doc = $this->getDom();
4187 $position = intval($position);
4188 $resultList = $doc->querySelectorAll($selector);
4189 if (isset($resultList[$position]))
4190 {
4191 $resultList[$position]->setInnerHTML(
4192 $content
4193 );
4194 $this->saveContent($doc->saveHTML());
4195 return true;
4196 }
4197
4198 $this->error->addError(
4199 'CARD_NOT_FOUND',
4200 Loc::getMessage('LANDING_BLOCK_CARD_NOT_FOUND')
4201 );
4202 return false;
4203 }
4204
4211 public function getCardContent($selector, $position)
4212 {
4213 if (!is_string($selector))
4214 {
4215 return '';
4216 }
4217
4218 $doc = $this->getDom();
4219 $position = intval($position);
4220 $resultList = $doc->querySelectorAll($selector);
4221 if (isset($resultList[$position]))
4222 {
4223 return $resultList[$position]->getOuterHtml();
4224 }
4225
4226 return null;
4227 }
4228
4234 public function getCardCount($selector)
4235 {
4236 if (!is_string($selector))
4237 {
4238 return 0;
4239 }
4240
4241 $doc = $this->getDom();
4242 $resultList = $doc->querySelectorAll($selector);
4243 return count($resultList);
4244 }
4245
4252 public function removeCard($selector, $position)
4253 {
4254 if (!is_string($selector))
4255 {
4256 return false;
4257 }
4258
4259 if ($this->access < $this::ACCESS_W)
4260 {
4261 $this->error->addError(
4262 'ACCESS_DENIED',
4263 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4264 );
4265 return false;
4266 }
4267
4268 $manifest = $this->getManifest();
4269 $position = intval($position);
4270 if (isset($manifest['cards'][$selector]))
4271 {
4272 $doc = $this->getDom();
4273 $resultList = $doc->querySelectorAll($selector);
4274 if (isset($resultList[$position]))
4275 {
4276 $resultList[$position]->getParentNode()->removeChild(
4277 $resultList[$position]
4278 );
4279
4280 // history before save!
4281 if (History::isActive())
4282 {
4283 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
4284 $history->push('REMOVE_CARD', [
4285 'block' => $this,
4286 'selector' => $selector,
4287 'position' => $position,
4288 ]);
4289 }
4290
4291 $this->saveContent($doc->saveHTML());
4292 Assets\PreProcessing::blockUpdateNodeProcessing($this);
4293
4294 return true;
4295 }
4296 }
4297
4298 $this->error->addError(
4299 'CARD_NOT_FOUND',
4300 Loc::getMessage('LANDING_BLOCK_CARD_NOT_FOUND')
4301 );
4302 return false;
4303 }
4304
4310 public function changeNodeName($data)
4311 {
4312 if ($this->access < $this::ACCESS_W)
4313 {
4314 $this->error->addError(
4315 'ACCESS_DENIED',
4316 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4317 );
4318 return false;
4319 }
4320
4321 $doc = $this->getDom();
4322 $manifest = $this->getManifest();
4323 $valueBefore = [];
4324 // find available nodes by manifest from data
4325 foreach ($manifest['nodes'] as $selector => $node)
4326 {
4327 if (isset($data[$selector]))
4328 {
4329 $resultList = $doc->querySelectorAll($selector);
4330
4331 foreach ($data[$selector] as $pos => $value)
4332 {
4333 $value = trim($value['tagName'] ?? $value);
4334 if (
4335 preg_match('/^[a-z0-9]+$/i', $value) &&
4336 isset($resultList[$pos]))
4337 {
4338 $valueBefore[$selector][$pos] = $resultList[$pos]->getNodeName();
4339 $resultList[$pos]->setNodeName($value);
4340 }
4341 }
4342 }
4343 }
4344
4345 if (History::isActive())
4346 {
4347 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
4348 $history->push('CHANGE_NODE_NAME_ACTION', [
4349 'block' => $this,
4350 'valueBefore' => $valueBefore,
4351 'valueAfter' => $data,
4352 ]);
4353 }
4354
4355 // save rebuild html as text
4356 $this->saveContent($doc->saveHTML());
4357
4358 return true;
4359 }
4360
4367 public function updateNodes($data, $additional = array())
4368 {
4369 if ($this->access < $this::ACCESS_W)
4370 {
4371 $this->error->addError(
4372 'ACCESS_DENIED',
4373 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4374 );
4375 return false;
4376 }
4377
4378 $affected = [];
4379 $doc = $this->getDom();
4380 $manifest = $this->getManifest();
4381
4382 // find available nodes by manifest from data
4383 $manifest['nodes'] = $manifest['nodes'] ?? [];
4384 foreach ($manifest['nodes'] as $selector => $node)
4385 {
4386 $isFind = false;
4387 $dataSelector = [];
4388 if (isset($data[$selector]))
4389 {
4390 if (!is_array($data[$selector]))
4391 {
4392 $data[$selector] = array(
4393 $data[$selector]
4394 );
4395 }
4396 $dataSelector = $data[$selector];
4397 $isFind = true;
4398 }
4399 if (!$isFind && ($node['isWrapper'] ?? false) === true)
4400 {
4401 if (isset($data['#wrapper']) && $node['type'] === 'styleimg')
4402 {
4403 if (!is_array($data['#wrapper']))
4404 {
4405 $data['#wrapper'] = array(
4406 $data['#wrapper']
4407 );
4408 }
4409 $dataSelector = $data['#wrapper'];
4410 }
4411 else
4412 {
4413 $selector = '#wrapper';
4414 if (!is_array($data[$selector]))
4415 {
4416 $data[$selector] = array(
4417 $data[$selector]
4418 );
4419 }
4420 $dataSelector = $data[$selector];
4421 }
4422 $isFind = true;
4423 }
4424 if ($node['type'] === 'img')
4425 {
4426 $node = Img::changeNodeType($node, $this);
4427 }
4428 if ($isFind)
4429 {
4430 // and save content from frontend in DOM by handler-class
4431 $affected[$selector] = call_user_func_array(array(
4432 Node\Type::getClassName($node['type']),
4433 'saveNode'
4434 ), array(
4435 $this,
4436 $selector,
4437 $dataSelector,
4438 $additional
4439 ));
4440 }
4441 }
4442
4443 // additional work with menu
4444 if (isset($additional['appendMenu']) && $additional['appendMenu'])
4445 {
4446 $export = $this->export();
4447 }
4448 else
4449 {
4450 $additional['appendMenu'] = false;
4451 }
4452 $manifest['menu'] = $manifest['menu'] ?? [];
4453 foreach ($manifest['menu'] as $selector => $node)
4454 {
4455 if (isset($data[$selector]) && is_array($data[$selector]))
4456 {
4457 if (isset($data[$selector][0][0]))
4458 {
4459 $data[$selector] = array_shift($data[$selector]);
4460 }
4461 if ($additional['appendMenu'] && isset($export['menu'][$selector]))
4462 {
4463 $data[$selector] = array_merge(
4464 $export['menu'][$selector],
4465 $data[$selector]
4466 );
4467 }
4468
4469 $resultList = $doc->querySelectorAll($selector);
4470 foreach ($resultList as $pos => $resultNode)
4471 {
4472 $parentNode = $resultNode->getParentNode();
4473 if ($parentNode)
4474 {
4475 $parentNode->setInnerHtml(
4476 $this->getMenuHtml(
4477 $data[$selector],
4478 $node
4479 )
4480 );
4481 }
4482 break;// we need only first position
4483 }
4484 }
4485 }
4486
4487 // save rebuild html as text
4488 $this->saveContent($doc->saveHTML());
4489
4490 // check affected content in block's content
4491 if (!($additional['skipCheckAffected'] ?? false) && Manager::getOption('strict_verification_update') === 'Y')
4492 {
4493 $pos = 0;
4494 $domCorrect = true;
4495 $content = $this->content;
4496
4497 foreach ($affected as $selector => $resultItem)
4498 {
4499 $selector = trim($selector, '.');
4500
4501 // prepare content for search
4502 $content = str_replace('class="', 'class=" ', $content);
4503 $content = preg_replace_callback(
4504 '/class="[^"]*[\s]+(' . $selector . ')[\s"]+[^"]*"[^>]*>/s',
4505 function($match) use(&$pos)
4506 {
4507 return str_replace($match[1], $match[1] . '@' . ($pos++), $match[0]);
4508 },
4509 $content
4510 );
4511
4512 if (is_array($resultItem))
4513 {
4514 foreach ($resultItem as $pos => $affectedItem)
4515 {
4516 if ($affectedItem['content'] ?? null)
4517 {
4518 $affectedItem['content'] = str_replace('/', '\/', $affectedItem['content']);
4519 $mask = '/class="[^"]*[\s]+' . $selector . '@' . $pos . '[\s"]+[^"]*"[^>]*>' . $affectedItem['content'] . '<\//s';
4520 $domCorrect = preg_match_all($mask, $content);
4521 if (!$domCorrect)
4522 {
4523 break 2;
4524 }
4525 }
4526 }
4527 }
4528 }
4529
4530 if (!$domCorrect)
4531 {
4532 $this->error->addError(
4533 'INCORRECT_AFFECTED',
4534 Loc::getMessage('LANDING_BLOCK_INCORRECT_AFFECTED')
4535 );
4536 return false;
4537 }
4538 }
4539
4540 Assets\PreProcessing::blockUpdateNodeProcessing($this);
4541
4542 return true;
4543 }
4544
4552 protected function getMenuHtml($data, $manifestNode, $level = 'root')
4553 {
4554 if (!is_array($data) || !isset($manifestNode[$level]))
4555 {
4556 return '';
4557 }
4558
4559 $htmlContent = '';
4560 $rootSelector = $manifestNode[$level];
4561
4562 if (
4563 isset($rootSelector['ulClassName']) &&
4564 isset($rootSelector['liClassName']) &&
4565 isset($rootSelector['aClassName']) &&
4566 is_string($rootSelector['ulClassName']) &&
4567 is_string($rootSelector['liClassName']) &&
4568 is_string($rootSelector['aClassName'])
4569 )
4570 {
4571 foreach ($data as $menuItem)
4572 {
4573 if (
4574 isset($menuItem['text']) && is_string($menuItem['text']) &&
4575 isset($menuItem['href']) && is_string($menuItem['href'])
4576 )
4577 {
4578 if ($menuItem['href'] === 'page:#landing0')
4579 {
4580 $res = Landing::addByTemplate(
4581 $this->getSiteId(),
4582 Assets\PreProcessing\Theme::getNewPageTemplate($this->getSiteId()),
4583 [
4584 'TITLE' => $menuItem['text']
4585 ]
4586 );
4587 if ($res->isSuccess())
4588 {
4589 $menuItem['href'] = '#landing' . $res->getId();
4590 }
4591 }
4592 if (isset($menuItem['target']) && is_string($menuItem['target']))
4593 {
4594 $target = $menuItem['target'];
4595 }
4596 else
4597 {
4598 $target = '_self';
4599 }
4600 $htmlContent .= '<li class="' . \htmlspecialcharsbx($rootSelector['liClassName']) . '">';
4601 $htmlContent .= '<a href="' . \htmlspecialcharsbx($menuItem['href']) . '" target="' . $target . '"
4602 class="' . \htmlspecialcharsbx($rootSelector['aClassName']) . '">';
4603 $htmlContent .= \htmlspecialcharsbx($menuItem['text']);
4604 $htmlContent .= '</a>';
4605 if (isset($menuItem['children']))
4606 {
4607 $htmlContent .= $this->getMenuHtml(
4608 $menuItem['children'],
4609 $manifestNode,
4610 'children'
4611 );
4612 }
4613 $htmlContent .= '</li>';
4614 }
4615 }
4616 if ($htmlContent)
4617 {
4618 $htmlContent = '<ul class="' . \htmlspecialcharsbx($rootSelector['ulClassName']) . '">' .
4619 $htmlContent .
4620 '</ul>';
4621 }
4622 else if ($level == 'root')
4623 {
4624 $htmlContent = '<ul class="' . \htmlspecialcharsbx($rootSelector['ulClassName']) . '"></ul>';
4625 }
4626 }
4627
4628 return $htmlContent;
4629 }
4630
4636 public function updateCards(array $data = array())
4637 {
4638 if ($this->access < $this::ACCESS_W)
4639 {
4640 $this->error->addError(
4641 'ACCESS_DENIED',
4642 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4643 );
4644 return false;
4645 }
4646
4647 $manifest = $this->getManifest();
4648
4649 foreach ($data as $selector => $item)
4650 {
4651 $cardManifest = $manifest['cards'][$selector];
4652 // first gets content of current cards
4653 $cardContent = array();
4654 $cardCount = $this->getCardCount($selector);
4655 for ($i = 0; $i < $cardCount; $i++)
4656 {
4657 $cardContent[$i] = $this->getCardContent(
4658 $selector,
4659 $i
4660 );
4661 }
4662 // then fill all cards by content from existing cards and presets
4663 if (
4664 isset($item['source']) &&
4665 is_array($item['source'])
4666 )
4667 {
4668 $newContent = array();
4669 foreach ($item['source'] as $i => $source)
4670 {
4671 $type = isset($source['type'])
4672 ? $source['type']
4673 : self::CARD_SYM_CODE;
4674 $value = isset($source['value'])
4675 ? $source['value']
4676 : 0;
4677 // clone card
4678 if (
4679 $type == self::CARD_SYM_CODE &&
4680 isset($cardContent[$value])
4681 )
4682 {
4683 $newContent[$i] = $cardContent[$value];
4684 }
4685 // clone preset
4686 else if (
4687 $type == 'preset' &&
4688 isset($cardManifest['presets'][$value]['html'])
4689 )
4690 {
4691 $newContent[$i] = $cardManifest['presets'][$value]['html'];
4692 }
4693 else
4694 {
4695 $newContent[$i] = '';
4696 }
4697 }
4698 $newContent = trim(implode('', $newContent));
4699 if ($newContent)
4700 {
4701 $dom = $this->getDom();
4702 $resultList = $dom->querySelectorAll($selector);
4703 if (isset($resultList[0]))
4704 {
4705 $resultList[0]->getParentNode()->setInnerHtml(
4706 $newContent
4707 );
4708 }
4709 $this->saveContent(
4710 $dom->saveHTML()
4711 );
4712 }
4713 }
4714 // and finally update content cards
4715 if (
4716 isset($item['values']) &&
4717 is_array($item['values'])
4718 )
4719 {
4720 $updNodes = array();
4721 foreach ($item['values'] as $upd)
4722 {
4723 if (is_array($upd))
4724 {
4725 foreach ($upd as $sel => $content)
4726 {
4727 if(mb_strpos($sel, '@'))
4728 {
4729 [$sel, $pos] = explode('@', $sel);
4730 }
4731 if (!isset($updNodes[$sel]))
4732 {
4733 $updNodes[$sel] = array();
4734 }
4735 $updNodes[$sel][$pos] = $content;
4736 }
4737 }
4738 }
4739 if (!empty($updNodes))
4740 {
4741 $this->updateNodes($updNodes);
4742 }
4743 }
4744 }
4745
4746 return true;
4747 }
4748
4755 protected function removeStyle(DOM\Node $node, array $styleToRemove)
4756 {
4757 foreach ($node->getChildNodesArray() as $nodeChild)
4758 {
4759 if ($nodeChild instanceof DOM\Element)
4760 {
4761 $styles = DOM\StyleInliner::getStyle($nodeChild, false);
4762 if (!empty($styles))
4763 {
4764 foreach ($styleToRemove as $remove)
4765 {
4766 if (!is_array($remove))
4767 {
4768 $remove = [$remove => $remove];
4769 }
4770 $styles = array_diff_key($styles, $remove);
4771 }
4772 DOM\StyleInliner::setStyle($nodeChild, $styles);
4773 }
4774 }
4775 $node = $this->removeStyle($nodeChild, $styleToRemove);
4776 }
4777
4778 return $node;
4779 }
4780
4786 public function setClasses($data)
4787 {
4788 if ($this->access < $this::ACCESS_V)
4789 {
4790 $this->error->addError(
4791 'ACCESS_DENIED',
4792 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4793 );
4794 return false;
4795 }
4796
4797 $doc = $this->getDom();
4798 $manifest = $this->getManifest();
4799
4800 // detects position
4801 $positions = [];
4802 $position = -1;
4803 foreach ((array)$data as $selector => $item)
4804 {
4805 if (mb_strpos($selector, '@') !== false)
4806 {
4807 [$selector, $position] = explode('@', $selector);
4808 }
4809 else
4810 {
4811 $position = -1;
4812 }
4813 if ($selector === '#wrapper')
4814 {
4815 $selector = '#block' . $this->id;
4816 }
4817 if ($position >= 0)
4818 {
4819 if (!isset($positions[$selector]))
4820 {
4821 $positions[$selector] = [];
4822 }
4823 $positions[$selector][] = (int)$position;
4824 }
4825 $data[$selector] = $item;
4826 }
4827
4828 // wrapper (not realy exist)
4829 $wrapper = '#' . $this->getAnchor($this->id);
4830
4831 // find available nodes by manifest from data
4832 $styles = $manifest['style']['nodes'];
4833 $styles[$wrapper] = $manifest['style']['block'];
4834 foreach ($styles as $selector => $node)
4835 {
4836 if (isset($data[$selector]))
4837 {
4838 // prepare data
4839 if (!is_array($data[$selector]))
4840 {
4841 $data[$selector] = [
4842 $data[$selector]
4843 ];
4844 }
4845
4846 if (!isset($data[$selector]['classList']))
4847 {
4848 $data[$selector] = [
4849 'classList' => $data[$selector]
4850 ];
4851 }
4852 if (!isset($data[$selector]['affect']))
4853 {
4854 $data[$selector]['affect'] = [];
4855 }
4856 // apply classes to the block
4857 if ($selector === $wrapper)
4858 {
4859 $nodesArray = $doc->getChildNodesArray();
4860 $resultList = [array_pop($nodesArray)];
4861 }
4862 // or by selector
4863 else
4864 {
4865 $resultList = $doc->querySelectorAll($selector);
4866 }
4867 foreach ($resultList as $pos => $resultNode)
4868 {
4869 $relativeSelector = $selector;
4870 if (isset($positions[$selector]))
4871 {
4872 if (!in_array($pos, $positions[$selector], true))
4873 {
4874 continue;
4875 }
4876 $relativeSelector .= '@' . $pos;
4877 }
4878
4879 $contentBefore = $resultNode->getOuterHTML();
4880
4881 if ($resultNode)
4882 {
4883 if ((int)$resultNode->getNodeType() === $resultNode::ELEMENT_NODE)
4884 {
4885 $resultNode->setClassName(
4886 implode(' ', $data[$relativeSelector]['classList'])
4887 );
4888 }
4889
4890 // affected styles
4891 if (!empty($data[$relativeSelector]['affect']))
4892 {
4893 $this->removeStyle(
4894 $resultNode,
4895 $data[$relativeSelector]['affect']
4896 );
4897 }
4898
4899 // inline styles
4900 if (!empty($data[$relativeSelector]['style']))
4901 {
4902 $stylesInline = DOM\StyleInliner::getStyle($resultNode, false);
4903 DOM\StyleInliner::setStyle(
4904 $resultNode,
4905 array_merge($stylesInline, $data[$relativeSelector]['style'])
4906 );
4907 }
4908 else if (preg_match_all('/background-image:.*;/i', $resultNode->getAttribute('style'), $matches))
4909 {
4910 $resultNode->removeAttribute('style');
4911 $resultNode->setAttribute('style', implode('', $matches[0]));
4912 }
4913
4914 if (History::isActive())
4915 {
4916 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
4917 $history->push('EDIT_STYLE', [
4918 'block' => $this,
4919 'selector' => $selector,
4920 'isWrapper' => ($selector === $wrapper),
4921 'position' => $position >= 0 ? (int)$pos : -1,
4922 'affect' => $data[$relativeSelector]['affect'],
4923 'contentBefore' => $contentBefore,
4924 'contentAfter' => $resultNode->getOuterHTML(),
4925 ]);
4926 }
4927 }
4928 }
4929 }
4930 }
4931 // save rebuild html as text
4932 $this->saveContent($doc->saveHTML());
4933 Assets\PreProcessing::blockUpdateClassesProcessing($this);
4934 return true;
4935 }
4936
4943 protected static function collectAllowedAttrs(array $mixed, array &$allowed, $selector = null)
4944 {
4945 foreach ($mixed as $itemSelector => $item)
4946 {
4947 if (!is_string($itemSelector))
4948 {
4949 $itemSelector = $selector;
4950 }
4951 if (
4952 isset($item['attrs']) &&
4953 is_array($item['attrs'])
4954 )
4955 {
4956 self::collectAllowedAttrs($item['attrs'], $allowed, $itemSelector);
4957 }
4958 else if (
4959 isset($item['additional']['attrsType']) ||
4960 $itemSelector === 'additional'
4961 )
4962 {
4963 $manifestAttrs = self::getAttrs();
4964 $attrs = $manifestAttrs['bitrix']['attrs'];
4965 if (is_array($item['additional']['attrsType']))
4966 {
4967 foreach ($attrs as $attr) {
4968 $allowed[$itemSelector][] = $attr['attribute'];
4969 }
4970 }
4971 if (is_array($item['attrsType']))
4972 {
4973 foreach ($attrs as $attr) {
4974 $allowed['#wrapper'][] = $attr['attribute'];
4975 }
4976 }
4977 }
4978 else if (
4979 isset($item['additional']['attrs']) &&
4980 is_array($item['additional']['attrs'])
4981 )
4982 {
4983 self::collectAllowedAttrs($item['additional']['attrs'], $allowed, $itemSelector);
4984 }
4985 else if (
4986 isset($item['additional']) &&
4987 is_array($item['additional'])
4988 )
4989 {
4990 self::collectAllowedAttrs($item['additional'], $allowed, $itemSelector);
4991 }
4992 else if (
4993 isset($item['attribute']) &&
4994 is_string($item['attribute'])
4995 )
4996 {
4997 if (
4998 isset($item['selector']) &&
4999 is_string($item['selector'])
5000 )
5001 {
5002 $itemSelector = trim($item['selector']);
5003 }
5004 if ($itemSelector)
5005 {
5006 if (!isset($allowed[$itemSelector]))
5007 {
5008 $allowed[$itemSelector] = [];
5009 }
5010 $allowed[$itemSelector][] = $item['attribute'];
5011 }
5012 }
5013 else if (is_array($item))
5014 {
5015 self::collectAllowedAttrs($item, $allowed, $itemSelector);
5016 }
5017 }
5018 }
5019
5025 public function setAttributes($data)
5026 {
5027 if ($this->access < $this::ACCESS_W)
5028 {
5029 $this->error->addError(
5030 'ACCESS_DENIED',
5031 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
5032 );
5033 return;
5034 }
5035
5036 $doc = $this->getDom();
5037 $manifest = $this->getManifest();
5038 $wrapper = '#' . $this->getAnchor($this->id);
5039
5040 // collect allowed attrs
5041 $allowedAttrs = [];
5042 self::collectAllowedAttrs($manifest['style']['nodes'], $allowedAttrs);
5043 self::collectAllowedAttrs($manifest['attrs'], $allowedAttrs);
5044 self::collectAllowedAttrs($manifest['cards'], $allowedAttrs);
5045 self::collectAllowedAttrs($manifest['style']['block'], $allowedAttrs);
5046
5047 // update attrs
5048 if ($allowedAttrs)
5049 {
5050 // all allowed attrs from manifest with main selector ([selector] => [data-test, data-test2])
5051 foreach ($allowedAttrs as $selector => $allowed)
5052 {
5053 // it's not interesting for us, if there is no new data for this selector
5054 if ((isset($data[$selector]) && is_array($data[$selector])) || isset($data[$wrapper]) )
5055 {
5056 // set attrs to the block
5057 if ($selector === '#wrapper')
5058 {
5059 $selector = $wrapper;
5060 }
5061 if ($selector == $wrapper)
5062 {
5063 $nodesArray = $doc->getChildNodesArray();
5064 $resultList = [array_pop($nodesArray)];
5065 }
5066 // or by selector
5067 else
5068 {
5069 $resultList = $doc->querySelectorAll($selector);
5070 }
5071 // external data for changing in allowed attrs
5072 foreach ($data[$selector] as $attrKey => $attrData)
5073 {
5074 // if key without position (compatibility)
5075 if (!($attrKey == (string)(int)$attrKey))
5076 {
5077 $attrData = [$attrKey => $attrData];
5078 $attrKey = -1;
5079 }
5080 if (!is_array($attrData))
5081 {
5082 continue;
5083 }
5084 // attrs new data in each selector ([data-test] => value)
5085 foreach ($attrData as $key => $value)
5086 {
5087 if (!in_array($key, $allowed))
5088 {
5089 continue;
5090 }
5091 $key = \htmlspecialcharsbx($key);
5092 $value = is_array($value) ? json_encode($value) : $value;
5093
5094 // result nodes by main selector
5095 foreach ($resultList as $pos => $resultNode)
5096 {
5097 // if position of node that we try to find
5098 if ($attrKey == -1 || $attrKey == $pos)
5099 {
5100 $valueBefore = $resultNode->getAttribute($key);
5101 // update node
5102 $resultNode->setAttribute($key, $value);
5103 if (History::isActive())
5104 {
5105 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
5106 $history->push('EDIT_ATTRIBUTES', [
5107 'block' => $this,
5108 'selector' => $selector,
5109 'isWrapper' => ($selector === $wrapper),
5110 'attribute' => $key,
5111 'position' => (int)$attrKey,
5112 'valueBefore' => $valueBefore,
5113 'valueAfter' => $value,
5114 ]);
5115 }
5116 }
5117 }
5118 }
5119 }
5120 }
5121 }
5122 }
5123
5124 // save result
5125 $this->saveContent($doc->saveHTML());
5126 }
5127
5133 protected static function replaceMetaMarkers($content)
5134 {
5135 if (mb_strpos($content, '#breadcrumb#') !== false)
5136 {
5137 ob_start();
5138 $arResult = array(
5139 array(
5140 'LINK' => '#',
5141 'TITLE' => ''
5142 ),
5143 array(
5144 'LINK' => '#',
5145 'TITLE' => Loc::getMessage('LANDING_BLOCK_BR1')
5146 ),
5147 array(
5148 'LINK' => '#',
5149 'TITLE' => Loc::getMessage('LANDING_BLOCK_BR2')
5150 ),
5151 array(
5152 'LINK' => '#',
5153 'TITLE' => ''
5154 )
5155 );
5156 $tplId = Manager::getTemplateId(
5158 );
5159 $strChainTemplate = getLocalPath('templates/' . $tplId . '/chain_template.php');
5160 $strChainTemplate = Manager::getDocRoot() . $strChainTemplate;
5161 if (file_exists($strChainTemplate))
5162 {
5163 echo include $strChainTemplate;
5164 }
5165 $breadcrumb = ob_get_contents();
5166 ob_end_clean();
5167 $content = str_replace(
5168 '#breadcrumb#',
5169 $breadcrumb,
5170 $content
5171 );
5172 }
5173
5174 if (mb_strpos($content, '#title#') !== false)
5175 {
5176 $content = str_replace(
5177 '#title#',
5178 Loc::getMessage('LANDING_BLOCK_TITLE'),
5179 $content
5180 );
5181 }
5182
5183 return $content;
5184 }
5185
5191 public static function deleteByCode($code)
5192 {
5193 if (!is_array($code))
5194 {
5195 $code = array($code);
5196 }
5197 $res = parent::getList(array(
5198 'select' => array(
5199 'ID'
5200 ),
5201 'filter' => array(
5202 '=CODE' => $code
5203 )
5204 ));
5205 while ($row = $res->fetch())
5206 {
5207 self::parentDelete($row['ID']);
5208 }
5209 }
5210
5216 private static function parentDelete($id)
5217 {
5218 return parent::delete($id);
5219 }
5220
5226 public static function deleteAll($lid)
5227 {
5228 $res = parent::getList([
5229 'select' => [
5230 'ID'
5231 ],
5232 'filter' => [
5233 'LID' => (int)$lid
5234 ]
5235 ]);
5236 while ($row = $res->fetch())
5237 {
5238 parent::delete($row['ID']);
5239 }
5240 }
5241
5246 public function getSearchContent()
5247 {
5248 $manifest = $this->getManifest();
5249 $search = [];
5250
5251 // get content nodes
5252 if (isset($manifest['nodes']))
5253 {
5254 foreach ($manifest['nodes'] as $selector => $node)
5255 {
5257 $class = NodeType::getClassName($node['type']);
5258 if (is_callable([$class, 'getSearchableNode']))
5259 {
5260 $search = array_merge($search, $class::getSearchableNode($this, $selector));
5261 }
5262 }
5263 }
5264
5265 return $search ? implode(' ', $search) : '';
5266 }
5267
5273 public function export(array $params = [])
5274 {
5275 $manifest = $this->getManifest();
5276 $doc = $this->getDom();
5277
5278 $cards = [];
5279 $nodes = [];
5280 $menu = [];
5281 $styles = [];
5282 $allAttrs = [];
5283
5284 // prepare params
5285 if (!isset($params['clear_form']))
5286 {
5287 $params['clear_form'] = true;
5288 }
5289
5290 // get actual cards content
5291 if (isset($manifest['cards']))
5292 {
5293 foreach ($manifest['cards'] as $selector => $node)
5294 {
5295 $cards[$selector] = [
5296 'source' => []
5297 ];
5298 $resultList = $doc->querySelectorAll($selector);
5299 $resultListCnt = count($resultList);
5300 foreach ($resultList as $pos => $result)
5301 {
5302 $cards[$selector]['source'][$pos] = array(
5303 'value' => $result->getAttribute('data-card-preset'),
5304 'type' => Block::PRESET_SYM_CODE
5305 );
5306 if (!$cards[$selector]['source'][$pos]['value'])
5307 {
5308 //@tmp for menu first item
5309 if (mb_strpos($this->getCode(), 'menu') !== false)
5310 {
5311 $cards[$selector]['source'][$pos]['value'] = $resultListCnt > 0 ? 1 : 0;
5312 }
5313 else
5314 {
5315 $cards[$selector]['source'][$pos]['value'] = 0;
5316 }
5317 $cards[$selector]['source'][$pos]['type'] = Block::CARD_SYM_CODE;
5318 }
5319 }
5320 // attrs
5321 if (
5322 isset($node['additional']['attrs']) &&
5323 is_array($node['additional']['attrs'])
5324 )
5325 {
5326 foreach ($node['additional']['attrs'] as $attr)
5327 {
5328 if (isset($attr['attribute']))
5329 {
5330 if (!isset($allAttrs[$selector]))
5331 {
5332 $allAttrs[$selector] = [];
5333 }
5334 $allAttrs[$selector][] = $attr['attribute'];
5335 }
5336 }
5337 }
5338 }
5339 }
5340 // get content nodes
5341 if (isset($manifest['nodes']))
5342 {
5343 foreach ($manifest['nodes'] as $selector => $node)
5344 {
5346 $class = NodeType::getClassName($node['type']);
5347 $nodes[$selector] = $class::getNode($this, $selector);
5348 }
5349 }
5350 if (isset($manifest['menu']))
5351 {
5352 // recursive getting menu
5353 $exportMenu = function($resultList) use(&$exportMenu)
5354 {
5355 if(!$resultList)
5356 {
5357 return [];
5358 }
5359
5360 $menu = [];
5361 foreach ($resultList->getChildNodesArray() as $pos => $node)
5362 {
5363 $menu[$pos] = [];
5364 if ($node->getNodeName() == 'LI')
5365 {
5366 foreach ($node->getChildNodesArray() as $nodeInner)
5367 {
5368 if ($nodeInner->getNodeName() == 'A')
5369 {
5370 $menu[$pos]['text'] = trim($nodeInner->getTextContent());
5371 $menu[$pos]['href'] = trim($nodeInner->getAttribute('href'));
5372 $menu[$pos]['target'] = trim($nodeInner->getAttribute('target'));
5373 }
5374 else if ($nodeInner->getNodeName() == 'UL')
5375 {
5376 $menu[$pos]['children'] = $exportMenu($nodeInner);
5377 }
5378 }
5379 }
5380 if (!$menu[$pos])
5381 {
5382 unset($menu[$pos]);
5383 }
5384 }
5385 return array_values($menu);
5386 };
5387 foreach ($manifest['menu'] as $selector => $menuNode)
5388 {
5389 $menu[$selector] = $exportMenu($doc->querySelector($selector));
5390 }
5391 }
5392 // get actual css from nodes
5393 if (isset($manifest['style']['nodes']))
5394 {
5395 foreach ($manifest['style']['nodes'] as $selector => $node)
5396 {
5397 $nodeStyle = Node\Style::getStyle($this, $selector);
5398 if ($nodeStyle)
5399 {
5400 $styles[$selector] = $nodeStyle;
5401 }
5402 // attrs
5403 if (
5404 isset($node['additional']['attrs']) &&
5405 is_array($node['additional']['attrs'])
5406 )
5407 {
5408 foreach ($node['additional']['attrs'] as $attr)
5409 {
5410 if (isset($attr['attribute']))
5411 {
5412 if (!isset($allAttrs[$selector]))
5413 {
5414 $allAttrs[$selector] = [];
5415 }
5416 $allAttrs[$selector][] = $attr['attribute'];
5417 }
5418 }
5419 }
5420 }
5421 }
5422 // get actual css from block wrapper
5423 if (!empty($manifest['style']['block']))
5424 {
5425 $selector = '#wrapper';
5426 $wrapperStyle = Node\Style::getStyle($this, $selector);
5427 if ($wrapperStyle)
5428 {
5429 $styles[$selector] = $wrapperStyle;
5430 }
5431 }
5432 // attrs
5433 if (
5434 isset($manifest['style']['block']['additional']['attrs']) &&
5435 is_array($manifest['style']['block']['additional']['attrs'])
5436 )
5437 {
5438 $selector = '#wrapper';
5439 foreach ($manifest['style']['block']['additional']['attrs'] as $attr)
5440 {
5441 if (isset($attr['attribute']))
5442 {
5443 if (!isset($allAttrs[$selector]))
5444 {
5445 $allAttrs[$selector] = [];
5446 }
5447 $allAttrs[$selector][] = $attr['attribute'];
5448 }
5449 }
5450 }
5451 // get actual attrs from nodes
5452 if (isset($manifest['attrs']))
5453 {
5454 foreach ($manifest['attrs'] as $selector => $item)
5455 {
5456 if (isset($item['attribute']))
5457 {
5458 if (!isset($allAttrs[$selector]))
5459 {
5460 $allAttrs[$selector] = [];
5461 }
5462 $allAttrs[$selector][] = $item['attribute'];
5463 }
5464 else if (is_array($item))
5465 {
5466 foreach ($item as $itemAttr)
5467 {
5468 if (isset($itemAttr['attribute']))
5469 {
5470 if (!isset($allAttrs[$selector]))
5471 {
5472 $allAttrs[$selector] = [];
5473 }
5474 $allAttrs[$selector][] = $itemAttr['attribute'];
5475 }
5476 }
5477 }
5478 }
5479 }
5480 // remove some system attrs
5481 if (
5482 $params['clear_form'] &&
5483 isset($allAttrs['.bitrix24forms'])
5484 )
5485 {
5486 unset($allAttrs['.bitrix24forms']);
5487 }
5488 // collect attrs
5489 $allAttrsNew = [];
5490 if (isset($allAttrs['#wrapper']))
5491 {
5492 $allAttrsNew['#wrapper'] = [];
5493 $nodesArray = $doc->getChildNodesArray();
5494 $resultList = [array_pop($nodesArray)];
5495 foreach ($resultList as $pos => $result)
5496 {
5497 foreach ($allAttrs['#wrapper'] as $attrKey)
5498 {
5499 if (!isset($allAttrsNew['#wrapper'][$pos]))
5500 {
5501 $allAttrsNew['#wrapper'][$pos] = [];
5502 }
5503 $allAttrsNew['#wrapper'][$pos][$attrKey] = $result->getAttribute($attrKey);
5504 }
5505 }
5506 unset($allAttrs['#wrapper']);
5507 }
5508 foreach ($allAttrs as $selector => $attr)
5509 {
5510 $resultList = $doc->querySelectorAll($selector);
5511 foreach ($resultList as $pos => $result)
5512 {
5513 if (!isset($allAttrsNew[$selector]))
5514 {
5515 $allAttrsNew[$selector] = [];
5516 }
5517 if (!isset($allAttrsNew[$selector][$pos]))
5518 {
5519 $allAttrsNew[$selector][$pos] = [];
5520 }
5521 foreach ($attr as $attrKey)
5522 {
5523 $allAttrsNew[$selector][$pos][$attrKey] = $result->getAttribute($attrKey);
5524 }
5525 unset($attrVal);
5526 }
5527 }
5528 $allAttrs = $allAttrsNew;
5529 unset($allAttrsNew);
5530
5531 return [
5532 'cards' => $cards,
5533 'nodes' => $nodes,
5534 'menu' => $menu,
5535 'style' => $styles,
5536 'attrs' => $allAttrs,
5537 'dynamic' => $this->dynamicParams
5538 ];
5539 }
5540
5549 public static function search($query, array $filter = [], array $select = ['LID'], array $group = ['LID'])
5550 {
5551 $result = [];
5552
5553 $filter['*%SEARCH_CONTENT'] = $query;
5554 $filter['=DELETED'] = 'N';
5555
5556 $res = Internals\BlockTable::getList([
5557 'select' => $select,
5558 'filter' => $filter,
5559 'group' => $group,
5560 'order' => ['SORT' => 'desc']
5561 ]);
5562 while ($row = $res->fetch())
5563 {
5564 $result[] = $row;
5565 }
5566
5567 return $result;
5568 }
5569
5575 public static function add($fields)
5576 {
5577 if (
5578 !defined('LANDING_MUTATOR_MODE') ||
5579 LANDING_MUTATOR_MODE !== true
5580 )
5581 {
5582 throw new \Bitrix\Main\SystemException(
5583 'Disabled for direct access.'
5584 );
5585 }
5586 else
5587 {
5588 return parent::add($fields);
5589 }
5590 }
5591
5598 public static function update($id, $fields = array())
5599 {
5600 if (
5601 !defined('LANDING_MUTATOR_MODE') ||
5602 LANDING_MUTATOR_MODE !== true
5603 )
5604 {
5605 throw new \Bitrix\Main\SystemException(
5606 'Disabled for direct access.'
5607 );
5608 }
5609 else
5610 {
5611 return parent::update($id, $fields);
5612 }
5613 }
5614
5620 public static function delete($id)
5621 {
5622 if (
5623 !defined('LANDING_MUTATOR_MODE') ||
5624 LANDING_MUTATOR_MODE !== true
5625 )
5626 {
5627 throw new \Bitrix\Main\SystemException(
5628 'Disabled for direct access.'
5629 );
5630 }
5631 else
5632 {
5633 return parent::delete($id);
5634 }
5635 }
5636
5642 public static function getFavorites(?string $tplCode): array
5643 {
5644 return parent::getList([
5645 'filter' => [
5646 'LID' => 0,
5647 '=DELETED' => 'N',
5648 '=TPL_CODE' => $tplCode
5649 ],
5650 'order' => [
5651 'ID' => 'asc'
5652 ]
5653 ])->fetchAll();
5654 }
5655
5661 public static function getList($fields = array())
5662 {
5663 if (
5664 !defined('LANDING_MUTATOR_MODE') ||
5665 LANDING_MUTATOR_MODE !== true
5666 )
5667 {
5668 throw new \Bitrix\Main\SystemException(
5669 'Disabled for direct access.'
5670 );
5671 }
5672 else
5673 {
5674 return parent::getList($fields);
5675 }
5676 }
5677
5685 protected static function getAjaxInitiatedAssets()
5686 {
5687 Asset::getInstance()->getJs();
5688 Asset::getInstance()->getCss();
5689 Asset::getInstance()->getStrings();
5690
5691 $targetTypeList = array('JS', 'CSS');
5692 $CSSList = $JSList = $stringsList = [];
5693
5694 foreach ($targetTypeList as $targetType)
5695 {
5696 $targetAssetList = Asset::getInstance()->getTargetList($targetType);
5697
5698 foreach ($targetAssetList as $targetAsset)
5699 {
5700 $assetInfo = Asset::getInstance()->getAssetInfo($targetAsset['NAME'], \Bitrix\Main\Page\AssetMode::ALL);
5701
5702 if (!empty($assetInfo['JS']))
5703 {
5704 $JSList = array_merge($JSList, $assetInfo['JS']);
5705 }
5706
5707 if (!empty($assetInfo['CSS']))
5708 {
5709 $CSSList = array_merge($CSSList, $assetInfo['CSS']);
5710 }
5711
5712 if (!empty($assetInfo['STRINGS']))
5713 {
5714 $stringsList = array_merge($stringsList, $assetInfo['STRINGS']);
5715 }
5716 }
5717 }
5718
5719 return [
5720 'js' => array_unique($JSList),
5721 'css' => array_unique($CSSList),
5722 'strings' => array_unique($stringsList),
5723 ];
5724 }
5725
5734 public static function isContains(int $entityId, string $needed, bool $isLanding = false): bool
5735 {
5736 $filter = [
5737 '=ACTIVE' => 'Y',
5738 '=DELETED' => 'N',
5739 'CONTENT' => '%' . $needed . '%',
5740 ];
5741 if ($isLanding)
5742 {
5743 $filter['LID'] = $entityId;
5744 }
5745 else
5746 {
5747 $filter['ID'] = $entityId;
5748 }
5749 $res = parent::getList([
5750 'select' => [
5751 'LID',
5752 'SITE_ID' => 'LANDING.SITE_ID',
5753 ],
5754 'filter' => $filter,
5755 ]);
5756 if ($row = $res->fetch())
5757 {
5758 $res = Landing::getList([
5759 'select' => [
5760 'ID'
5761 ],
5762 'filter' => [
5763 'ID' => $row['LID']
5764 ]
5765 ]);
5766 if ($res->fetch())
5767 {
5768 return true;
5769 }
5770
5771 if (\Bitrix\Landing\Site\Scope\Group::getGroupIdBySiteId($row['SITE_ID'], true))
5772 {
5773 return true;
5774 }
5775 }
5776
5777 return false;
5778 }
5779
5784 public static function checkComponentExists(string $componentName): bool
5785 {
5786 $path2Component = \CComponentEngine::MakeComponentPath($componentName);
5787 if ($path2Component !== '')
5788 {
5789 $componentPath = getLocalPath("components" . $path2Component);
5790 $componentFile = $_SERVER["DOCUMENT_ROOT"] . $componentPath . "/component.php";
5791
5792 return file_exists($componentFile) && is_file($componentFile);
5793 }
5794
5795 return false;
5796 }
5797}
getMenuHtml($data, $manifestNode, $level='root')
Definition block.php:4552
save(array $additionalFields=[])
Definition block.php:3136
getManifest($extended=false, $missCache=false, array $params=array())
Definition block.php:2117
includeBlockClass($path)
Definition block.php:2622
static getList($fields=array())
Definition block.php:5661
static getFavorites(?string $tplCode)
Definition block.php:5642
adjustCards($selector, $count, &$changed=false)
Definition block.php:4003
const PRESET_SYM_CODE
Definition block.php:90
static getBlockContent($id, $editMode=false, array $params=array())
Definition block.php:1604
static getAjaxInitiatedAssets()
Definition block.php:5685
static getMessageBlock($params, $template='')
Definition block.php:2668
getCardCount($selector)
Definition block.php:4234
saveAssets(array $assets)
Definition block.php:3067
static getNormalizedBlock(string $code)
Definition block.php:606
static removeAsUsed(string $blockCode)
Definition block.php:1511
checkDesignedManifest(array $manifest)
Definition block.php:2071
static update($id, $fields=array())
Definition block.php:5598
static getManifestFile($code)
Definition block.php:2404
static getBlockNamespace($code)
Definition block.php:1727
static getContentFromRepository(string $code, string $namespace=null)
Definition block.php:647
static getSpecialManifest(string $type)
Definition block.php:1558
updateNodes($data, $additional=array())
Definition block.php:4367
setCardContent($selector, $position, $content)
Definition block.php:4170
const DEFAULT_WRAPPER_STYLE
Definition block.php:95
changeFavoriteMeta(array $meta)
Definition block.php:3200
static $internalClass
Definition block.php:111
saveContent(string $content, $designed=false)
Definition block.php:3108
static getRepository($withManifest=false)
Definition block.php:980
saveDynamicParams(array $sourceParams=[], array $params=[])
Definition block.php:3396
static getLandingRowByBlockId($id, array $select=array('ID'))
Definition block.php:564
static getBlockPath($code, $namespace=null)
Definition block.php:1772
removeStyle(DOM\Node $node, array $styleToRemove)
Definition block.php:4755
static getNamespaces()
Definition block.php:926
static collectAllowedAttrs(array $mixed, array &$allowed, $selector=null)
Definition block.php:4943
static getRowByBlockId($id, array $select=array('ID'))
Definition block.php:575
static add($fields)
Definition block.php:5575
static clearRepositoryCache()
Definition block.php:914
updateCards(array $data=array())
Definition block.php:4636
static getSemantic()
Definition block.php:1540
const FAVOURITE_BLOCKS_LIMIT_WITH_PREVIEW
Definition block.php:105
setAllowedByTariff(bool $mark)
Definition block.php:1882
setRuntimeRequiredUserAction(array $action)
Definition block.php:3952
cloneCard($selector, $position, $content='')
Definition block.php:4060
getDom($clear=false)
Definition block.php:3962
static deleteByCode($code)
Definition block.php:5191
static fillLanding(Landing $landing, $limit=0, array $params=array())
Definition block.php:372
removeCard($selector, $position)
Definition block.php:4252
static createFromRepository(Landing $landing, string $code, array $data=array())
Definition block.php:705
localizationManifest(array &$manifest, array $lang)
Definition block.php:2381
static getLastUsed(int $count=15)
Definition block.php:1449
getCardContent($selector, $position)
Definition block.php:4211
getAsset($type=null)
Definition block.php:2446
static markAsUsed(string $blockCode)
Definition block.php:1478
static deleteAll($lid)
Definition block.php:5226
view($edit=false, \Bitrix\Landing\Landing $landing=null, array $params=array())
Definition block.php:2690
static getAnchor($id)
Definition block.php:1717
static replaceMetaMarkers($content)
Definition block.php:5133
static getLandingIdByBlockId($id)
Definition block.php:529
const PREVIEW_FILE_NAME
Definition block.php:35
static checkComponentExists(string $componentName)
Definition block.php:5784
static isNewBlock($block)
Definition block.php:852
static cloneForEdit(\Bitrix\Landing\Landing $landing)
Definition block.php:467
static isContains(int $entityId, string $needed, bool $isLanding=false)
Definition block.php:5734
__construct($id, $data=[], array $params=[])
Definition block.php:272
static getGeneralPaths()
Definition block.php:891
getDynamicParams($id=null)
Definition block.php:3334
static search($query, array $filter=[], array $select=['LID'], array $group=['LID'])
Definition block.php:5549
static publicationBlocks(\Bitrix\Landing\Landing $landing)
Definition block.php:519
const FAVOURITE_BLOCKS_LIMIT
Definition block.php:100
static getDownloadLink(string $scope, int $blockId, ?int $fileId=null)
Definition diskfile.php:31
static addToBlock(int $blockId, $fileId, bool $temp=false)
Definition file.php:305
static getFilesFromBlockContent($blockId, $content)
Definition file.php:374
static getFilePath($fileId)
Definition file.php:600
static getDynamicFilter()
Definition landing.php:826
static isDynamicDetailPage()
Definition landing.php:851
static getDynamicElementId()
Definition landing.php:842
static createInstance($id, array $params=array())
Definition landing.php:534
static getOption($code, $default=null)
Definition manager.php:160
static getApplication()
Definition manager.php:71
static getCacheManager()
Definition manager.php:89
static setPageTitle($title, $single=false)
Definition manager.php:211
static getTemplateId($siteId=null)
Definition manager.php:508
static getUrlFromFile($file)
Definition manager.php:1067
static blocksPublication(\Bitrix\Landing\Landing $landing, $_591201104=null)
Definition mutator.php:1
static changeNodeType(array $node, \Bitrix\Landing\Block $block)
Definition img.php:397
static getAppByCode($code)
Definition repo.php:293
static getRepository()
Definition repo.php:70
static getById($id)
Definition repo.php:193
static getBlock($id)
Definition repo.php:144
static getAppInfo($id)
Definition repo.php:236
static get(int $id, bool $active=false, bool $force=false)
Definition syspage.php:100
static loadLanguageFile($file, $language=null, $normalize=true)
Definition loc.php:224
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static getList(array $parameters=array())