1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
block.php
См. документацию.
1<?php
2namespace Bitrix\Landing;
3
4use Bitrix\Landing\Block\BlockRepo;
5use Bitrix\Landing\Site\Scope;
6use Bitrix\Landing\Site\Type;
7use \Bitrix\Main\Page\Asset;
8use Bitrix\Main\UI\Extension;
9use \Bitrix\Main\Web\Json;
10use \Bitrix\Main\Web\DOM;
11use \Bitrix\Main\Localization\Loc;
12use \Bitrix\Landing\Connector;
13use \Bitrix\Landing\Controller;
14use \Bitrix\Landing\Internals;
15use \Bitrix\Landing\Assets;
16use \Bitrix\Landing\Block\Cache;
17use \Bitrix\Landing\Restriction;
18use \Bitrix\Landing\Node\Type as NodeType;
19use \Bitrix\Landing\Node\Img;
20use \Bitrix\Landing\PublicAction\Utils as UtilsAction;
21
22Loc::loadMessages(__FILE__);
23
25{
29 public const PREVIEW_FILE_NAME = 'preview.jpg';
30
34 const CSS_FILE_NAME = 'style.css';
35
39 const JS_FILE_NAME = 'script.js';
40
44 const REPO_MASK = '/^repo_([\d]+)$/';
45
49 const ACCESS_A = 'A';
50
54 const ACCESS_D = 'D';
55
59 const ACCESS_V = 'V';
60
64 const ACCESS_W = 'W';
65
69 const ACCESS_X = 'X';
70
74 const CARD_SYM_CODE = 'card';
75
79 const PRESET_SYM_CODE = 'preset';
80
84 public const DEFAULT_WRAPPER_STYLE = ['block-default'];
85
90 public static $internalClass = 'BlockTable';
91
96 protected $id = 0;
97
102 protected $lid = 0;
103
108 protected $parentId = 0;
109
114 protected $siteId = 0;
115
120 protected $sort = 0;
121
126 protected $repoId = 0;
127
132 protected $repoInfo = [];
133
138 protected $code = '';
139
144 protected $anchor = '';
145
150 protected $content = '';
151
157
163 protected $access = 'X';
164
169 protected $metaData = array();
170
175 protected $assets = array();
176
181 protected $active = false;
182
187 protected $landingActive = false;
188
193 protected $deleted = false;
194
199 protected $designed = false;
200
205 protected $public = false;
206
211 protected $allowedByTariff = true;
212
217 protected $docRoot = '';
218
223 protected $error = null;
224
229 protected $dynamicParams = [];
230
236 'landing_form',
237 'landing_carousel',
238 'landing_google_maps_new',
239 'landing_map',
240 'landing_countdown',
241 'landing_gallery_cards',
242 'landing_chat',
243 ];
244
250 'landing.widgetvue',
251 ];
252
259 public function __construct($id, $data = [], array $params = [])
260 {
261 if (empty($data) || !is_array($data))
262 {
263 $data = parent::getList(array(
264 'select' => array(
265 '*',
266 'LANDING_TITLE' => 'LANDING.TITLE',
267 'LANDING_ACTIVE' => 'LANDING.ACTIVE',
268 'LANDING_TPL_CODE' => 'LANDING.TPL_CODE',
269 'SITE_TPL_CODE' => 'LANDING.SITE.TPL_CODE',
270 'SITE_TYPE' => 'LANDING.SITE.TYPE',
271 'SITE_ID' => 'LANDING.SITE_ID',
272 ),
273 'filter' => array(
274 'ID' => (int)$id,
275 ),
276 ))->fetch();
277 if (!$data)
278 {
279 $id = 0;
280 }
281 }
282
283 // if content is empty, fill from repository
284 if (!isset($data['CONTENT']) || trim($data['CONTENT']) == '')
285 {
286 $data['CONTENT'] = '';
287 }
288
289 $this->id = intval($id);
290 $this->lid = isset($data['LID']) ? intval($data['LID']) : 0;
291 $this->parentId = isset($data['PARENT_ID']) ? intval($data['PARENT_ID']) : 0;
292 $this->siteId = isset($data['SITE_ID']) ? intval($data['SITE_ID']) : 0;
293 $this->sort = isset($data['SORT']) ? intval($data['SORT']) : '';
294 $this->code = isset($data['CODE']) ? trim($data['CODE']) : '';
295 $this->anchor = isset($data['ANCHOR']) ? trim($data['ANCHOR']) : '';
296 $this->active = isset($data['ACTIVE']) && $data['ACTIVE'] == 'Y';
297 $this->landingActive = isset($data['LANDING_ACTIVE']) && $data['LANDING_ACTIVE'] == 'Y';
298 $this->deleted = isset($data['DELETED']) && $data['DELETED'] == 'Y';
299 $this->designed = isset($data['DESIGNED']) && $data['DESIGNED'] == 'Y';
300 $this->public = isset($data['PUBLIC']) && $data['PUBLIC'] == 'Y';
301 $this->content = (!$this->deleted && isset($data['CONTENT'])) ? trim($data['CONTENT']) : '';
302
303 // access
304 if (isset($data['ACCESS']))
305 {
306 $this->access = $data['ACCESS'];
307 }
308
309 // assets
310 if (isset($data['ASSETS']))
311 {
312 $this->assets = $data['ASSETS'];
313 }
314
315 // fill meta data
316 $keys = [
317 'LID', 'FAVORITE_META', 'CREATED_BY_ID', 'DATE_CREATE',
318 'MODIFIED_BY_ID', 'DATE_MODIFY', 'SITE_TYPE',
319 ];
320 foreach ($keys as $key)
321 {
322 if (isset($data[$key]))
323 {
324 $this->metaData[$key] = $data[$key];
325 }
326 }
327 $this->metaData['LANDING_TITLE'] = isset($data['LANDING_TITLE']) ? $data['LANDING_TITLE'] : '';
328 $this->metaData['LANDING_TPL_CODE'] = isset($data['LANDING_TPL_CODE']) ? $data['LANDING_TPL_CODE'] : '';
329 $this->metaData['SITE_TPL_CODE'] = isset($data['SITE_TPL_CODE']) ? $data['SITE_TPL_CODE'] : '';
330 $this->metaData['XML_ID'] = isset($data['XML_ID']) ? $data['XML_ID'] : '';
331 $this->metaData['DESIGNER_MODE'] = isset($params['designer_mode']) && $params['designer_mode'] === true;
332
333 // other data
334 if (preg_match(self::REPO_MASK, $this->code, $matches))
335 {
336 $this->repoId = $matches[1];
337 }
338 if (!$this->content && !$this->deleted)
339 {
340 $this->content = self::getContentFromRepository($this->code);
341 }
342 $this->error = new Error;
343 $this->docRoot = Manager::getDocRoot();
344
345 // dynamic params
346 if (isset($data['SOURCE_PARAMS']))
347 {
348 $this->dynamicParams = (array)$data['SOURCE_PARAMS'];
349 }
350 }
351
359 public static function fillLanding(Landing $landing, $limit = 0, array $params = array())
360 {
361 if ($landing->exist())
362 {
363 $editMode = $landing->getEditMode() || $landing->getPreviewMode();
364 $repo = array();
365 $blocks = array();
366 // get all blocks by filter
367 $filter = array(
368 'LID' => $landing->getId(),
369 '=PUBLIC' => $editMode ? 'N' : 'Y',
370 '=DELETED' => (isset($params['deleted']) && $params['deleted'] === true)
371 ? 'Y'
372 : 'N',
373 );
374 if (isset($params['id']) && $params['id'])
375 {
376 $filter['ID'] = $params['id'];
377 }
378 $res = parent::getList([
379 'select' => [
380 '*',
381 'LANDING_ACTIVE' => 'LANDING.ACTIVE',
382 'LANDING_TPL_CODE' => 'LANDING.TPL_CODE',
383 'SITE_TPL_CODE' => 'LANDING.SITE.TPL_CODE',
384 'SITE_TYPE' => 'LANDING.SITE.TYPE',
385 'SITE_ID' => 'LANDING.SITE_ID',
386 ],
387 'filter' => $filter,
388 'order' => [
389 'SORT' => 'ASC',
390 'ID' => 'ASC',
391 ],
392 'limit' => $limit ?: null,
393 ]);
394 while ($row = $res->fetch())
395 {
396 $blockParams = [];
397 if (!$landing->canEdit())
398 {
399 $row['ACCESS'] = self::ACCESS_A;
400 }
401 $row['SITE_ID'] = $landing->getSiteId();
402 $block = new self(
403 $row['ID'],
404 $row,
405 $blockParams
406 );
407 if ($block->getRepoId())
408 {
409 $repo[] = $block->getRepoId();
410 }
411 $blocks[$row['ID']] = $block;
412 }
413 unset($row, $res);
414 if (!empty($repo))
415 {
416 $repo = Repo::getAppInfo($repo);
417 }
418 // add blocks to landing
419 foreach ($blocks as $block)
420 {
421 if (
422 isset($repo[$block->getRepoId()]['PAYMENT_ALLOW'])
423 && $repo[$block->getRepoId()]['PAYMENT_ALLOW'] != 'Y'
424 )
425 {
426 $allowedByTariff = false;
427 }
428 else
429 {
430 $allowedByTariff = true;
431 }
432 if ($editMode)
433 {
434 $block->setAllowedByTariff($allowedByTariff);
435 $landing->addBlockToCollection($block);
436 }
438 {
439 $landing->addBlockToCollection($block);
440 }
441 }
442 unset($blocks, $block, $repo);
443 return true;
444 }
445
446 return false;
447 }
448
454 public static function cloneForEdit(\Bitrix\Landing\Landing $landing)
455 {
456 if ($landing->exist())
457 {
458 $clone = true;
459 $forClone = array();
460
461 $res = parent::getList(array(
462 'select' => array(
463 'ID', 'LID', 'CODE', 'SORT', 'ACTIVE',
464 'CONTENT', 'PUBLIC', 'ACCESS', 'ANCHOR',
465 'DESIGNED',
466 ),
467 'filter' => array(
468 'LID' => $landing->getId(),
469 ),
470 ));
471 while ($row = $res->fetch())
472 {
473 if ($row['PUBLIC'] != 'Y')
474 {
475 $clone = false;
476 break;
477 }
478 else
479 {
480 if (!$row['ANCHOR'])
481 {
482 $row['ANCHOR'] = 'b' . $row['ID'];
483 }
484 $row['PUBLIC'] = 'N';
485 $row['PARENT_ID'] = $row['ID'];
486 unset($row['ID']);
487 $forClone[] = $row;
488 }
489 }
490
491 if ($clone)
492 {
493 foreach ($forClone as $row)
494 {
495 parent::add($row);
496 }
497 }
498 }
499 }
500
506 public static function publicationBlocks(\Bitrix\Landing\Landing $landing)
507 {
509 }
510
516 public static function getLandingIdByBlockId($id)
517 {
518 $data = array();
519 $res = parent::getList(array(
520 'select' => array(
521 'ID', 'LID',
522 ),
523 'filter' => array(
524 'ID' => $id,
525 ),
526 ));
527 while ($row = $res->fetch())
528 {
529 $data[$row['ID']] = $row['LID'];
530 }
531
532 if (is_array($id))
533 {
534 return $data;
535 }
536 elseif (!empty($data))
537 {
538 return array_pop($data);
539 }
540
541 return false;
542 }
543
551 public static function getLandingRowByBlockId($id, array $select = array('ID'))
552 {
553 return self::getRowByBlockId($id, $select);
554 }
555
562 public static function getRowByBlockId($id, array $select = array('ID'))
563 {
564 $data = array();
565 $res = parent::getList(array(
566 'select' => $select,
567 'filter' => array(
568 'ID' => $id,
569 ),
570 ));
571 while ($row = $res->fetch())
572 {
573 $data[$row['ID']] = $row;
574 }
575
576 if (is_array($id))
577 {
578 return $data;
579 }
580 elseif (!empty($data))
581 {
582 return array_pop($data);
583 }
584
585 return false;
586 }
587
593 protected static function getNormalizedBlock(string $code): ?array
594 {
595 static $cached = [];
596
597 if (isset($cached[$code]))
598 {
599 return $cached[$code];
600 }
601
602 $codeOriginal = $code;
603 [$code, $blockId] = explode('@', $code);
604 $filter = [
605 'LID' => 0,
606 '=DELETED' => 'N',
607 '=CODE' => $code,
608 ];
609 if ($blockId)
610 {
611 $filter['ID'] = $blockId;
612 }
613 $res = Internals\BlockTable::getList([
614 'select' => [
615 'ID', 'CODE', 'CONTENT', 'SOURCE_PARAMS', 'DESIGNED',
616 ],
617 'filter' => $filter,
618 ]);
619 if ($row = $res->fetch())
620 {
621 $cached[$codeOriginal] = $row;
622 $cached[$codeOriginal]['FILES'] = File::getFilesFromBlockContent($row['ID'], $row['CONTENT']);
623 }
624
625 return $cached[$codeOriginal] ?? null;
626 }
627
634 public static function getContentFromRepository(string $code, string $namespace = null): ?string
635 {
636 if (!is_string($code))
637 {
638 return null;
639 }
640
641 if (strpos($code, '@'))
642 {
643 $normalizedBlock = self::getNormalizedBlock($code);
644 return $normalizedBlock['CONTENT'] ?? null;
645 }
646
647 $content = null;
648
649 // local repo
650 if (preg_match(self::REPO_MASK, $code, $matches))
651 {
652 $repo = Repo::getById($matches[1])->fetch();
653 $content = $repo['CONTENT'];
654 }
655 // files storage
656 elseif ($path = self::getBlockPath($code, $namespace))
657 {
658 $path = Manager::getDocRoot() . $path . '/block.php';
659 if (file_exists($path))
660 {
661 $content = file_get_contents($path);
662 if (preg_match('/MESS\[[^\]]+\]/', $content))
663 {
664 $mess = Loc::loadLanguageFile($path);
665 if ($mess)
666 {
667 $replace = [];
668 foreach ($mess as $key => $title)
669 {
670 $replace['MESS[' . $key . ']'] = $title;
671 }
672 $content = str_replace(
673 array_keys($replace),
674 array_values($replace),
676 );
677 }
678 }
679 }
680 }
681
682 return $content;
683 }
684
692 public static function createFromRepository(Landing $landing, string $code, array $data = array()): bool|Block
693 {
694 $blockRepo = (new BlockRepo())->disableFilter(BlockRepo::FILTER_SKIP_SYSTEM_BLOCKS);
695 if (!$blockRepo->isBlockInRepo($code))
696 {
697 $landing->getError()->addError(
698 'BLOCK_CANT_BE_ADDED',
699 Loc::getMessage('LANDING_BLOCK_CANT_BE_ADDED')
700 );
701
702 return false;
703 }
704 $blockRepo->enableFilter(BlockRepo::FILTER_DEFAULTS);
705
706 // get content and manifest
707 $filesFromContent = [];
708 $sourceParams = [];
709 $codeOriginal = null;
710 $designed = 'N';
711 $content = $data['CONTENT'] ?? self::getContentFromRepository($code);
712 if (isset($data['PREPARE_BLOCK_DATA']['ACTION']))
713 {
714 if (
715 $data['PREPARE_BLOCK_DATA']['ACTION'] === 'changeComponentParams'
716 && isset($data['PREPARE_BLOCK_DATA']['PARAMS'])
717 && is_array($data['PREPARE_BLOCK_DATA']['PARAMS'])
718 )
719 {
720 foreach ($data['PREPARE_BLOCK_DATA']['PARAMS'] as $paramName => $paramValue)
721 {
722 $search = "'" . $paramName . "' => '',";
723 $replace = "'" . $paramName . "' => '". $paramValue . "',";
724 $content = str_replace($search, $replace, $content);
725 }
726 }
727 }
728 if (strpos($code, '@'))
729 {
730 $codeOriginal = $code;
731 $normalizedBlock = self::getNormalizedBlock($code);
732 $designed = $normalizedBlock['DESIGNED'] ?? 'N';
733 $filesFromContent = $normalizedBlock['FILES'] ?? [];
734 $sourceParams = $normalizedBlock['SOURCE_PARAMS'] ?? [];
735 [$code, ] = explode('@', $code);
736 }
737 $manifest = self::getManifestFile($code);
738 // version control
739 if (
740 isset($manifest['block']['version']) &&
741 version_compare(Manager::getVersion(), $manifest['block']['version']) < 0
742 )
743 {
744 $landing->getError()->addError(
745 'BLOCK_WRONG_VERSION',
746 Loc::getMessage('LANDING_BLOCK_WRONG_VERSION')
747 );
748 return false;
749 }
750 // check errors
751 if (!$landing->exist())
752 {
753 $landing->getError()->addError(
754 'LANDING_NOT_EXIST',
755 Loc::getMessage('LANDING_BLOCK_LANDING_NOT_EXIST')
756 );
757 return false;
758 }
759 if ($content == '')
760 {
761 $landing->getError()->addError(
762 'BLOCK_NOT_FOUND',
763 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
764 );
765 return false;
766 }
767 // add
768 $fields = array(
769 'LID' => $landing->getId(),
770 'CODE' => $code,
771 'SOURCE_PARAMS' => $sourceParams,
772 'CONTENT' => $content,
773 'ACTIVE' => 'Y',
774 'DESIGNED' => $designed,
775 );
776 $availableReplace = array(
777 'ACTIVE', 'PUBLIC', 'ACCESS', 'SORT',
778 'CONTENT', 'ANCHOR', 'SOURCE_PARAMS',
779 'INITIATOR_APP_CODE', 'XML_ID',
780 'DESIGNED', 'FAVORITE_META',
781 );
782 foreach ($availableReplace as $replace)
783 {
784 if (isset($data[$replace]))
785 {
786 $fields[$replace] = $data[$replace];
787 }
788 }
789 $res = parent::add($fields);
790 if ($res->isSuccess())
791 {
792 $block = new self($res->getId());
793 $manifest = $block->getManifest();
794 if (!$block->getLocalAnchor())
795 {
796 $historyActivity = History::isActive();
797 History::deactivate();
798 $block->setAnchor('b' . $block->getId());
799 $historyActivity ? History::activate() : History::deactivate();
800 }
801 Assets\PreProcessing::blockAddProcessing($block);
802 if (
803 isset($manifest['callbacks']['afteradd']) &&
804 is_callable($manifest['callbacks']['afteradd'])
805 )
806 {
807 $manifest['callbacks']['afteradd']($block);
808 }
809 // calling class(es) of block
810 foreach ($block->getClass() as $class)
811 {
812 $classBlock = $block->includeBlockClass($class);
813 $classBlock->beforeAdd($block);
814 }
815 // for set filter
816 if ($fields['SOURCE_PARAMS'])
817 {
818 $block->saveDynamicParams(
819 $fields['SOURCE_PARAMS']
820 );
821 }
822 self::prepareBlockContentFromRepository($block);
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
847 protected static function prepareBlockContentFromRepository($block): void
848 {
849 $blockContent = $block->getContent();
850 if (mb_strpos($blockContent, '#YEAR#') !== false)
851 {
852 $replace = [];
853 $replace['#YEAR#'] = date("Y");
854 $blockContent = str_replace(
855 array_keys($replace),
856 array_values($replace),
857 $blockContent
858 );
859 $block->saveContent($blockContent);
860 }
861
862 if (mb_strpos($blockContent, '#COUNTDOWN#') !== false)
863 {
864 $replace = [];
865 $replace['#COUNTDOWN#'] = Countdown::getTimestamp();
866 $blockContent = str_replace(
867 array_keys($replace),
868 array_values($replace),
869 $blockContent
870 );
871 $block->saveContent($blockContent);
872 }
873
874 self::replaceVideoPlaceholders($block);
875 }
876
877 private static function replaceVideoPlaceholders($block): void
878 {
879 $blockContent = $block->getContent();
880 if (mb_strpos($blockContent, '#DEFAULT_VIDEO_SRC#') !== false)
881 {
882 if (Manager::getZone() === 'ru')
883 {
884 $replace = [
885 '#DEFAULT_VIDEO_SRC#' => 'data-src=""',
886 '#DEFAULT_VIDEO_SOURCE#' => 'data-source=""',
887 '#DEFAULT_VIDEO_PREVIEW#' => 'data-preview=""',
888 '#DEFAULT_VIDEO_STYLE#' => 'style=""',
889 '#DEFAULT_VIDEO_SRC_2#' => 'data-src=""',
890 '#DEFAULT_VIDEO_SOURCE_2#' => 'data-source=""',
891 '#DEFAULT_VIDEO_PREVIEW_2#' => 'data-preview=""',
892 '#DEFAULT_VIDEO_STYLE_2#' => 'style=""',
893 ];
894 }
895 else
896 {
897 $replace = [
898 '#DEFAULT_VIDEO_SRC#' => 'data-src="//www.youtube.com/embed/q4d8g9Dn3ww?autoplay=0&controls=1&loop=1&mute=0&rel=0"',
899 '#DEFAULT_VIDEO_SOURCE#' => 'data-source="https://www.youtube.com/watch?v=q4d8g9Dn3ww"',
900 '#DEFAULT_VIDEO_PREVIEW#' => 'data-preview="//img.youtube.com/vi/q4d8g9Dn3ww/sddefault.jpg"',
901 '#DEFAULT_VIDEO_STYLE#' => 'style="background-image:url(//img.youtube.com/vi/q4d8g9Dn3ww/sddefault.jpg)"',
902 '#DEFAULT_VIDEO_SRC_2#' => 'data-src="//www.youtube.com/embed/IISycTRZ-UA?autoplay=0&controls=1&loop=1&mute=0&rel=0"',
903 '#DEFAULT_VIDEO_SOURCE_2#' => 'data-source="https://www.youtube.com/watch?v=IISycTRZ-UA"',
904 '#DEFAULT_VIDEO_PREVIEW_2#' => 'data-preview="//img.youtube.com/vi/q4d8g9Dn3ww/sddefault.jpg"',
905 '#DEFAULT_VIDEO_STYLE_2#' => 'style="background-image:url(//img.youtube.com/vi/IISycTRZ-UA/sddefault.jpg)"',
906 ];
907 }
908 $blockContent = str_replace(
909 array_keys($replace),
910 array_values($replace),
911 $blockContent
912 );
913 $block->saveContent($blockContent);
914 }
915 }
916
922 protected static function isNewBlock($block)
923 {
924 static $newBlocks = null;
925
926 if (!is_string($block))
927 {
928 return false;
929 }
930
931 if ($newBlocks === null)
932 {
933 $newBlocks = unserialize(Manager::getOption('new_blocks'), ['allowed_classes' => false]);
934 if (!is_array($newBlocks))
935 {
936 $newBlocks = array();
937 }
938 if (
939 !isset($newBlocks['date']) ||
940 (
941 isset($newBlocks['date']) &&
942 ((time() - $newBlocks['date']) > BlockRepo::NEW_BLOCK_LT)
943 )
944 )
945 {
946 $newBlocks = array();
947 }
948 if (isset($newBlocks['items']))
949 {
950 $newBlocks = $newBlocks['items'];
951 }
952 }
953
954 return in_array($block, $newBlocks);
955 }
956
961 public static function clearRepositoryCache(): void
962 {
963 (new BlockRepo())->clearCache();
964 }
965
971 public static function getRepository()
972 {
973 return (new BlockRepo())->getRepository();
974 }
975
981 public static function getLastUsed(int $count = 15): array
982 {
983 $res = Internals\BlockLastUsedTable::getList([
984 'select' => [
985 'CODE',
986 ],
987 'filter' => [
988 'USER_ID' => Manager::getUserId(),
989 ],
990 'order' => [
991 'DATE_CREATE' => 'DESC',
992 ],
993 'limit' => $count ?: null,
994 ]);
995 $blocks = [];
996 while ($row = $res->fetch())
997 {
998 $blocks[] = $row['CODE'];
999 }
1000
1001 return $blocks;
1002 }
1003
1009 public static function markAsUsed(string $blockCode): void
1010 {
1011 $res = Internals\BlockLastUsedTable::getList([
1012 'select' => [
1013 'ID',
1014 ],
1015 'filter' => [
1016 'USER_ID' => Manager::getUserId(),
1017 '=CODE' => $blockCode,
1018 ],
1019 'limit' => 1,
1020 ]);
1021 if ($row = $res->fetch())
1022 {
1023 Internals\BlockLastUsedTable::update($row['ID'], [
1024 'DATE_CREATE' => new \Bitrix\Main\Type\DateTime,
1025 ]);
1026 }
1027 else
1028 {
1029 Internals\BlockLastUsedTable::add([
1030 'CODE' => $blockCode,
1031 'USER_ID' => Manager::getUserId(),
1032 'DATE_CREATE' => new \Bitrix\Main\Type\DateTime,
1033 ]);
1034 }
1035 }
1036
1042 public static function removeAsUsed(string $blockCode): void
1043 {
1044 $res = Internals\BlockLastUsedTable::getList([
1045 'select' => [
1046 'ID',
1047 ],
1048 'filter' => [
1049 '=CODE' => $blockCode,
1050 ],
1051 ]);
1052 while ($row = $res->fetch())
1053 {
1054 Internals\BlockLastUsedTable::delete($row['ID']);
1055 }
1056 }
1057
1062 public static function getStyle(): array
1063 {
1064 return self::getSpecialManifest('style');
1065 }
1066
1071 public static function getSemantic(): array
1072 {
1073 return self::getSpecialManifest('semantic');
1074 }
1075
1080 public static function getAttrs(): array
1081 {
1082 return self::getSpecialManifest('attrs');
1083 }
1084
1090 protected static function getSpecialManifest(string $type): array
1091 {
1092 static $style = [];
1093
1094 if (array_key_exists($type, $style))
1095 {
1096 return $style[$type];
1097 }
1098
1099 $style[$type] = [];
1101
1102 // read all subdirs ($namespaces) in block dir
1103 foreach ($paths as $path)
1104 {
1106 if (($handle = opendir($path)))
1107 {
1108 while ((($entry = readdir($handle)) !== false))
1109 {
1110 if (
1111 $entry != '.' && $entry != '..' &&
1112 is_dir($path . '/' . $entry) &&
1113 file_exists($path . '/' . $entry . '/.' . $type . '.php')
1114 )
1115 {
1116 $style[$type][$entry] = include $path . '/' . $entry . '/.' . $type . '.php';
1117 if (!is_array($style[$type][$entry]))
1118 {
1119 unset($style[$type][$entry]);
1120 }
1121 }
1122 }
1123 }
1124 }
1125
1126 return $style[$type];
1127 }
1128
1136 public static function getBlockContent($id, $editMode = false, array $params = array())
1137 {
1138 if (!isset($params['wrapper_show']))
1139 {
1140 $params['wrapper_show'] = true;
1141 }
1142 if ($editMode)
1143 {
1144 $params['force_unactive'] = true;
1145 }
1146 $params['skip_system_script'] = true;
1147
1148 ob_start();
1149 $id = intval($id);
1150 $block = new self($id);
1151 $extContent = '';
1152 $lang = [];
1153 if (($ext = $block->getExt()))
1154 {
1155 foreach ($ext as $extCode)
1156 {
1157 $extInfo =
1158 \CJSCore::getExtInfo($extCode)
1159 ?? Extension::getConfig($extCode)
1160 ;
1161
1162 if (!empty($extInfo['lang']) && is_string($extInfo['lang']))
1163 {
1164 $messages = Loc::loadLanguageFile($_SERVER['DOCUMENT_ROOT'] . $extInfo['lang']);
1165 $lang = array_merge($lang, $messages);
1166 }
1167 }
1168
1169 $extContent = \CUtil::initJSCore($ext, true);
1170 $extContent = preg_replace(
1171 '#<script(\sdata\-skip\-moving\="true")?>.*?</script>#is',
1172 '',
1173 $extContent
1174 );
1175 }
1176 $landing = Landing::createInstance(
1177 $block->getLandingId(),
1178 [
1179 'skip_blocks' => true,
1180 ]
1181 );
1182 if ($editMode)
1183 {
1185 }
1186 $block->view(
1187 $editMode,
1188 $landing->exist() ? $landing : null,
1189 $params
1190 );
1191 if ($editMode)
1192 {
1194 }
1195 $content = ob_get_contents();
1196 $content = self::replaceMetaMarkers($content);
1197 if ($landing->exist())
1198 {
1199 if (mb_strpos($content, '#crm') !== false)
1200 {
1201 $replace = Connector\Crm::getReplacesForContent($landing->getSiteId(), false);
1202 $content = str_replace(
1203 array_keys($replace),
1204 array_values($replace),
1205 $content
1206 );
1207 }
1208 if (mb_strpos($content, '#YEAR#') !== false)
1209 {
1210 $replace = [];
1211 $replace['#YEAR#'] = date("Y");
1212 $content = str_replace(
1213 array_keys($replace),
1214 array_values($replace),
1215 $content
1216 );
1217 }
1218 }
1219 ob_end_clean();
1220 if ($block->exist())
1221 {
1222 Manager::getApplication()->restartBuffer();
1223
1224 $availableJS = !$editMode || !$block->getRepoId();
1225 $assetsManager = Assets\Manager::getInstance();
1226
1227 $manifest = $block->getManifest();
1228 if (
1229 !isset($manifest['requiredUserAction']) &&
1230 $block->getRuntimeRequiredUserAction()
1231 )
1232 {
1233 $manifest['requiredUserAction'] = $block->getRuntimeRequiredUserAction();
1234 }
1235 $sections = (array)(($manifest['block']['section']) ?? null);
1236 $return = array(
1237 'id' => $id,
1238 'sections' => implode(',', $sections),
1239 'active' => $block->isActive(),
1240 'access' => $block->getAccess(),
1241 'anchor' => $block->getLocalAnchor(),
1242 'php' => mb_strpos($block->getContent(), '<?') !== false,
1243 'designed' => $block->isDesigned(),
1244 'repoId' => $block->repoId ? (int)$block->repoId : null,
1245 'content' => $content,
1246 'content_ext' => $extContent,
1247 'css' => $block->getCSS(),
1248 'js' => $availableJS ? $block->getJS() : array(),
1249 'assetStrings' => $assetsManager->getStrings(),
1250 'lang' => $lang,
1251 'manifest' => $manifest,
1252 'dynamicParams' => $block->dynamicParams,
1253 );
1254 if (
1255 $editMode &&
1256 isset($return['manifest']['requiredUserAction'])
1257 )
1258 {
1259 $return['requiredUserAction'] = $return['manifest']['requiredUserAction'];
1260 }
1261
1262 // add ajax initiated assets to output
1263 $ajaxAssets = self::getAjaxInitiatedAssets();
1264 $return['js'] = array_merge($return['js'], $ajaxAssets['js']);
1265 $return['css'] = array_merge($return['css'], $ajaxAssets['css']);
1266 // todo: what about strings, langs?
1267 // todo: what about core.js in strings. And etc relative extensions, which already init
1268
1269 return $return;
1270 }
1271 else
1272 {
1273 return array();
1274 }
1275 }
1276
1282 public static function getAnchor($id)
1283 {
1284 return 'block' . (int)$id;
1285 }
1286
1292 protected static function getBlockNamespace($code)
1293 {
1294 static $paths = array();
1295 static $namespace = array();
1296
1297 if (!is_string($code))
1298 {
1299 return '';
1300 }
1301
1302 $code = trim($code);
1303
1304 if (isset($paths[$code]))
1305 {
1306 return $paths[$code];
1307 }
1308
1309 $paths[$code] = '';
1310
1311 $namespaces = BlockRepo::getNamespaces();
1312 $generalPaths = BlockRepo::getGeneralPaths();
1313
1314 // get first needed block from end
1315 foreach (array_reverse($namespaces) as $subdir)
1316 {
1317 foreach ($generalPaths as $path)
1318 {
1320 if (file_exists($path . '/' . $subdir . '/' . $code . '/.description.php'))
1321 {
1322 $paths[$code] = $subdir;
1323 break 2;
1324 }
1325 }
1326 }
1327
1328 return $paths[$code];
1329 }
1330
1337 protected static function getBlockPath($code, $namespace = null)
1338 {
1339 if (!is_string($code))
1340 {
1341 return '';
1342 }
1343
1344 if (strpos($code, '@'))
1345 {
1346 [$code, ] = explode('@', $code);
1347 }
1348
1349 if (!$namespace)
1350 {
1351 $namespace = self::getBlockNamespace($code);
1352 }
1353 if ($namespace)
1354 {
1355 return \getLocalPath(
1356 BlockRepo::BLOCKS_DIR . '/' . $namespace . '/' . $code
1357 );
1358 }
1359
1360 return '';
1361 }
1362
1367 public function exist()
1368 {
1369 return $this->id > 0;
1370 }
1371
1376 public function getId()
1377 {
1378 return $this->id;
1379 }
1380
1385 public function getLandingId()
1386 {
1387 return $this->lid;
1388 }
1389
1394 public function getSiteId()
1395 {
1396 return $this->siteId;
1397 }
1398
1403 public function getCode()
1404 {
1405 return $this->code;
1406 }
1407
1412 public function getLocalAnchor()
1413 {
1414 return $this->anchor;
1415 }
1416
1421 public function getContent()
1422 {
1423 return $this->content;
1424 }
1425
1430 public function getBlockClass()
1431 {
1432 $class = $this->getClass();
1433 $class = !empty($class) ? $class[0] : null;
1434 if ($class !== null)
1435 {
1436 return $this->includeBlockClass($class);
1437 }
1438
1439 return null;
1440 }
1441
1447 public function setAllowedByTariff(bool $mark): void
1448 {
1449 $this->allowedByTariff = $mark;
1450 }
1451
1456 public function resetContent()
1457 {
1458 $data = parent::getList([
1459 'select' => [
1460 'CONTENT',
1461 ],
1462 'filter' => [
1463 'ID' => $this->id,
1464 ],
1465 ])->fetch();
1466 if ($data)
1467 {
1468 $this->content = $data['CONTENT'];
1469 }
1470 }
1471
1476 public function isActive()
1477 {
1478 return $this->active;
1479 }
1480
1485 public function isPublic()
1486 {
1487 return $this->public;
1488 }
1489
1494 public function isDesigned(): bool
1495 {
1496 return $this->designed;
1497 }
1498
1503 public function getAccess()
1504 {
1505 return $this->access;
1506 }
1507
1513 public function setAccess($letter)
1514 {
1515 if (is_string($letter))
1516 {
1517 $this->access = $letter;
1518 }
1519 }
1520
1526 public function setActive($active)
1527 {
1528 if ($this->access < $this::ACCESS_W)
1529 {
1530 $this->error->addError(
1531 'ACCESS_DENIED',
1532 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
1533 );
1534 return false;
1535 }
1536 $this->active = (boolean) $active;
1537 return true;
1538 }
1539
1544 public function getRepoId()
1545 {
1546 return $this->repoId;
1547 }
1548
1553 public function getSite()
1554 {
1555 static $site = null;
1556
1557 if (
1558 $site === null &&
1559 $this->siteId
1560 )
1561 {
1562 $site = Site::getList(array(
1563 'filter' => array(
1564 'ID' => $this->siteId,
1565 ),
1566 ))->fetch();
1567 }
1568
1569 return $site;
1570 }
1571
1576 public function getPreview()
1577 {
1578 $path = self::getBlockPath($this->code);
1579 if ($path && file_exists($this->docRoot . '/' . $path . '/' . self::PREVIEW_FILE_NAME))
1580 {
1581 return $path . '/' . self::PREVIEW_FILE_NAME;
1582 }
1583 return '';
1584 }
1585
1590 public function getError()
1591 {
1592 return $this->error;
1593 }
1594
1604 protected function getTypeClass($type)
1605 {
1606 return Node\Type::getClassName($type);
1607 }
1608
1613 protected function parseManifest(): array
1614 {
1615 static $manifests = [];
1616
1617 if (!$this->id || !$this->designed)
1618 {
1619 return [];
1620 }
1621 if (array_key_exists($this->id, $manifests))
1622 {
1623 return $manifests[$this->id];
1624 }
1625
1626 $manifests[$this->id] = Block\Designer::parseManifest($this->content);
1627
1628 return $manifests[$this->id];
1629 }
1630
1636 protected function checkDesignedManifest(array $manifest): array
1637 {
1638 if (isset($manifest['block']['name']))
1639 {
1640 $designerBlockManifest = $this->parseManifest();
1641 if (!empty($designerBlockManifest['nodes']))
1642 {
1643 foreach ($designerBlockManifest['nodes'] as $keyNode => $node)
1644 {
1645 if (isset($manifest['nodes'][$keyNode]))
1646 {
1647 continue;
1648 }
1649 $node['code'] = $keyNode;
1650 $class = Node\Type::getClassName($node['type']);
1651 if (isset($node['type']) && class_exists($class))
1652 {
1653 $node['handler'] = call_user_func(
1654 [
1655 $class,
1656 'getHandlerJS',
1657 ]
1658 );
1659 $manifest['nodes'][$keyNode] = $node;
1660 }
1661 }
1662 }
1663 if (!empty($designerBlockManifest['style']))
1664 {
1665 $manifest['style']['nodes'] = array_merge(
1666 $designerBlockManifest['style'],
1667 $manifest['style']['nodes']
1668 );
1669 }
1670 }
1671
1672 return $manifest;
1673 }
1674
1682 public function getManifest(bool $extended = false, bool $missCache = false, array $params = array()): array
1683 {
1684 $manifest = self::getManifestInternal($missCache, $params);
1685
1686 // prepare manifest
1687 if (isset($manifest['block']['name']))
1688 {
1689 // set empty array if no exists
1690 foreach (['cards', 'nodes', 'attrs', 'menu'] as $code)
1691 {
1692 if (!isset($manifest[$code]) || !is_array($manifest[$code]))
1693 {
1694 $manifest[$code] = array();
1695 }
1696 }
1697
1698 // prepare every node
1699 foreach ($manifest['nodes'] as $keyNode => &$node)
1700 {
1701 if (is_callable($node) && !$this->repoId)
1702 {
1703 $node = $node();
1704 }
1705 $node['code'] = $keyNode;
1706 $class = Node\Type::getClassName($node['type']);
1707 if (isset($node['type']) && class_exists($class))
1708 {
1709 $node['handler'] = call_user_func(array(
1710 $class,
1711 'getHandlerJS',
1712 ));
1713 if (method_exists($class, 'prepareManifest'))
1714 {
1715 $node = call_user_func_array(array(
1716 $class,
1717 'prepareManifest',
1718 ), array(
1719 $this,
1720 $node,
1721 &$manifest,
1722 ));
1723 if (!is_array($node))
1724 {
1725 unset($manifest['nodes'][$keyNode]);
1726 }
1727 }
1728 }
1729 else
1730 {
1731 unset($manifest['nodes'][$keyNode]);
1732 }
1733 }
1734 unset($node);
1735 // and attrs
1736 foreach ($manifest['attrs'] as $keyNode => &$node)
1737 {
1738 if (is_callable($node) && !$this->repoId)
1739 {
1740 $node = $node();
1741 }
1742 }
1743 unset($node);
1744
1745 // prepare styles
1746 if (!isset($manifest['namespace']))
1747 {
1748 $manifest['namespace'] = $this->getBlockNamespace($this->code);
1749 }
1750 if (
1751 isset($manifest['style'])
1752 && !(
1753 isset($manifest['style']['block'])
1754 && isset($manifest['style']['nodes'])
1755 && count($manifest['style']) == 2
1756 )
1757 )
1758 {
1759 $manifest['style'] = [
1760 'block' => [],
1761 'nodes' => is_array($manifest['style'])
1762 ? $manifest['style']
1763 : [],
1764 ];
1765 }
1766 elseif (
1767 !isset($manifest['style'])
1768 || !is_array($manifest['style'])
1769 )
1770 {
1771 $manifest['style'] = [
1772 'block' => [],
1773 'nodes' => [],
1774 ];
1775 }
1776
1777 // default block types
1778 if (
1779 !is_array($manifest['style']['block'])
1780 || empty($manifest['style']['block'])
1781 )
1782 {
1783 $manifest['style']['block'] = ['type' => self::DEFAULT_WRAPPER_STYLE];
1784 $manifest['block']['section'] = (array)$manifest['block']['section'];
1785 if (
1786 Site\Type::getCurrentScopeId() === 'MAINPAGE'
1787 && !in_array('widgets_separators', $manifest['block']['section'], true)
1788 )
1789 {
1790 $manifest['style']['block']['type'][] = 'widget-type';
1791 }
1792 }
1793
1794 // fake nodes for images from style
1795 $styleNodes = [];
1796 foreach ($manifest['style']['nodes'] as $selector => $styleNode)
1797 {
1798 if (!isset($manifest['nodes'][$selector]) && isset($styleNode['type']) && is_array($styleNode))
1799 {
1800 $styleNodes[$selector] = is_array($styleNode['type']) ? $styleNode['type'] : [$styleNode['type']];
1801 }
1802 }
1803 $styleNodes['#wrapper'] = is_array($manifest['style']['block']['type'])
1804 ? $manifest['style']['block']['type']
1805 : [$manifest['style']['block']['type']];
1806
1807 foreach ($styleNodes as $selector => $type)
1808 {
1809 if (!empty(array_intersect($type, Node\StyleImg::STYLES_WITH_IMAGE)))
1810 {
1811 $manifest['nodes'][$selector] = [
1812 'type' => Node\Type::STYLE_IMAGE,
1813 'code' => $selector,
1814 ];
1815 }
1816 }
1817
1818 // other
1819 $manifest['code'] = $this->code;
1820 }
1821 else
1822 {
1823 $manifest = [];
1824 }
1825
1826 $manifest['preview'] = $this->getPreview();
1827
1828 // localization
1829 if (
1830 isset($manifest['lang'])
1831 && isset($manifest['lang_original'])
1832 && is_array($manifest['lang'])
1833 )
1834 {
1835 // detect translated messages
1836 $lang = null;
1837 $langPortal = LANGUAGE_ID;
1838 if (in_array($langPortal, ['ru', 'kz', 'by', 'uz']))
1839 {
1840 $langPortal = 'ru';
1841 }
1842 $langArray = $manifest['lang'];
1843 $langOrig = $manifest['lang_original'];
1844 if (isset($langArray[$langPortal]))
1845 {
1846 $lang = $langArray[$langPortal];
1847 }
1848 elseif (
1849 $langOrig != $langPortal
1850 && isset($langArray['en'])
1851 )
1852 {
1853 $lang = $langArray['en'];
1854 }
1855 // replace all 'name' keys in manifest
1856 if ($lang)
1857 {
1858 $this->localizationManifest(
1859 $manifest,
1860 $lang
1861 );
1862 }
1863 unset($manifest['lang']);
1864 }
1865
1866 return $this->checkDesignedManifest($manifest);
1867 }
1868
1875 private function getManifestInternal(bool $missCache = false): array
1876 {
1877 static $manifestsBase = [];
1878 static $manifestsFull = [];
1879
1880 $code = $this->getCode();
1881 $id = $this->getId();
1882
1883 if (
1884 !$missCache
1885 && !empty($manifestsBase[$code])
1886 && (
1887 !isset($manifestsBase[$code]['disableCache'])
1888 || $manifestsBase[$code]['disableCache'] !== true
1889 )
1890 )
1891 {
1892 $manifest = $manifestsBase[$code];
1893 }
1894 else
1895 {
1896 if ($this->repoId)
1897 {
1898 $manifest = Repo::getBlock($this->repoId);
1899 }
1900 elseif ($path = self::getBlockPath($code))
1901 {
1902 // isolate variables from .description.php
1903 $includeDesc = function ($path)
1904 {
1905 Loc::loadLanguageFile($path . '/.description.php');
1906 $manifest = include $path . '/.description.php';
1907 $manifest['timestamp'] = file_exists($path . '/block.php')
1908 ? filectime($path . '/block.php')
1909 : time();
1910
1911 return $manifest;
1912 };
1913
1914 $manifest = $includeDesc($this->docRoot . $path);
1915 }
1916 }
1917
1918 if (!isset($manifest['block']['name']))
1919 {
1920 return [];
1921 }
1922
1923 $manifest = Type::prepareBlockManifest($manifest);
1924 $manifestsBase[$code] = $manifest;
1925
1926 if (isset($manifestsFull[$code][$id]))
1927 {
1928 return $manifestsFull[$code][$id];
1929 }
1930
1931 // prepare by subtype
1932 if (
1933 isset($manifest['block']['subtype'])
1934 && (
1935 !isset($params['miss_subtype'])
1936 || $params['miss_subtype'] !== true
1937 )
1938 )
1939 {
1940 $subtypes = (array)$manifest['block']['subtype'];
1941 foreach ($subtypes as $subtype)
1942 {
1943 $subtypeClass = '\\Bitrix\\Landing\\Subtype\\';
1944 $subtypeClass .= $subtype;
1945 if (class_exists($subtypeClass))
1946 {
1947 $manifest = $subtypeClass::prepareManifest(
1948 $manifest,
1949 $this,
1950 isset($manifest['block']['subtype_params'])
1951 ? (array)$manifest['block']['subtype_params']
1952 : array()
1953 );
1954 }
1955 }
1956 }
1957
1958 // callbacks
1959 if (
1960 isset($manifest['callbacks'])
1961 && is_array($manifest['callbacks'])
1962 )
1963 {
1964 $callbacks = [];
1965 foreach ($manifest['callbacks'] as $name => $callback)
1966 {
1967 $callbacks[mb_strtolower($name)] = $callback;
1968 }
1969 $manifest['callbacks'] = $callbacks;
1970 }
1971
1972 $manifestsFull[$code][$id] = $manifest;
1973
1974 return $manifest;
1975 }
1976
1983 protected function localizationManifest(array &$manifest, array $lang)
1984 {
1985 foreach ($manifest as $key => &$value)
1986 {
1987 if (is_array($value))
1988 {
1989 $this->localizationManifest($value, $lang);
1990 }
1991 if (
1992 $key == 'name' &&
1993 isset($lang[$value])
1994 )
1995 {
1996 $value = $lang[$value];
1997 }
1998 }
1999 }
2000
2006 public static function getManifestFile($code)
2007 {
2008 static $manifests = array();
2009
2010 if (!is_string($code))
2011 {
2012 return [];
2013 }
2014
2015 if (preg_match('/[^a-z0-9_.:-]+/i', $code))
2016 {
2017 return [];
2018 }
2019
2020 if (isset($manifests[$code]))
2021 {
2022 return $manifests[$code];
2023 }
2024
2025 $manifests[$code] = array();
2026 $namespace = null;
2027
2028 if (mb_strpos($code, ':') !== false)
2029 {
2030 [$namespace, $code] = explode(':', $code);
2031 }
2032
2033 if ($path = self::getBlockPath($code ,$namespace))
2034 {
2036 Loc::loadLanguageFile($docRoot . $path . '/.description.php');
2037 $manifests[$code] = include $docRoot . $path . '/.description.php';
2038 }
2039
2040 return $manifests[$code];
2041 }
2042
2048 public function getAsset($type = null)
2049 {
2050 $manifest = $this->getManifestInternal();
2051 $asset = [
2052 'css' => [],
2053 'js' => [],
2054 'ext' => [],
2055 'class' => [],
2056 'callbacks' => [],
2057 ];
2058
2059 if (isset($manifest['block']['namespace']))
2060 {
2061 $classFile = BlockRepo::BLOCKS_DIR;
2062 $classFile .= '/' . $manifest['block']['namespace'] . '/';
2063 $classFile .= $this->getCode() . '/class.php';
2064 $classFile = \getLocalPath($classFile);
2065 if ($classFile)
2066 {
2067 $asset['class'][] = $this->docRoot . $classFile;
2068 }
2069 }
2070
2071 foreach (array_keys($asset) as $ass)
2072 {
2073 if (!empty($manifest['assets'][$ass]))
2074 {
2075 foreach ($manifest['assets'][$ass] as $file)
2076 {
2077 if (!is_string($file))
2078 {
2079 continue;
2080 }
2081 if ($ass !== 'ext')
2082 {
2083 $asset[$ass][] = trim($file);
2084 }
2085 // for rest block allowed only this
2086 elseif (
2087 !$this->repoId
2088 || in_array($file, $this->allowedExtensions)
2089 )
2090 {
2091 $asset[$ass][] = trim($file);
2092 }
2093 elseif (
2094 $this->repoId
2095 && in_array($file, $this->allowedRepoExtensions)
2096 )
2097 {
2098 $asset[$ass][] = trim($file);
2099 }
2100 }
2101 $asset[$ass] = array_unique($asset[$ass]);
2102 }
2103 }
2104
2105 // next is phis files
2106 $path = self::getBlockPath($this->code);
2107 if (
2108 $path
2109 && !$this->repoId
2110 )
2111 {
2112 // base files next
2113 $file = $path . '/' . ($this->metaData['DESIGNER_MODE'] ? 'design_' : '') . self::CSS_FILE_NAME;
2114 if (file_exists($this->docRoot . $file))
2115 {
2116 $asset['css'][] = $file;
2117 }
2118 $file = $path . '/' . self::JS_FILE_NAME;
2119 if (file_exists($this->docRoot . $file))
2120 {
2121 $asset['js'][] = $file;
2122 }
2123 }
2124
2125 $designerBlockManifest = $this->parseManifest();
2126 if (!empty($designerBlockManifest['assets']))
2127 {
2128 foreach ($designerBlockManifest['assets'] as $key => $assets)
2129 {
2130 $asset[$key] = array_merge($asset[$key], $assets);
2131 $asset[$key] = array_unique($asset[$key]);
2132 }
2133 }
2134
2135 return $asset[$type] ?? $asset;
2136 }
2137
2142 public function getCSS()
2143 {
2144 return $this->getAsset('css');
2145 }
2146
2151 public function getJS(): array
2152 {
2153 return $this->getAsset('js');
2154 }
2155
2160 public function getExt(): array
2161 {
2162 return $this->getAsset('ext');
2163 }
2164
2169 public function getClass(): array
2170 {
2171 return $this->getAsset('class');
2172 }
2173
2179 protected function includeBlockClass($path)
2180 {
2181 static $classes = [];
2182 static $calledClasses = [];
2183
2184 if (!isset($classes[$path]))
2185 {
2186 // include class
2187 $beforeClasses = get_declared_classes();
2188 $beforeClassesCount = count($beforeClasses);
2189 include_once($path);
2190 $afterClasses = get_declared_classes();
2191 $afterClassesCount = count($afterClasses);
2192
2193 // ... and detect class name
2194 for ($i = $beforeClassesCount; $i < $afterClassesCount; $i++)
2195 {
2196 if (is_subclass_of($afterClasses[$i], '\\Bitrix\\Landing\\LandingBlock'))
2197 {
2198 $classes[$path] = $afterClasses[$i];
2199 }
2200 }
2201 }
2202
2203 $landingId = $this->getLandingId();
2204 $landingPath = $path . '@' . $landingId;
2205
2206 // call init method
2207 if (!isset($calledClasses[$landingPath]))
2208 {
2209 $calledClasses[$landingPath] = new $classes[$path];
2210 $calledClasses[$landingPath]->init([
2211 'site_id' => $this->getSiteId(),
2212 'landing_id' => $this->getLandingId(),
2213 ]);
2214 }
2215
2216 return $calledClasses[$landingPath];
2217 }
2218
2225 protected static function getMessageBlock($params, $template = '')
2226 {
2227 ob_start();
2228 Manager::getApplication()->includeComponent(
2229 'bitrix:landing.blocks.message',
2230 $template,
2231 $params,
2232 false
2233 );
2234 $blockMesage = ob_get_contents();
2235 ob_end_clean();
2236
2237 return $blockMesage;
2238 }
2239
2247 public function view($edit = false, \Bitrix\Landing\Landing $landing = null, array $params = array())
2248 {
2249 global $APPLICATION;
2250
2251 $blockRepo = (new BlockRepo())
2254 ;
2255 if (!$blockRepo->isBlockInRepo($this->getCode()))
2256 {
2257 return;
2258 }
2259 $blockRepo->enableFilter(BlockRepo::FILTER_DEFAULTS);
2260
2261 if ($this->dynamicParams)
2262 {
2263 $this->setDynamic($edit);
2264 }
2265
2266 if (!isset($params['wrapper_show']))
2267 {
2268 $params['wrapper_show'] = true;
2269 }
2270 if (!isset($params['force_unactive']))
2271 {
2272 $params['force_unactive'] = false;
2273 }
2274 if (!isset($params['skip_system_script']))
2275 {
2276 $params['skip_system_script'] = false;
2277 }
2278 if (
2279 !$edit &&
2280 $params['wrapper_show'] &&
2281 !Config::get('public_wrapper_block')
2282 )
2283 {
2284 $params['wrapper_show'] = false;
2285 }
2286
2287 if ($this->deleted)
2288 {
2289 return;
2290 }
2291
2292 if ($edit || $this->active || $params['force_unactive'])
2293 {
2295 if ($css = $this->getCSS())
2296 {
2298 }
2299 if ($ext = $this->getExt())
2300 {
2302 }
2303 if (!$edit || !$this->repoId)
2304 {
2305 if ($js = $this->getJS())
2306 {
2308 }
2309 }
2310
2311 // calling class(es) of block
2312 foreach ($this->getClass() as $class)
2313 {
2314 $classBlock = $this->includeBlockClass($class);
2315 if ($classBlock->beforeView($this) === false)
2316 {
2317 return;
2318 }
2319 }
2320 }
2321
2322 // get manifest
2323 if ($edit && !$params['skip_system_script'])
2324 {
2325 $manifest = $this->getManifest();
2326 }
2327
2328 // develop mode - rebuild and reset content
2329 if (
2330 $this->id > 0 &&
2331 !$params['skip_system_script'] &&
2332 defined('LANDING_DEVELOPER_MODE') &&
2333 LANDING_DEVELOPER_MODE === true
2334 )
2335 {
2336 if (!isset($manifest))
2337 {
2338 $manifest = $this->getManifest();
2339 }
2340 if (isset($this->metaData['DATE_MODIFY']))
2341 {
2342 $modifyTime = $this->metaData['DATE_MODIFY']->getTimeStamp();
2343 }
2344 else
2345 {
2346 $modifyTime = 0;
2347 }
2348 if ($modifyTime < $manifest['timestamp'])
2349 {
2350 $count = 0;
2351 $limit = 1;
2352 Update\Block::executeStep([
2353 'ID' => $this->id,
2354 ], $count, $limit, $paramsUpdater = []);
2355 $this->resetContent();
2356 $this->content = $this->getContent();
2357 }
2358 }
2359
2360 if (!\Bitrix\Main\ModuleManager::isModuleInstalled('bitrix24'))
2361 {
2362 if (mb_strpos($this->content, '/upload/') !== false)
2363 {
2364 $this->content = preg_replace(
2365 '#"//[^\'^"]+/upload/#',
2366 '"/upload/',
2367 $this->content
2368 );
2369 }
2370 if (Manager::getOption('badpicture2x') == 'Y')
2371 {
2372 if (mb_strpos($this->content, 'srcset="') !== false)
2373 {
2374 $this->content = str_replace(
2375 'srcset="',
2376 'data-srcset-bad="',
2377 $this->content
2378 );
2379 }
2380 if (mb_strpos($this->content, '2x)') !== false)
2381 {
2382 $this->content = preg_replace(
2383 "#(, url\‍('[^'^\"]+'\‍) 2x)#",
2384 '',
2385 $this->content
2386 );
2387 }
2388 }
2389 }
2390
2391 // show or not a wrapper of block
2392 if ($params['wrapper_show'])
2393 {
2394 if ($this->id > 0)
2395 {
2396 if ($edit)
2397 {
2398 $anchor = $this->getAnchor($this->id);
2399 }
2400 else
2401 {
2402 $anchor = $this->anchor
2403 ? \htmlspecialcharsbx($this->anchor)
2404 : $this->getAnchor($this->id);
2405 }
2406 }
2407 else
2408 {
2409 $anchor = 'block' . rand(10000, 100000);
2410 }
2411 $classFromCode = 'block-' . $this->code;
2412 $classFromCode = preg_replace('/([^a-z0-9-])/i', '-', $classFromCode);
2413 $classFromCode = ' ' . $classFromCode;
2414 $content = '';
2415 if ($landing && $landing->getPreviewMode())
2416 {
2417 $content .= "<a id=\"editor{$this->getId()}\"></a>";
2418 }
2419 $content .= '<div id="' . $anchor . '" ' .
2420 ($edit ? 'data-id="' . $this->id . '" ' : '') .
2421 (($edit && isset($manifest['block']['subtype'])) ? 'data-subtype="' . $manifest['block']['subtype'] . '" ' : '') .
2422 'class="block-wrapper' .
2423 (!$this->active ? ' landing-block-deactive' : '') .
2424 ($this->metaData['DESIGNER_MODE'] ? ' landing-designer-block-mode' : '') .
2425 $classFromCode .
2426 '">' .
2427 $this->content .
2428 '</div>';
2429 }
2430 else
2431 {
2433 }
2434
2435 // replace in requisite page, marker to company title
2436 if (mb_strpos($this->content, '#requisiteCompanyTitle') !== false)
2437 {
2438 $replace = Connector\Crm::getReplaceRequisiteCompanyNameForContent($landing->getXmlId());
2439 $content = str_replace(
2440 array_keys($replace),
2441 array_values($replace),
2442 $content
2443 );
2444 }
2445
2446 // @tmp bug with setInnerHTML save result
2447 $content = preg_replace('/&amp;([^\s]{1})/is', '&$1', $content);
2448 if ($edit)
2449 {
2450 if ($manifest ?? null)
2451 {
2452 if (
2453 !isset($manifest['requiredUserAction']) &&
2454 $this->runtimeRequiredUserAction
2455 )
2456 {
2457 $manifest['requiredUserAction'] = $this->runtimeRequiredUserAction;
2458 }
2459 $sections = (array)($manifest['block']['section'] ?? null);
2460 $designerRepository = $this->metaData['DESIGNER_MODE'] ? \Bitrix\Landing\Block\Designer::getRepository() : [];
2461 $anchor = $this->anchor;
2462 if (!$anchor)
2463 {
2464 $anchor = $this->parentId
2465 ? 'block' . $this->parentId
2466 : 'b' . $this->id;
2467 }
2468 $autoPublicationEnabled = Manager::isAutoPublicationEnabled();
2469 echo '<script>'
2470 . 'BX.ready(function(){'
2471 . 'if (typeof BX.Landing.Block !== "undefined")'
2472 . '{'
2473 . 'new BX.Landing.' . ($this->metaData['DESIGNER_MODE'] ? 'DesignerBlock' : 'Block') . '('
2474 . 'BX("block' . $this->id . '"), '
2475 . '{'
2476 . 'id: ' . $this->id . ', '
2477 . 'lid: ' . $this->lid . ', '
2478 . 'code: "' . $this->code . '", '
2479 . 'sections: "' . implode(',', $sections) . '", '
2480 . 'repoId: ' . ($this->repoId ? (int)$this->repoId : "null") . ', '
2481 . 'php: ' . (mb_strpos($content, '<?') !== false ? 'true' : 'false') . ', '
2482 . 'designed: ' . ($this->designed ? 'true' : 'false') . ', '
2483 . 'active: ' . ($this->active ? 'true' : 'false') . ', '
2484 . 'allowedByTariff: ' . ($this->allowedByTariff ? 'true' : 'false') . ', '
2485 . 'autoPublicationEnabled: ' . ($autoPublicationEnabled ? 'true' : 'false') . ', '
2486 . 'anchor: ' . '"' . \CUtil::jsEscape($anchor) . '"' . ', '
2487 . 'access: ' . '"' . $this->access . '"' . ', '
2488 . 'dynamicParams: ' . Json::encode($this->dynamicParams) . ','
2489 . ($this->metaData['DESIGNER_MODE'] ? 'repository: ' . Json::encode($designerRepository) . ',' : '')
2490 . 'manifest: ' . Json::encode($manifest)
2491 . (
2492 isset($manifest['requiredUserAction'])
2493 ? ', requiredUserAction: ' . Json::encode($manifest['requiredUserAction'])
2494 : ''
2495 )
2496 . '}'
2497 . ');'
2498 . '}'
2499 . '});'
2500 . '</script>';
2501 }
2502 $content = $this::replaceMetaMarkers($content);
2503
2504 $event = new \Bitrix\Main\Event('landing', 'onBlockEditView', [
2505 'block' => $this,
2506 'outputContent' => $content,
2507 ]);
2508 $event->send();
2509 foreach ($event->getResults() as $result)
2510 {
2511 $content = $result->getParameters();
2512 }
2513
2514 if ($this->repoId)
2515 {
2516 echo $content;
2517 }
2518 else
2519 {
2520 try
2521 {
2522 eval('?>' . $content . '<?');
2523 }
2524 catch (\ParseError $e)
2525 {
2526 $errMessage = $this::getMessageBlock([
2527 'MESSAGE' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_EVAL'),
2528 ]);
2529 if ($params['wrapper_show'])
2530 {
2531 echo '<div id="' . $anchor . '" class="block-wrapper' .
2532 (!$this->active ? ' landing-block-deactive' : '') . '">' .
2533 $errMessage .
2534 '</div>';
2535 }
2536 else
2537 {
2538 echo $errMessage;
2539 }
2540 }
2541 }
2542 }
2543 elseif ($this->active || $params['force_unactive'])
2544 {
2545 // @todo make better
2546 static $sysPagesSites = [];
2547
2548 if (!array_key_exists($this->siteId, $sysPagesSites))
2549 {
2550 $sysPages = array();
2551 foreach (Syspage::get($this->siteId) as $syspage)
2552 {
2553 $sysPages['@#system_' . $syspage['TYPE'] . '@'] = $syspage['LANDING_ID'];
2554 }
2555 // for main page we should get current site main page
2556 if (!isset($sysPages['@#system_mainpage@']))
2557 {
2558 $currentSite = $this->getSite();
2559 if ($currentSite['LANDING_ID_INDEX'])
2560 {
2561 $sysPages['@#system_mainpage@'] = $currentSite['LANDING_ID_INDEX'];
2562 }
2563 }
2564 if (!empty($sysPages))
2565 {
2566 $urls = $landing->getPublicUrl($sysPages);
2567 foreach ($sysPages as $code => $lid)
2568 {
2569 if (isset($urls[$lid]))
2570 {
2571 $sysPages[$code] = \htmlspecialcharsbx($urls[$lid]);
2572 }
2573 else
2574 {
2575 unset($sysPages[$code]);
2576 }
2577 }
2578 }
2579
2580 $sysPagesSites[$this->siteId] = $sysPages;
2581 }
2582
2583 $sysPages = $sysPagesSites[$this->siteId];
2584
2586 {
2587 $sysPages['@' . Connector\Disk::FILE_MASK_HREF . '@i'] = str_replace(
2588 '#fileId#', '$2',
2589 Controller\DiskFile::getDownloadLink($this->metaData['SITE_TYPE'], $this->id)
2590 );
2591 }
2592
2593 if (!empty($sysPages))
2594 {
2595 $content = preg_replace(
2596 array_keys($sysPages),
2597 array_values($sysPages),
2598 $content
2599 );
2600 }
2601
2602 $event = new \Bitrix\Main\Event('landing', 'onBlockPublicView', [
2603 'block' => $this,
2604 'outputContent' => $content,
2605 ]);
2606 $event->send();
2607 foreach ($event->getResults() as $result)
2608 {
2609 $content = $result->getParameters();
2610 }
2611
2612 if ($this->repoId)
2613 {
2614 echo $content;
2615 }
2616 else
2617 {
2618 try
2619 {
2620 eval('?>' . $content . '<?');
2621 }
2622 catch (\ParseError $e)
2623 {
2624 }
2625 }
2626 }
2627 Assets\PreProcessing::blockViewProcessing($this, $edit);
2628 }
2629
2635 public function saveAssets(array $assets): void
2636 {
2637 if ($this->access < $this::ACCESS_W)
2638 {
2639 $this->error->addError(
2640 'ACCESS_DENIED',
2641 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2642 );
2643 return;
2644 }
2645
2646 foreach (['font', 'icon', 'ext'] as $assetCode)
2647 {
2648 if (isset($this->assets[$assetCode]) && !isset($assets[$assetCode]))
2649 {
2650 $assets[$assetCode] = $this->assets[$assetCode];
2651 }
2652 if (isset($assets[$assetCode]) && !$assets[$assetCode])
2653 {
2654 unset($assets[$assetCode]);
2655 }
2656 }
2657
2658 $this->assets = $assets;
2659 }
2660
2665 public function getAssets(): array
2666 {
2667 return $this->assets;
2668 }
2669
2676 public function saveContent(string $content, $designed = false): void
2677 {
2678 if (!is_string($content))
2679 {
2680 return;
2681 }
2682
2683 if ($this->access < $this::ACCESS_W)
2684 {
2685 $this->error->addError(
2686 'ACCESS_DENIED',
2687 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2688 );
2689 return;
2690 }
2691 if ($designed)
2692 {
2693 $this->designed = true;
2694 }
2695 $this->content = trim($content);
2696 $this->getDom(true);
2697 }
2698
2704 public function save(array $additionalFields = [])
2705 {
2706 if ($this->access == $this::ACCESS_A)
2707 {
2708 $this->error->addError(
2709 'ACCESS_DENIED',
2710 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2711 );
2712 return false;
2713 }
2714
2715 $data = array(
2716 'SORT' => $this->sort,
2717 'ACTIVE' => $this->active ? 'Y' : 'N',
2718 'ANCHOR' => $this->anchor,
2719 'DELETED' => $this->deleted ? 'Y' : 'N',
2720 'DESIGNED' => $this->designed ? 'Y' : 'N',
2721 'ASSETS' => $this->assets ? $this->assets : null,
2722 );
2723 if ($additionalFields)
2724 {
2725 $data = array_merge($data, $additionalFields);
2726 }
2727 if ($this->content)
2728 {
2729 $data['CONTENT'] = $this->content;
2730 $data['SEARCH_CONTENT'] = $this->getSearchContent();
2731 }
2732 Cache::clear($this->id);
2733 $res = parent::update($this->id, $data);
2734 $this->error->addFromResult($res);
2735 return $res->isSuccess();
2736 }
2737
2744 public function changeLanding($lid)
2745 {
2746 if ($this->access < $this::ACCESS_W)
2747 {
2748 $this->error->addError(
2749 'ACCESS_DENIED',
2750 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2751 );
2752 return false;
2753 }
2754 $res = parent::update($this->id, array(
2755 'LID' => (int)$lid,
2756 'PARENT_ID' => null,
2757 'PUBLIC' => 'N',
2758 ));
2759 $this->error->addFromResult($res);
2760 return $res->isSuccess();
2761 }
2762
2768 public function changeFavoriteMeta(array $meta): bool
2769 {
2770 $res = parent::update($this->id, [
2771 'TPL_CODE' => $meta['tpl_code'] ?? null,
2772 'FAVORITE_META' => $meta,
2773 ]);
2774 $this->error->addFromResult($res);
2775 return $res->isSuccess();
2776 }
2777
2782 public function unlink()
2783 {
2784 if ($this->access < $this::ACCESS_X)
2785 {
2786 $this->error->addError(
2787 'ACCESS_DENIED',
2788 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2789 );
2790 return false;
2791 }
2792
2793 $manifest = $this->getManifest();
2794 $res = self::parentDelete($this->id);
2795 if (!$res->isSuccess())
2796 {
2797 $this->error->addFromResult($res);
2798 }
2799 return $res->isSuccess();
2800 }
2801
2807 public function markDeleted($mark)
2808 {
2809 if ($this->access < $this::ACCESS_X)
2810 {
2811 $this->error->addError(
2812 'ACCESS_DENIED',
2813 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2814 );
2815 return;
2816 }
2817 $this->deleted = (boolean) $mark;
2818 }
2819
2825 public function setSort($sort)
2826 {
2827 $this->sort = $sort;
2828 }
2829
2835 public function setAnchor($anchor)
2836 {
2837 if (!is_string($anchor))
2838 {
2839 return false;
2840 }
2841 $anchor = trim($anchor);
2842 $check = !$anchor || preg_match_all('/^[a-z]{1}[a-z0-9\-\_\.\:]+$/i', $anchor);
2843 if (!$check)
2844 {
2845 $this->error->addError(
2846 'BAD_ANCHOR',
2847 Loc::getMessage('LANDING_BLOCK_BAD_ANCHOR')
2848 );
2849 return false;
2850 }
2851
2852 if (History::isActive())
2853 {
2854 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
2855 $history->push('CHANGE_ANCHOR', [
2856 'block' => $this,
2857 'valueBefore' => $this->anchor,
2858 'valueAfter' => $anchor,
2859 ]);
2860 }
2861
2862 $this->anchor = $anchor;
2863 return true;
2864 }
2865
2871 public function saveSort($sort)
2872 {
2873 if ($this->access < $this::ACCESS_W)
2874 {
2875 $this->error->addError(
2876 'ACCESS_DENIED',
2877 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2878 );
2879 return;
2880 }
2881 $sort = intval($sort);
2882 $this->sort = $sort;
2883 Internals\BlockTable::update($this->id, array(
2884 'SORT' => $sort,
2885 ));
2886 }
2887
2892 public function getSort()
2893 {
2894 return $this->sort;
2895 }
2896
2902 public function getDynamicParams($id = null)
2903 {
2904 $params = [];
2905
2906 if ($id !== null)
2907 {
2908 $id = intval($id);
2909 $res = parent::getList([
2910 'select' => [
2911 'SOURCE_PARAMS',
2912 ],
2913 'filter' => [
2914 'ID' => $id,
2915 ],
2916 ]);
2917 if ($row = $res->fetch())
2918 {
2919 $params = $row['SOURCE_PARAMS'];
2920 }
2921 unset($row, $res);
2922 }
2923 else
2924 {
2925 $params = $this->dynamicParams;
2926 }
2927
2928 return $params;
2929 }
2930
2936 private function dynamicLinkReplacer(array $data, array $replace)
2937 {
2938 foreach ($data as $key => $value)
2939 {
2940 if (is_array($value))
2941 {
2942 $data[$key] = $this->dynamicLinkReplacer($value, $replace);
2943 }
2944 else
2945 {
2946 $data[$key] = str_replace(
2947 array_keys($replace),
2948 array_values($replace),
2949 $data[$key]
2950 );
2951 }
2952 }
2953 unset($key, $value);
2954
2955 return $data;
2956 }
2957
2964 public function saveDynamicParams(array $sourceParams = [], array $params = [])
2965 {
2966 if ($this->access < $this::ACCESS_W)
2967 {
2968 $this->error->addError(
2969 'ACCESS_DENIED',
2970 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2971 );
2972 return;
2973 }
2974 // replace old link to new in dynamic manifest
2975 if (
2976 isset($params['linkReplace']) &&
2977 is_array($params['linkReplace'])
2978 )
2979 {
2980 $sourceParams = $this->dynamicLinkReplacer(
2981 $sourceParams,
2982 $params['linkReplace']
2983 );
2984 }
2985 // save
2986 $paramsBefore = $this->dynamicParams;
2987 $this->dynamicParams = $sourceParams;
2988 $resUpdate = Internals\BlockTable::update($this->id, [
2989 'SOURCE_PARAMS' => $sourceParams,
2990 ]);
2991
2992 if ($resUpdate->isSuccess())
2993 {
2994 if (History::isActive())
2995 {
2996 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
2997 $history->push('UPDATE_DYNAMIC', [
2998 'block' => $this,
2999 'valueBefore' => $paramsBefore,
3000 'valueAfter' => $sourceParams,
3001 ]);
3002 }
3003 }
3004
3005 unset($sourceParams, $params);
3006 }
3007
3013 protected function setDynamic($edit)
3014 {
3015 static $sourceList = null;
3016 static $isDetailDynamic = null;
3017 static $dynamicElementId = null;
3018 static $dynamicFilter = null;
3019
3020 $data = $this->dynamicParams;
3021 $caching = false;
3022 $cache = null;
3023
3024 // check if is true dynamic
3025 if (!$this->active || !$this->content)
3026 {
3027 return;
3028 }
3029 if (!is_array($data) || empty($data))
3030 {
3031 return;
3032 }
3033
3034 // check feature
3035 $availableFeature = Restriction\Manager::isAllowed(
3036 'limit_sites_dynamic_blocks',
3037 ['targetBlockId' => $this->id]
3038 );
3039 if (!$availableFeature)
3040 {
3041 $hackContent = preg_replace(
3042 '/^<([a-z]+)\s/',
3043 '<$1 style="display: none;" ',
3044 $this->content
3045 );
3046
3047 $this->saveContent(
3048 $hackContent .
3049 $this::getMessageBlock([
3050 'HEADER' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_DYNAMIC_LIMIT_TITLE'),
3051 'MESSAGE' => Restriction\Manager::getSystemErrorMessage('limit_sites_dynamic_blocks'),
3052 'BUTTON' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_LIMIT_BUTTON'),
3053 'LINK' => Manager::BUY_LICENSE_PATH,
3054 ], 'locked')
3055 );
3056 return;
3057 }
3058
3059 // if is detail page
3060 if ($isDetailDynamic === null)
3061 {
3062 $isDetailDynamic = Landing::isDynamicDetailPage();
3063 }
3064 if ($dynamicElementId === null)
3065 {
3066 $dynamicElementId = Landing::getDynamicElementId();
3067 }
3068 if ($dynamicFilter === null)
3069 {
3070 $dynamicFilter = Landing::getDynamicFilter();
3071 }
3072
3073 if (!$edit && Cache::isCaching())
3074 {
3075 $cache = new \CPHPCache();
3076 $cacheTime = 3600;
3077 $cacheId = 'block_' . $this->id . '_' . $dynamicElementId . '_';
3078 $cacheId .= md5(serialize($dynamicFilter));
3079 $cachePath = '/landing/dynamic/' . $this->id;
3080 if ($cache->initCache($cacheTime, $cacheId, $cachePath))
3081 {
3082 $result = $cache->getVars();
3083 if ($result['title'])
3084 {
3085 Manager::setPageTitle($result['title'], true);
3086 Landing\Seo::changeValue('title', $result['title']);
3087 }
3088 $rememberAccess = $this->access;
3089 $this->access = $this::ACCESS_W;
3090 $this->saveContent($result['content']);
3091 $this->access = $rememberAccess;
3092 header('X-Bitrix24-Page: dynamic');
3093 return;
3094 }
3095 else
3096 {
3097 $caching = true;
3098 $cache->startDataCache($cacheTime, $cacheId, $cachePath);
3099 Manager::getCacheManager()->startTagCache($cachePath);
3100 Cache::register($this->id);
3101 }
3102 }
3103
3104 $updated = false;
3105 // @todo: remove after refactoring
3106 $manifest = $this->getManifest();
3107
3108 // build sources list
3109 if ($sourceList === null)
3110 {
3111 $sourceList = new Source\Selector();
3112 }
3113
3114 // @todo: remove after refactoring
3115 $getDetailPage = function(array $detailPage, $filterId = 0, $elemId = 0)
3116 {
3117 $filterId = intval($filterId);
3118 $elemId = intval($elemId);
3119 $query = [];
3120
3121 if (isset($detailPage['query']))
3122 {
3123 $query = (array) $detailPage['query'];
3124 unset($detailPage['query']);
3125 }
3126
3127 // normalize the array
3128 $detailPage = array_merge(
3129 array_fill_keys(['text', 'href', 'target'], ''),
3130 $detailPage
3131 );
3132 foreach ($detailPage as $key => &$detailPageItem)
3133 {
3134 if (!is_array($detailPageItem))
3135 {
3136 $detailPageItem = trim($detailPageItem);
3137 }
3138 if (empty($detailPageItem))
3139 {
3140 unset($detailPage[$key]);
3141 }
3142 }
3143 unset($detailPageItem);
3144
3145 if ($filterId && $elemId && $detailPage['href'])
3146 {
3147 $detailPage['href'] = str_replace(
3148 '#landing',
3149 '#dynamic',
3150 $detailPage['href']
3151 );
3152 $detailPage['href'] .= '_' . $filterId;
3153 $detailPage['href'] .= '_' . $elemId;
3154 }
3155 else if ($filterId && $elemId)
3156 {
3157 $detailPage['href'] = '#';
3158 }
3159
3160 if ($detailPage['href'] && $query)
3161 {
3162 $detailPage['query'] = http_build_query($query);
3163 }
3164
3165 return $detailPage;
3166 };
3167
3168 // apply for each selector dynamic data from source
3169 $disableUpdate = false;
3170 $pageTitle = '';
3171 foreach ($data as $cardSelector => $item)
3172 {
3173 $update = [];
3174 $itemDetail = $cardSelector == 'wrapper';
3175 if (
3176 !isset($item['source']) ||
3177 !isset($item['settings']) ||
3178 !isset($item['references'])
3179 )
3180 {
3181 continue;
3182 }
3183 // build start params
3184 $sourceId = $item['source'];
3185 $settings = $item['settings'];
3186 $references = (array)$item['references'];
3187 $filterId = isset($item['filterId'])
3188 ? intval($item['filterId'])
3189 : 0;
3190 $detailPage = isset($settings['detailPage'])
3191 ? (array)$settings['detailPage']
3192 : [];
3193 $pagesCount = (
3194 isset($settings['pagesCount']) &&
3195 $settings['pagesCount'] > 0
3196 )
3197 ? (int)$settings['pagesCount']
3198 : 10;
3199 $filter = isset($settings['source']['filter'])
3200 ? (array)$settings['source']['filter']
3201 : [];
3202 $order = isset($settings['source']['sort'])
3203 ? (array)$settings['source']['sort']
3204 : [];
3205 $additional = isset($settings['source']['additional'])
3206 ? (array)$settings['source']['additional']
3207 : [];
3208 $stubs = isset($item['stubs'])
3209 ? (array)$item['stubs']
3210 : [];
3211 // load external filter, if we on detail
3212 if (
3213 $isDetailDynamic && $itemDetail &&
3214 $dynamicFilter['SOURCE_ID'] == $sourceId
3215 )
3216 {
3217 $filter = $dynamicFilter['FILTER'];
3218 }
3219 $sourceParameters = [
3220 'select' => array_values($references),
3221 'filter' => $filter,
3222 'order' => $order,
3223 'limit' => $pagesCount,
3224 'additional' => $additional,
3225 ];
3226 // gets list or singleton data
3227 $sourceData = [];
3228 $source = $sourceList->getDataLoader(
3229 $sourceId,
3230 $sourceParameters,
3231 [
3232 'context_filter' => [
3233 'SITE_ID' => $this->siteId,
3234 'LANDING_ID' => $this->lid,
3235 'LANDING_ACTIVE' => $this->landingActive ? 'Y' : ['Y', 'N'],
3236 ],
3237 'cache' => $cache,
3238 'block' => $this,
3239 ]
3240 );
3241 if (is_object($source))
3242 {
3243 // detail page
3244 if ($isDetailDynamic && $itemDetail)
3245 {
3246 $sourceData = $source->getElementData($dynamicElementId);
3247 if (!$sourceData)
3248 {
3249 $disableUpdate = true;
3250 continue;
3251 }
3252 $pageTitle = $source->getSeoTitle();
3255 }
3256 // element list
3257 else
3258 {
3259 $sourceData = $source->getElementListData();
3260 $pagesCount = max(1, count($sourceData));
3261 }
3262 }
3263 // apply getting data in block
3264 if (!empty($sourceData) && is_array($sourceData))
3265 {
3266 // collect array for update html
3267 foreach ($references as $selector => $field)
3268 {
3269 if (empty($field) || !is_array($field))
3270 {
3271 continue;
3272 }
3273 if (empty($field['id']))
3274 {
3275 continue;
3276 }
3277 if (mb_strpos($selector, '@') !== false)
3278 {
3279 [$selector,] = explode('@', $selector);
3280 }
3281 if (!isset($update[$selector]))
3282 {
3283 $update[$selector] = [];
3284 }
3285 $fieldCode = $field['id'];
3286 $fieldType = isset($manifest['nodes'][$selector]['type'])
3287 ? $manifest['nodes'][$selector]['type']
3288 : NodeType::TEXT;
3289 // fill ever selector with data, if data exist
3290 $detailPageData = [];
3291 foreach ($sourceData as $dataItem)
3292 {
3293 // set link to the card
3294 // @todo: need refactoring
3295 if (
3296 $fieldType == NodeType::LINK &&
3297 isset($field['action'])
3298 )
3299 {
3300 switch ($field['action'])
3301 {
3302 case 'detail':
3303 {
3304 $detailPage['text'] = isset($field['text'])
3305 ? $field['text']
3306 : '';
3307 $update[$selector][] = $detailPageData[$selector][] = $getDetailPage(
3308 $detailPage,
3309 $filterId,
3310 $dataItem['ID']
3311 );
3312 break;
3313 }
3314 case 'link':
3315 {
3316 if (isset($field['link']))
3317 {
3318 $field['link'] = (array) $field['link'];
3319 if (isset($field['text']))
3320 {
3321 $field['link']['text'] = $field['text'];
3322 }
3323 $update[$selector][] = $getDetailPage(
3324 $field['link']
3325 );
3326 }
3327 break;
3328 }
3329 case 'landing':
3330 {
3331 if (isset($dataItem['LINK']))
3332 {
3333 $update[$selector][] = $detailPageData[$selector][] = $getDetailPage([
3334 'text' => isset($field['text'])
3335 ? $field['text']
3336 : '',
3337 'href' => $dataItem['LINK'],
3338 'target' => '_self',
3339 'query' => isset($dataItem['_GET']) ? $dataItem['_GET'] : [],
3340 ]);
3341 }
3342 }
3343 }
3344 }
3345 else// if ($fieldType != NodeType::LINK)
3346 {
3347 $value = isset($dataItem[$fieldCode])
3348 ? $dataItem[$fieldCode]
3349 : '';
3350 $update[$selector][] = $value;
3351 if ($detailPage)
3352 {
3353 $detailPageData[$selector][] = $getDetailPage(
3354 $detailPage,
3355 $filterId,
3356 $dataItem['ID']
3357 );;
3358 }
3359 else if (isset($dataItem['LINK']))
3360 {
3361 $detailPageData[$selector][] = $getDetailPage([
3362 'text' => isset($field['text'])
3363 ? $field['text']
3364 : '',
3365 'href' => $dataItem['LINK'],
3366 'target' => '_self',
3367 'query' => isset($dataItem['_GET']) ? $dataItem['_GET'] : [],
3368 ]);
3369 }
3370 }
3371 }
3372 // not touch the selector, if there is no data
3373 if (!$update[$selector])
3374 {
3375 unset($update[$selector]);
3376 }
3377 // set detail url for nodes
3378 // @todo: refactor
3379 else if (
3380 isset($field['link']) &&
3381 (
3382 $fieldType == NodeType::IMAGE ||
3383 $fieldType == NodeType::TEXT
3384 )
3385 )
3386 {
3387 if (!isset($detailPageData[$selector]))
3388 {
3389 continue;
3390 }
3391 foreach ($update[$selector] as $i => &$value)
3392 {
3393 if ($fieldType == NodeType::IMAGE)
3394 {
3395 $value = (array) $value;
3396 }
3397 else
3398 {
3399 $value = [
3400 'text' => (string) $value,
3401 ];
3402 }
3403 if (
3404 $detailPageData[$selector][$i] &&
3405 UtilsAction::isTrue($field['link'])
3406 )
3407 {
3408 $detailPageData[$selector][$i]['enabled'] = true;
3409 }
3410 else
3411 {
3412 $detailPageData[$selector][$i]['enabled'] = false;
3413 }
3414 if ($detailPageData[$selector][$i]['enabled'])
3415 {
3416 $value['url'] = $detailPageData[$selector][$i];
3417 }
3418 }
3419 unset($value);
3420 }
3421 }
3422 if (!$itemDetail)
3423 {
3424 $rememberAccess = $this->access;
3425 $this->access = $this::ACCESS_W;
3426 $this->adjustCards(
3427 $cardSelector,
3428 $pagesCount
3429 );
3430 $this->access = $rememberAccess;
3431 }
3432 }
3433
3434 // stubs (common content)
3435 if ($stubs)
3436 {
3437 foreach ($stubs as $selector => $stub)
3438 {
3439 if (mb_strpos($selector, '@') !== false)
3440 {
3441 [$selector,] = explode('@', $selector);
3442 }
3443 $update[$selector] = array_fill(0, $pagesCount, $stub);
3444 }
3445 }
3446
3447 // update dynamic
3448 if ($update)
3449 {
3450 $updated = true;
3451 $rememberAccess = $this->access;
3452 $this->access = $this::ACCESS_W;
3453 $this->updateNodes(
3454 $update,
3455 [
3456 'sanitize' => false,
3457 'skipCheckAffected' => true,
3458 ]
3459 );
3460 if(!$edit)
3461 {
3462 Assets\PreProcessing::blockSetDynamicProcessing($this);
3463 }
3464 $this->access = $rememberAccess;
3465
3466 header('X-Bitrix24-Page: dynamic');
3467 if ($caching)
3468 {
3469 $cache->endDataCache([
3470 'title' => $pageTitle,
3471 'content' => $this->content,
3472 ]);
3473 Manager::getCacheManager()->endTagCache();
3474 }
3475 }
3476 else if (false)
3477 {
3478 $this->runtimeRequiredUserAction = [
3479 'header' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_NO_DATA_TITLE'),
3480 'description' => Loc::getMessage('LANDING_BLOCK_MESSAGE_ERROR_NO_DATA_TEXT'),
3481 ];
3482 }
3483 }
3484
3485 if (
3486 $disableUpdate ||
3487 (!$updated && !Landing::getEditMode())
3488 )
3489 {
3490 if ($cache)
3491 {
3492 $cache->abortDataCache();
3493 }
3494 $this->deleted = true;
3495 }
3496 }
3497
3502 public function clearDynamic()
3503 {
3504 $this->saveDynamicParams();
3505 }
3506
3512 {
3513 return $this->runtimeRequiredUserAction;
3514 }
3515
3521 {
3522 $this->runtimeRequiredUserAction = $action;
3523 }
3524
3530 public function getDom($clear = false)
3531 {
3532 static $doc = array();
3533
3534 if (
3535 $clear &&
3536 isset($doc[$this->id])
3537 )
3538 {
3539 unset($doc[$this->id]);
3540 }
3541
3542 if (!isset($doc[$this->id]))
3543 {
3544 $doc[$this->id] = new DOM\Document;
3545 try
3546 {
3547 $doc[$this->id]->loadHTML($this->content);
3548 }
3549 catch (\Exception $e) {}
3550 }
3551
3552 return $doc[$this->id];
3553 }
3554
3559 public function getMeta()
3560 {
3561 return $this->metaData;
3562 }
3563
3571 public function adjustCards($selector, $count, &$changed = false)
3572 {
3573 if (!is_string($selector))
3574 {
3575 return false;
3576 }
3577
3578 if ($this->access < $this::ACCESS_W)
3579 {
3580 $this->error->addError(
3581 'ACCESS_DENIED',
3582 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3583 );
3584 return false;
3585 }
3586
3587 $manifest = $this->getManifest();
3588 if (isset($manifest['cards'][$selector]))
3589 {
3590 $count = (int)$count;
3591 $doc = $this->getDom();
3592 $resultList = $doc->querySelectorAll($selector);
3593 $resultCount = count($resultList);
3594 if ($count > $resultCount)
3595 {
3596 for ($i = $resultCount; $i < $count; $i++)
3597 {
3598 $changed = true;
3599 $this->cloneCard($selector, $i - 1);
3600 }
3601 }
3602 elseif ($count < $resultCount)
3603 {
3604 for ($i = $resultCount; $i > $count; $i--)
3605 {
3606 $changed = true;
3607 $this->removeCard($selector, $i - 1);
3608 }
3609 }
3610 return true;
3611 }
3612
3613 $this->error->addError(
3614 'CARD_NOT_FOUND',
3615 Loc::getMessage('LANDING_BLOCK_CARD_NOT_FOUND')
3616 );
3617
3618 return false;
3619 }
3620
3628 public function cloneCard($selector, $position, $content = '')
3629 {
3630 if (!is_string($selector))
3631 {
3632 return false;
3633 }
3634
3635 if ($this->access < $this::ACCESS_W)
3636 {
3637 $this->error->addError(
3638 'ACCESS_DENIED',
3639 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3640 );
3641 return false;
3642 }
3643
3644 $manifest = $this->getManifest();
3645 if (isset($manifest['cards'][$selector]))
3646 {
3647 $position = intval($position);
3648 $position = max($position, -1);
3649 $realPosition = max($position, 0);
3650 $doc = $this->getDom();
3651 $resultList = $doc->querySelectorAll($selector);
3652 if (isset($resultList[$realPosition]))
3653 {
3654 $parentNode = $resultList[$realPosition]->getParentNode();
3655 $refChild = isset($resultList[$position + 1])
3656 ? $resultList[$position + 1]
3657 : null;
3658 $haveChild = false;
3659 if ($refChild)
3660 {
3661 foreach ($parentNode->getChildNodes() as $child)
3662 {
3663 if ($child === $refChild)
3664 {
3665 $haveChild = true;
3666 break;
3667 }
3668 }
3669 }
3670 if ($parentNode && (!$refChild || $haveChild))
3671 {
3672 // some dance for set new content ;)
3673 if ($content)
3674 {
3675 $tmpCardName = mb_strtolower('tmpcard'.randString(10));
3676 $newChild = new DOM\Element($tmpCardName);
3677 $newChild->setOwnerDocument($doc);
3678 $newChild->setInnerHTML($content);
3679 }
3680 else
3681 {
3682 $newChild = $resultList[$realPosition];
3683 }
3684 $parentNode->insertBefore(
3685 $newChild,
3686 $refChild,
3687 false
3688 );
3689
3690 // history before save content
3691 if (History::isActive())
3692 {
3693 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
3694 $history->push('ADD_CARD', [
3695 'block' => $this,
3696 'selector' => $selector,
3697 'position' => $position,
3698 'content' => $content,
3699 ]);
3700 }
3701
3702 // cleaning and save
3703 if (isset($tmpCardName))
3704 {
3705 $this->saveContent(
3706 str_replace(
3707 array('<' . $tmpCardName . '>', '</' . $tmpCardName . '>'),
3708 '',
3709 $doc->saveHTML()
3710 )
3711 );
3712 }
3713 else
3714 {
3715 $this->saveContent($doc->saveHTML());
3716 }
3717 }
3718
3719 return true;
3720 }
3721
3722 }
3723
3724 $this->error->addError(
3725 'CARD_NOT_FOUND',
3726 Loc::getMessage('LANDING_BLOCK_CARD_NOT_FOUND')
3727 );
3728 return false;
3729 }
3730
3738 public function setCardContent($selector, $position, $content)
3739 {
3740 if (!is_string($selector))
3741 {
3742 return false;
3743 }
3744
3745 if ($this->access < $this::ACCESS_W)
3746 {
3747 $this->error->addError(
3748 'ACCESS_DENIED',
3749 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3750 );
3751 return false;
3752 }
3753
3754 $doc = $this->getDom();
3755 $position = intval($position);
3756 $resultList = $doc->querySelectorAll($selector);
3757 if (isset($resultList[$position]))
3758 {
3759 $resultList[$position]->setInnerHTML(
3760 $content
3761 );
3762 $this->saveContent($doc->saveHTML());
3763 return true;
3764 }
3765
3766 $this->error->addError(
3767 'CARD_NOT_FOUND',
3768 Loc::getMessage('LANDING_BLOCK_CARD_NOT_FOUND')
3769 );
3770 return false;
3771 }
3772
3779 public function getCardContent($selector, $position)
3780 {
3781 if (!is_string($selector))
3782 {
3783 return '';
3784 }
3785
3786 $doc = $this->getDom();
3787 $position = intval($position);
3788 $resultList = $doc->querySelectorAll($selector);
3789 if (isset($resultList[$position]))
3790 {
3791 return $resultList[$position]->getOuterHtml();
3792 }
3793
3794 return null;
3795 }
3796
3802 public function getCardCount($selector)
3803 {
3804 if (!is_string($selector))
3805 {
3806 return 0;
3807 }
3808
3809 $doc = $this->getDom();
3810 $resultList = $doc->querySelectorAll($selector);
3811 return count($resultList);
3812 }
3813
3820 public function removeCard($selector, $position)
3821 {
3822 if (!is_string($selector))
3823 {
3824 return false;
3825 }
3826
3827 if ($this->access < $this::ACCESS_W)
3828 {
3829 $this->error->addError(
3830 'ACCESS_DENIED',
3831 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3832 );
3833 return false;
3834 }
3835
3836 $manifest = $this->getManifest();
3837 $position = intval($position);
3838 if (isset($manifest['cards'][$selector]))
3839 {
3840 $doc = $this->getDom();
3841 $resultList = $doc->querySelectorAll($selector);
3842 if (isset($resultList[$position]))
3843 {
3844 $resultList[$position]->getParentNode()->removeChild(
3845 $resultList[$position]
3846 );
3847
3848 // history before save!
3849 if (History::isActive())
3850 {
3851 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
3852 $history->push('REMOVE_CARD', [
3853 'block' => $this,
3854 'selector' => $selector,
3855 'position' => $position,
3856 ]);
3857 }
3858
3859 $this->saveContent($doc->saveHTML());
3860 Assets\PreProcessing::blockUpdateNodeProcessing($this);
3861
3862 return true;
3863 }
3864 }
3865
3866 $this->error->addError(
3867 'CARD_NOT_FOUND',
3868 Loc::getMessage('LANDING_BLOCK_CARD_NOT_FOUND')
3869 );
3870 return false;
3871 }
3872
3878 public function changeNodeName($data)
3879 {
3880 if ($this->access < $this::ACCESS_W)
3881 {
3882 $this->error->addError(
3883 'ACCESS_DENIED',
3884 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3885 );
3886 return false;
3887 }
3888
3889 $doc = $this->getDom();
3890 $manifest = $this->getManifest();
3891 $valueBefore = [];
3892 // find available nodes by manifest from data
3893 foreach ($manifest['nodes'] as $selector => $node)
3894 {
3895 if (isset($data[$selector]))
3896 {
3897 $resultList = $doc->querySelectorAll($selector);
3898
3899 foreach ($data[$selector] as $pos => $value)
3900 {
3901 $value = trim($value['tagName'] ?? $value);
3902 if (
3903 preg_match('/^[a-z0-9]+$/i', $value) &&
3904 isset($resultList[$pos]))
3905 {
3906 $valueBefore[$selector][$pos] = $resultList[$pos]->getNodeName();
3907 $resultList[$pos]->setNodeName($value);
3908 }
3909 }
3910 }
3911 }
3912
3913 if (History::isActive())
3914 {
3915 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
3916 $history->push('CHANGE_NODE_NAME_ACTION', [
3917 'block' => $this,
3918 'valueBefore' => $valueBefore,
3919 'valueAfter' => $data,
3920 ]);
3921 }
3922
3923 // save rebuild html as text
3924 $this->saveContent($doc->saveHTML());
3925
3926 return true;
3927 }
3928
3935 public function updateNodes($data, $additional = array())
3936 {
3937 if ($this->access < $this::ACCESS_W)
3938 {
3939 $this->error->addError(
3940 'ACCESS_DENIED',
3941 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
3942 );
3943 return false;
3944 }
3945
3946 $affected = [];
3947 $doc = $this->getDom();
3948 $manifest = $this->getManifest();
3949
3950 // find available nodes by manifest from data
3951 $manifest['nodes'] = $manifest['nodes'] ?? [];
3952 foreach ($manifest['nodes'] as $selector => $node)
3953 {
3954 $isFind = false;
3955 $nodeData = [];
3956 if (isset($data[$selector]))
3957 {
3958 if (!is_array($data[$selector]))
3959 {
3960 $data[$selector] = array(
3961 $data[$selector],
3962 );
3963 }
3964 $data[$selector] = array_filter($data[$selector], fn($value) => !is_null($value));
3965 $nodeData = $data[$selector];
3966 $isFind = true;
3967 }
3968 if (!$isFind && ($node['isWrapper'] ?? false) === true)
3969 {
3970 if (isset($data['#wrapper']) && $node['type'] === 'styleimg')
3971 {
3972 if (!is_array($data['#wrapper']))
3973 {
3974 $data['#wrapper'] = array(
3975 $data['#wrapper'],
3976 );
3977 }
3978 $nodeData = $data['#wrapper'];
3979 }
3980 else
3981 {
3982 $selector = '#wrapper';
3983 if (!is_array($data[$selector]))
3984 {
3985 $data[$selector] = array(
3986 $data[$selector],
3987 );
3988 }
3989 $nodeData = $data[$selector];
3990 }
3991 $isFind = true;
3992 }
3993 if ($node['type'] === 'img')
3994 {
3995 $node = Img::changeNodeType($node, $this);
3996 }
3997 if ($isFind)
3998 {
3999 // and save content from frontend in DOM by handler-class
4000 $affected[$selector] = call_user_func_array(array(
4001 Node\Type::getClassName($node['type']),
4002 'saveNode',
4003 ), array(
4004 $this,
4005 $selector,
4006 $nodeData,
4007 $additional,
4008 ));
4009 }
4010 }
4011
4012 // additional work with menu
4013 if (isset($additional['appendMenu']) && $additional['appendMenu'])
4014 {
4015 $export = $this->export();
4016 }
4017 else
4018 {
4019 $additional['appendMenu'] = false;
4020 }
4021 $manifest['menu'] = $manifest['menu'] ?? [];
4022 foreach ($manifest['menu'] as $selector => $node)
4023 {
4024 if (isset($data[$selector]) && is_array($data[$selector]))
4025 {
4026 if (isset($data[$selector][0][0]))
4027 {
4028 $data[$selector] = array_shift($data[$selector]);
4029 }
4030 if ($additional['appendMenu'] && isset($export['menu'][$selector]))
4031 {
4032 $data[$selector] = array_merge(
4033 $export['menu'][$selector],
4034 $data[$selector]
4035 );
4036 }
4037
4038 $resultList = $doc->querySelectorAll($selector);
4039 foreach ($resultList as $pos => $resultNode)
4040 {
4041 $parentNode = $resultNode->getParentNode();
4042 if ($parentNode)
4043 {
4044 $parentNode->setInnerHtml(
4045 $this->getMenuHtml(
4046 $data[$selector],
4047 $node
4048 )
4049 );
4050 }
4051 break;// we need only first position
4052 }
4053 }
4054 }
4055
4056 // save rebuild html as text
4057 $this->saveContent($doc->saveHTML());
4058
4059 // check affected content in block's content
4060 if (!($additional['skipCheckAffected'] ?? false) && Manager::getOption('strict_verification_update') === 'Y')
4061 {
4062 $pos = 0;
4063 $domCorrect = true;
4065
4066 foreach ($affected as $selector => $resultItem)
4067 {
4068 $selector = trim($selector, '.');
4069
4070 // prepare content for search
4071 $content = str_replace('class="', 'class=" ', $content);
4072 $content = preg_replace_callback(
4073 '/class="[^"]*[\s]+(' . $selector . ')[\s"]+[^"]*"[^>]*>/s',
4074 function($match) use(&$pos)
4075 {
4076 return str_replace($match[1], $match[1] . '@' . ($pos++), $match[0]);
4077 },
4078 $content
4079 );
4080
4081 if (is_array($resultItem))
4082 {
4083 foreach ($resultItem as $pos => $affectedItem)
4084 {
4085 if ($affectedItem['content'] ?? null)
4086 {
4087 $affectedItem['content'] = str_replace('/', '\/', $affectedItem['content']);
4088 $mask = '/class="[^"]*[\s]+' . $selector . '@' . $pos . '[\s"]+[^"]*"[^>]*>' . $affectedItem['content'] . '<\//s';
4089 $domCorrect = preg_match_all($mask, $content);
4090 if (!$domCorrect)
4091 {
4092 break 2;
4093 }
4094 }
4095 }
4096 }
4097 }
4098
4099 if (!$domCorrect)
4100 {
4101 $this->error->addError(
4102 'INCORRECT_AFFECTED',
4103 Loc::getMessage('LANDING_BLOCK_INCORRECT_AFFECTED')
4104 );
4105 return false;
4106 }
4107 }
4108
4109 Assets\PreProcessing::blockUpdateNodeProcessing($this);
4110
4111 return true;
4112 }
4113
4121 protected function getMenuHtml($data, $manifestNode, $level = 'root')
4122 {
4123 if (!is_array($data) || !isset($manifestNode[$level]))
4124 {
4125 return '';
4126 }
4127
4128 $htmlContent = '';
4129 $rootSelector = $manifestNode[$level];
4130
4131 if (
4132 isset($rootSelector['ulClassName']) &&
4133 isset($rootSelector['liClassName']) &&
4134 isset($rootSelector['aClassName']) &&
4135 is_string($rootSelector['ulClassName']) &&
4136 is_string($rootSelector['liClassName']) &&
4137 is_string($rootSelector['aClassName'])
4138 )
4139 {
4140 foreach ($data as $menuItem)
4141 {
4142 if (
4143 isset($menuItem['text']) && is_string($menuItem['text']) &&
4144 isset($menuItem['href']) && is_string($menuItem['href'])
4145 )
4146 {
4147 if ($menuItem['href'] === 'page:#landing0')
4148 {
4149 $res = Landing::addByTemplate(
4150 $this->getSiteId(),
4151 Assets\PreProcessing\Theme::getNewPageTemplate($this->getSiteId()),
4152 [
4153 'TITLE' => $menuItem['text'],
4154 ]
4155 );
4156 if ($res->isSuccess())
4157 {
4158 $menuItem['href'] = '#landing' . $res->getId();
4159 }
4160 }
4161 if (isset($menuItem['target']) && is_string($menuItem['target']))
4162 {
4163 $target = $menuItem['target'];
4164 }
4165 else
4166 {
4167 $target = '_self';
4168 }
4169 $htmlContent .= '<li class="' . \htmlspecialcharsbx($rootSelector['liClassName']) . '">';
4170 $htmlContent .= '<a href="' . \htmlspecialcharsbx($menuItem['href']) . '" target="' . $target . '"
4171 class="' . \htmlspecialcharsbx($rootSelector['aClassName']) . '">';
4172 $htmlContent .= \htmlspecialcharsbx($menuItem['text']);
4173 $htmlContent .= '</a>';
4174 if (isset($menuItem['children']))
4175 {
4176 $htmlContent .= $this->getMenuHtml(
4177 $menuItem['children'],
4178 $manifestNode,
4179 'children'
4180 );
4181 }
4182 $htmlContent .= '</li>';
4183 }
4184 }
4185 if ($htmlContent)
4186 {
4187 $htmlContent = '<ul class="' . \htmlspecialcharsbx($rootSelector['ulClassName']) . '">' .
4188 $htmlContent .
4189 '</ul>';
4190 }
4191 else if ($level == 'root')
4192 {
4193 $htmlContent = '<ul class="' . \htmlspecialcharsbx($rootSelector['ulClassName']) . '"></ul>';
4194 }
4195 }
4196
4197 return $htmlContent;
4198 }
4199
4205 public function updateCards(array $data = array())
4206 {
4207 if ($this->access < $this::ACCESS_W)
4208 {
4209 $this->error->addError(
4210 'ACCESS_DENIED',
4211 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4212 );
4213 return false;
4214 }
4215
4216 $manifest = $this->getManifest();
4217
4218 foreach ($data as $selector => $item)
4219 {
4220 $cardManifest = $manifest['cards'][$selector];
4221 // first gets content of current cards
4222 $cardContent = array();
4223 $cardCount = $this->getCardCount($selector);
4224 for ($i = 0; $i < $cardCount; $i++)
4225 {
4226 $cardContent[$i] = $this->getCardContent(
4227 $selector,
4228 $i
4229 );
4230 }
4231 // then fill all cards by content from existing cards and presets
4232 if (
4233 isset($item['source']) &&
4234 is_array($item['source'])
4235 )
4236 {
4237 $newContent = array();
4238 foreach ($item['source'] as $i => $source)
4239 {
4240 $type = isset($source['type'])
4241 ? $source['type']
4242 : self::CARD_SYM_CODE;
4243 $value = isset($source['value'])
4244 ? $source['value']
4245 : 0;
4246 // clone card
4247 if (
4248 $type == self::CARD_SYM_CODE &&
4249 isset($cardContent[$value])
4250 )
4251 {
4252 $newContent[$i] = $cardContent[$value];
4253 }
4254 // clone preset
4255 else if (
4256 $type == 'preset' &&
4257 isset($cardManifest['presets'][$value]['html'])
4258 )
4259 {
4260 $newContent[$i] = $cardManifest['presets'][$value]['html'];
4261 }
4262 else
4263 {
4264 $newContent[$i] = '';
4265 }
4266 }
4267 $newContent = trim(implode('', $newContent));
4268 if ($newContent)
4269 {
4270 $dom = $this->getDom();
4271 $resultList = $dom->querySelectorAll($selector);
4272 if (isset($resultList[0]))
4273 {
4274 $resultList[0]->getParentNode()->setInnerHtml(
4275 $newContent
4276 );
4277 }
4278 $this->saveContent(
4279 $dom->saveHTML()
4280 );
4281 }
4282 }
4283 // and finally update content cards
4284 if (
4285 isset($item['values']) &&
4286 is_array($item['values'])
4287 )
4288 {
4289 $updNodes = array();
4290 foreach ($item['values'] as $upd)
4291 {
4292 if (is_array($upd))
4293 {
4294 foreach ($upd as $sel => $content)
4295 {
4296 if(mb_strpos($sel, '@'))
4297 {
4298 [$sel, $pos] = explode('@', $sel);
4299 }
4300 if (!isset($updNodes[$sel]))
4301 {
4302 $updNodes[$sel] = array();
4303 }
4304 $updNodes[$sel][$pos] = $content;
4305 }
4306 }
4307 }
4308 if (!empty($updNodes))
4309 {
4310 $this->updateNodes($updNodes);
4311 }
4312 }
4313 }
4314
4315 return true;
4316 }
4317
4324 protected function removeStyle(DOM\Node $node, array $styleToRemove)
4325 {
4326 foreach ($node->getChildNodesArray() as $nodeChild)
4327 {
4328 if ($nodeChild instanceof DOM\Element)
4329 {
4330 $styles = DOM\StyleInliner::getStyle($nodeChild, false);
4331 if (!empty($styles))
4332 {
4333 foreach ($styleToRemove as $remove)
4334 {
4335 if (!is_array($remove))
4336 {
4337 $remove = [$remove => $remove];
4338 }
4339 $styles = array_diff_key($styles, $remove);
4340 }
4341 DOM\StyleInliner::setStyle($nodeChild, $styles);
4342 }
4343 }
4344 $node = $this->removeStyle($nodeChild, $styleToRemove);
4345 }
4346
4347 return $node;
4348 }
4349
4355 public function setClasses($data)
4356 {
4357 if ($this->access < $this::ACCESS_V)
4358 {
4359 $this->error->addError(
4360 'ACCESS_DENIED',
4361 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4362 );
4363 return false;
4364 }
4365
4366 $doc = $this->getDom();
4367 $manifest = $this->getManifest();
4368
4369 // detects position
4370 $positions = [];
4371 $position = -1;
4372 foreach ((array)$data as $selector => $item)
4373 {
4374 if (mb_strpos($selector, '@') !== false)
4375 {
4376 [$selector, $position] = explode('@', $selector);
4377 }
4378 else
4379 {
4380 $position = -1;
4381 }
4382 if ($selector === '#wrapper')
4383 {
4384 $selector = '#block' . $this->id;
4385 }
4386 if ($position >= 0)
4387 {
4388 if (!isset($positions[$selector]))
4389 {
4390 $positions[$selector] = [];
4391 }
4392 $positions[$selector][] = (int)$position;
4393 }
4394 $data[$selector] = $item;
4395 }
4396
4397 // wrapper (not realy exist)
4398 $wrapper = '#' . $this->getAnchor($this->id);
4399
4400 // find available nodes by manifest from data
4401 $styles = $manifest['style']['nodes'];
4402 $styles[$wrapper] = $manifest['style']['block'];
4403 foreach ($styles as $selector => $node)
4404 {
4405 if (isset($data[$selector]))
4406 {
4407 // prepare data
4408 if (!is_array($data[$selector]))
4409 {
4410 $data[$selector] = [
4411 $data[$selector],
4412 ];
4413 }
4414
4415 if (!isset($data[$selector]['classList']))
4416 {
4417 $data[$selector] = [
4418 'classList' => (array)$data[$selector],
4419 ];
4420 }
4421 if (!isset($data[$selector]['affect']))
4422 {
4423 $data[$selector]['affect'] = [];
4424 }
4425 // apply classes to the block
4426 if ($selector === $wrapper)
4427 {
4428 $nodesArray = $doc->getChildNodesArray();
4429 $resultList = [array_pop($nodesArray)];
4430 }
4431 // or by selector
4432 else
4433 {
4434 $resultList = $doc->querySelectorAll($selector);
4435 }
4436 foreach ($resultList as $pos => $resultNode)
4437 {
4438 $relativeSelector = $selector;
4439 if (isset($positions[$selector]))
4440 {
4441 if (!in_array($pos, $positions[$selector], true))
4442 {
4443 continue;
4444 }
4445 $relativeSelector .= '@' . $pos;
4446 }
4447
4448 if ($resultNode)
4449 {
4450 $contentBefore = $resultNode->getOuterHTML();
4451 if ((int)$resultNode->getNodeType() === $resultNode::ELEMENT_NODE)
4452 {
4453 $resultNode->setClassName(
4454 implode(' ', $data[$relativeSelector]['classList'])
4455 );
4456 }
4457
4458 // affected styles
4459 if (!empty($data[$relativeSelector]['affect']))
4460 {
4461 $this->removeStyle(
4462 $resultNode,
4463 $data[$relativeSelector]['affect']
4464 );
4465 }
4466
4467 // inline styles
4468 if (!empty($data[$relativeSelector]['style']))
4469 {
4470 $stylesInline = DOM\StyleInliner::getStyle($resultNode, false);
4472 $resultNode,
4473 array_merge($stylesInline, $data[$relativeSelector]['style'])
4474 );
4475 }
4476 else if (preg_match_all('/background-image:.*;/i', $resultNode->getAttribute('style'), $matches))
4477 {
4478 $resultNode->removeAttribute('style');
4479 $resultNode->setAttribute('style', implode('', $matches[0]));
4480 }
4481
4482 if (History::isActive())
4483 {
4484 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
4485 $history->push('EDIT_STYLE', [
4486 'block' => $this,
4487 'selector' => $selector,
4488 'isWrapper' => ($selector === $wrapper),
4489 'position' => $position >= 0 ? (int)$pos : -1,
4490 'affect' => $data[$relativeSelector]['affect'],
4491 'contentBefore' => $contentBefore,
4492 'contentAfter' => $resultNode->getOuterHTML(),
4493 ]);
4494 }
4495 }
4496 }
4497 }
4498 }
4499 // save rebuild html as text
4500 $this->saveContent($doc->saveHTML());
4501 Assets\PreProcessing::blockUpdateClassesProcessing($this);
4502 return true;
4503 }
4504
4511 protected static function collectAllowedAttrs(array $mixed, array &$allowed, $selector = null)
4512 {
4513 foreach ($mixed as $itemSelector => $item)
4514 {
4515 if (!is_string($itemSelector))
4516 {
4517 $itemSelector = $selector;
4518 }
4519 if (
4520 isset($item['attrs']) &&
4521 is_array($item['attrs'])
4522 )
4523 {
4524 self::collectAllowedAttrs($item['attrs'], $allowed, $itemSelector);
4525 }
4526 else if (
4527 isset($item['additional']['attrsType']) ||
4528 $itemSelector === 'additional'
4529 )
4530 {
4531 $manifestAttrs = self::getAttrs();
4532 $attrs = $manifestAttrs['bitrix']['attrs'];
4533 if (is_array($item['additional']['attrsType']))
4534 {
4535 foreach ($attrs as $attr) {
4536 $allowed[$itemSelector][] = $attr['attribute'];
4537 }
4538 }
4539 if (is_array($item['attrsType']))
4540 {
4541 foreach ($attrs as $attr) {
4542 $allowed['#wrapper'][] = $attr['attribute'];
4543 }
4544 }
4545 }
4546 else if (
4547 isset($item['additional']['attrs']) &&
4548 is_array($item['additional']['attrs'])
4549 )
4550 {
4551 self::collectAllowedAttrs($item['additional']['attrs'], $allowed, $itemSelector);
4552 }
4553 else if (
4554 isset($item['additional']) &&
4555 is_array($item['additional'])
4556 )
4557 {
4558 self::collectAllowedAttrs($item['additional'], $allowed, $itemSelector);
4559 }
4560 else if (
4561 isset($item['attribute']) &&
4562 is_string($item['attribute'])
4563 )
4564 {
4565 if (
4566 isset($item['selector']) &&
4567 is_string($item['selector'])
4568 )
4569 {
4570 $itemSelector = trim($item['selector']);
4571 }
4572 if ($itemSelector)
4573 {
4574 if (!isset($allowed[$itemSelector]))
4575 {
4576 $allowed[$itemSelector] = [];
4577 }
4578 $allowed[$itemSelector][] = $item['attribute'];
4579 }
4580 }
4581 else if (is_array($item))
4582 {
4583 self::collectAllowedAttrs($item, $allowed, $itemSelector);
4584 }
4585 }
4586 }
4587
4593 public function setAttributes($data)
4594 {
4595 if ($this->access < $this::ACCESS_W)
4596 {
4597 $this->error->addError(
4598 'ACCESS_DENIED',
4599 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
4600 );
4601 return;
4602 }
4603
4604 $doc = $this->getDom();
4605 $manifest = $this->getManifest();
4606 $wrapper = '#' . $this->getAnchor($this->id);
4607
4608 // collect allowed attrs
4609 $allowedAttrs = [];
4610 self::collectAllowedAttrs($manifest['style']['nodes'], $allowedAttrs);
4611 self::collectAllowedAttrs($manifest['attrs'], $allowedAttrs);
4612 self::collectAllowedAttrs($manifest['cards'], $allowedAttrs);
4613 self::collectAllowedAttrs($manifest['style']['block'], $allowedAttrs);
4614
4615 // update attrs
4616 if ($allowedAttrs)
4617 {
4618 // all allowed attrs from manifest with main selector ([selector] => [data-test, data-test2])
4619 foreach ($allowedAttrs as $selector => $allowed)
4620 {
4621 // it's not interesting for us, if there is no new data for this selector
4622 if ((isset($data[$selector]) && is_array($data[$selector])) || isset($data[$wrapper]) )
4623 {
4624 // set attrs to the block
4625 if ($selector === '#wrapper')
4626 {
4627 $selector = $wrapper;
4628 }
4629 if ($selector == $wrapper)
4630 {
4631 $nodesArray = $doc->getChildNodesArray();
4632 $resultList = [array_pop($nodesArray)];
4633 }
4634 // or by selector
4635 else
4636 {
4637 $resultList = $doc->querySelectorAll($selector);
4638 }
4639 // external data for changing in allowed attrs
4640 foreach ($data[$selector] as $attrKey => $attrData)
4641 {
4642 // if key without position (compatibility)
4643 if (!($attrKey == (string)(int)$attrKey))
4644 {
4645 $attrData = [$attrKey => $attrData];
4646 $attrKey = -1;
4647 }
4648 if (!is_array($attrData))
4649 {
4650 continue;
4651 }
4652 // attrs new data in each selector ([data-test] => value)
4653 foreach ($attrData as $key => $value)
4654 {
4655 if (!in_array($key, $allowed))
4656 {
4657 continue;
4658 }
4660 $value = is_array($value) ? json_encode($value) : $value;
4661
4662 // result nodes by main selector
4663 foreach ($resultList as $pos => $resultNode)
4664 {
4665 // if position of node that we try to find
4666 if ($attrKey == -1 || $attrKey == $pos)
4667 {
4668 $valueBefore = $resultNode->getAttribute($key);
4669 // update node
4670 $resultNode->setAttribute($key, $value);
4671 if (History::isActive())
4672 {
4673 $history = new History($this->getLandingId(), History::ENTITY_TYPE_LANDING);
4674 $history->push('EDIT_ATTRIBUTES', [
4675 'block' => $this,
4676 'selector' => $selector,
4677 'isWrapper' => ($selector === $wrapper),
4678 'attribute' => $key,
4679 'position' => (int)$attrKey,
4680 'valueBefore' => $valueBefore,
4681 'valueAfter' => $value,
4682 ]);
4683 }
4684 }
4685 }
4686 }
4687 }
4688 }
4689 }
4690 }
4691
4692 // save result
4693 $this->saveContent($doc->saveHTML());
4694 }
4695
4701 protected static function replaceMetaMarkers($content)
4702 {
4703 if (mb_strpos($content, '#breadcrumb#') !== false)
4704 {
4705 ob_start();
4706 $arResult = array(
4707 array(
4708 'LINK' => '#',
4709 'TITLE' => '',
4710 ),
4711 array(
4712 'LINK' => '#',
4713 'TITLE' => Loc::getMessage('LANDING_BLOCK_BR1'),
4714 ),
4715 array(
4716 'LINK' => '#',
4717 'TITLE' => Loc::getMessage('LANDING_BLOCK_BR2'),
4718 ),
4719 array(
4720 'LINK' => '#',
4721 'TITLE' => '',
4722 ),
4723 );
4724 $tplId = Manager::getTemplateId(
4726 );
4727 $strChainTemplate = getLocalPath('templates/' . $tplId . '/chain_template.php');
4728 $strChainTemplate = Manager::getDocRoot() . $strChainTemplate;
4729 if (file_exists($strChainTemplate))
4730 {
4731 echo include $strChainTemplate;
4732 }
4733 $breadcrumb = ob_get_contents();
4734 ob_end_clean();
4735 $content = str_replace(
4736 '#breadcrumb#',
4737 $breadcrumb,
4738 $content
4739 );
4740 }
4741
4742 if (mb_strpos($content, '#title#') !== false)
4743 {
4744 $content = str_replace(
4745 '#title#',
4746 Loc::getMessage('LANDING_BLOCK_TITLE'),
4747 $content
4748 );
4749 }
4750
4751 return $content;
4752 }
4753
4759 public static function deleteByCode($code)
4760 {
4761 if (!is_array($code))
4762 {
4763 $code = array($code);
4764 }
4765 $res = parent::getList(array(
4766 'select' => array(
4767 'ID',
4768 ),
4769 'filter' => array(
4770 '=CODE' => $code,
4771 ),
4772 ));
4773 while ($row = $res->fetch())
4774 {
4775 self::parentDelete($row['ID']);
4776 }
4777 }
4778
4784 private static function parentDelete($id)
4785 {
4786 return parent::delete($id);
4787 }
4788
4794 public static function deleteAll($lid)
4795 {
4796 $res = parent::getList([
4797 'select' => [
4798 'ID',
4799 ],
4800 'filter' => [
4801 'LID' => (int)$lid,
4802 ],
4803 ]);
4804 while ($row = $res->fetch())
4805 {
4806 parent::delete($row['ID']);
4807 }
4808 }
4809
4814 public function getSearchContent()
4815 {
4816 $manifest = $this->getManifest();
4817 $search = [];
4818
4819 // get content nodes
4820 if (isset($manifest['nodes']))
4821 {
4822 foreach ($manifest['nodes'] as $selector => $node)
4823 {
4825 $class = NodeType::getClassName($node['type']);
4826 if (is_callable([$class, 'getSearchableNode']))
4827 {
4828 $search = array_merge($search, $class::getSearchableNode($this, $selector));
4829 }
4830 }
4831 }
4832
4833 return $search ? implode(' ', $search) : '';
4834 }
4835
4841 public function export(array $params = [])
4842 {
4843 $manifest = $this->getManifest();
4844 $doc = $this->getDom();
4845
4846 $cards = [];
4847 $nodes = [];
4848 $menu = [];
4849 $styles = [];
4850 $allAttrs = [];
4851
4852 // prepare params
4853 if (!isset($params['clear_form']))
4854 {
4855 $params['clear_form'] = true;
4856 }
4857
4858 // get actual cards content
4859 if (isset($manifest['cards']))
4860 {
4861 foreach ($manifest['cards'] as $selector => $node)
4862 {
4863 $cards[$selector] = [
4864 'source' => [],
4865 ];
4866 $resultList = $doc->querySelectorAll($selector);
4867 $resultListCnt = count($resultList);
4868 foreach ($resultList as $pos => $result)
4869 {
4870 $cards[$selector]['source'][$pos] = array(
4871 'value' => $result->getAttribute('data-card-preset'),
4872 'type' => Block::PRESET_SYM_CODE,
4873 );
4874 if (!$cards[$selector]['source'][$pos]['value'])
4875 {
4876 //@tmp for menu first item
4877 if (mb_strpos($this->getCode(), 'menu') !== false)
4878 {
4879 $cards[$selector]['source'][$pos]['value'] = $resultListCnt > 0 ? 1 : 0;
4880 }
4881 else
4882 {
4883 $cards[$selector]['source'][$pos]['value'] = 0;
4884 }
4885 $cards[$selector]['source'][$pos]['type'] = Block::CARD_SYM_CODE;
4886 }
4887 }
4888 // attrs
4889 if (
4890 isset($node['additional']['attrs']) &&
4891 is_array($node['additional']['attrs'])
4892 )
4893 {
4894 foreach ($node['additional']['attrs'] as $attr)
4895 {
4896 if (isset($attr['attribute']))
4897 {
4898 if (!isset($allAttrs[$selector]))
4899 {
4900 $allAttrs[$selector] = [];
4901 }
4902 $allAttrs[$selector][] = $attr['attribute'];
4903 }
4904 }
4905 }
4906 }
4907 }
4908 // get content nodes
4909 if (isset($manifest['nodes']))
4910 {
4911 foreach ($manifest['nodes'] as $selector => $node)
4912 {
4914 $class = NodeType::getClassName($node['type']);
4915 $nodes[$selector] = $class::getNode($this, $selector);
4916 }
4917 }
4918 if (isset($manifest['menu']))
4919 {
4920 // recursive getting menu
4921 $exportMenu = function($resultList) use(&$exportMenu)
4922 {
4923 if(!$resultList)
4924 {
4925 return [];
4926 }
4927
4928 $menu = [];
4929 foreach ($resultList->getChildNodesArray() as $pos => $node)
4930 {
4931 $menu[$pos] = [];
4932 if ($node->getNodeName() == 'LI')
4933 {
4934 foreach ($node->getChildNodesArray() as $nodeInner)
4935 {
4936 if ($nodeInner->getNodeName() == 'A')
4937 {
4938 $menu[$pos]['text'] = trim($nodeInner->getTextContent());
4939 $menu[$pos]['href'] = trim($nodeInner->getAttribute('href'));
4940 $menu[$pos]['target'] = trim($nodeInner->getAttribute('target'));
4941 }
4942 else if ($nodeInner->getNodeName() == 'UL')
4943 {
4944 $menu[$pos]['children'] = $exportMenu($nodeInner);
4945 }
4946 }
4947 }
4948 if (!$menu[$pos])
4949 {
4950 unset($menu[$pos]);
4951 }
4952 }
4953 return array_values($menu);
4954 };
4955 foreach ($manifest['menu'] as $selector => $menuNode)
4956 {
4957 $menu[$selector] = $exportMenu($doc->querySelector($selector));
4958 }
4959 }
4960 // get actual css from nodes
4961 if (isset($manifest['style']['nodes']))
4962 {
4963 foreach ($manifest['style']['nodes'] as $selector => $node)
4964 {
4965 $nodeStyle = Node\Style::getStyle($this, $selector);
4966 if ($nodeStyle)
4967 {
4968 $styles[$selector] = $nodeStyle;
4969 }
4970 // attrs
4971 if (
4972 isset($node['additional']['attrs']) &&
4973 is_array($node['additional']['attrs'])
4974 )
4975 {
4976 foreach ($node['additional']['attrs'] as $attr)
4977 {
4978 if (isset($attr['attribute']))
4979 {
4980 if (!isset($allAttrs[$selector]))
4981 {
4982 $allAttrs[$selector] = [];
4983 }
4984 $allAttrs[$selector][] = $attr['attribute'];
4985 }
4986 }
4987 }
4988 }
4989 }
4990 // get actual css from block wrapper
4991 if (!empty($manifest['style']['block']))
4992 {
4993 $selector = '#wrapper';
4994 $wrapperStyle = Node\Style::getStyle($this, $selector);
4995 if ($wrapperStyle)
4996 {
4997 $styles[$selector] = $wrapperStyle;
4998 }
4999 }
5000 // attrs
5001 if (
5002 isset($manifest['style']['block']['additional']['attrs']) &&
5003 is_array($manifest['style']['block']['additional']['attrs'])
5004 )
5005 {
5006 $selector = '#wrapper';
5007 foreach ($manifest['style']['block']['additional']['attrs'] as $attr)
5008 {
5009 if (isset($attr['attribute']))
5010 {
5011 if (!isset($allAttrs[$selector]))
5012 {
5013 $allAttrs[$selector] = [];
5014 }
5015 $allAttrs[$selector][] = $attr['attribute'];
5016 }
5017 }
5018 }
5019 // get actual attrs from nodes
5020 if (isset($manifest['attrs']))
5021 {
5022 foreach ($manifest['attrs'] as $selector => $item)
5023 {
5024 if (isset($item['attribute']))
5025 {
5026 if (!isset($allAttrs[$selector]))
5027 {
5028 $allAttrs[$selector] = [];
5029 }
5030 $allAttrs[$selector][] = $item['attribute'];
5031 }
5032 else if (is_array($item))
5033 {
5034 foreach ($item as $itemAttr)
5035 {
5036 if (isset($itemAttr['attribute']))
5037 {
5038 if (!isset($allAttrs[$selector]))
5039 {
5040 $allAttrs[$selector] = [];
5041 }
5042 $allAttrs[$selector][] = $itemAttr['attribute'];
5043 }
5044 }
5045 }
5046 }
5047 }
5048 // remove some system attrs
5049 if (
5050 $params['clear_form'] &&
5051 isset($allAttrs['.bitrix24forms'])
5052 )
5053 {
5054 unset($allAttrs['.bitrix24forms']);
5055 }
5056 // collect attrs
5057 $allAttrsNew = [];
5058 if (isset($allAttrs['#wrapper']))
5059 {
5060 $allAttrsNew['#wrapper'] = [];
5061 $nodesArray = $doc->getChildNodesArray();
5062 $resultList = [array_pop($nodesArray)];
5063 foreach ($resultList as $pos => $result)
5064 {
5065 foreach ($allAttrs['#wrapper'] as $attrKey)
5066 {
5067 if (!isset($allAttrsNew['#wrapper'][$pos]))
5068 {
5069 $allAttrsNew['#wrapper'][$pos] = [];
5070 }
5071 $allAttrsNew['#wrapper'][$pos][$attrKey] = $result->getAttribute($attrKey);
5072 }
5073 }
5074 unset($allAttrs['#wrapper']);
5075 }
5076 foreach ($allAttrs as $selector => $attr)
5077 {
5078 $resultList = $doc->querySelectorAll($selector);
5079 foreach ($resultList as $pos => $result)
5080 {
5081 if (!isset($allAttrsNew[$selector]))
5082 {
5083 $allAttrsNew[$selector] = [];
5084 }
5085 if (!isset($allAttrsNew[$selector][$pos]))
5086 {
5087 $allAttrsNew[$selector][$pos] = [];
5088 }
5089 foreach ($attr as $attrKey)
5090 {
5091 $allAttrsNew[$selector][$pos][$attrKey] = $result->getAttribute($attrKey);
5092 }
5093 unset($attrVal);
5094 }
5095 }
5096 $allAttrs = $allAttrsNew;
5097 unset($allAttrsNew);
5098
5099 return [
5100 'cards' => $cards,
5101 'nodes' => $nodes,
5102 'menu' => $menu,
5103 'style' => $styles,
5104 'attrs' => $allAttrs,
5105 'dynamic' => $this->dynamicParams,
5106 ];
5107 }
5108
5117 public static function search($query, array $filter = [], array $select = ['LID'], array $group = ['LID'])
5118 {
5119 $result = [];
5120
5121 $filter['*%SEARCH_CONTENT'] = $query;
5122 $filter['=DELETED'] = 'N';
5123
5124 $res = Internals\BlockTable::getList([
5125 'select' => $select,
5126 'filter' => $filter,
5127 'group' => $group,
5128 'order' => ['SORT' => 'desc'],
5129 ]);
5130 while ($row = $res->fetch())
5131 {
5132 $result[] = $row;
5133 }
5134
5135 return $result;
5136 }
5137
5143 public static function add($fields)
5144 {
5145 if (
5146 !defined('LANDING_MUTATOR_MODE') ||
5147 LANDING_MUTATOR_MODE !== true
5148 )
5149 {
5150 throw new \Bitrix\Main\SystemException(
5151 'Disabled for direct access.'
5152 );
5153 }
5154 else
5155 {
5156 return parent::add($fields);
5157 }
5158 }
5159
5166 public static function update($id, $fields = array())
5167 {
5168 if (
5169 !defined('LANDING_MUTATOR_MODE') ||
5170 LANDING_MUTATOR_MODE !== true
5171 )
5172 {
5173 throw new \Bitrix\Main\SystemException(
5174 'Disabled for direct access.'
5175 );
5176 }
5177 else
5178 {
5179 return parent::update($id, $fields);
5180 }
5181 }
5182
5188 public static function delete($id)
5189 {
5190 if (
5191 !defined('LANDING_MUTATOR_MODE') ||
5192 LANDING_MUTATOR_MODE !== true
5193 )
5194 {
5195 throw new \Bitrix\Main\SystemException(
5196 'Disabled for direct access.'
5197 );
5198 }
5199 else
5200 {
5201 return parent::delete($id);
5202 }
5203 }
5204
5210 public static function getFavorites(?string $tplCode): array
5211 {
5212 return parent::getList([
5213 'filter' => [
5214 'LID' => 0,
5215 '=DELETED' => 'N',
5216 '=TPL_CODE' => $tplCode,
5217 ],
5218 'order' => [
5219 'ID' => 'asc',
5220 ],
5221 ])->fetchAll();
5222 }
5223
5229 public static function getList($fields = array())
5230 {
5231 if (
5232 !defined('LANDING_MUTATOR_MODE') ||
5233 LANDING_MUTATOR_MODE !== true
5234 )
5235 {
5236 throw new \Bitrix\Main\SystemException(
5237 'Disabled for direct access.'
5238 );
5239 }
5240 else
5241 {
5242 return parent::getList($fields);
5243 }
5244 }
5245
5253 protected static function getAjaxInitiatedAssets()
5254 {
5255 Asset::getInstance()->getJs();
5256 Asset::getInstance()->getCss();
5257 Asset::getInstance()->getStrings();
5258
5259 $targetTypeList = array('JS', 'CSS');
5260 $CSSList = $JSList = $stringsList = [];
5261
5262 foreach ($targetTypeList as $targetType)
5263 {
5264 $targetAssetList = Asset::getInstance()->getTargetList($targetType);
5265
5266 foreach ($targetAssetList as $targetAsset)
5267 {
5268 $assetInfo = Asset::getInstance()->getAssetInfo($targetAsset['NAME'], \Bitrix\Main\Page\AssetMode::ALL);
5269
5270 if (!empty($assetInfo['JS']))
5271 {
5272 $JSList = array_merge($JSList, $assetInfo['JS']);
5273 }
5274
5275 if (!empty($assetInfo['CSS']))
5276 {
5277 $CSSList = array_merge($CSSList, $assetInfo['CSS']);
5278 }
5279
5280 if (!empty($assetInfo['STRINGS']))
5281 {
5282 $stringsList = array_merge($stringsList, $assetInfo['STRINGS']);
5283 }
5284 }
5285 }
5286
5287 return [
5288 'js' => array_unique($JSList),
5289 'css' => array_unique($CSSList),
5290 'strings' => array_unique($stringsList),
5291 ];
5292 }
5293
5302 public static function isContains(int $entityId, string $needed, bool $isLanding = false): bool
5303 {
5304 $filter = [
5305 '=ACTIVE' => 'Y',
5306 '=DELETED' => 'N',
5307 'CONTENT' => '%' . $needed . '%',
5308 ];
5309 if ($isLanding)
5310 {
5311 $filter['LID'] = $entityId;
5312 }
5313 else
5314 {
5315 $filter['ID'] = $entityId;
5316 }
5317 $res = parent::getList([
5318 'select' => [
5319 'LID',
5320 'SITE_ID' => 'LANDING.SITE_ID',
5321 ],
5322 'filter' => $filter,
5323 ]);
5324 if ($row = $res->fetch())
5325 {
5327 'select' => [
5328 'ID',
5329 ],
5330 'filter' => [
5331 'ID' => $row['LID'],
5332 ],
5333 ]);
5334 if ($res->fetch())
5335 {
5336 return true;
5337 }
5338
5339 if (\Bitrix\Landing\Site\Scope\Group::getGroupIdBySiteId($row['SITE_ID'], true))
5340 {
5341 return true;
5342 }
5343 }
5344
5345 return false;
5346 }
5347
5352 public static function checkComponentExists(string $componentName): bool
5353 {
5354 $path2Component = \CComponentEngine::MakeComponentPath($componentName);
5355 if ($path2Component !== '')
5356 {
5357 $componentPath = getLocalPath("components" . $path2Component);
5358 $componentFile = $_SERVER["DOCUMENT_ROOT"] . $componentPath . "/component.php";
5359
5360 return file_exists($componentFile) && is_file($componentFile);
5361 }
5362
5363 return false;
5364 }
5365}
$path
Определения access_edit.php:21
$positions
Определения access_edit.php:237
$count
Определения admin_tab.php:4
$type
Определения options.php:106
global $APPLICATION
Определения include.php:80
$arResult
Определения generate_coupon.php:16
const LOCATION_TEMPLATE
Определения location.php:9
const LOCATION_AFTER_TEMPLATE
Определения location.php:10
static getInstance()
Определения manager.php:59
const BLOCKS_DIR
Определения blockrepo.php:28
static getNamespaces()
Определения blockrepo.php:840
const FILTER_SKIP_HIDDEN_BLOCKS
Определения blockrepo.php:73
const NEW_BLOCK_LT
Определения blockrepo.php:43
const FILTER_SKIP_SYSTEM_BLOCKS
Определения blockrepo.php:72
const FILTER_DEFAULTS
Определения blockrepo.php:70
static getGeneralPaths()
Определения blockrepo.php:817
static register($id)
Определения cache.php:52
static enableCache()
Определения cache.php:28
static clear($id)
Определения cache.php:69
static isCaching()
Определения cache.php:37
static disableCache()
Определения cache.php:19
static getRepository(bool $installRepo=true)
Определения designer.php:262
static parseManifest(string $content)
Определения designer.php:219
getMenuHtml($data, $manifestNode, $level='root')
Определения block.php:4121
save(array $additionalFields=[])
Определения block.php:2704
setClasses($data)
Определения block.php:4355
includeBlockClass($path)
Определения block.php:2179
getAccess()
Определения block.php:1503
getRuntimeRequiredUserAction()
Определения block.php:3511
static getList($fields=array())
Определения block.php:5229
static getFavorites(?string $tplCode)
Определения block.php:5210
$deleted
Определения block.php:193
isDesigned()
Определения block.php:1494
getId()
Определения block.php:1376
$designed
Определения block.php:199
adjustCards($selector, $count, &$changed=false)
Определения block.php:3571
const PRESET_SYM_CODE
Определения block.php:79
parseManifest()
Определения block.php:1613
static getBlockContent($id, $editMode=false, array $params=array())
Определения block.php:1136
static getAjaxInitiatedAssets()
Определения block.php:5253
const CARD_SYM_CODE
Определения block.php:74
getSite()
Определения block.php:1553
unlink()
Определения block.php:2782
static getMessageBlock($params, $template='')
Определения block.php:2225
setAttributes($data)
Определения block.php:4593
getCardCount($selector)
Определения block.php:3802
getClass()
Определения block.php:2169
getError()
Определения block.php:1590
saveAssets(array $assets)
Определения block.php:2635
static getNormalizedBlock(string $code)
Определения block.php:593
$assets
Определения block.php:175
static removeAsUsed(string $blockCode)
Определения block.php:1042
checkDesignedManifest(array $manifest)
Определения block.php:1636
static update($id, $fields=array())
Определения block.php:5166
saveSort($sort)
Определения block.php:2871
static getManifestFile($code)
Определения block.php:2006
static getBlockNamespace($code)
Определения block.php:1292
static getContentFromRepository(string $code, string $namespace=null)
Определения block.php:634
static getSpecialManifest(string $type)
Определения block.php:1090
exist()
Определения block.php:1367
updateNodes($data, $additional=array())
Определения block.php:3935
setCardContent($selector, $position, $content)
Определения block.php:3738
const DEFAULT_WRAPPER_STYLE
Определения block.php:84
getSiteId()
Определения block.php:1394
changeFavoriteMeta(array $meta)
Определения block.php:2768
static $internalClass
Определения block.php:90
$runtimeRequiredUserAction
Определения block.php:156
saveContent(string $content, $designed=false)
Определения block.php:2676
$public
Определения block.php:205
saveDynamicParams(array $sourceParams=[], array $params=[])
Определения block.php:2964
$repoInfo
Определения block.php:132
getRepoId()
Определения block.php:1544
static getLandingRowByBlockId($id, array $select=array('ID'))
Определения block.php:551
static getAttrs()
Определения block.php:1080
static getBlockPath($code, $namespace=null)
Определения block.php:1337
changeLanding($lid)
Определения block.php:2744
removeStyle(DOM\Node $node, array $styleToRemove)
Определения block.php:4324
$landingActive
Определения block.php:187
setAnchor($anchor)
Определения block.php:2835
static getRepository()
Определения block.php:971
setActive($active)
Определения block.php:1526
getExt()
Определения block.php:2160
$content
Определения block.php:150
getContent()
Определения block.php:1421
static collectAllowedAttrs(array $mixed, array &$allowed, $selector=null)
Определения block.php:4511
$sort
Определения block.php:120
static getRowByBlockId($id, array $select=array('ID'))
Определения block.php:562
static add($fields)
Определения block.php:5143
getManifest(bool $extended=false, bool $missCache=false, array $params=array())
Определения block.php:1682
getLandingId()
Определения block.php:1385
static clearRepositoryCache()
Определения block.php:961
updateCards(array $data=array())
Определения block.php:4205
$code
Определения block.php:138
static getSemantic()
Определения block.php:1071
getBlockClass()
Определения block.php:1430
setAllowedByTariff(bool $mark)
Определения block.php:1447
$repoId
Определения block.php:126
setSort($sort)
Определения block.php:2825
getTypeClass($type)
Определения block.php:1604
getLocalAnchor()
Определения block.php:1412
const ACCESS_X
Определения block.php:69
setAccess($letter)
Определения block.php:1513
array $allowedRepoExtensions
Определения block.php:249
const CSS_FILE_NAME
Определения block.php:34
resetContent()
Определения block.php:1456
setRuntimeRequiredUserAction(array $action)
Определения block.php:3520
getSort()
Определения block.php:2892
changeNodeName($data)
Определения block.php:3878
cloneCard($selector, $position, $content='')
Определения block.php:3628
$allowedByTariff
Определения block.php:211
getDom($clear=false)
Определения block.php:3530
getCSS()
Определения block.php:2142
clearDynamic()
Определения block.php:3502
$docRoot
Определения block.php:217
$dynamicParams
Определения block.php:229
markDeleted($mark)
Определения block.php:2807
static deleteByCode($code)
Определения block.php:4759
isActive()
Определения block.php:1476
setDynamic($edit)
Определения block.php:3013
$active
Определения block.php:181
$lid
Определения block.php:102
$siteId
Определения block.php:114
static fillLanding(Landing $landing, $limit=0, array $params=array())
Определения block.php:359
getAssets()
Определения block.php:2665
removeCard($selector, $position)
Определения block.php:3820
getCode()
Определения block.php:1403
getPreview()
Определения block.php:1576
static createFromRepository(Landing $landing, string $code, array $data=array())
Определения block.php:692
const ACCESS_V
Определения block.php:59
localizationManifest(array &$manifest, array $lang)
Определения block.php:1983
static getLastUsed(int $count=15)
Определения block.php:981
getCardContent($selector, $position)
Определения block.php:3779
isPublic()
Определения block.php:1485
const ACCESS_D
Определения block.php:54
getAsset($type=null)
Определения block.php:2048
const JS_FILE_NAME
Определения block.php:39
static markAsUsed(string $blockCode)
Определения block.php:1009
static deleteAll($lid)
Определения block.php:4794
const ACCESS_W
Определения block.php:64
view($edit=false, \Bitrix\Landing\Landing $landing=null, array $params=array())
Определения block.php:2247
$access
Определения block.php:163
getJS()
Определения block.php:2151
$anchor
Определения block.php:144
$metaData
Определения block.php:169
const REPO_MASK
Определения block.php:44
static getAnchor($id)
Определения block.php:1282
static replaceMetaMarkers($content)
Определения block.php:4701
static getLandingIdByBlockId($id)
Определения block.php:516
getMeta()
Определения block.php:3559
const PREVIEW_FILE_NAME
Определения block.php:29
$parentId
Определения block.php:108
static checkComponentExists(string $componentName)
Определения block.php:5352
static isNewBlock($block)
Определения block.php:922
array $allowedExtensions
Определения block.php:235
$id
Определения block.php:96
static cloneForEdit(\Bitrix\Landing\Landing $landing)
Определения block.php:454
static isContains(int $entityId, string $needed, bool $isLanding=false)
Определения block.php:5302
static getStyle()
Определения block.php:1062
$error
Определения block.php:223
__construct($id, $data=[], array $params=[])
Определения block.php:259
static prepareBlockContentFromRepository($block)
Определения block.php:847
getDynamicParams($id=null)
Определения block.php:2902
static search($query, array $filter=[], array $select=['LID'], array $group=['LID'])
Определения block.php:5117
const ACCESS_A
Определения block.php:49
static publicationBlocks(\Bitrix\Landing\Landing $landing)
Определения block.php:506
static getReplaceRequisiteCompanyNameForContent(string $xmlId)
Определения crm.php:279
static getReplacesForContent(int $siteId, bool $attributesReplace=true)
Определения crm.php:241
const FILE_MASK_HREF
Определения disk.php:7
static isMobileHit()
Определения mobile.php:154
static getDownloadLink(string $scope, int $blockId, ?int $fileId=null)
Определения diskfile.php:31
static getTimestamp()
Определения Countdown.php:7
static addToBlock(int $blockId, $fileId, bool $temp=false)
Определения file.php:305
static getFilesFromBlockContent($blockId, $content)
Определения file.php:374
static changeValue($key, $value)
Определения seo.php:89
static getOption($code, $default=null)
Определения manager.php:160
static getVersion()
Определения manager.php:1255
static getDocRoot()
Определения manager.php:180
static getApplication()
Определения manager.php:71
static getUserId()
Определения manager.php:107
static getCacheManager()
Определения manager.php:89
static getMainSiteId()
Определения manager.php:546
static setPageTitle($title, $single=false)
Определения manager.php:211
static getTemplateId($siteId=null)
Определения manager.php:513
static isAutoPublicationEnabled()
Определения manager.php:442
static getZone()
Определения manager.php:930
const BUY_LICENSE_PATH
Определения manager.php:31
static blocksPublication(\Bitrix\Landing\Landing $landing, $_245979103=null)
Определения mutator.php:1
const STYLES_WITH_IMAGE
Определения styleimg.php:15
const STYLE_IMAGE
Определения type.php:13
static getManifest($lid, $block, array $params=array())
Определения block.php:939
static getAppInfo($code)
Определения repo.php:399
static getById($id)
Определения repo.php:202
static getBlock($id)
Определения repo.php:150
static isAllowed(string $code, array $params=[], string $cacheSalt='')
Определения manager.php:214
static get(int $id, bool $active=false, bool $force=false)
Определения syspage.php:100
Определения error.php:15
$code
Определения error.php:17
getCode()
Определения error.php:54
static getList(array $parameters=array())
Определения datamanager.php:431
$value
Определения date.php:11
static setStyle(Element $node, $styleList, $append=false)
Определения styleinliner.php:61
static getStyle(Element $node, $singleStyle=true)
Определения styleinliner.php:43
static getExtInfo($ext)
Определения jscore.php:541
$content
Определения commerceml.php:144
if( $strWarning=="") if($strWarning=="") $componentPath
Определения component_props2.php:197
$componentName
Определения component_props2.php:49
$data['IS_AVAILABLE']
Определения .description.php:13
$template
Определения file_edit.php:49
for($fileNumber="";; $fileNumber++) $pageTitle
Определения file_new.php:204
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$handle
Определения include.php:55
$result
Определения get_property_values.php:14
$query
Определения get_search.php:11
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
if(!defined('SITE_ID')) $lang
Определения include.php:91
if(!is_array($deviceNotifyCodes)) $access
Определения options.php:174
$siteId
Определения ajax.php:8
$public
Определения backup.php:109
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
getLocalPath($path, $baseFolder="/bitrix")
Определения tools.php:5092
randString($pass_len=10, $pass_chars=false)
Определения tools.php:2154
Определения builder.php:3
Определения ai.php:3
Определения cookies.php:2
Определения agent.php:3
Определения aliases.php:54
Определения collection.php:2
Определения attr.php:3
$order
Определения payment.php:8
$entityId
Определения payment.php:4
$settings
Определения product_settings.php:43
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$messages
Определения template.php:8
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$title
Определения pdf.php:123
$paths
Определения options.php:2080
$matches
Определения index.php:22
$error
Определения subscription_card_product.php:20
$action
Определения file_dialog.php:21
$site
Определения yandex_run.php:614
$fields
Определения yandex_run.php:501