Your IP : 18.222.112.142


Current Path : /home/bitrix/ext_www/klimatlend.ua/bitrix/modules/landing/lib/
Upload File :
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
		));
	}
}