1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
landing.php
См. документацию.
1<?php
2
3namespace Bitrix\Landing;
4
5use Bitrix\Landing\Block\BlockRepo;
6use Bitrix\Landing\Internals\BlockTable;
7use Bitrix\Landing\Metrika\Types;
8use Bitrix\Landing\Site\Type;
9use Bitrix\Main\Application;
10use Bitrix\Main\Config\Option;
11use Bitrix\Main\Event;
12use Bitrix\Main\EventResult;
13use Bitrix\Main\Page\Asset;
14use Bitrix\Main\Localization\Loc;
15use Bitrix\Main\ModuleManager;
16use Bitrix\Main\UI\Extension;
17use Bitrix\Rest\AppTable;
18
19Loc::loadMessages(__FILE__);
20
22{
26 protected const META_KEYS = [
27 'CREATED_BY_ID', 'MODIFIED_BY_ID', 'DATE_CREATE',
28 'DATE_MODIFY', 'DATE_PUBLIC', 'INITIATOR_APP_CODE', 'VIEWS', 'TPL_CODE',
29 'ACTIVE', 'PUBLIC', 'SITE_CODE', 'SITE_SPECIAL', 'RULE',
30 'SITE_VERSION', 'SITE_LANG', 'SITE_TPL_CODE',
31 ];
32
36 protected const META_KEYS_MODIFIABLE = [
37 'DATE_MODIFY', 'DATE_PUBLIC', 'ACTIVE', 'PUBLIC',
38 ];
39
44 public static $internalClass = 'LandingTable';
45
50 protected static $enabledUpdate = true;
51
56 protected static $checkUniqueAddress = true;
57
62 protected static $checkDelete = true;
63
68 protected static $editMode = false;
69
74 protected static $previewMode = false;
75
80 protected static $variables = array();
81
86 protected static $dynamicFilterId = 0;
87
92 protected static $dynamicElementId = 0;
93
98 protected static string $siteType = '';
99
104 protected $mainInstance = true;
105
110 protected $metaData = array();
111
116 protected $blocks = array();
117
122 protected $id = 0;
123
128 protected $title = '';
129
134 protected $code = '';
135
140 protected $xmlId = '';
141
146 protected $siteRow = [];
147
152 protected $siteId = 0;
153
158 protected $siteTitle = '';
159
164 protected $domainId = 0;
165
170 protected $folderId = 0;
171
176 protected int $tplId = 0;
177
182 protected $tplType = 'landing';
183
188 protected $active = false;
189
194 protected Error $error;
195
200 protected $rights = [];
201
206 protected $checkPermissions = true;
207
212 protected $version = 1;
213
218 protected $disableLinkPreview = false;
219
225 protected function __construct($id, $params = array())
226 {
227 $id = intval($id);
228 $this->error = new Error;
229 $filter = array(
230 'ID' => $id,
231 );
232 if (
233 isset($params['force_deleted'])
234 && $params['force_deleted'] === true
235 )
236 {
237 $filter['=DELETED'] = ['Y', 'N'];
238 $filter['=SITE.DELETED'] = ['Y', 'N'];
239 }
240 if (
241 isset($params['check_permissions'])
242 && $params['check_permissions'] === false
243 )
244 {
245 $filter['CHECK_PERMISSIONS'] = 'N';
246 $this->checkPermissions = false;
247 }
248 if (
249 isset($params['disable_link_preview'])
250 && $params['disable_link_preview'] === true
251 )
252 {
253 $this->disableLinkPreview = true;
254 }
255
256 if ($id)
257 {
258 $landing = self::getList(array(
259 'select' => array(
260 '*',
261 'SITE_TPL_ID' => 'SITE.TPL_ID',
262 'SITE_TPL_CODE' => 'SITE.TPL_CODE',
263 'SITE_CODE' => 'SITE.CODE',
264 'SITE_TYPE' => 'SITE.TYPE',
265 'SITE_SPECIAL' => 'SITE.SPECIAL',
266 'SITE_TITLE' => 'SITE.TITLE',
267 'SITE_VERSION' => 'SITE.VERSION',
268 'SITE_LANG' => 'SITE.LANG',
269 'DOMAIN_ID' => 'SITE.DOMAIN_ID',
270 'SITE_LANDING_ID_INDEX' => 'SITE.LANDING_ID_INDEX',
271 ),
272 'filter' => $filter,
273 ))->fetch();
274 }
275 // check landing folder if exists
276 if (!empty($landing['FOLDER_ID']))
277 {
278 $breadCrumbs = Folder::getBreadCrumbs($landing['FOLDER_ID'], $landing['SITE_ID']);
279 foreach ($breadCrumbs as $crumb)
280 {
281 if ($crumb['DELETED'] === 'Y')
282 {
283 $id = 0;
284 break;
285 }
286 }
287 }
288 if ($id && isset($landing) && is_array($landing))
289 {
290 /*
291 * $this->getEditMode()
292 * @todo return if no access
293 */
294 // get base data
295 self::$siteType = (string)$landing['SITE_TYPE'];
296 $this->title = $landing['TITLE'];
297 $this->code = $landing['CODE'];
298 $this->xmlId = $landing['XML_ID'];
299 $this->id = (int)$landing['ID'];
300 $this->version = (int)$landing['VERSION'];
301 $this->siteId = (int)$landing['SITE_ID'];
302 $this->siteTitle = $landing['SITE_TITLE'];
303 $this->domainId = (int)$landing['DOMAIN_ID'];
304 $this->folderId = (int)$landing['FOLDER_ID'];
305 $this->active = $landing['ACTIVE'] == 'Y';
306 if ($this->checkPermissions)
307 {
308 $this->rights = Rights::getOperationsForSite(
309 $this->siteId
310 );
311 }
312 $this->siteRow = [
313 'TPL_ID' => $landing['SITE_TPL_ID'],
314 'LANDING_ID_INDEX' => $landing['SITE_LANDING_ID_INDEX'],
315 ];
316 $siteTplId = max($landing['SITE_TPL_ID'], 0);
317 $this->tplId = (int)$landing['TPL_ID'] > 0
318 ? (int)$landing['TPL_ID']
319 : $siteTplId
320 ;
321 if (isset($params['is_area']) && $params['is_area'])
322 {
323 $this->mainInstance = false;
324 }
325 if ($landing['SITE_TPL_ID'] > 0 && !$landing['TPL_ID'])
326 {
327 $this->tplType = 'site';
328 }
329 // if edit mode - create copy for edit
330 if ($this->getEditMode()/* && $landing['PUBLIC'] == 'Y'*/)
331 {
332 Block::cloneForEdit($this);
333 }
334 // some update if we need
335 $this->updateVersion();
336 // get available blocks
337 if (
338 !isset($params['skip_blocks'])
339 || $params['skip_blocks'] !== true
340 )
341 {
342 Block::fillLanding(
343 $this,
344 $params['blocks_limit'] ?? null,
345 [
346 'id' => $params['blocks_id'] ?? 0,
347 'deleted' => isset($params['deleted']) && $params['deleted'] === true,
348 ]
349 );
350 }
351 // fill meta data
352 foreach (self::META_KEYS as $key)
353 {
354 $this->metaData[$key] = $landing[$key];
355 }
356 }
357 // landing not found
358 else
359 {
360 $this->error->addError(
361 'LANDING_NOT_EXIST',
362 Loc::getMessage('LANDING_NOT_FOUND')
363 );
364 $this->title = Loc::getMessage('LANDING_TITLE_NOT_FOUND');
365 }
366 }
367
373 public function setMetaData(array $metaData): void
374 {
375 foreach ($metaData as $key => $value)
376 {
377 if (in_array($key, self::META_KEYS_MODIFIABLE, true))
378 {
379 $this->metaData[$key] = $value;
380 }
381 }
382 }
383
390 public static function ping($id, $deleted = false)
391 {
392 $returnCheckDelete = false;
393 $filter = [
394 'ID' => $id,
395 ];
396
397 if ($deleted)
398 {
399 if (self::$checkDelete)
400 {
401 $returnCheckDelete = true;
402 self::$checkDelete = false;
403 }
404 $filter['=DELETED'] = ['Y', 'N'];
405 }
406
407 $check = self::getList([
408 'select' => [
409 'ID',
410 ],
411 'filter' => $filter,
412 ]);
413
414 if ($returnCheckDelete)
415 {
416 self::$checkDelete = true;
417 }
418
419 return (boolean)$check->fetch();
420 }
421
427 public static function setEditMode($mode = true)
428 {
429 self::$editMode = (boolean)$mode;
430 }
431
436 public static function getEditMode()
437 {
438 return self::$editMode;
439 }
440
446 public static function setPreviewMode($mode = true)
447 {
448 self::$previewMode = (boolean)$mode;
449 }
450
455 public static function getPreviewMode()
456 {
457 return self::$previewMode;
458 }
459
464 public static function checkDeleted()
465 {
466 return self::$checkDelete;
467 }
468
473 public static function disableCheckDeleted()
474 {
475 self::$checkDelete = false;
476 }
477
482 public static function enableCheckDeleted()
483 {
484 self::$checkDelete = true;
485 }
486
491 public static function disableUpdate()
492 {
493 self::$enabledUpdate = false;
494 }
495
500 public static function enableUpdate()
501 {
502 self::$enabledUpdate = true;
503 }
504
509 public static function isCheckUniqueAddress(): bool
510 {
511 return self::$checkUniqueAddress;
512 }
513
518 public static function disableCheckUniqueAddress(): void
519 {
520 self::$checkUniqueAddress = false;
521 }
522
527 public static function enableCheckUniqueAddress(): void
528 {
529 self::$checkUniqueAddress = true;
530 }
531
538 public static function createInstance($id, array $params = array())
539 {
540 return new self($id, $params);
541 }
542
548 public static function markDelete(int $id): \Bitrix\Main\Result
549 {
551 {
552 $result = new \Bitrix\Main\Result;
553 $result->addError(
554 new \Bitrix\Main\Error(
555 Loc::getMessage('LANDING_BLOCK_UNABLE_DEL_INC'),
556 'UNABLE_DELETE_INCLUDE'
557 )
558 );
559
560 return $result;
561 }
562
563 $event = new Event('landing', 'onBeforeLandingRecycle', array(
564 'id' => $id,
565 'delete' => 'Y',
566 ));
567 $event->send();
568
569 foreach ($event->getResults() as $result)
570 {
571 if ($result->getType() == EventResult::ERROR)
572 {
573 $return = new \Bitrix\Main\Result;
574 foreach ($result->getErrors() as $error)
575 {
576 $return->addError(
577 $error
578 );
579 }
580
581 return $return;
582 }
583 }
584
585 if (($currentScope = Site\Type::getCurrentScopeId()))
586 {
587 Agent::addUniqueAgent('clearRecycleScope', [$currentScope]);
588 }
589
590 $landing = self::createInstance($id, ['skip_blocks' => true]);
591
592 $res = parent::update($id, array(
593 'DELETED' => 'Y',
594 ));
595
596 if ($res->isSuccess())
597 {
598 $landing->clearFolderIndex();
599 }
600
601 return $res;
602 }
603
609 public static function markUnDelete($id)
610 {
611 $id = intval($id);
612
613 $event = new Event('landing', 'onBeforeLandingRecycle', array(
614 'id' => $id,
615 'delete' => 'N',
616 ));
617 $event->send();
618
619 foreach ($event->getResults() as $result)
620 {
621 if ($result->getType() == EventResult::ERROR)
622 {
623 $return = new \Bitrix\Main\Result;
624 foreach ($result->getErrors() as $error)
625 {
626 $return->addError(
627 $error
628 );
629 }
630
631 return $return;
632 }
633 }
634
635 return parent::update($id, array(
636 'DELETED' => 'N',
637 ));
638 }
639
646 public static function delete($id, bool $forceDeleted = false): \Bitrix\Main\Result
647 {
648 $result = new \Bitrix\Main\Entity\DeleteResult();
649 $params = [];
650
651 if ($forceDeleted)
652 {
653 $params['force_deleted'] = true;
654 }
655
656 // first check
657 if (Rights::isOn())
658 {
659 foreach (['draft', 'public'] as $code)
660 {
661 self::setEditMode($code == 'draft');
662 $landing = self::createInstance($id, $params);
663 if ($landing->exist())
664 {
665 foreach ($landing->getBlocks() as $block)
666 {
667 if ($block->getAccess() < $block::ACCESS_X)
668 {
669 $result->addError(
670 new \Bitrix\Main\Error(
671 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED'),
672 'ACCESS_DENIED'
673 )
674 );
675
676 return $result;
677 }
678 }
679 }
680 else
681 {
682 if (!$landing->getError()->isEmpty())
683 {
684 $result->addError(
685 $landing->getError()->getErrors()[0]
686 );
687 }
688
689 return $result;
690 }
691 }
692 }
693
694 // delete blocks
695 $params['skip_blocks'] = true;
696 foreach (array('draft', 'public') as $code)
697 {
698 self::setEditMode($code == 'draft');
699 $landing = self::createInstance($id, $params);
700 if ($landing->exist())
701 {
702 Block::deleteAll($id);
705 }
706 }
707
708 $res = parent::delete($id);
709 if ($res->isSuccess())
710 {
711 $landing->clearFolderIndex();
712 }
713
714 return $res;
715 }
716
722 public static function getHooks($id)
723 {
725 {
726 return [];
727 }
728
729 return Hook::getForLanding($id);
730 }
731
737 public static function getAdditionalFields($id)
738 {
739 $fields = array();
740
741 // now we can get additional fields only from hooks
742 foreach (self::getHooks($id) as $hook)
743 {
744 $fields += $hook->getPageFields();
745 }
746
747 return $fields;
748 }
749
756 public static function getAdditionalFieldsAsArray(int $landingId, bool $skipEmpty = true): array
757 {
758 $hookFiles = Hook::HOOKS_CODES_FILES;
759 $fields = self::getAdditionalFields($landingId);
760
761 foreach ($fields as $key => $field)
762 {
763 $fields[$key] = $field->getValue();
764
765 if ($skipEmpty && !$fields[$key])
766 {
767 unset($fields[$key]);
768 continue;
769 }
770
771 if (in_array($key, $hookFiles))
772 {
773 $fields['~' . $key] = $fields[$key];
774 if (intval($fields[$key]) > 0)
775 {
777 }
778 }
779 }
780
781 return $fields;
782 }
783
790 public static function saveAdditionalFields($id, array $data)
791 {
792 // now we can get additional fields only from hooks
793 Hook::saveForLanding($id, $data);
794 }
795
801 public static function setVariables(array $vars)
802 {
803 foreach ($vars as $code => $val)
804 {
805 self::$variables[$code] = $val;
806 }
807 }
808
813 public static function getVariables()
814 {
815 return self::$variables;
816 }
817
824 public static function setDynamicParams($filterId, $elementId)
825 {
826 self::$dynamicFilterId = $filterId;
827 self::$dynamicElementId = $elementId;
828 }
829
834 public static function getDynamicFilter()
835 {
836 static $filter = null;
837 if ($filter === null)
838 {
840 self::$dynamicFilterId
841 );
842 }
843
844 return $filter;
845 }
846
851 public static function getDynamicElementId()
852 {
853 return self::$dynamicElementId;
854 }
855
860 public static function isDynamicDetailPage()
861 {
862 return self::$dynamicFilterId && self::$dynamicElementId;
863 }
864
872 public function getPreview(?int $id = null, bool $skipCloud = false, ?string $publicUrl = null): ?string
873 {
874 if (
875 !$skipCloud &&
876 Manager::isB24() &&
878 )
879 {
880 if (!$publicUrl)
881 {
882 $publicUrl = $this->getPublicUrl();
883 }
884
885 return rtrim($publicUrl, '/') . '/preview.jpg';
886 }
887
888 static $hookPics = null;
889
890 if ($hookPics === null)
891 {
892 $hookPics = Hook\Page\MetaOg::getAllImages();
893 }
894
895 if (!$id)
896 {
897 $id = $this->id;
898 }
899
900 if (isset($hookPics[$id]))
901 {
902 $pic = $hookPics[$id];
903 if (intval($pic) > 0)
904 {
905 $pic = File::getFilePath($pic);
906 }
907
908 return $pic;
909 }
910
911 return Manager::getUrlFromFile('/bitrix/images/landing/nopreview.jpg');
912 }
913
922 public function getPublicUrl($id = false, $absolute = true, $createPubPath = false, &$fullUrl = [])
923 {
924 if ($id === false)
925 {
926 $id = $this->id;
927 }
928
929 $previewMode = self::$previewMode && !$this->disableLinkPreview;
930 $siteKeyCode = Site\Type::getKeyCode();
931 $hostUrl = Domain::getHostUrl();
933 $bitrix24 = Manager::isB24();
934 $bitrix24originalVar = $bitrix24;
935 $disableCloud = Manager::isCloudDisable();
936 $domainDefault = null;
937 $data = array();
939 'select' => array(
940 'ID',
941 'CODE',
942 'RULE',
943 'SITE_ID',
944 'FOLDER_ID',
945 'SITE_ID_INDEX' => 'SITE.LANDING_ID_INDEX',
946 'SITE_PROTOCOL' => 'SITE.DOMAIN.PROTOCOL',
947 'SITE_DOMAIN' => 'SITE.DOMAIN.DOMAIN',
948 'SITE_CODE' => 'SITE.CODE',
949 'SITE_TYPE' => 'SITE.TYPE',
950 'SITE_SMN_ID' => 'SITE.SMN_SITE_ID',
951 ),
952 'filter' => array(
953 'ID' => $id,
954 '=DELETED' => ['Y', 'N'],
955 'CHECK_PERMISSIONS' => 'N',
956 ),
957 ));
958 while ($row = $res->fetch())
959 {
960 if ($row['SITE_TYPE'] == 'SMN')
961 {
962 $bitrix24 = false;
963 }
964 else
965 {
966 $bitrix24 = $bitrix24originalVar;
967 }
968 $domainReplace = false;
969 $row['SITE_ID_ORIG'] = $row['SITE_ID'];
970 // build site domain by default
971 if (!$row['SITE_DOMAIN'])
972 {
973 if (!$domainDefault)
974 {
975 $domainDefault = Domain::getList(array(
976 'filter' => array(
977 'ID' => Domain::getCurrentId(),
978 ),
979 ))->fetch();
980 }
981 if (isset($domainDefault['PROTOCOL']))
982 {
983 $row['SITE_PROTOCOL'] = $domainDefault['PROTOCOL'];
984 }
985 if (isset($domainDefault['DOMAIN']))
986 {
987 $row['SITE_DOMAIN'] = $domainDefault['DOMAIN'];
988 }
989 $domainReplace = true;
990 }
991 // force https
992 if (Manager::isHttps())
993 {
994 $row['SITE_PROTOCOL'] = 'https';
995 }
996 if ($domainReplace || !$bitrix24 || $disableCloud)
997 {
999 null,
1000 $row['SITE_SMN_ID'] ? $row['SITE_SMN_ID'] : $siteId,
1001 $createPubPath
1002 );
1003 $pubPath = rtrim($pubPath, '/');
1004 }
1005 // for create publication path
1006 if (!ModuleManager::isModuleInstalled('bitrix24'))
1007 {
1009 null,
1010 $row['SITE_SMN_ID'] ? $row['SITE_SMN_ID'] : $siteId,
1011 $createPubPath
1012 );
1013 }
1014 if (isset($row['SITE_ID']))
1015 {
1016 if ($siteKeyCode == 'CODE')
1017 {
1018 $row['SITE_ID'] = $row['SITE_CODE'];
1019 }
1020 else
1021 {
1022 $row['SITE_ID'] = '/' . $row['SITE_ID'] . '/';
1023 }
1024 }
1025 $publicHash = '';
1026 if ($previewMode)
1027 {
1028 if ($siteKeyCode == 'CODE')
1029 {
1030 $publicHash = Site::getPublicHash(trim($row['SITE_CODE'], '/'), $row['SITE_DOMAIN']);
1031 }
1032 else
1033 {
1034 $publicHash = Site::getPublicHash($row['SITE_ID_ORIG'], $row['SITE_DOMAIN']);
1035 }
1036 }
1037 if ($row['CODE'])
1038 {
1039 $row['CODE'] .= '/';
1040 }
1041 if ($disableCloud)
1042 {
1043 $lastFolderItem = [];
1044 $fullUrl[$row['ID']] = ($absolute ? $hostUrl : '') .
1045 $pubPath .
1046 ($bitrix24 ? $row['SITE_ID'] : '/') .
1047 ($previewMode ? 'preview/' . $publicHash . '/' : '') .
1048 ($row['FOLDER_ID'] ? ltrim(Folder::getFullPath($row['FOLDER_ID'], $row['SITE_ID_ORIG'], $lastFolderItem), '/') : '');
1049 $folderIndex = $row['ID'] == ($lastFolderItem['INDEX_ID'] ?? 0)
1050 ||
1051 !($lastFolderItem['INDEX_ID'] ?? 0)
1052 && ($row['CODE'] ?? null)
1053 && ($lastFolderItem['CODE'] ?? null)
1054 && trim($row['CODE'], '/') === $lastFolderItem['CODE']
1055 ;
1056 $data[$row['ID']] = $fullUrl[$row['ID']] .
1057 (($row['ID'] == $row['SITE_ID_INDEX'] || $folderIndex || $row['RULE']) ? '' : $row['CODE']);
1058 if (!$row['RULE'])
1059 {
1060 $fullUrl[$row['ID']] .= $row['CODE'];
1061 }
1062 }
1063 else
1064 {
1065 $lastFolderItem = [];
1066 $fullUrl[$row['ID']] = (
1067 $absolute
1068 ? (
1069 $row['SITE_PROTOCOL'] . '://' .
1070 $row['SITE_DOMAIN']
1071 )
1072 : ''
1073 ) .
1074 (($domainReplace || !$bitrix24) ? $pubPath : '') .
1075 (($previewMode && !$bitrix24) ? '/preview/' . $publicHash : '') .
1076 (($domainReplace && $bitrix24) ? $row['SITE_ID'] : '/') .
1077 (($previewMode && $bitrix24) ? 'preview/' . $publicHash . '/' : '') .
1078 ($row['FOLDER_ID'] ? ltrim(Folder::getFullPath($row['FOLDER_ID'], $row['SITE_ID_ORIG'], $lastFolderItem), '/') : '');
1079 $folderIndex = $row['ID'] == $lastFolderItem['INDEX_ID'] || !$lastFolderItem['INDEX_ID'] && trim($row['CODE'], '/') === $lastFolderItem['CODE'];
1080 $data[$row['ID']] = $fullUrl[$row['ID']] .
1081 (($row['ID'] == $row['SITE_ID_INDEX'] || $folderIndex || $row['RULE']) ? '' : $row['CODE']);
1082 if (!$row['RULE'])
1083 {
1084 $fullUrl[$row['ID']] .= $row['CODE'];
1085 }
1086 }
1087 }
1088
1089 if (is_array($id))
1090 {
1091 return $data;
1092 }
1093 elseif (!empty($data))
1094 {
1095 return array_pop($data);
1096 }
1097
1098 return false;
1099 }
1100
1107 public static function resolveIdByPublicUrl(string $landingUrl, int $siteId): ?int
1108 {
1109 if (Manager::isB24() && !Manager::isCloudDisable() && Site\Type::isPublicScope())
1110 {
1111 $landingUrl = rtrim(Manager::getPublicationPath($siteId), '/') . '/' . ltrim($landingUrl, '/');
1112 }
1113 $publicationPath = Manager::getPublicationPath();
1114 $componentName = 'bitrix:landing.pub';
1115 $className = \CBitrixComponent::includeComponentClass($componentName);
1116 $demoCmp = new $className;
1117 $demoCmp->initComponent($componentName);
1118 $demoCmp->arParams = [
1119 'PATH' => mb_substr($landingUrl, mb_strlen($publicationPath)),
1120 'DRAFT_MODE' => 'Y',
1121 'SITE_ID' => $siteId,
1122 'SITE_TYPE' => self::getSiteType(),
1123 'CHECK_PERMISSIONS' => 'N',
1124 'NOT_CHECK_DOMAIN' => 'Y',
1125 'NOT_SEND_HTTP_STATUS' => 'Y',
1126 'SKIP_404' => 'Y',
1127 ];
1128
1129 return $demoCmp->detectPage() ?: null;
1130 }
1131
1137 public function view(array $params = array())
1138 {
1139 $blockEditMode = $this->getEditMode();
1140 $editMode = $this->mainInstance && $blockEditMode;
1141
1142 if (!isset($params['parse_link']))
1143 {
1144 $params['parse_link'] = true;
1145 }
1146
1147 if (!isset($params['apply_template']))
1148 {
1149 $params['apply_template'] = true;
1150 }
1151
1152 if (!isset($params['check_permissions']))
1153 {
1154 $params['check_permissions'] = true;
1155 }
1156
1157 if (!$params['check_permissions'])
1158 {
1160 }
1161
1162 // title
1163 if ($this->mainInstance)
1164 {
1166 \htmlspecialcharsbx($this->title)
1167 );
1168 }
1169
1170 // add chain item if need
1171 if ($this->mainInstance)
1172 {
1173 if ($this->folderId)
1174 {
1175 $chains = Folder::getBreadCrumbs($this->folderId, $this->siteId);
1176 foreach ($chains as $chain)
1177 {
1178 Manager::getApplication()->addChainItem(
1179 $chain['TITLE'],
1180 $chain['INDEX_ID'] ? '#landing' . $chain['INDEX_ID'] : '#'
1181 );
1182 }
1183 }
1184 elseif (($this->siteRow['LANDING_ID_INDEX'] ?? 0) != $this->id)
1185 {
1186 Manager::getApplication()->addChainItem(
1187 $this->title,
1188 '#landing' . $this->id
1189 );
1190 }
1191 }
1192
1193 // assets
1194 if ($editMode)
1195 {
1196 $options = [
1197 'site_id' => $this->siteId,
1198 'server_name' => $_SERVER['SERVER_NAME'],
1199 'xml_id' => $this->xmlId,
1200 'blocks' => (new BlockRepo())->getRepository(),
1201 'style' => Block::getStyle(),
1202 'attrs' => Block::getAttrs(),
1203 'mainOptions' => [
1204 'saveOriginalFileName' => Option::get('main', 'save_original_file_name') === 'Y',
1205 ],
1206 ];
1207 // event for redefine $options
1208 $event = new Event('landing', 'onLandingView', [
1209 'options' => $options,
1210 ]);
1211 $event->send();
1212 foreach ($event->getResults() as $result)
1213 {
1214 if ($result->getResultType() != EventResult::ERROR)
1215 {
1216 if (($modified = $result->getModified()))
1217 {
1218 if (isset($modified['options']) && is_array($modified['options']))
1219 {
1220 $options = array_merge($options, $modified['options']);
1221 }
1222 }
1223 }
1224 }
1225 // output js
1226 Asset::getInstance()->addString(
1227 '<script>' .
1228 'BX.ready(function(){'
1229 . 'if (typeof BX.Landing.Main !== "undefined")'
1230 . '{'
1231 . 'BX.Landing.Env.createInstance(' . \CUtil::phpToJSObject($options, false, false, true) . ');'
1232 . 'BX.Landing.Main.createInstance(' . $this->id . ');'
1233 . '}'
1234 . '});' .
1235 '</script>'
1236 );
1237 }
1238
1239 $content = '';
1240
1241 // templates part - first
1242 if ($params['apply_template'] && $this->mainInstance)
1243 {
1244 if (!TemplateRef::landingIsArea($this->id))
1245 {
1246 $content = $this->applyTemplate();
1247 }
1248 else
1249 {
1250 $content = '<div class="landing-main"' .
1251 ' data-site="' . $this->siteId . '"' .
1252 ' data-landing="' . $this->id . '">' .
1253 '#CONTENT#' .
1254 '</div>';
1255 }
1256 }
1257
1258 // then content
1259 ob_start();
1261 foreach ($this->blocks as $block)
1262 {
1263 $block->view($blockEditMode, $this);
1264 }
1266 if ($this->mainInstance)
1267 {
1268 $this->execHooks();
1269 }
1270 $contentMain = ob_get_contents();
1271 ob_end_clean();
1272
1273 $replace = [];
1274
1275 if (!$content)
1276 {
1277 $content = $contentMain;
1278 }
1279
1280 if (mb_strpos($content, '#CONTENT#') !== false)
1281 {
1282 $replace['#CONTENT#'] = '<a id="workarea"></a>' . $contentMain;
1283 }
1284
1285 if (mb_strpos($content . $contentMain, '#crm') !== false)
1286 {
1287 $replace = array_merge($replace, Connector\Crm::getReplacesForContent($this->siteId, !$blockEditMode));
1288 }
1289
1290 if ($replace)
1291 {
1292 $content = str_replace(
1293 array_keys($replace),
1294 array_values($replace),
1295 $content
1296 );
1297 }
1298
1299 // breadcrumb (see chain_template.php in tpl) and title
1300 if (!$blockEditMode && $this->mainInstance)
1301 {
1302 ob_start();
1303 echo Manager::getApplication()->getNavChain(
1304 false, 0, false, true
1305 );
1306 $breadcrumb = ob_get_contents();
1307 ob_end_clean();
1308 $content = str_replace(
1309 array(
1310 '#breadcrumb#',
1311 '#title#',
1312 ),
1313 array(
1314 $breadcrumb,
1316 ),
1317 $content
1318 );
1319 }
1320
1321 // parse links between landings
1322 if ($params['parse_link'] === true && !$blockEditMode)
1323 {
1324 echo $this->parseLocalUrl($content);
1325 }
1326 else
1327 {
1328 echo $content;
1329 }
1330
1331 if (!$params['check_permissions'])
1332 {
1333 Rights::setOn();
1334 }
1335 }
1336
1341 public function getAreas()
1342 {
1343 if ($this->tplType == 'site')
1344 {
1345 return TemplateRef::getForSite($this->siteId);
1346 }
1347 else
1348 {
1349 return TemplateRef::getForLanding($this->id);
1350 }
1351 }
1352
1358 protected function applyTemplate($content = null)
1359 {
1360 if ($this->tplId)
1361 {
1362 $template = Template::getList(array(
1363 'filter' => array(
1364 'ID' => $this->tplId,
1365 ),
1366 ))->fetch();
1367 if ($template)
1368 {
1369 $editMode = $this->getEditMode();
1370 if ($template['XML_ID'] == 'empty')
1371 {
1372 $template['CONTENT'] = '<div class="landing-main">' .
1373 $template['CONTENT'] .
1374 '</div>';
1375 }
1376 if ($editMode)
1377 {
1378 $replace = array(
1379 '>#CONTENT#<' => ' data-site="' . $this->siteId .
1380 '" data-landing="' . $this->id .
1381 '">#CONTENT#<',
1382 '#CONTENT#' => $content ? $content : '#CONTENT#',
1383 );
1384 }
1385 else
1386 {
1387 $replace = array(
1388 '#CONTENT#' => $content ? $content : '#CONTENT#',
1389 );
1390 }
1391 // if areas exist, get landings
1392 if ($template['AREA_COUNT'] > 0)
1393 {
1394 foreach ($this->getAreas() as $area => $lid)
1395 {
1396 ob_start();
1397 $landing = self::createInstance($lid, array(
1398 'is_area' => true,
1399 'check_permissions' => false,
1400 'disable_link_preview' => $this->disableLinkPreview,
1401 ));
1402 if ($landing->exist())
1403 {
1404 $landing->view();
1405 }
1406 if ($editMode)
1407 {
1408 $rights = Rights::getOperationsForSite($landing->getSiteId());
1409 $replace['>#AREA_' . $area . '#<'] = ' data-site="' . $landing->getSiteId() .
1410 '" data-landing="' . $lid .
1411 '" data-rights="' . implode(',', $rights) .
1412 '">#AREA_' . $area . '#<';
1413 }
1414 $replace['#AREA_' . $area . '#'] = ob_get_contents();
1415 ob_end_clean();
1416 }
1417 }
1418 $content = str_replace(
1419 array_keys($replace),
1420 array_values($replace),
1421 $template['CONTENT']
1422 );
1423 }
1424 }
1425 else if ($this->getEditMode())
1426 {
1427 if (!$content)
1428 {
1429 $content = '#CONTENT#';
1430 }
1431 $content = '<div class="landing-main" ' .
1432 'data-site="' . $this->siteId . '" ' .
1433 'data-landing="' . $this->id . '">' .
1434 $content .
1435 '</div>';
1436 }
1437
1438 return $content;
1439 }
1440
1445 public function getTplId(): int
1446 {
1447 return $this->tplId;
1448 }
1449
1455 protected function parseLocalUrl(string $content): string
1456 {
1457 $pattern = '/([",\'\;]{1})(page:|block:|user:|help:)?#(landing|block|dynamic|user|helpdesk=|slider=)([\w\_]+)\@{0,1}([^\'"]*)([",\'\&]{1})/is';
1458 $patternWithoutUser = '/([",\'\;]{1})(page:|block:)?#(landing|block|dynamic)([\d\_]+)\@{0,1}([^\'"]*)([",\'\&]{1})/is';
1459 static $isIframe = null;
1460
1461 if (!self::$editMode && $content)
1462 {
1464 $content
1465 );
1466 }
1467
1468 if ($isIframe === null)
1469 {
1470 $request = Application::getInstance()->getContext()->getRequest();
1471 $isIframe = $request->get('IFRAME') == 'Y';
1472 }
1473
1474 // replace catalog links in preview mode
1475 if (self::$previewMode)
1476 {
1477 $content = preg_replace_callback(
1478 '/href\="(product:)?#catalog(Element|Section)([\d]+)"/i',
1479 function($href)
1480 {
1481 return 'href="' . PublicAction\Utils::getIblockURL(
1482 $href[3],
1483 mb_strtolower($href[2])
1484 ) . '"';
1485 },
1486 $content);
1487 }
1488
1489 $replace = [];
1490
1491 // for form in frames we should insert hidden tag
1492 if ($isIframe)
1493 {
1494 $replace['</form>'] = '<input type="hidden" name="IFRAME" value="Y" /></form>';
1495 }
1496
1497 // fix breadcrumb navigation
1498 if ($this->siteRow['LANDING_ID_INDEX'] > 0)
1499 {
1500 $replace['#system_mainpage'] = '#landing' . $this->siteRow['LANDING_ID_INDEX'];
1501 }
1502
1503 if ($replace)
1504 {
1505 $content = str_replace(
1506 array_keys($replace),
1507 array_values($replace),
1508 $content
1509 );
1510 }
1511
1512 // replace in content
1513 if (preg_match_all($pattern, $content, $matches))
1514 {
1515 $urls = array(
1516 'LANDING' => array(),
1517 'BLOCK' => array(),
1518 'USER' => array(),
1519 'DYNAMIC' => array(),
1520 'HELP' => array(),
1521 );
1522 for ($i = 0, $c = count($matches[0]); $i < $c; $i++)
1523 {
1524 if (mb_strtoupper($matches[3][$i]) == 'LANDING')
1525 {
1526 $urls['LANDING'][] = $matches[4][$i];
1527 }
1528 else if (mb_strtoupper($matches[3][$i]) == 'DYNAMIC')
1529 {
1530 [$dynamicId, ] = explode('_', $matches[4][$i]);
1531 $urls['DYNAMIC'][] = $dynamicId;
1532 }
1533 else if (mb_strtoupper($matches[3][$i]) == 'USER')
1534 {
1535 $urls['USER'][] = $matches[4][$i];
1536 }
1537 else if ($matches[2][$i] === 'help:')
1538 {
1539 $urls['HELP'][] = $matches[4][$i];
1540 }
1541 else
1542 {
1543 $urls['BLOCK'][] = $matches[4][$i];
1544 }
1545 }
1546 // get parent landings for blocks
1547 // and public version of blocks too
1548 $anchorsId = array();
1549 $anchorsPublicId = array();
1550 if (!empty($urls['BLOCK']))
1551 {
1552 $urls['BLOCK'] = Block::getRowByBlockId(
1553 $urls['BLOCK'],
1554 array(
1555 'ID', 'LID', 'PARENT_ID', 'ANCHOR',
1556 )
1557 );
1558 foreach ($urls['BLOCK'] as $bid => &$bidRow)
1559 {
1560 if (
1561 !self::$previewMode
1562 && $bidRow['PARENT_ID']
1563 )
1564 {
1565 $anchorsPublicId[$bid] = $bidRow['PARENT_ID'];
1566 }
1567 else
1568 {
1569 $anchorsId[$bid] = $bidRow['ANCHOR']
1570 ? \htmlspecialcharsbx($bidRow['ANCHOR'])
1571 : Block::getAnchor($bidRow['ID'])
1572 ;
1573 }
1574 $bidRow = $bidRow['LID'];
1575 }
1576 unset($bidRow);
1577 $urls['LANDING'] = array_unique(array_merge(
1578 $urls['LANDING'],
1579 $urls['BLOCK']
1580 ));
1581 }
1582 $urls['LANDING'] = array_unique(array_merge(
1583 $urls['LANDING'],
1584 $urls['DYNAMIC']
1585 ));
1586 // get anchors for public version
1587 if ($anchorsPublicId)
1588 {
1589 $anchorsPublicIdTmp = Block::getRowByBlockId(
1590 $anchorsPublicId,
1591 array(
1592 'ID', 'LID', 'PARENT_ID', 'ANCHOR',
1593 )
1594 );
1595 foreach ($anchorsPublicId as $bid => $bidParent)
1596 {
1597 if (!isset($anchorsPublicIdTmp[$bidParent]))
1598 {
1599 continue;
1600 }
1601 $bidParent = $anchorsPublicIdTmp[$bidParent];
1602 $anchorsPublicId[$bid] = $bidParent['ANCHOR']
1603 ? \htmlspecialcharsbx($bidParent['ANCHOR'])
1604 : Block::getAnchor($bidParent['ID'])
1605 ;
1606 }
1607 }
1608 $anchorsPublicId += $anchorsId;
1609 $landingFull = [];
1610 $lidEncoded = [];
1611 // get landing and blocks urls
1612 if (!empty($urls['LANDING']))
1613 {
1614 $urls['LANDING'] = $this->getPublicUrl(
1615 $urls['LANDING'],
1617 false,
1618 $landingFull
1619 );
1620 foreach ($urls['LANDING'] as $lid => &$url)
1621 {
1622 $lidEncoded[] = $lid;
1624 if ($isIframe)
1625 {
1626 $url .= '?IFRAME=Y';
1627 }
1628 }
1629 unset($url);
1630 }
1631 if (!empty($urls['BLOCK']))
1632 {
1633 foreach ($urls['BLOCK'] as $bid => $lid)
1634 {
1635 if (isset($urls['LANDING'][$lid]))
1636 {
1637 if (!in_array($lid, $lidEncoded))
1638 {
1639 $urls['LANDING'][$lid] = \htmlspecialcharsbx($urls['LANDING'][$lid]);
1640 }
1641 $urls['LANDING'][$lid] .= ($isIframe ? '?IFRAME=Y' : '');
1642 $urls['BLOCK'][$bid] = $urls['LANDING'][$lid] . '#' . $anchorsPublicId[$bid];
1643 }
1644 else
1645 {
1646 unset($urls['BLOCK'][$bid]);
1647 }
1648 }
1649 }
1650 // replace urls
1651 if (!empty($urls['LANDING']))
1652 {
1653 krsort($urls['LANDING']);
1654 $content = preg_replace_callback(
1655 $patternWithoutUser,
1656 function ($matches) use ($urls, $landingFull, $isIframe)
1657 {
1658 $dynamicPart = '';
1659 $matches[3] = mb_strtoupper($matches[3]);
1660 if ($matches[3] == 'DYNAMIC')
1661 {
1662 $matches[3] = 'LANDING';
1663 if (($underPos = mb_strpos($matches[4], '_')) !== false)
1664 {
1665 $dynamicPart = mb_substr($matches[4], $underPos);
1666 $matches[4] = mb_substr($matches[4], 0, $underPos);
1667 }
1668 [$dynamicId, ] = explode('_', $matches[4]);
1669 $matches[4] = $dynamicId;
1670 }
1671 if (isset($urls[$matches[3]][$matches[4]]))
1672 {
1673 if ($dynamicPart)
1674 {
1675 $landingUrl = $urls[$matches[3]][$matches[4]];
1676 if (isset($landingFull[$matches[4]]))
1677 {
1678 $landingUrl = $landingFull[$matches[4]];
1679 }
1680 $url = mb_substr($landingUrl, 0, mb_strlen($landingUrl) - 1);
1681 $url .= $dynamicPart . ($isIframe ? '/?IFRAME=Y' : '/');
1682 }
1683 else
1684 {
1685 $url = $urls[$matches[3]][$matches[4]];
1686 }
1687 return $matches[1] .
1688 $url . $matches[5] .
1689 $matches[6];
1690 }
1691 else
1692 {
1693 return $matches[1] .
1694 '#landing' . $matches[4] . $matches[5] . $dynamicPart .
1695 $matches[6];
1696 }
1697 },
1698 $content
1699 );
1700 $landingUrls = array();
1701 foreach ($urls['LANDING'] as $lid => $url)
1702 {
1703 $landingUrls['@#landing' . $lid.'@'] = $url;
1704 }
1705 }
1706 // replace user urls
1707 if (!empty($urls['USER']))
1708 {
1709 $patternForPseudoUrlUser = '/data-pseudo-url="{\S*(user:)?#user([\d\_]+)\S*}"/is';
1710 $content = preg_replace_callback(
1711 $patternForPseudoUrlUser,
1712 function($matches)
1713 {
1714 $url = "'" . Domain::getHostUrl() . '/company/personal/user/' . $matches[2] . "/'";
1715 return 'onClick="BX.SidePanel.Instance.open('. $url . ')" ';
1716 },
1717 $content
1718 );
1719 $patternForUser = '/(user:)?#(user)([\d\_]+)/is';
1720 $content = preg_replace_callback(
1721 $patternForUser,
1722 function ($matches)
1723 {
1724 return Domain::getHostUrl() . '/company/personal/user/' . $matches[3] . '/';
1725 },
1726 $content
1727 );
1728 }
1729
1730 // replace help urls
1731 if (!empty($urls['HELP']))
1732 {
1733 $patternForHelpdesk = '/help:#helpdesk=(\d+)/i';
1734 $content = preg_replace_callback(
1735 $patternForHelpdesk,
1736 function ($matches)
1737 {
1738 return "javascript:BX.Helper.show('redirect=detail&code=" . $matches[1] . "')";
1739 },
1740 $content
1741 );
1742
1743 $patternForSlider = '/help:#slider=(\w+)/i';
1744 $content = preg_replace_callback(
1745 $patternForSlider,
1746 function ($matches)
1747 {
1748 return "javascript:BX.UI.InfoHelper.show('" . $matches[1] . "')";
1749 },
1750 $content
1751 );
1752 }
1753 }
1754
1755 return $content;
1756 }
1757
1762 protected function execHooks()
1763 {
1764 $hooksExec = array();
1765
1766 foreach (Hook::getForSite($this->siteId) as $hook)
1767 {
1768 if ($hook->enabled())
1769 {
1770 $hook = $this->prepareHook($hook);
1771 $hooksExec[$hook->getCode()] = $hook;
1772 }
1773 }
1774
1775 foreach (Hook::getForLanding($this->id) as $hook)
1776 {
1777 if ($hook->enabled())
1778 {
1779 $hooksExec[$hook->getCode()] = $hook;
1780 }
1781 }
1782
1783 foreach ($hooksExec as $hook)
1784 {
1785 if (
1786 !$this->getEditMode()
1787 || $hook->enabledInEditMode()
1788 )
1789 {
1790 $hook->exec();
1791 }
1792 }
1793 }
1794
1795 protected function prepareHook($hook)
1796 {
1797 if ($hook->getCode() === 'GMAP')
1798 {
1799 $hook->setSiteId($this->siteId);
1800 }
1801
1802 return $hook;
1803 }
1804
1809 public function exist()
1810 {
1811 return $this->id > 0;
1812 }
1813
1818 public function isActive()
1819 {
1820 return $this->active;
1821 }
1822
1827 public function getId()
1828 {
1829 return $this->id;
1830 }
1831
1836 public function getXmlId()
1837 {
1838 return $this->xmlId;
1839 }
1840
1845 public function getTitle()
1846 {
1847 return $this->title;
1848 }
1849
1854 public function getCode(): string
1855 {
1856 return $this->code;
1857 }
1858
1863 public function getMeta()
1864 {
1865 return $this->metaData;
1866 }
1867
1872 public function canEdit()
1873 {
1874 if (!$this->checkPermissions)
1875 {
1876 return true;
1877 }
1878
1879 return in_array(Rights::ACCESS_TYPES['edit'], $this->rights);
1880 }
1881
1886 public function canPublication()
1887 {
1888 if (!$this->checkPermissions)
1889 {
1890 return true;
1891 }
1892
1893 return in_array(Rights::ACCESS_TYPES['public'], $this->rights);
1894 }
1895
1900 public function canDelete()
1901 {
1902 if (!$this->checkPermissions)
1903 {
1904 return true;
1905 }
1906
1907 return in_array(Rights::ACCESS_TYPES['delete'], $this->rights);
1908 }
1909
1914 public function getFolderId()
1915 {
1916 return $this->folderId;
1917 }
1918
1923 public function getSiteId()
1924 {
1925 return $this->siteId;
1926 }
1927
1932 public function getSiteTitle()
1933 {
1934 return $this->siteTitle;
1935 }
1936
1941 public function getDomainId()
1942 {
1943 return $this->domainId;
1944 }
1945
1950 public static function getSiteType(): string
1951 {
1952 return self::$siteType;
1953 }
1954
1955 public function getSpecialType(): ?string
1956 {
1957 if (
1958 ($this->getMeta()['SITE_SPECIAL'] ?? 'N') === 'Y'
1959 && ($this->getMeta()['SITE_CODE'] ?? '') !== ''
1960 )
1961 {
1962 return Type::getSiteSpecialType($this->getMeta()['SITE_CODE']);
1963 }
1964
1965 return null;
1966 }
1967
1972 public function getSmnSiteId(): ?string
1973 {
1974 if ($this->siteId)
1975 {
1976 $res = Site::getList(array(
1977 'select' => array(
1978 'SMN_SITE_ID',
1979 ),
1980 'filter' => array(
1981 'ID' => $this->siteId,
1982 ),
1983 ));
1984 if ($row = $res->fetch())
1985 {
1986 return $row['SMN_SITE_ID'];
1987 }
1988 }
1989
1990 return null;
1991 }
1992
1997 public function getBlocks()
1998 {
1999 return $this->blocks;
2000 }
2001
2007 public function getBlockById($id)
2008 {
2009 $id = intval($id);
2010
2011 return isset($this->blocks[$id])
2012 ? $this->blocks[$id]
2013 : null
2014 ;
2015 }
2016
2022 public function addBlockToCollection(\Bitrix\Landing\Block $block)
2023 {
2024 if ($block->exist())
2025 {
2026 $this->blocks[$block->getId()] = $block;
2027 }
2028 }
2029
2034 public function getError()
2035 {
2036 return $this->error;
2037 }
2038
2043 public function getErrorMain(): ?\Bitrix\Main\Error
2044 {
2045 if ($this->error)
2046 {
2047 $error = $this->error->getFirstError();
2048
2049 return new \Bitrix\Main\Error(
2050 $error->getMessage(),
2051 $error->getCode()
2052 );
2053 }
2054
2055 return null;
2056 }
2057
2062 public function touch()
2063 {
2064 static $touched = [];
2065
2066 if (isset($touched[$this->id]))
2067 {
2068 return;
2069 }
2070
2071 $touched[$this->id] = true;
2072
2073 if (self::update($this->id, ['PUBLIC' => 'N'])->isSuccess())
2074 {
2075 Site::touch($this->siteId);
2076 }
2077 }
2078
2083 public function updateVersion()
2084 {
2085 $needUpdate = false;
2086 // double hooks: public and draft
2087 if ($this->version <= 1)
2088 {
2089 $needUpdate = true;
2090 $this->version = 2;
2091 $hookEditMode = Hook::getEditMode();
2092 if (!$hookEditMode)
2093 {
2094 Hook::setEditMode(true);
2095 }
2096 Hook::publicationSite($this->siteId);
2097 Hook::publicationLanding($this->id);
2098 if (!$hookEditMode)
2099 {
2100 Hook::setEditMode(false);
2101 }
2102 }
2103 // block assets
2104 if ($this->version <= 2)
2105 {
2106 $needUpdate = true;
2107 $this->version = 3;
2110 }
2111 if ($this->version <= 3)
2112 {
2113 $needUpdate = true;
2114 $this->version = 4;
2116 }
2117 if ($this->version <= 4)
2118 {
2119 $needUpdate = true;
2120 $this->version = 5;
2121 Hook\Page\ThemeFonts::migrateFromTypoThemes($this->id, $this->siteId);
2122 }
2123 if ($this->version <= 5)
2124 {
2125 // fix 126641
2126 $needUpdate = true;
2127 $this->version = 6;
2130 }
2131 if ($this->version <= 6)
2132 {
2133 $needUpdate = true;
2136 $this->version = 7;
2137 }
2138 if ($this->version <= 7)
2139 {
2140 $needUpdate = true;
2143 $this->version = 8;
2144 }
2145 if ($this->version <= 9)
2146 {
2147 // +1 version for reupdate in hotfix
2148 $needUpdate = true;
2151 $this->version = 10;
2152 }
2153 if ($this->version <= 10)
2154 {
2155 $needUpdate = true;
2157
2158 $this->version = 11;
2159 }
2160 if ($needUpdate)
2161 {
2163 self::update($this->id, [
2164 'VERSION' => $this->version,
2165 'DATE_MODIFY' => false,
2166 'MODIFIED_BY_ID' => false,
2167 ]);
2168 Rights::setOn();
2169 }
2170 }
2171
2178 public function publication($blockId = null, ?Metrika\FieldsDto $metrikaFields = null): bool
2179 {
2180 $result = false;
2181
2182 $specialType = $this->getSpecialType();
2183 $category =
2184 $specialType === Type::PSEUDO_SCOPE_CODE_FORMS
2185 ? Metrika\Categories::CrmForms
2186 : Metrika\Categories::getBySiteType(self::$siteType)
2187 ;
2188 $metrika = new Metrika\Metrika($category, Metrika\Events::publishSite);
2189
2190 if ($this->canPublication())
2191 {
2192 $result = Mutator::landingPublication($this, $blockId);
2193 }
2194 else
2195 {
2196 $metrika->setError('access_denied');
2197 }
2198
2199 if (!$result && !empty($this->getError()->getErrors()))
2200 {
2201 $errors = [];
2202 foreach ($this->getError()->getErrors() as $error)
2203 {
2204 $errors[] = $error->getCode();
2205 }
2206 if (!empty($errors))
2207 {
2208 $metrika->setError(implode('|', $errors));
2209 }
2210 }
2211
2212 $metrika->setType($metrikaFields?->type);
2213 $metrika->setSubSection($metrikaFields?->subSection);
2214 $metrika->setElement($metrikaFields?->element);
2215 $metrika->setParam(3, 'siteId', $this->siteId);
2216
2217 $metrika->send();
2218
2219 return $result;
2220 }
2221
2226 public function fakePublication(): bool
2227 {
2228 if ($this->canPublication())
2229 {
2230 return Mutator::landingPublication($this, null, true);
2231 }
2232
2233 return false;
2234 }
2235
2240 public function unpublic()
2241 {
2242 static $siteUpdated = [];
2243
2244 $res = parent::update($this->id, array(
2245 'ACTIVE' => 'N',
2246 'PUBLIC' => 'N',
2247 'DATE_MODIFY' => false,
2248 ));
2249 if ($res->isSuccess())
2250 {
2251 if (
2252 !in_array($this->siteId, $siteUpdated)
2253 && Manager::isB24()
2254 )
2255 {
2256 $siteUpdated[] = $this->siteId;
2257 Site::update($this->siteId, array());
2258 }
2259 // send event
2260 $event = new Event('landing', 'onLandingAfterUnPublication', array(
2261 'id' => $this->getId(),
2262 ));
2263 $event->send();
2264
2265 return true;
2266 }
2267 else
2268 {
2269 $this->error->addFromResult($res);
2270
2271 return false;
2272 }
2273 }
2274
2282 public function addBlock(string $code, array $data = array(), bool $saveInLastUsed = false)
2283 {
2284 $metrika = new Metrika\Metrika(
2285 Metrika\Categories::getBySiteType(self::$siteType),
2286 Metrika\Events::addWidget,
2287 );
2288
2289 if (!$this->canEdit())
2290 {
2291 $this->error->addError(
2292 'ACCESS_DENIED',
2293 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2294 );
2295
2296 if (self::$siteType === Type::SCOPE_CODE_MAINPAGE)
2297 {
2298 $metrika
2299 ->setError('ACCESS_DENIED')
2300 ->send()
2301 ;
2302 }
2303
2304 return false;
2305 }
2306
2307 if (!isset($data['PUBLIC']))
2308 {
2309 $data['PUBLIC'] = $this::$editMode ? 'N' : 'Y';
2310 }
2311
2312 $block = Block::createFromRepository($this, $code, $data);
2313
2314 if ($block)
2315 {
2316 if ($saveInLastUsed)
2317 {
2318 Block::markAsUsed($code);
2319 }
2320
2321 $this->touch();
2322 $this->addBlockToCollection($block);
2323
2324 if (History::isActive())
2325 {
2326 $history = new History($this->id, History::ENTITY_TYPE_LANDING);
2327 $history->push('ADD_BLOCK', ['block' => $block]);
2328 }
2329
2330 if (self::$siteType === Type::SCOPE_CODE_MAINPAGE)
2331 {
2332 $this->parametrizeMetrikaByBlock($metrika, $block)->send();
2333 }
2334
2335 return $block->getId();
2336 }
2337
2338 if (self::$siteType === Type::SCOPE_CODE_MAINPAGE)
2339 {
2340 $metrika
2341 ->setError('BLOCK_NOT_FOUND')
2342 ->send()
2343 ;
2344 }
2345
2346 return false;
2347 }
2348
2354 public function deleteBlock($id)
2355 {
2356 $id = intval($id);
2357 if (isset($this->blocks[$id]))
2358 {
2359 $result = $this->blocks[$id]->unlink();
2360 $this->error->copyError(
2361 $this->blocks[$id]->getError()
2362 );
2363 if ($result)
2364 {
2365 unset($this->blocks[$id]);
2366 }
2367 $this->touch();
2368
2369 return $result;
2370 }
2371 else
2372 {
2373 $this->error->addError(
2374 'BLOCK_NOT_FOUND',
2375 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
2376 );
2377
2378 return false;
2379 }
2380 }
2381
2388 public function markDeletedBlock($id, $mark)
2389 {
2390 $id = intval($id);
2391 if (!isset($this->blocks[$id]))
2392 {
2393 $this->blocks[$id] = new Block($id);
2394 }
2395
2396 $metrika = new Metrika\Metrika(
2397 Metrika\Categories::getBySiteType(self::$siteType),
2398 Metrika\Events::deleteWidget,
2399 );
2400
2401 if (
2402 isset($this->blocks[$id]) &&
2403 $this->blocks[$id]->exist() &&
2404 $this->blocks[$id]->getLandingId() == $this->getId()
2405 )
2406 {
2407 if ($this->blocks[$id]->getAccess() >= $this->blocks[$id]::ACCESS_X)
2408 {
2409 $this->blocks[$id]->markDeleted($mark);
2410 if (!$mark)
2411 {
2412 Assets\PreProcessing::blockUndeleteProcessing(
2413 $this->blocks[$id]
2414 );
2415 }
2416 if ($this->blocks[$id]->save())
2417 {
2418 if (self::$siteType === Type::SCOPE_CODE_MAINPAGE)
2419 {
2420 $this
2421 ->parametrizeMetrikaByBlock($metrika, $this->blocks[$id])
2422 ->send()
2423 ;
2424 }
2425
2426 if ($mark)
2427 {
2428 if (History::isActive())
2429 {
2430 $history = new History($this->id, History::ENTITY_TYPE_LANDING);
2431 $history->push('REMOVE_BLOCK', ['block' => $this->blocks[$id]]);
2432 }
2433
2434 unset($this->blocks[$id]);
2435 }
2436 else
2437 {
2438 $this->addBlockToCollection(
2439 $this->blocks[$id]
2440 );
2441 }
2442 $this->touch();
2443
2444 return true;
2445 }
2446 else
2447 {
2448 $this->error->copyError(
2449 $this->blocks[$id]->getError()
2450 );
2451
2452 if (self::$siteType === Type::SCOPE_CODE_MAINPAGE)
2453 {
2454 $metrika
2455 ->setError('SAVE_ERROR')
2456 ->send()
2457 ;
2458 }
2459 }
2460 }
2461 else
2462 {
2463 $this->error->addError(
2464 'ACCESS_DENIED',
2465 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2466 );
2467 if (self::$siteType === Type::SCOPE_CODE_MAINPAGE)
2468 {
2469 $metrika
2470 ->setError('ACCESS_DENIED')
2471 ->send()
2472 ;
2473 }
2474
2475 return false;
2476 }
2477 }
2478 else
2479 {
2480 $this->error->addError(
2481 'BLOCK_NOT_FOUND',
2482 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
2483 );
2484 if (self::$siteType === Type::SCOPE_CODE_MAINPAGE)
2485 {
2486 $metrika
2487 ->setError('BLOCK_NOT_FOUND')
2488 ->send()
2489 ;
2490 }
2491
2492 return false;
2493 }
2494
2495 return false;
2496 }
2497
2498 protected function parametrizeMetrikaByBlock(Metrika\Metrika $metrika, Block $block): Metrika\Metrika
2499 {
2500 // todo: Now method callsed just for widget
2501 // todo: If need for all types - add self::$siteType === Type::SCOPE_CODE_MAINPAGE checking
2502
2503 if ($block->getRepoId())
2504 {
2505 $metrika->setType(Types::widgetPartner);
2506
2507 $manifest = $block->getManifest();
2508 $appCode = $manifest['block']['app_code'] ?? null;
2509 if ($appCode)
2510 {
2511 $metrika->setParam(1, 'appCode', $appCode);
2512 $app = AppTable::getList([
2513 'filter' => [
2514 '=CODE' => $appCode,
2515 ],
2516 ])->fetch();
2517 if ($app && $app['STATUS'] === AppTable::STATUS_LOCAL)
2518 {
2519 $metrika->setType(Types::widgetLocal);
2520 }
2521 }
2522 }
2523 else
2524 {
2525 $metrika
2526 ->setType(Types::widgetSystem)
2527 ->setParam(1, 'appCode', 'system')
2528 ;
2529 }
2530
2531 $metrika->setParam(2, 'widgetId', $block->getCode());
2532
2533 return $metrika;
2534 }
2535
2542 protected function transferBlock($id, $lid)
2543 {
2544 $id = intval($id);
2545 if (isset($this->blocks[$id]))
2546 {
2547 $result = $this->blocks[$id]->changeLanding($lid);
2548 $this->error->copyError($this->blocks[$id]->getError());
2549 if ($result)
2550 {
2551 unset($this->blocks[$id]);
2552 }
2553
2554 return $result;
2555 }
2556 else
2557 {
2558 $this->error->addError(
2559 'BLOCK_NOT_FOUND',
2560 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
2561 );
2562
2563 return false;
2564 }
2565 }
2566
2571 public function resortBlocks()
2572 {
2573 uasort($this->blocks, function ($a, $b)
2574 {
2575 if ($a->getSort() == $b->getSort())
2576 {
2577 return ($a->getId() < $b->getId()) ? -1 : 1;
2578 }
2579
2580 return ($a->getSort() < $b->getSort()) ? -1 : 1;
2581 });
2582 $sort = 0;
2583 foreach ($this->blocks as $id => $block)
2584 {
2585 $block->saveSort($sort);
2586 $sort += 500;
2587 }
2588 }
2589
2596 protected function sortBlock(int $id, string $action): bool
2597 {
2598 $id = intval($id);
2599 if (isset($this->blocks[$id]))
2600 {
2601 $blocks = array_keys($this->blocks);
2602 for ($i = 0, $c = count($blocks); $i < $c; $i++)
2603 {
2604 if ($blocks[$i] == $id)
2605 {
2606 // change sort between two blocks
2607 $targetKey = $i + ($action === 'up' ? -1 : 1);
2608 if (isset($blocks[$targetKey]))
2609 {
2610 $thisBlock = $this->blocks[$id];
2611 $targetBlock = $this->blocks[$blocks[$targetKey]];
2612 $thisBlockSort = $thisBlock->getSort();
2613 $targetBlockSort = $targetBlock->getSort();
2614
2615 $thisBlock->setSort($targetBlockSort);
2616 $targetBlock->setSort($thisBlockSort);
2617 $res1 = $thisBlock->save();
2618 $res2 = $targetBlock->save();
2619
2620 $this->error->copyError($thisBlock->getError());
2621 $this->error->copyError($targetBlock->getError());
2622
2623 if ($res1 || $res2)
2624 {
2625 $this->touch();
2626 }
2627
2628 return $res1 && $res2;
2629 }
2630
2631 $this->error->addError(
2632 'BLOCK_WRONG_SORT',
2633 Loc::getMessage('LANDING_BLOCK_WRONG_SORT')
2634 );
2635
2636 return false;
2637 }
2638 }
2639 }
2640 else
2641 {
2642 $this->error->addError(
2643 'BLOCK_NOT_FOUND',
2644 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
2645 );
2646 }
2647
2648 return false;
2649 }
2650
2656 public function upBlock(int $id): bool
2657 {
2658 if ($this->sortBlock($id, 'up'))
2659 {
2660 if (History::isActive())
2661 {
2662 $history = new History($this->id, History::ENTITY_TYPE_LANDING);
2663 $history->push('SORT_BLOCK', [
2664 'block' => $id,
2665 'lid' => $this->getId(),
2666 'up' => true,
2667 ]);
2668 }
2669
2670 return true;
2671 }
2672
2673 return false;
2674 }
2675
2681 public function downBlock(int $id): bool
2682 {
2683 if ($this->sortBlock($id, 'down'))
2684 {
2685 if (History::isActive())
2686 {
2687 $history = new History($this->id, History::ENTITY_TYPE_LANDING);
2688 $history->push('SORT_BLOCK', [
2689 'block' => $id,
2690 'lid' => $this->getId(),
2691 'up' => false,
2692 ]);
2693 }
2694
2695 return true;
2696 }
2697
2698 return false;
2699 }
2700
2707 protected function activateBlock($id, $action)
2708 {
2709 $id = intval($id);
2710 if (isset($this->blocks[$id]))
2711 {
2712 if ($this->blocks[$id]->setActive($action == 'show'))
2713 {
2714 if ($res = $this->blocks[$id]->save())
2715 {
2716 $this->touch();
2717 }
2718 }
2719 $this->error->copyError($this->blocks[$id]->getError());
2720
2721 return $res;
2722 }
2723 else
2724 {
2725 $this->error->addError(
2726 'BLOCK_NOT_FOUND',
2727 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
2728 );
2729
2730 return false;
2731 }
2732 }
2733
2739 public function showBlock($id)
2740 {
2741 return $this->activateBlock($id, 'show');
2742 }
2743
2749 public function hideBlock($id)
2750 {
2751 return $this->activateBlock($id, 'hide');
2752 }
2753
2760 public function favoriteBlock(int $id, array $meta = []): ?int
2761 {
2762 $bewBlockId = $this->copyBlock($id, $id);
2763 if ((int)$bewBlockId > 0)
2764 {
2765 if (
2766 $this->blocks[$bewBlockId]->changeLanding(0) &&
2767 $this->blocks[$bewBlockId]->changeFavoriteMeta($meta)
2768 )
2769 {
2770 Block::markAsUsed($this->blocks[$bewBlockId]->getCode() . '@' . $bewBlockId);
2771 \Bitrix\Landing\Block::clearRepositoryCache();
2772 if ($meta['preview'] ?? null)
2773 {
2774 File::deleteFromBlock($id, $meta['preview']);
2775 }
2776 }
2777 else
2778 {
2779 $this->error->copyError($this->blocks[$bewBlockId]->getError());
2780 }
2781
2782 return $bewBlockId;
2783 }
2784
2785 return null;
2786 }
2787
2793 public function unFavoriteBlock(int $blockId): bool
2794 {
2795 $block = new Block($blockId);
2796 if (!$block || empty($block->getMeta()['FAVORITE_META']))
2797 {
2798 $this->error->addError(
2799 'BLOCK_NOT_FOUND',
2800 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
2801 );
2802
2803 return false;
2804 }
2805
2806 if (
2807 $block->getAccess() < Block::ACCESS_X
2808 || (int)$block->getMeta()['CREATED_BY_ID'] !== Manager::getUserId()
2809 )
2810 {
2811 $this->error->addError(
2812 'ACCESS_DENIED',
2813 Loc::getMessage('LANDING_BLOCK_ACCESS_DENIED')
2814 );
2815
2816 return false;
2817 }
2818
2819 if (BlockTable::delete($blockId)->isSuccess())
2820 {
2821 File::deleteFromBlock($blockId);
2822 Block::removeAsUsed($block->getCode() . '@' . $block->getId());
2823 Block::clearRepositoryCache();
2824
2825 return true;
2826 }
2827
2828 return false;
2829 }
2830
2837 protected function changeParentOfBlock(int $block, array $params): ?int
2838 {
2839 $block = intval($block);
2840 $move = isset($params['MOVE']) && $params['MOVE'];
2841 $afterId = isset($params['AFTER_ID']) ? (int)$params['AFTER_ID'] : 0;
2842 $fromLandingRow = Block::getRowByBlockId($block, ['ID', 'LID', 'SITE_TYPE' => 'LANDING.SITE.TYPE']);
2843 $fromLandingId = $fromLandingRow['LID'] ?? null;
2844 $currentScopeId = Site\Type::getCurrentScopeId();
2845 $same = $this->id == $fromLandingId;
2846 $newBlock = null;
2847
2848 if ($currentScopeId !== $fromLandingRow['SITE_TYPE'])
2849 {
2850 Site\Type::setScope($fromLandingRow['SITE_TYPE']);
2851 }
2852
2853 if ($same)
2854 {
2855 $fromLanding = clone $this;
2856 }
2857 else
2858 {
2859 $fromLanding = self::createInstance($fromLandingId);
2860 }
2861
2862 // if landing exist and available, get it blocks
2863 if ($this->exist() && $fromLanding->exist())
2864 {
2865 $fromLandingBlocks = $fromLanding->getBlocks();
2866 // if move, just change landing id
2867 if ($move)
2868 {
2869 $res = $fromLanding->transferBlock($block, $this->id);
2870 $this->error->copyError($fromLanding->getError());
2871 if ($res)
2872 {
2873 $newBlock = $fromLandingBlocks[$block];
2874 }
2875 }
2876 // else create copy
2877 elseif (isset($fromLandingBlocks[$block]))
2878 {
2879 $srcBlock = $fromLandingBlocks[$block];
2880 $newBlock = Block::createFromRepository(
2881 $this,
2882 $srcBlock->getCode(),
2883 array(
2884 'ACTIVE' => $srcBlock->isActive() ? 'Y' : 'N',
2885 'DESIGNED' => $srcBlock->isDesigned() ? 'Y' : 'N',
2886 'ACCESS' => $srcBlock->getAccess(),
2887 'SORT' => $srcBlock->getSort(),
2888 'CONTENT' => $srcBlock->getContent(),
2889 'SOURCE_PARAMS' => $srcBlock->getDynamicParams(),
2890 'PUBLIC' => 'N',
2891 ));
2892 if ($newBlock)
2893 {
2894 // we should save original content after all callbacks
2895 $newBlock->saveContent(
2896 $srcBlock->getContent()
2897 );
2898 $newBlock->save();
2899 // copy files
2900 if ($newBlock)
2901 {
2903 $srcBlock->getId(),
2904 $newBlock->getId()
2905 );
2906 }
2907 }
2908 }
2909 // add block to collection and resort
2910 if ($newBlock)
2911 {
2912 if ($afterId > 0 && isset($this->blocks[$afterId]))
2913 {
2914 $targetBlock = $this->blocks[$afterId];
2915 }
2916 else
2917 {
2918 $blocksTmp = array_values($this->blocks);
2919 $targetBlock = array_pop($blocksTmp);
2920 }
2921 if ($targetBlock)
2922 {
2923 $newBlock->setSort($targetBlock->getSort() + 1);
2924 }
2925 $this->addBlockToCollection($newBlock);
2926 $this->resortBlocks();
2927 // search index
2928 $newBlock->save();
2929 }
2930 //change dates
2931 if ($this->error->isEmpty())
2932 {
2933 if ($move && !$same)
2934 {
2935 $fromLanding->touch();
2936 }
2937 }
2938 }
2939
2940 $this->error->copyError($fromLanding->getError());
2941
2942 if ($currentScopeId !== $fromLandingRow['SITE_TYPE'])
2943 {
2944 Site\Type::setScope($currentScopeId);
2945 }
2946
2947 if ($this->error->isEmpty())
2948 {
2949 $this->touch();
2950 }
2951
2952 return $newBlock ? $newBlock->getId() : null;
2953 }
2954
2961 public function copyBlock(int $id, int $afterId): ?int
2962 {
2963 $blockId = $this->changeParentOfBlock($id, [
2964 'MOVE' => false,
2965 'AFTER_ID' => $afterId,
2966 ]);
2967 if (!$blockId)
2968 {
2969 $this->error->addError(
2970 'BLOCK_NOT_FOUND',
2971 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
2972 );
2973 }
2974
2975 return $blockId;
2976 }
2977
2985 public function copyAllBlocks($lid, $replaceLinks = true, array &$references = [])
2986 {
2987 $landing = self::createInstance($lid);
2988
2989 if ($this->exist() && $landing->exist())
2990 {
2991 $oldNew = array();
2992 // copy blocks
2993 foreach ($landing->getBlocks() as $block)
2994 {
2995 $newBlock = Block::createFromRepository(
2996 $this,
2997 $block->getCode(),
2998 array(
2999 'ACTIVE' => $block->isActive() ? 'Y' : 'N',
3000 'DESIGNED' => $block->isDesigned() ? 'Y' : 'N',
3001 'PUBLIC' => $block->isPublic() ? 'Y' : 'N',
3002 'ACCESS' => $block->getAccess(),
3003 'SORT' => $block->getSort(),
3004 'CONTENT' => $block->getContent(),
3005 'SOURCE_PARAMS' => $block->getDynamicParams(),
3006 ));
3007 if ($newBlock)
3008 {
3009 $oldNew[$block->getId()] = $newBlock;
3010 $references[$block->getId()] = $newBlock->getId();
3011 $this->addBlockToCollection($newBlock);
3012 }
3013 }
3014 // replace old id of blocks to the new one and clone files
3015 foreach ($this->getBlocks() as $block)
3016 {
3017 $content = $block->getContent();
3018 foreach ($oldNew as $oldId => $newBlock)
3019 {
3020 // clone files
3022 $newBlock->getId(),
3024 );
3025 // replace ids
3026 if ($replaceLinks)
3027 {
3028 $content = str_replace(
3029 '#block' . $oldId,
3030 '#block' . $newBlock->getId(),
3031 $content
3032 );
3033 $block->saveContent($content);
3034 }
3035 $block->save();
3036 }
3037 }
3038 $this->touch();
3039 }
3040
3041 $this->error->copyError($this->getError());
3042 $this->error->copyError($landing->getError());
3043 }
3044
3049 public function clearFolderIndex(): void
3050 {
3051 if ($this->folderId)
3052 {
3053 $resFolder = Folder::getList([
3054 'select' => [
3055 'ID',
3056 ],
3057 'filter' => [
3058 'ID' => $this->folderId,
3059 'INDEX_ID' => $this->id,
3060 ],
3061 ]);
3062 if ($resFolder->fetch())
3063 {
3064 Folder::update($this->folderId, [
3065 'INDEX_ID' => null,
3066 ]);
3067 }
3068 }
3069 }
3070
3076 private function canPublicAfterCopy(): bool
3077 {
3078 $siteId = $this->getSiteId();
3079 $folderId = $this->getFolderId();
3080
3081 // if permissions enough
3082 if (!$this->canPublication())
3083 {
3084 return false;
3085 }
3086
3087 // in root, we can
3088 if (!$folderId)
3089 {
3090 return true;
3091 }
3092
3093 // check all folders above the page
3094 $crumbs = Folder::getBreadCrumbs($folderId, $siteId);
3095 foreach ($crumbs as $crumb)
3096 {
3097 // if folder is active, we don't care about
3098 if ($crumb['ACTIVE'] === 'Y')
3099 {
3100 continue;
3101 }
3102
3103 // check active pages in each folder above
3104 $res = self::getList([
3105 'select' => [
3106 'ID',
3107 ],
3108 'filter' => [
3109 '=ACTIVE' => 'Y',
3110 'FOLDER_ID' => $crumb['ID'],
3111 ],
3112 'limit' => 1,
3113 ]);
3114 if ($res->fetch())
3115 {
3116 // if such folder exists we cant public any folder
3117 return false;
3118 }
3119
3120 // check active folders in folders above
3121 $res = Folder::getList([
3122 'select' => [
3123 'ID',
3124 ],
3125 'filter' => [
3126 '=ACTIVE' => 'Y',
3127 'PARENT_ID' => $crumb['ID'],
3128 ],
3129 'limit' => 1,
3130 ]);
3131 if ($res->fetch())
3132 {
3133 // if such folder exists we cant public any folder
3134 return false;
3135 }
3136 }
3137
3138 // all folders are active or not exists active pages
3139 return true;
3140 }
3141
3148 public function move(?int $toSiteId = null, ?int $toFolderId = null): bool
3149 {
3150 if (!$this->exist())
3151 {
3152 return false;
3153 }
3154
3155 if (!$toSiteId)
3156 {
3157 $toSiteId = $this->getSiteId();
3158 }
3159
3160 $rightsSite = Rights::getOperationsForSite($toSiteId);
3161 if (!in_array(Rights::ACCESS_TYPES['edit'], $rightsSite))
3162 {
3163 $this->error->addError(
3164 'ACCESS_DENIED',
3165 Loc::getMessage('LANDING_SITE_ACCESS_DENIED')
3166 );
3167
3168 return false;
3169 }
3170
3171 if (!$this->canDelete())
3172 {
3173 $this->error->addError(
3174 'DELETE_ACCESS_DENIED',
3175 Loc::getMessage('LANDING_DELETE_ACCESS_DENIED')
3176 );
3177
3178 return false;
3179 }
3180
3181 $result = self::update($this->id, [
3182 'ACTIVE' => 'N',
3183 'PUBLIC' => 'N',
3184 'CODE' => $this->code,
3185 'SITE_ID' => $toSiteId,
3186 'FOLDER_ID' => $toFolderId,
3187 ]);
3188
3189 if ($result->isSuccess())
3190 {
3191 $this->clearFolderIndex();
3192 if ($this->active && $this->canPublicAfterCopy($toFolderId))
3193 {
3194 $this->publication();
3195 }
3196 }
3197
3198 $this->error->addFromResult($result);
3199
3200 return $result->isSuccess();
3201 }
3202
3211 public function copy(?int $toSiteId = null, ?int $toFolderId = null, bool $withoutBlocks = false, bool $skipSystem = false): ?int
3212 {
3213 if ($this->exist())
3214 {
3215 $landingRow = Landing::getList([
3216 'filter' => [
3217 'ID' => $this->id,
3218 ],
3219 ])->fetch();
3220 $folderId = null;
3221 if (!$toSiteId)
3222 {
3223 $toSiteId = $this->getSiteId();
3224 }
3225 if ($toFolderId !== null)
3226 {
3227 $folderId = $toFolderId;
3228 }
3229 // check if folder in the same site
3230 if ($folderId)
3231 {
3232 $folderRow = Site::getFolder($folderId);
3233 if (intval($folderRow['SITE_ID'] ?? null) !== $toSiteId)
3234 {
3235 $folderId = null;
3236 }
3237 }
3238 // add 'copy' to new page?
3239 $addCopyMark = !!Landing::getList([
3240 'select' => [
3241 'ID',
3242 ],
3243 'filter' => [
3244 '=TITLE' => $landingRow['TITLE'],
3245 '=DELETED' => 'N',
3246 'FOLDER_ID' => $folderId,
3247 'SITE_ID' => $toSiteId,
3248 ],
3249 ])->fetch();
3250 // create new page
3251 $res = Landing::add([
3252 'CODE' => $landingRow['CODE'],
3253 'ACTIVE' => 'N',
3254 'PUBLIC' => 'N',
3255 'TITLE' => $addCopyMark
3256 ? $landingRow['TITLE'] . ' ' . Loc::getMessage('LANDING_COPY_SUFFIX')
3257 : $landingRow['TITLE'],
3258 'SYS' => $skipSystem ? 'N' : $landingRow['SYS'],
3259 'XML_ID' => $landingRow['XML_ID'],
3260 'TPL_CODE' => $landingRow['TPL_CODE'],
3261 'INITIATOR_APP_CODE' => $landingRow['INITIATOR_APP_CODE'],
3262 'DESCRIPTION' => $landingRow['DESCRIPTION'],
3263 'TPL_ID' => $landingRow['TPL_ID'],
3264 'SITE_ID' => $toSiteId,
3265 'SITEMAP' => $landingRow['SITEMAP'],
3266 'FOLDER_ID' => $folderId,
3267 'RULE' => '',
3268 ]);
3269 // landing allready create, just copy the blocks
3270 if ($res->isSuccess())
3271 {
3272 Landing::setEditMode();
3273 $landingNew = Landing::createInstance($res->getId());
3274 if ($landingNew->exist())
3275 {
3276 if (!$withoutBlocks)
3277 {
3278 $landingNew->copyAllBlocks($this->id);
3279 }
3280 // copy hook data
3281 Hook::copyLanding(
3282 $this->id,
3283 $landingNew->getId()
3284 );
3285 // copy files
3287 $this->id,
3288 $landingNew->getId()
3289 );
3290 // copy template refs
3291 if (($refs = TemplateRef::getForLanding($this->id)))
3292 {
3293 TemplateRef::setForLanding($landingNew->getId(), $refs);
3294 }
3295 // publication if needed
3296 if ($landingRow['ACTIVE'] === 'Y' && $landingNew->canPublicAfterCopy($toFolderId))
3297 {
3298 $landingNew->publication();
3299 }
3300
3301 return $landingNew->getId();
3302 }
3303 $this->error->copyError($landingNew->getError());
3304 }
3305 else
3306 {
3307 $this->error->addFromResult($res);
3308 }
3309 }
3310
3311 return null;
3312 }
3313
3320 public function moveBlock(int $id, int $afterId): ?int
3321 {
3322 $blockId = $this->changeParentOfBlock($id, [
3323 'MOVE' => true,
3324 'AFTER_ID' => $afterId,
3325 ]);
3326 if (!$blockId)
3327 {
3328 $this->error->addError(
3329 'BLOCK_NOT_FOUND',
3330 Loc::getMessage('LANDING_BLOCK_NOT_FOUND')
3331 );
3332 }
3333
3334 return $blockId;
3335 }
3336
3343 public static function update($id, $fields = array())
3344 {
3345 if (self::$enabledUpdate)
3346 {
3347 return parent::update($id, $fields);
3348 }
3349 else
3350 {
3351 return new \Bitrix\Main\Result;
3352 }
3353 }
3354
3362 public static function addByTemplate(int $siteId, string $code, array $fields = []): \Bitrix\Main\Entity\AddResult
3363 {
3364 $result = new \Bitrix\Main\Entity\AddResult;
3365
3366 // get type by siteId
3367 $res = Site::getList([
3368 'select' => [
3369 'TYPE',
3370 ],
3371 'filter' => [
3372 'ID' => $siteId,
3373 ],
3374 ]);
3375 if (!($site = $res->fetch()))
3376 {
3377 $result->addError(new \Bitrix\Main\Error(
3378 'SITE_ERROR',
3379 Loc::getMessage('LANDING_SITE_ERROR')
3380 ));
3381
3382 return $result;
3383 }
3384
3385 // get landing if specified
3386 if ($fields['ID'] ?? null)
3387 {
3388 $res = self::getList([
3389 'select' => [
3390 'ID', 'FOLDER_ID', 'FOLDER', 'ACTIVE',
3391 ],
3392 'filter' => [
3393 'SITE_ID' => $siteId,
3394 'ID' => $fields['ID'],
3395 ],
3396 ]);
3397 if (!($landing = $res->fetch()))
3398 {
3399 $result->addError(new \Bitrix\Main\Error(
3400 'LANDING_ERROR',
3401 Loc::getMessage('LANDING_NOT_FOUND')
3402 ));
3403
3404 return $result;
3405 }
3406 if ($landing['FOLDER'] === 'Y')
3407 {
3408 $landing['FOLDER_ID'] = $landing['ID'];
3409 }
3410 }
3411
3412 // include the component
3413 $componentName = 'bitrix:landing.demo';
3414 $className = \CBitrixComponent::includeComponentClass($componentName);
3416 $demoCmp = new $className;
3417 $demoCmp->initComponent($componentName);
3418 $demoCmp->arParams = [
3419 'TYPE' => $fields['SITE_TYPE'] ?? (($site['TYPE'] == 'STORE' || $site['TYPE'] == 'SMN') ? 'PAGE' : $site['TYPE']),
3420 'SITE_ID' => $siteId,
3421 'SITE_WORK_MODE' => 'N',
3422 'DISABLE_REDIRECT' => 'Y',
3423 'DONT_LEAVE_FRAME' => 'N',
3424 'FOLDER_ID' => $landing['FOLDER_ID'] ?? $fields['FOLDER_ID'] ?? 0,
3425 'META' => $fields,
3426 ];
3427 if (
3428 isset($fields['PREPARE_BLOCKS'], $fields['PREPARE_BLOCKS_DATA'])
3429 && $fields['PREPARE_BLOCKS'] === true
3430 && is_array($fields['PREPARE_BLOCKS_DATA'])
3431 )
3432 {
3433 $demoCmp->arParams['PREPARE_BLOCKS_DATA'] = $fields['PREPARE_BLOCKS_DATA'];
3434 }
3435
3436 // ... and create the page by component's method
3437 $landingId = $demoCmp->createPage($siteId, $code);
3438
3439 // prepare success or failure
3440 if ($landingId)
3441 {
3442 $result->setId($landingId);
3443 if (($landing['ACTIVE'] ?? 'N') === 'Y')
3444 {
3445 Landing::createInstance($landingId)->publication();
3446 }
3447 }
3448 else
3449 {
3450 foreach ($demoCmp->getErrors() as $code => $title)
3451 {
3452 $result->addError(new \Bitrix\Main\Error($code, $title));
3453 }
3454 }
3455
3456 return $result;
3457 }
3458}
xml version
Определения yandex.php:67
if(!Loader::includeModule('catalog')) if(!AccessController::getCurrent() ->check(ActionDictionary::ACTION_PRICE_EDIT)) if(!check_bitrix_sessid()) $request
Определения catalog_reindex.php:36
static addUniqueAgent(string $funcName, array $params=[], int $time=7200, ?int $nextExecDelay=null)
Определения agent.php:28
static rebuildWebpackForLanding($lid=[])
Определения manager.php:488
static processingLanding(int $landingId)
Определения font.php:62
static processingLanding(int $landingId)
Определения icon.php:466
getRepoId()
Определения block.php:1544
getManifest(bool $extended=false, bool $missCache=false, array $params=array())
Определения block.php:1682
getCode()
Определения block.php:1403
static getReplacesForContent(int $siteId, bool $attributesReplace=true)
Определения crm.php:241
static isMobileHit()
Определения mobile.php:154
static copyLandingFiles($from, $to)
Определения file.php:559
static addToBlock(int $blockId, $fileId, bool $temp=false)
Определения file.php:305
static deleteFromAsset(int $assetId, $fileId=[])
Определения file.php:456
static copyBlockFiles($from, $to)
Определения file.php:570
static deleteFromBlock($blockId, $fileId=array())
Определения file.php:363
static deleteFromLanding($lid, $fileId=array())
Определения file.php:293
static getFilesFromBlockContent($blockId, $content)
Определения file.php:374
static getFilePath($fileId)
Определения file.php:600
static getFullPath(int $folderId, ?int $siteId=null, array &$lastFolder=[])
Определения folder.php:226
static getBreadCrumbs(int $folderId, ?int $siteId=null)
Определения folder.php:134
static getAllImages($entityType=Hook::ENTITY_TYPE_LANDING)
Определения metaog.php:63
static migrateFromTypoThemes(int $lid, int $siteId)
Определения themefonts.php:353
static beforeLandingView()
Определения seo.php:32
static afterLandingView()
Определения seo.php:50
getAreas()
Определения landing.php:1341
__construct($id, $params=array())
Определения landing.php:225
static getDynamicFilter()
Определения landing.php:834
static getHooks($id)
Определения landing.php:722
static disableCheckDeleted()
Определения landing.php:473
static setPreviewMode($mode=true)
Определения landing.php:446
static getEditMode()
Определения landing.php:436
getId()
Определения landing.php:1827
static setVariables(array $vars)
Определения landing.php:801
canDelete()
Определения landing.php:1900
getBlocks()
Определения landing.php:1997
static disableUpdate()
Определения landing.php:491
getErrorMain()
Определения landing.php:2043
$version
Определения landing.php:212
resortBlocks()
Определения landing.php:2571
setMetaData(array $metaData)
Определения landing.php:373
publication($blockId=null, ?Metrika\FieldsDto $metrikaFields=null)
Определения landing.php:2178
moveBlock(int $id, int $afterId)
Определения landing.php:3320
static $variables
Определения landing.php:80
int $tplId
Определения landing.php:176
getError()
Определения landing.php:2034
static getPreviewMode()
Определения landing.php:455
getTplId()
Определения landing.php:1445
static getSiteType()
Определения landing.php:1950
static getAdditionalFieldsAsArray(int $landingId, bool $skipEmpty=true)
Определения landing.php:756
$siteRow
Определения landing.php:146
addBlockToCollection(\Bitrix\Landing\Block $block)
Определения landing.php:2022
applyTemplate($content=null)
Определения landing.php:1358
static update($id, $fields=array())
Определения landing.php:3343
getSpecialType()
Определения landing.php:1955
$mainInstance
Определения landing.php:104
$blocks
Определения landing.php:116
static markUnDelete($id)
Определения landing.php:609
updateVersion()
Определения landing.php:2083
transferBlock($id, $lid)
Определения landing.php:2542
execHooks()
Определения landing.php:1762
move(?int $toSiteId=null, ?int $toFolderId=null)
Определения landing.php:3148
static string $siteType
Определения landing.php:98
exist()
Определения landing.php:1809
getSiteId()
Определения landing.php:1923
static $internalClass
Определения landing.php:44
$tplType
Определения landing.php:182
hideBlock($id)
Определения landing.php:2749
changeParentOfBlock(int $block, array $params)
Определения landing.php:2837
$domainId
Определения landing.php:164
touch()
Определения landing.php:2062
static $checkDelete
Определения landing.php:62
static setEditMode($mode=true)
Определения landing.php:427
static getAdditionalFields($id)
Определения landing.php:737
$folderId
Определения landing.php:170
$siteTitle
Определения landing.php:158
Error $error
Определения landing.php:194
unpublic()
Определения landing.php:2240
static isDynamicDetailPage()
Определения landing.php:860
$checkPermissions
Определения landing.php:206
static getDynamicElementId()
Определения landing.php:851
getSiteTitle()
Определения landing.php:1932
static disableCheckUniqueAddress()
Определения landing.php:518
canPublication()
Определения landing.php:1886
unFavoriteBlock(int $blockId)
Определения landing.php:2793
getFolderId()
Определения landing.php:1914
$rights
Определения landing.php:200
copyBlock(int $id, int $afterId)
Определения landing.php:2961
upBlock(int $id)
Определения landing.php:2656
view(array $params=array())
Определения landing.php:1137
static $enabledUpdate
Определения landing.php:50
static markDelete(int $id)
Определения landing.php:548
sortBlock(int $id, string $action)
Определения landing.php:2596
getTitle()
Определения landing.php:1845
downBlock(int $id)
Определения landing.php:2681
showBlock($id)
Определения landing.php:2739
static $dynamicElementId
Определения landing.php:92
static enableUpdate()
Определения landing.php:500
clearFolderIndex()
Определения landing.php:3049
isActive()
Определения landing.php:1818
static isCheckUniqueAddress()
Определения landing.php:509
getDomainId()
Определения landing.php:1941
const META_KEYS
Определения landing.php:26
canEdit()
Определения landing.php:1872
$active
Определения landing.php:188
deleteBlock($id)
Определения landing.php:2354
$siteId
Определения landing.php:152
getXmlId()
Определения landing.php:1836
static getVariables()
Определения landing.php:813
getCode()
Определения landing.php:1854
markDeletedBlock($id, $mark)
Определения landing.php:2388
static ping($id, $deleted=false)
Определения landing.php:390
static resolveIdByPublicUrl(string $landingUrl, int $siteId)
Определения landing.php:1107
getPreview(?int $id=null, bool $skipCloud=false, ?string $publicUrl=null)
Определения landing.php:872
prepareHook($hook)
Определения landing.php:1795
activateBlock($id, $action)
Определения landing.php:2707
static setDynamicParams($filterId, $elementId)
Определения landing.php:824
copy(?int $toSiteId=null, ?int $toFolderId=null, bool $withoutBlocks=false, bool $skipSystem=false)
Определения landing.php:3211
static $dynamicFilterId
Определения landing.php:86
const META_KEYS_MODIFIABLE
Определения landing.php:36
getSmnSiteId()
Определения landing.php:1972
favoriteBlock(int $id, array $meta=[])
Определения landing.php:2760
static enableCheckDeleted()
Определения landing.php:482
static $previewMode
Определения landing.php:74
fakePublication()
Определения landing.php:2226
static createInstance($id, array $params=array())
Определения landing.php:538
$metaData
Определения landing.php:110
static saveAdditionalFields($id, array $data)
Определения landing.php:790
getMeta()
Определения landing.php:1863
addBlock(string $code, array $data=array(), bool $saveInLastUsed=false)
Определения landing.php:2282
static $checkUniqueAddress
Определения landing.php:56
getPublicUrl($id=false, $absolute=true, $createPubPath=false, &$fullUrl=[])
Определения landing.php:922
$disableLinkPreview
Определения landing.php:218
static $editMode
Определения landing.php:68
static enableCheckUniqueAddress()
Определения landing.php:527
parseLocalUrl(string $content)
Определения landing.php:1455
parametrizeMetrikaByBlock(Metrika\Metrika $metrika, Block $block)
Определения landing.php:2498
getBlockById($id)
Определения landing.php:2007
static checkDeleted()
Определения landing.php:464
copyAllBlocks($lid, $replaceLinks=true, array &$references=[])
Определения landing.php:2985
static isB24()
Определения manager.php:1135
static getApplication()
Определения manager.php:71
static getPublicationPath($siteCode=null, $siteId=null, $createPubPath=false)
Определения manager.php:401
static getUserId()
Определения manager.php:107
static isHttps()
Определения manager.php:1032
static getMainSiteId()
Определения manager.php:546
static setPageTitle($title, $single=false)
Определения manager.php:211
static isCloudDisable()
Определения manager.php:1244
static getUrlFromFile($file)
Определения manager.php:1082
static landingPublication(Landing $landing, $_245979103=null, bool $_1014965041=false)
Определения mutator.php:1
static getIblockURL($elementId, $urlType)
Определения utils.php:328
static hasAccessForLanding($landingId, $accessType)
Определения rights.php:580
static isOn()
Определения rights.php:125
static getOperationsForSite($siteId)
Определения rights.php:520
static setOff()
Определения rights.php:89
static setOn()
Определения rights.php:98
const ACCESS_TYPES
Определения rights.php:21
static setScope($scope, array $params=[])
Определения type.php:88
static getKeyCode()
Определения type.php:160
static getCurrentScopeId()
Определения type.php:188
static getFilter($filterId)
Определения filterentity.php:35
static prepareFormsToView(string $content)
Определения form.php:72
static updateLandingToEmbedForms(int $landingId)
Определения form.php:905
static getForLanding($id)
Определения templateref.php:153
static landingIsArea(int|array $lid)
Определения templateref.php:165
static getForSite($id)
Определения templateref.php:143
static setForLanding($id, array $data=array())
Определения templateref.php:130
static updateLanding(int $lid)
Определения buttons.php:92
static updateLanding(int $lid)
Определения fontweight.php:51
Определения result.php:20
Определения error.php:15
$code
Определения error.php:17
$siteId
Определения filesystementry.php:12
static getList(array $parameters=array())
Определения datamanager.php:431
$fields
Определения entity.php:45
static getStyle(Element $node, $singleStyle=true)
Определения styleinliner.php:43
$options
Определения commerceml2.php:49
$content
Определения commerceml.php:144
$componentName
Определения component_props2.php:49
$data['IS_AVAILABLE']
Определения .description.php:13
$template
Определения file_edit.php:49
</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
$result
Определения get_property_values.php:14
$errors
Определения iblock_catalog_edit.php:74
$filter
Определения iblock_catalog_list.php:54
$app
Определения proxy.php:8
$_SERVER["DOCUMENT_ROOT"]
Определения cron_frame.php:9
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
$siteId
Определения ajax.php:8
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
Определения ai.php:3
@ getBySiteType
Определения Categories.php:18
@ widgetLocal
Определения Types.php:12
Определения cookies.php:2
Определения agent.php:3
Определения ufield.php:9
getErrors()
Определения errorableimplementation.php:34
Определения buffer.php:3
$event
Определения prolog_after.php:141
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
<? endif;?> window document title
Определения prolog_main_admin.php:76
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
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
else $a
Определения template.php:137
$title
Определения pdf.php:123
$val
Определения options.php:1793
if(!Loader::includeModule('sale')) $pattern
Определения index.php:20
$matches
Определения index.php:22
$error
Определения subscription_card_product.php:20
$action
Определения file_dialog.php:21
$url
Определения iframe.php:7
$site
Определения yandex_run.php:614
$fields
Определения yandex_run.php:501