Current Path : /home/bitrix/ext_www/klimatlend.ua/bitrix/modules/landing/lib/ |
Current File : /home/bitrix/ext_www/klimatlend.ua/bitrix/modules/landing/lib/landing.php |
<?php namespace Bitrix\Landing; use \Bitrix\Main\Page\Asset; use \Bitrix\Main\Localization\Loc; Loc::loadMessages(__FILE__); class Landing extends \Bitrix\Landing\Internals\BaseTable { /** * Internal class. * @var string */ public static $internalClass = 'LandingTable'; /** * Current mode is edit. * @var boolean */ protected static $editMode = false; /** * Set false if landing view as area. * @var boolean */ protected $mainInstance = true; /** * All blocks of current landing. * @var array */ protected $blocks = array(); /** * Id of current landing. * @var int */ protected $id = 0; /** * Title of current landing. * @var string */ protected $title = ''; /** * Code (part of URL) of current landing. * @var string */ protected $code = ''; /** * Site id of current landing. * @var int */ protected $siteId = 0; /** * Current template id. * @var int */ protected $tplId = 0; /** * Current template type (site or landing). * @var string */ protected $tplType = 'landing'; /** * Active or not current landing. * @var boolean */ protected $active = false; /** * Instance of Error. * @var \Bitrix\Landing\Error */ protected $error = null; /** * Constructor. * @param int $id Landing id. * @param array $params Some params. */ protected function __construct($id, $params = array()) { $this->error = new Error; $landing = self::getList(array( 'select' => array( '*', 'SITE_TPL_ID' => 'SITE.TPL_ID' ), 'filter' => array( 'ID' => $id ) ))->fetch(); if ($landing) { /* * $this->getEditMode() * @todo return if no access */ // get base data $this->title = $landing['TITLE']; $this->code = $landing['CODE']; $this->id = (int)$landing['ID']; $this->siteId = (int)$landing['SITE_ID']; $this->active = $landing['ACTIVE'] == 'Y'; $this->tplId = $landing['TPL_ID'] > 0 ? $landing['TPL_ID'] : ( $landing['SITE_TPL_ID'] > 0 ? $landing['SITE_TPL_ID'] : 0 ); if (isset($params['is_area']) && $params['is_area']) { $this->mainInstance = false; } if ($landing['SITE_TPL_ID'] > 0) { $this->tplType = 'site'; } // if edit mode - create copy for edit if ($this->getEditMode()) { if ($landing['PUBLIC'] == 'Y') { self::update($id, array( 'PUBLIC' => 'N' )); Block::cloneForEdit($this); } } // if landing is unactive if ( false && !$this->active ) { //add error ? //add title ? $this->title = Loc::getMessage('LANDING_TITLE_NOT_FOUND'); } // get available blocks else { Block::fillLanding( $this, isset($params['blocks_limit']) ? $params['blocks_limit'] : 0 ); } } // landing not found else { $this->error->addError( 'LANDING_NOT_FOUND', Loc::getMessage('LANDING_NOT_FOUND') ); $this->title = Loc::getMessage('LANDING_TITLE_NOT_FOUND'); } } /** * Set work mode to edit. * @return void */ public static function setEditMode() { self::$editMode = true; } /** * Get state of edit mode. * @return boolean */ public static function getEditMode() { return self::$editMode; } /** * Create current instance. * @param int $id Landing id. * @param array $params Additional params. * @return Landing */ public static function createInstance($id, array $params = array()) { return new self($id, $params); } /** * Delete landing by id and its blocks. * @param int $id Landing id. * @return \Bitrix\Main\Result */ public static function delete($id) { $landing = self::createInstance($id); $result = new \Bitrix\Main\Entity\AddResult(); if ($landing->exist()) { foreach ($landing->getBlocks() as $block) { if (!$block->unlink()) { $result->addErrors($block->getError()->getErrors()); break; } } if ($result->isSuccess()) { $result = parent::delete($id); } } return $result; } /** * Get hooks of Landing. * @param int $id Landing id. * @return array Array of Hook. */ public static function getHooks($id) { return Hook::getForLanding($id); } /** * Get additional fields of Landing. * @param int $id Landing id. * @return array Array of Field. */ public static function getAdditionalFields($id) { $fields = array(); // now we can get additional fields only from hooks foreach (self::getHooks($id) as $hook) { $fields += $hook->getPageFields(); } return $fields; } /** * Save additional fields for Landing. * @param int $id Landing id. * @param array $data Data array. * @return void */ public static function saveAdditionalFields($id, array $data) { // now we can get additional fields only from hooks Hook::saveForLanding($id, $data); } /** * Get preview picture of the landing. * Is the preview of first block. * @return string */ public function getPreview() { if (count($this->blocks) > 0) { $firstBlock = array_shift($this->blocks); return $firstBlock->getPreview(); } return ''; } /** * Get full pubic URL for this landing. * @param int|array $id Landing id (id array), optional. * @return string */ public function getPublicUrl($id = false) { if ($id === false) { $id = $this->id; } $bitrix24 = \Bitrix\Main\ModuleManager::isModuleInstalled('bitrix24'); $data = array(); $res = self::getList(array( 'select' => array( 'ID', 'CODE', 'SITE_ID_INDEX' => 'SITE.LANDING_ID_INDEX', 'SITE_PROTOCOL' => 'SITE.DOMAIN.PROTOCOL', 'SITE_DOMAIN' => 'SITE.DOMAIN.DOMAIN', 'SITE_CODE' => 'SITE.CODE' ), 'filter' => array( 'ID' => $id ) )); while ($row = $res->fetch()) { $data[$row['ID']] = $row['SITE_PROTOCOL'] . '://' . $row['SITE_DOMAIN'] . (!$bitrix24 ? '/pub/site' : '') . //@todo $row['SITE_CODE'] . ($row['ID'] == $row['SITE_ID_INDEX'] ? '' : $row['CODE']); } if (is_array($id)) { return $data; } elseif (!empty($data)) { return array_pop($data); } return false; } /** * View landing in public or edit mode. * @return void */ public function view() { $editMode = $this->mainInstance && $this->getEditMode(); // title if ($this->mainInstance) { Manager::getApplication()->setTitle( \htmlspecialcharsbx($this->title) ); } // assets if ($editMode) { $options = array( 'site_id' => $this->siteId, 'blocks' => Block::getRepository(), 'style' => Block::getStyle() ); Asset::getInstance()->addString( '<script type="text/javascript">' . 'BX.ready(function(){' . 'if (typeof BX.Landing.Main !== "undefined")' . '{' . 'BX.Landing.Main.createInstance(' . $this->id . ', ' . \CUtil::PhpToJSObject($options, false, false, true) . ');' . '}' . '});' . '</script>' ); } // content ob_start(); foreach ($this->blocks as $block) { $block->view($editMode); } if ($this->mainInstance) { $this->execHooks(); } $content = ob_get_contents(); ob_end_clean(); // and template if ($this->mainInstance) { $content = $this->applyTemplate($content); } // parse links between landings if (!$editMode) { echo $this->parseLocalUrl($content); } else { echo $content; } } /** * Apply template for this landing. * @param string $content Landing content. * @return string */ protected function applyTemplate($content) { if ($this->tplId) { $template = Template::getList(array( 'filter' => array( 'ID' => $this->tplId ) ))->fetch(); if ($template) { $replace = array( '#CONTENT#' => $content ); // if areas exist, get landings if ($template['AREA_COUNT'] > 0) { if ($this->tplType == 'site') { $tplRef = TemplateRef::getForSite($this->siteId); } else { $tplRef = TemplateRef::getForLanding($this->id); } foreach ($tplRef as $area => $lid) { ob_start(); $landing = self::createInstance($lid, array( 'is_area' => true )); if ($landing->exist()) { $landing->view(); } $replace['#AREA_' . $area . '#'] = ob_get_contents(); ob_end_clean(); } } $content = str_replace( array_keys($replace), array_values($replace), $template['CONTENT'] ); } } return $content; } /** * Parse between-landings url in landing content. * @param string $content Landing content. * @return string */ protected function parseLocalUrl($content) { $pattern = '/"#([?landing|block]+)([\d]+)"/is'; if (preg_match_all($pattern, $content, $matches)) { $urls = array( 'LANDING' => array(), 'BLOCK' => array() ); for ($i = 0, $c = count($matches[0]); $i < $c; $i++) { if (strtoupper($matches[1][$i]) == 'LANDING') { $urls['LANDING'][] = $matches[2][$i]; } else { $urls['BLOCK'][] = $matches[2][$i]; } } // get parent landings for blocks if (!empty($urls['BLOCK'])) { $urls['BLOCK'] = Block::getLandingIdByBlockId($urls['BLOCK']); $urls['LANDING'] = array_unique(array_merge( $urls['LANDING'], $urls['BLOCK'] )); } // get landing and blocks urls if (!empty($urls['LANDING'])) { $urls['LANDING'] = $this->getPublicUrl($urls['LANDING']); } if (!empty($urls['BLOCK'])) { foreach ($urls['BLOCK'] as $bid => $lid) { if (isset($urls['LANDING'][$lid])) { $urls['BLOCK'][$bid] = $urls['LANDING'][$lid] . '#' . Block::getAnchor($bid); } else { unset($urls['BLOCK'][$bid]); } } } // replace urls if (!empty($urls['LANDING'])) { $content = preg_replace_callback( $pattern, function($matches) use($urls) { $matches[1] = strtoupper($matches[1]); if (isset($urls[$matches[1]][$matches[2]])) { return $urls[$matches[1]][$matches[2]]; } }, $content ); } } return $content; } /** * Exec hooks for landing (site and landing). * @return void */ protected function execHooks() { $hooksExec = array(); foreach (Hook::getForSite($this->siteId) as $hook) { if ($hook->enabled()) { $hooksExec[$hook->getCode()] = $hook; } } foreach (Hook::getForLanding($this->id) as $hook) { if ($hook->enabled()) { $hooksExec[$hook->getCode()] = $hook; } } foreach ($hooksExec as $hook) { if ( $hook->enabledInEditMode() || !$this->getEditMode() ) { $hook->exec(); } } } /** * Exist or not landing in current instance. * @return boolean */ public function exist() { return $this->id > 0; } /** * Get id of current landing. * @return int */ public function getId() { return $this->id; } /** * Get title of current landing. * @return int */ public function getTitle() { return $this->title; } /** * Get site id of current landing. * @return int */ public function getSiteId() { return $this->siteId; } /** * Get all blocks of current landing. * @return array */ public function getBlocks() { return $this->blocks; } /** * Add new Block to the current landing. * @param \Bitrix\Landing\Block $block New block instance. * @return void */ public function addBlockToCollection(\Bitrix\Landing\Block $block) { if ($block->exist()) { $this->blocks[$block->getId()] = $block; } } /** * Get error collection * @return \Bitrix\Landing\Error */ public function getError() { return $this->error; } /** * Change modified user and date for current landing. * @return void */ public function touch() { self::update($this->id); } /** * Publication current landing. * @return void */ public function publication() { Block::publicationBlocks($this); self::update($this->id, array( 'PUBLIC' => 'Y' )); } /** * Add new block to the landing. * @param array $code Code of block. * @param array $data Data array of block. * @return int|false Id of new block or false on failure. */ public function addBlock($code, $data = array()) { $block = Block::createFromRepository($this, $code, $data); if ($block) { $this->touch(); $this->addBlockToCollection($block); return $block->getId(); } return false; } /** * Delete one block from current landing. * @param int $id Block id. * @return boolean */ public function deleteBlock($id) { if (isset($this->blocks[$id])) { $result = $this->blocks[$id]->unlink(); $this->error->copyError($this->blocks[$id]->getError()); if ($result) { unset($this->blocks[$id]); } return $result; } else { $this->error->addError( 'BLOCK_NOT_FOUND', Loc::getMessage('LANDING_BLOCK_NOT_FOUND') ); return false; } } /** * Transfer one block to another landing. * @param int $id Block id. * @param int $lid Landing id. * @return boolean */ protected function transferBlock($id, $lid) { if (isset($this->blocks[$id])) { $result = $this->blocks[$id]->changeLanding($lid); $this->error->copyError($this->blocks[$id]->getError()); if ($result) { unset($this->blocks[$id]); } return $result; } else { $this->error->addError( 'BLOCK_NOT_FOUND', Loc::getMessage('LANDING_BLOCK_NOT_FOUND') ); return false; } } /** * Resort current blocks. * @return void */ public function resortBlocks() { uasort($this->blocks, function($a, $b) { if ($a->getSort() == $b->getSort()) { return 0; } return ($a->getSort() < $b->getSort()) ? -1 : 1; }); $sort = 0; foreach ($this->blocks as $id => $block) { $block->saveSort($sort); $sort += 500; } } /** * Sort the block on the landing. * @param int $id Block id. * @param string $action Code: up or down. * @return boolean */ protected function sortBlock($id, $action) { if (isset($this->blocks[$id])) { $blocks = array_keys($this->blocks); for ($i = 0, $c = count($blocks); $i < $c; $i++) { if ($blocks[$i] == $id) { // change sort between two blocks $targetKey = $i + ($action == 'up' ? -1 : 1); if (isset($blocks[$targetKey])) { $thisBlock = $this->blocks[$id]; $targetBlock = $this->blocks[$blocks[$targetKey]]; $thisBlockSort = $thisBlock->getSort(); $targetBlockSort = $targetBlock->getSort(); $thisBlock->setSort($targetBlockSort); $targetBlock->setSort($thisBlockSort); $res1 = $thisBlock->save(); $res2 = $targetBlock->save(); $this->error->copyError($thisBlock->getError()); $this->error->copyError($targetBlock->getError()); if ($res1 || $res2) { $this->touch(); } return $res1 && $res2; } else { $this->error->addError( 'BLOCK_WRONG_SORT', Loc::getMessage('LANDING_BLOCK_WRONG_SORT') ); return false; } } } } else { $this->error->addError( 'BLOCK_NOT_FOUND', Loc::getMessage('LANDING_BLOCK_NOT_FOUND') ); } return false; } /** * Sort up the block on the landing. * @param int $id Block id. * @return boolean */ public function upBlock($id) { return $this->sortBlock($id, 'up'); } /** * Sort down the block on the landing. * @param int $id Block id. * @return boolean */ public function downBlock($id) { return $this->sortBlock($id, 'down'); } /** * Show/hide the block on the landing. * @param int $id Block id. * @param string $action Code: up or down. * @return boolean */ protected function activateBlock($id, $action) { if (isset($this->blocks[$id])) { $this->blocks[$id]->setActive($action == 'show'); $res = $this->blocks[$id]->save(); $this->error->copyError($this->blocks[$id]->getError()); if ($res) { $this->touch(); } return $res; } else { $this->error->addError( 'BLOCK_NOT_FOUND', Loc::getMessage('LANDING_BLOCK_NOT_FOUND') ); return false; } } /** * Activate the block on the landing. * @param int $id Block id. * @return boolean */ public function showBlock($id) { return $this->activateBlock($id, 'show'); } /** * Dectivate the block on the landing. * @param int $id Block id. * @return boolean */ public function hideBlock($id) { return $this->activateBlock($id, 'hide'); } /** * Copy/move other block to this landing. * @param int $block Block id. * @param array $params Params array. * @return boolean */ protected function changeParentOfBlock($block, $params) { $move = isset($params['MOVE']) && $params['MOVE']; $afterId = isset($params['AFTER_ID']) ? $params['AFTER_ID'] : 0; $fromLandingId = Block::getLandingIdByBlockId($block); $same = $this->id == $fromLandingId; if ($same) { $fromLanding = clone $this; } else { $fromLanding = self::createInstance($fromLandingId); } // if landing exist and available, get it blocks if ($this->exist() && $fromLanding->exist()) { $fromLandingBlocks = $fromLanding->getBlocks(); // if move, just change landing id if ($move) { $res = $fromLanding->transferBlock($block, $this->id); $this->error->copyError($fromLanding->getError()); if ($res) { $newBlock = $fromLandingBlocks[$block]; } } // else create copy else { $srcBlock = $fromLandingBlocks[$block]; $newBlock = Block::createFromRepository( $this, $srcBlock->getCode(), array( 'ACTIVE' => $srcBlock->isActive() ? 'Y' : 'N', 'SORT' => $srcBlock->getSort(), 'CONTENT' => $srcBlock->getContent() )); } // add block to collection and resort if (isset($newBlock) && $newBlock) { if ($afterId > 0 && isset($this->blocks[$afterId])) { $targetBlock = $this->blocks[$afterId]; } else { $targetBlock = array_pop(array_values($this->blocks)); } $newBlock->setSort($targetBlock->getSort() + 1); $this->addBlockToCollection($newBlock); $this->resortBlocks(); } //change dates if ($this->error->isEmpty()) { $this->touch(); if ($move && !$same) { $fromLanding->touch(); } } } $this->error->copyError($fromLanding->getError()); return $this->error->isEmpty(); } /** * Copy other block to this landing. * @param int $id Block id (from another landing). * @param int $afterId Put after this block id (in this landing). * @return boolean */ public function copyBlock($id, $afterId) { return $this->changeParentOfBlock($id, array( 'MOVE' => false, 'AFTER_ID' => $afterId )); } /** * Copy all blocks from another landing to this. * @param int $lid Landing id. * @return boolean */ public function copyAllBlocks($lid) { $landing = self::createInstance($lid); if ($this->exist() && $landing->exist()) { foreach ($landing->getBlocks() as $block) { $newBlock = Block::createFromRepository( $this, $block->getCode(), array( 'ACTIVE' => $block->isActive() ? 'Y' : 'N', 'SORT' => $block->getSort(), 'CONTENT' => $block->getContent() )); if (!$newBlock) { $this->error->copyError($landing->getError()); return false; } $landing->addBlockToCollection($newBlock); } $this->touch(); } $this->error->copyError($landing->getError()); return true; } /** * Move other block to this landing. * @param int $id Block id (from another landing). * @param int $afterId Put after this block id (in this landing). * @return boolean */ public function moveBlock($id, $afterId) { return $this->changeParentOfBlock($id, array( 'MOVE' => true, 'AFTER_ID' => $afterId )); } }