Your IP : 3.145.158.60


Current Path : /var/www/admin_ftp_12/data/www/httpdocs/bitrix/components/bitrix/main.interface.buttons/
Upload File :
Current File : /var/www/admin_ftp_12/data/www/httpdocs/bitrix/components/bitrix/main.interface.buttons/class.php

<?php
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Text;
use Bitrix\Main;
use Bitrix\Main\Web\Uri;

if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();


Loc::loadMessages(__FILE__);



class CMainInterfaceButtons
	extends CBitrixComponent
	implements Main\Engine\Contract\Controllerable, Main\Errorable
{
	/**
	 * First argument for CUserOptions::GetOption
	 * this is Options Category
	 * @var string
	 */
	protected $userOptionsCategory = "ui";
	/**

	 * @var int
	 */
	protected $maxCounterSize = 99;

	/**
	 * User options settings key
	 * @var string
	 */
	protected $userOptionsKey = "settings";


	/**
	 * Component items settings
	 * @var array
	 */
	protected $settings = [];

	/**
	 * User options expanded items
	 * @var array
	 */
	private $expandedLists = [];
	private $randomSequences = [];
	private $pinnedItems = [];
	/**
	 * More item class
	 * @var string
	 */
	protected $defaultMoreItemClass = "main-buttons-item-more-default";

	/**
	 * @var Main\ErrorCollection
	 */
	protected $errorCollection;

	protected const EDIT_MODE_PERSONAL = 'PERSONAL';
	protected const EDIT_MODE_COMMON = 'COMMON';
	protected const EDIT_MODE_DISABLE = 'DISABLE';

	public function onPrepareComponentParams($arParams)
	{
		$this->errorCollection = new Main\ErrorCollection();

		if (empty($arParams["ID"]))
		{
			$this->errorCollection->setError(new Main\Error(Loc::getMessage("MIB_ID_NOT_SET"), "unknown"));
		}
		else
		{
			$arParams["ID"] = $this->prepareContainerId($arParams["ID"]);
			$arParams["EDIT_MODE"] = $this->prepareSaveMode(
				array_key_exists("EDIT_MODE", $arParams) ? $arParams["EDIT_MODE"] : null,
				$arParams["DISABLE_SETTINGS"] ?? false
			);
			$arParams["DISABLE_SETTINGS"] = $this->prepareDisableSettings($arParams["EDIT_MODE"]);

			$arParams["THEME"] =
				isset($arParams["THEME"]) && is_string($arParams["THEME"]) ? $arParams["THEME"] : "default"
			;

			$arParams["THEME_ID"] = " --" . $arParams["THEME"];
		}

		return $arParams;
	}

	protected function setStyles()
	{
		global $APPLICATION;

		if (isset($this->arParams["INCLUDE_CSS_FILE"]) && !empty($this->arParams["INCLUDE_CSS_FILE"]))
		{
			$APPLICATION->SetAdditionalCSS($this->arParams["INCLUDE_CSS_FILE"]);
		}
	}

	/**
	 * Prepares params
	 * @return object $this
	 */
	protected function prepareParams()
	{
		$this->arParams["CLASS_ITEM_ACTIVE"] = $this->prepareItemClass($this->arParams["CLASS_ITEM_ACTIVE"] ?? '');
		$this->arParams["CLASS_ITEM"] = $this->prepareItemClass($this->arParams["CLASS_ITEM"] ?? '');
		$this->arParams["CLASS_ITEM_LINK"] = $this->prepareItemClass($this->arParams["CLASS_ITEM_LINK"] ?? '');
		$this->arParams["CLASS_ITEM_ICON"] = $this->prepareItemClass($this->arParams["CLASS_ITEM_ICON"] ?? '');
		$this->arParams["CLASS_ITEM_TEXT"] = $this->prepareItemClass($this->arParams["CLASS_ITEM_TEXT"] ?? '');
		$this->arParams["CLASS_ITEM_COUNTER"] = $this->prepareItemClass($this->arParams["CLASS_ITEM_COUNTER"] ?? '');
		$this->arParams["ITEMS"] = $this->prepareItems($this->arParams["ITEMS"]);
		$this->arParams["MORE_BUTTON"] = $this->prepareMoreItem($this->arParams["MORE_BUTTON"] ?? []);
		$this->arParams["MAX_ITEM_LENGTH"] = $this->prepareMaxItemLength($this->arParams['MAX_ITEM_LENGTH'] ?? 0);

		return $this;
	}

	protected function prepareDisableSettings(string $mode): bool
	{
		return $mode === self::EDIT_MODE_DISABLE;
	}

	protected function prepareMaxItemLength($length): int
	{
		$length = (int)$length;
		if ($length > 6)
		{
			return $length;
		}

		if ($this->arParams['THEME'] === 'flat')
		{
			return 30;
		}
		else if ($this->arParams['THEME'] === 'small' || $this->arParams['THEME'] === 'tiny')
		{
			return 20;
		}

		return 40;
	}

	protected function prepareSaveMode($mode = null, $disableSettings = false): string
	{
		$result = self::EDIT_MODE_PERSONAL;
		if (is_string($mode))
		{
			$mode = mb_strtoupper($mode);
			if (in_array($mode, [self::EDIT_MODE_COMMON, self::EDIT_MODE_DISABLE]))
			{
				$result = $mode;
			}
		}
		else if (is_bool($mode))
		{
			$result = $mode === false ? self::EDIT_MODE_DISABLE : self::EDIT_MODE_PERSONAL;
		}
		else if ($disableSettings === true)
		{
			$result = self::EDIT_MODE_DISABLE;
		}
		return $result;
	}

	/**
	 * Gets user options as is
	 * @return array|bool
	 */
	protected function getUserOptions()
	{
		return CUserOptions::GetOption($this->userOptionsCategory, $this->arParams["ID"]);
	}

	/**
	 * Prepares container id
	 * @param string $id
	 * @return string Container id
	 */
	protected function prepareContainerId($id)
	{
		$id = $this->safeString($id);
		$id = preg_replace("/[^a-z0-9_-]/i", "", $id);
		$id = mb_strtolower($id);

		return $id;
	}

	protected function getRandomItemId($seed): string
	{
		if (!isset($this->randomSequences[$seed]))
		{
			$this->randomSequences[$seed] = new \Bitrix\Main\Type\RandomSequence($seed);
		}

		return $this->randomSequences[$seed]->randString(12);
	}

	/**
	 * Prepares user options
	 * @param array $userOptions
	 * @param $userOptionsKey
	 * @return array User options
	 */
	protected function prepareUserOptions($userOptions, $userOptionsKey)
	{
		$userOptionsSettings = array();

		if (is_array($userOptions) &&
			isset($userOptions[$userOptionsKey]) &&
			!empty($userOptions[$userOptionsKey]))
		{
			$userOptionsSettings = json_decode($userOptions[$userOptionsKey], true);
		}

		return $userOptionsSettings;
	}

	/**
	 * Prepares settings
	 */
	protected function prepareSettings()
	{
		$userOptionsRaw = $this->getUserOptions();
		$settings = $this->prepareUserOptions($userOptionsRaw, $this->userOptionsKey);
		$this->settings = $settings;

		$this->expandedLists = $this->prepareUserOptions($userOptionsRaw, 'expanded_lists');
	}

	/**
	 * Gets item settings by item id
	 * @param  string $id
	 * @return array
	 */
	protected function getItemSettingsByItemId($id)
	{
		$result = array();

		if (!empty($id) && is_array($this->settings) && !empty($this->settings[$id]))
		{
			$result = $this->settings[$id];
		}

		return $result;
	}

	/**
	 * Prepares item text value
	 * @param  string $text Text string
	 * @return string
	 */
	protected function prepareItemText($text)
	{
		if (!is_string($text) && !is_numeric($text))
		{
			return '';
		}

		return trim((string)$text);
	}

	/**
	 * Prepares item html
	 * @param  string $html
	 * @return string
	 */
	protected function prepareItemHtml($html)
	{
		return Text\Converter::getHtmlConverter()->decode($html);
	}

	/**
	 * Prepares item url
	 * @param  string $url
	 * @return string
	 */
	protected function prepareItemUrl($url)
	{
		return preg_match('#^(?:/|https?://)#', $url) ? (string)$url: '';
	}

	/**
	 * Prepares item class
	 * @param  string $class
	 * @return string
	 */
	protected function prepareItemClass($class)
	{
		return $this->safeString($class);
	}

	/**
	 * Prepares item id
	 * @param  string $id
	 * @return string
	 */
	protected function prepareItemId($id)
	{
		$result = "";

		if (!empty($id))
		{
			$result = $this->safeString($id);
			$result = str_replace('-', '_', $result);
			$result = preg_replace("/[^a-z0-9_\/]/i", "", $result);
			$result = mb_strtolower($result);
		}

		return $result;
	}

	/**
	 * Prepares item counter value
	 * @param  integer $counter
	 * @return integer|boolean
	 */
	protected function prepareItemCounter($counter)
	{
		$result = false;

		if (is_float($counter) || is_int($counter))
		{
			$result = $counter;
		}

		return $result;
	}

	/**
	 * Prepares item is locked value
	 * @param  boolean $isLocked
	 * @return boolean json_encode'd
	 */
	protected function prepareItemIsLocked($item)
	{
		return isset($item['IS_LOCKED']) && $item['IS_LOCKED'] === true;
	}

	protected function prepareItemIsPinned($item)
	{
		$result = false;
		$settings = $this->getItemSettingsByItemId($item['ID']);

		if (is_array($settings) && isset($settings["isPinned"]) && is_bool($settings["isPinned"]))
		{
			$result = $settings["isPinned"];
		}

		return $result;
	}

	/**
	 * Prepares item is disabled value
	 * @param  boolean $isDisabled
	 * @param  string $id
	 * @return boolean json_encode'd
	 */
	protected function prepareItemIsDisabled($isDisabled, $id)
	{
		$result = false;
		$settings = $this->getItemSettingsByItemId($id);

		if (!empty($isDisabled) && is_bool($isDisabled))
		{
			$result = $isDisabled;
		}

		if (is_array($settings) && isset($settings["isDisabled"]) && is_bool($settings["isDisabled"]))
		{
			$result = $settings["isDisabled"];
		}

		$result = json_encode($result);


		return $result;
	}


	/**
	 * Prepares item sublink array
	 * @param  array $sublink
	 * @return array|boolean return false if sublink not set
	 */
	protected function prepareItemSublink($sublink)
	{
		$result = false;

		if (!empty($sublink) && is_array($sublink))
		{
			if (!empty($sublink["URL"]))
			{
				$sublink["URL"] = $this->prepareItemUrl($sublink["URL"]);
			}
			else
			{
				$sublink["URL"] = '';
			}

			if (!empty($sublink["CLASS"]))
			{
				$sublink["CLASS"] = $this->prepareItemClass($sublink["CLASS"]);
			}

			$result = $sublink;
		}

		return $result;
	}

	/**
	 * Prepares item super title array
	 * @param  array $item
	 * @return array|boolean return false if super title not set
	 */
	protected function prepareItemSuperTitle($item)
	{
		if (isset($item['SUPER_TITLE']) && is_array($item['SUPER_TITLE']) && isset($item['SUPER_TITLE']['TEXT']))
		{
			return [
				'TEXT' => $this->safeString($item['SUPER_TITLE']['TEXT']),
				'CLASS' => isset($item['SUPER_TITLE']['CLASS']) ? $this->safeString($item['SUPER_TITLE']['CLASS']) : '',
				'COLOR' => isset($item['SUPER_TITLE']['COLOR']) ? $this->safeString($item['SUPER_TITLE']['COLOR']) : '',
			];
		}
		else if (isset($item['IS_NEW']) && $item['IS_NEW'] === true)
		{
			return [
				'TEXT' => Loc::getMessage('MIB_NEW_ITEM_LABEL'),
				'CLASS' => '',
				'COLOR' => '',
			];
		}

		return false;
	}

	/**
	 * Prepares item sort index value
	 *
	 * @param  string $id
	 * @param  integer $defaultSort
	 * @return integer Sort index
	 */
	protected function prepareItemSort($id, $defaultSort)
	{
		$result = $defaultSort;
		$settings = $this->getItemSettingsByItemId($id);

		if (!empty($settings) && is_array($settings))
		{
			if (is_int($settings["sort"]))
			{
				$result = $settings["sort"];
			}
		}

		return $result;
	}

	/**
	 * Prepares item is active value
	 * @param array $item
	 * @return boolean
	 */
	protected function prepareItemIsActive(&$item)
	{
		$result = false;

		if (!isset($item["IS_ACTIVE"]))
		{
			$requestUri = $this->request->getRequestUri();

			if ($requestUri == $item["URL"])
			{
				$result = true;
			}

			if (!$result && isset($item["ADDITIONAL_URL"]) && is_array($item["ADDITIONAL_URL"]))
			{
				$result = array_search($requestUri, $item["ADDITIONAL_URL"]);
				$result = !is_null($result);
			}
		}
		else
		{
			$result = $item['IS_ACTIVE'] === true || $item['IS_ACTIVE'] === 'true' || $item['IS_ACTIVE'] === 'Y';
		}

		return $result;
	}

	protected function prepareItemIsPassive($item)
	{
		return isset($item['IS_PASSIVE']) && $item['IS_PASSIVE'] === true;
	}

	/**
	 * Prepares item
	 *
	 * @param array $item
	 * @param integer $defaultSort
	 * @return array Prepared $item
	 */
	protected function prepareItem(array $item, int $defaultSort = 0, $rootItem = null)
	{
		$item["TEXT"] = $this->prepareItemText($item["TEXT"] ?? '');
		$item["TITLE"] = $this->prepareItemText($item["TITLE"] ?? '');
		$item["HTML"] = $this->prepareItemHtml($item["HTML"] ?? '');
		$item["URL"] = $this->prepareItemUrl($item["URL"] ?? '');
		$item["CLASS"] = $this->prepareItemClass($item["CLASS"] ?? '');
		$item["CLASS_SUBMENU_ITEM"] = $this->prepareItemClass($item["CLASS_SUBMENU_ITEM"] ?? '');
		$item["ON_CLICK"] = $item["ON_CLICK"] ?? '';

		$itemId = $this->prepareItemId($item['ID'] ?? '');
		if (empty($itemId))
		{
			if (!empty($item['URL']))
			{
				$itemId = crc32($item['URL']);
			}
			else if (!empty($item["ON_CLICK"]))
			{
				$itemId = crc32($item['ON_CLICK']);
			}
			else
			{
				$seed = 'mib_' . $this->arParams['ID'] . '_' . ($rootItem !== null ? $rootItem['ID'] : '');
				$itemId = $this->getRandomItemId($seed);
			}

			$itemId = "mib_$itemId";
		}

		if ($rootItem === null)
		{
			$globalId = join('_', [$this->arParams['ID'], $itemId]);
		}
		else
		{
			$globalId = $rootItem['ID'] . ':' . $itemId;
		}

		$item["DATA_ID"] = $itemId;
		$item["ID"] = $globalId;

		$item["MAX_COUNTER_SIZE"] = array_key_exists('MAX_COUNTER_SIZE', $item)
			? $item["MAX_COUNTER_SIZE"]
			: $this->maxCounterSize
		;

		if (isset($item["COUNTER"]))
		{
			$counter = $this->prepareItemCounter($item["COUNTER"]);

			if ($counter !== false && !empty($counter))
			{
				$item["COUNTER"] = $counter;
			}
			else
			{
				unset($item["COUNTER"]);
			}
		}

		if (isset($item["COUNTER_ID"]))
		{
			$counterId = $this->prepareItemCounterId($item);

			if (!empty($counterId) && is_string($counterId))
			{
				$item["COUNTER_ID"] = $counterId;
			}
			else
			{
				unset($item["COUNTER_ID"]);
			}
		}

		if ($rootItem !== null)
		{
			$item["IS_PINNED"] = $this->prepareItemIsPinned($item);
			if ($item["IS_PINNED"])
			{
				$this->pinnedItems[] = &$item;
			}
		}

		$item["IS_LOCKED"] = $this->prepareItemIsLocked($item);
		$item["IS_DISABLED"] = $this->prepareItemIsDisabled($item["IS_DISABLED"] ?? false, $item["ID"]);
		$item["SUB_LINK"] = $this->prepareItemSublink($item["SUB_LINK"] ?? '');
		$item["SUPER_TITLE"] = $this->prepareItemSuperTitle($item);
		$item["SORT"] = $this->prepareItemSort($item["ID"], $defaultSort);
		$item["IS_ACTIVE"] = $this->prepareItemIsActive($item);
		$item["IS_PASSIVE"] = $this->prepareItemIsPassive($item);

		$item["HAS_MENU"] = isset($item['ITEMS']) && is_array($item['ITEMS']) && !empty($item['ITEMS']);
		if ($item["HAS_MENU"])
		{
			$item["URL"] = '';
			$item["ON_CLICK"] = '';
			$item["IS_ACTIVE"] = false;

			$this->prepareSubItems($item, $item['ITEMS']);
		}

		return $item;
	}

	protected function prepareSubItems(&$item, array &$subItems)
	{
		$hasVisibleSubItems = false;
		for ($i = 0, $count = count($subItems); $i < $count; $i++)
		{
			$subItems[$i] = $this->prepareItem($subItems[$i], $i, $item);
			$isPinned = isset($subItems[$i]['IS_PINNED']) && $subItems[$i]['IS_PINNED'] === true;
			$isDelimiter = isset($subItems[$i]['IS_DELIMITER']) && $subItems[$i]['IS_DELIMITER'] === true;

			if (isset($subItems[$i]['COUNTER']) && !$isPinned && !$isDelimiter)
			{
				if (!isset($item['COUNTER']))
				{
					$item['COUNTER'] = 0;
				}

				$item['COUNTER'] += $subItems[$i]['COUNTER'];
			}

			if ($subItems[$i]['IS_ACTIVE'] && !$isPinned && !$isDelimiter)
			{
				$item['IS_ACTIVE'] = true;
			}

			if (isset($subItems[$i]['COUNTER']) || isset($subItems[$i]['COUNTER_ID']))
			{
				$item['HAS_COUNTERS'] = true;
				if (!isset($item['COUNTER_ID']))
				{
					$item['COUNTER_ID'] = $item['ID'] . '_counter';
				}
			}

			if (!$isPinned && !$isDelimiter)
			{
				$hasVisibleSubItems = true;
			}
		}

		if (!$hasVisibleSubItems)
		{
			$item['IS_DISBANDED'] = true;
		}
	}

	protected function prepareItemCounterId($item)
	{
		$id = '';

		if (isset($item["COUNTER_ID"]) && is_string($item["COUNTER_ID"]) && !empty($item["COUNTER_ID"]))
		{
			$id = $item["COUNTER_ID"];
		}

		return $id;
	}

	protected function prepareMoreItem($item)
	{
		$html = $this->prepareItemHtml($item["HTML"] ?? '');
		$class = $this->prepareItemClass($item["CLASS"] ?? '');

		$item["TEXT"] = Loc::getMessage("MIB_DEFAULT_MORE_ITEM_TEXT");
		$item["HTML"] = $html;
		$item["CLASS"] = !empty($class) ? $class : $this->defaultMoreItemClass;

		return $item;
	}

	protected function filterItems()
	{
		$items = array_filter($this->arParams["ITEMS"], function($item)
		{
			return is_array($item);
		});

		$itemsCount = count($items);

		if (!$itemsCount)
		{
			ShowError(Loc::getMessage("MIB_ITEMS_NOT_FOUND"));
			return 0;
		}

		$this->arParams["ITEMS"] = $items;

		return $itemsCount;
	}

	protected function prepareItems($items = array())
	{
		foreach ($items as $key => $item)
		{
			$items[$key] = $this->prepareItem($item, $key);
		}

		foreach ($this->pinnedItems as $item)
		{
			$items[] = unserialize(serialize($item), ['allowed_classes' => false]);
		}

		$items = $this->sortBySortIndex($items);

		foreach ($items as $key => $item)
		{
			$childItems = array_filter($items, function($currentItem) use ($item) {
				return isset($currentItem['PARENT_ITEM_ID']) && $currentItem['PARENT_ITEM_ID'] === $item['DATA_ID'];
			});

			$items[$key]['HAS_CHILD'] = is_array($childItems) && !empty($childItems);

			if ($items[$key]['HAS_CHILD'] === true)
			{
				if (array_key_exists($item['DATA_ID'], $this->expandedLists)
					&& $this->expandedLists[$item['DATA_ID']] == 'Y')
				{
					$items[$key]['EXPANDED'] = true;
				}
				else
				{
					$items[$key]['EXPANDED'] = false;
				}

				$items[$key]['CHILD_ITEMS'] = [];
				foreach ($childItems as $currentItem)
				{
					$items[$key]['CHILD_ITEMS'][] = $currentItem;
				}
			}
		}

		return $items;
	}

	/**
	 * Sorts array bi sort index
	 * @param  array $array
	 * @return array Sorted array
	 */
	protected function sortBySortIndex($array = array())
	{
		usort($array, function($a, $b)
		{
			return $a["SORT"] - $b["SORT"];
		});

		return $array;
	}

	protected function safeString($string)
	{
		if (!is_string($string) && !is_numeric($string) && !($string instanceof Uri))
		{
			return '';
		}

		$string = trim((string)$string);
		$string = Text\Converter::getHtmlConverter()->encode($string);

		return (String)$string;
	}

	/**
	 * Prepares arResult
	 */
	protected function prepareResult()
	{
		$this->arResult = $this->arParams;
	}

	public function executeComponent()
	{
		if ($this->errorCollection->isEmpty())
		{
			if ($this->filterItems())
			{
				$this->prepareSettings();
				$this->prepareParams();
				$this->prepareResult();
				$this->setStyles();
				$this->includeComponentTemplate();
			}
		}
	}

	public function configureActions()
	{
		return [];
	}

	public function getErrors()
	{
		return $this->errorCollection->toArray();
	}

	public function getErrorByCode($code)
	{
		return $this->errorCollection->getErrorByCode($code);
	}

	protected function listKeysSignedParameters()
	{
		return [
			'ID',
			'EDIT_MODE',
			'DISABLE_SETTINGS',
			'THEME',
		];
	}

	public function saveAction(array $options)
	{
		if ($this->errorCollection->isEmpty()
			&& ($this->arParams['EDIT_MODE'] !== self::EDIT_MODE_DISABLE)
		)
		{
			$value = array_merge(
				CUserOptions::GetOption($this->userOptionsCategory, $this->arParams['ID'], []),
				$options
			);
			CUserOptions::SetOption(
				$this->userOptionsCategory,
				$this->arParams['ID'],
				$value,
				$this->arParams['EDIT_MODE'] === self::EDIT_MODE_COMMON);
		}
	}
}