Your IP : 3.144.162.107
<?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
));
}
}