PageRenderTime 25ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/bitrix/modules/main/lib/component/baseufcomponent.php

https://gitlab.com/neuser/bitrix-core
PHP | 541 lines | 337 code | 67 blank | 137 comment | 21 complexity | d4d836f2bb7c19f9dddcd1c23f5a09ed MD5 | raw file
  1. <?php
  2. namespace Bitrix\Main\Component;
  3. use Bitrix\Main\Context;
  4. use Bitrix\Main\UserField\HtmlBuilder;
  5. use CBitrixComponent;
  6. use CBitrixComponentTemplate;
  7. use ReflectionClass;
  8. use Bitrix\Main\Localization\Loc;
  9. /**
  10. * Class BaseUfComponent
  11. * @package Bitrix\Main\Component
  12. */
  13. abstract class BaseUfComponent extends CBitrixComponent
  14. {
  15. public const
  16. MODE_DEFAULT = '.default',
  17. TEMPLATE_PAGE_DEFAULT = '.default',
  18. TEMPLATE_NAME_DEFAULT = '.default';
  19. /*
  20. * List of available media types
  21. * MediaType === Template folder by default, may be overriding in child class
  22. */
  23. public const
  24. MEDIA_TYPE_DEFAULT = '.default',
  25. MEDIA_TYPE_MOBILE = 'mobile';
  26. /**
  27. * @var array $htmlBuilder
  28. */
  29. protected static
  30. $htmlBuilder = [];
  31. /**
  32. * @var CBitrixComponentTemplate $componentTemplate
  33. * @var array $userField
  34. * @var array $additionalParameters
  35. */
  36. protected
  37. $componentTemplate,
  38. $userField = [],
  39. $additionalParameters = [];
  40. /**
  41. * @var string $mediaType
  42. * @var string $mode
  43. */
  44. private
  45. $mediaType = '',
  46. $mode = '',
  47. $availableModes = [];
  48. public function __construct($component = null)
  49. {
  50. parent::__construct($component);
  51. $this->componentTemplate = new CBitrixComponentTemplate();
  52. }
  53. final public function executeComponent()
  54. {
  55. $this->initResult();
  56. $this->prepareResult();
  57. $this->initAvailableModes();
  58. $this->initMode();
  59. $this->initMediaType();
  60. $templateName = $this->resolveTemplateName();
  61. $templatePage = $this->resolveTemplatePage();
  62. $this->setTemplateName($templateName);
  63. if($templatePage && !$this->isExistTemplatePage($templatePage))
  64. {
  65. // changing to default templatePage if file with $templatePage name not exist ...
  66. if($templatePage !== static::TEMPLATE_PAGE_DEFAULT)
  67. {
  68. $templatePage = static::TEMPLATE_PAGE_DEFAULT;
  69. }
  70. // ... or setting default templateName if $templatePage name
  71. // is equal to TEMPLATE_PAGE_DEFAULT and not exist in current templateName folder
  72. else
  73. {
  74. $this->setTemplateName(static::TEMPLATE_NAME_DEFAULT);
  75. }
  76. }
  77. if($templatePage)
  78. {
  79. $this->includeComponentTemplate($templatePage);
  80. }
  81. else
  82. {
  83. $this->__showError(str_replace(
  84. ['#NAME#', '#PAGE#'],
  85. [$this->getMode(), $this->getMediaType()],
  86. "Cannot find '#NAME#' template with page '#PAGE#'"
  87. ));
  88. }
  89. }
  90. final protected function initResult(): void
  91. {
  92. $this->setUserField($this->arParams['~userField']);
  93. $this->setAdditionalParameters($this->arParams['additionalParameters']);
  94. $this->setParentComponent($this->getAdditionalParameter('parentComponent'));
  95. $this->arResult['additionalParameters'] = $this->getAdditionalParameters();
  96. $this->arResult['userField'] = $this->getUserField();
  97. $this->arResult['fieldName'] = $this->getFieldName();
  98. $this->arResult['value'] = $this->getFieldValue();
  99. }
  100. /**
  101. * @return array|bool
  102. */
  103. public function getUserField()
  104. {
  105. return $this->userField;
  106. }
  107. /**
  108. * @param array|bool $userField
  109. */
  110. public function setUserField($userField): void
  111. {
  112. $this->userField = $userField;
  113. }
  114. /**
  115. * @param string $key
  116. * @return mixed|null
  117. */
  118. public function getAdditionalParameter(string $key)
  119. {
  120. return ($this->additionalParameters[$key] ?? null);
  121. }
  122. /**
  123. * @return array
  124. */
  125. public function getAdditionalParameters(): array
  126. {
  127. return $this->additionalParameters;
  128. }
  129. /**
  130. * @param array $additionalParameters
  131. */
  132. public function setAdditionalParameters(?array $additionalParameters): void
  133. {
  134. $this->additionalParameters = $additionalParameters;
  135. }
  136. /**
  137. * @return CBitrixComponent|null
  138. */
  139. public function getParentComponent(): ?CBitrixComponent
  140. {
  141. return $this->__parent;
  142. }
  143. /**
  144. * @param CBitrixComponent|null $_parent
  145. */
  146. public function setParentComponent(?CBitrixComponent $_parent): void
  147. {
  148. $this->__parent = $_parent;
  149. }
  150. /**
  151. * You can override this method in a child class
  152. * to add the personal custom functionality of the child class
  153. */
  154. protected function prepareResult(): void
  155. {
  156. }
  157. protected function initAvailableModes(): void
  158. {
  159. if(!empty($this->additionalParameters['mode']))
  160. {
  161. $modes = (is_array($this->additionalParameters['mode']) ?
  162. $this->additionalParameters['mode'] : [$this->additionalParameters['mode']]
  163. );
  164. }
  165. else
  166. {
  167. $modes = [static::MODE_DEFAULT];
  168. }
  169. $this->setAvailableModes($modes);
  170. }
  171. /**
  172. * @return array
  173. */
  174. public function getAvailableModes(): array
  175. {
  176. return $this->availableModes;
  177. }
  178. /**
  179. * @param array $availableModes
  180. */
  181. public function setAvailableModes(array $availableModes): void
  182. {
  183. $this->availableModes = $availableModes;
  184. }
  185. protected function initMode(): void
  186. {
  187. $availableModes = $this->getAvailableModes();
  188. $mode = array_shift($availableModes);
  189. $this->setMode($mode);
  190. }
  191. /**
  192. * @param string $mode
  193. */
  194. protected function setMode(string $mode): void
  195. {
  196. $this->mode = $mode;
  197. }
  198. protected function initMediaType(): void
  199. {
  200. $mediaType = ($this->additionalParameters['mediaType'] ?: static::MEDIA_TYPE_DEFAULT);
  201. $this->setMediaType($mediaType);
  202. }
  203. /**
  204. * @param string $mediaType
  205. */
  206. protected function setMediaType(string $mediaType): void
  207. {
  208. $this->mediaType = $mediaType;
  209. }
  210. /**
  211. * Resolving a mode name to template name.
  212. * By default mode === templateFolderName, can be otherwise in child class
  213. * @return string
  214. */
  215. protected function resolveTemplateName(): string
  216. {
  217. return ($this->getAvailableTemplateFolder() ?? static::TEMPLATE_NAME_DEFAULT);
  218. }
  219. /**
  220. * @return string
  221. */
  222. final public function getMode(): string
  223. {
  224. return $this->mode;
  225. }
  226. /**
  227. * @return string
  228. */
  229. protected function getTemplateNameFromMode(): string
  230. {
  231. return ($this->getMode() ?: static::TEMPLATE_NAME_DEFAULT);
  232. }
  233. /**
  234. * Return templatePage name from mediaType
  235. * or null if mediaType incorrect
  236. * @return null|string
  237. */
  238. final protected function resolveTemplatePage(): ?string
  239. {
  240. return ($this->isPossibleMediaType() ?
  241. $this->getTemplatePageFromMediaType() : null
  242. );
  243. }
  244. /**
  245. * @param string|null $templatePage
  246. * @return bool
  247. */
  248. final protected function isExistTemplatePage(?string $templatePage = ''): bool
  249. {
  250. if(empty($templatePage))
  251. {
  252. $templatePage = $this->getTemplatePage();
  253. }
  254. return $this->hasTemplatePage($templatePage);
  255. }
  256. /**
  257. * Checking if mediaType specified when calling the component is valid
  258. * @return bool
  259. */
  260. final protected function isPossibleMediaType(): bool
  261. {
  262. static $mediaTypes = null;
  263. if($mediaTypes === null)
  264. {
  265. $mediaTypes = $this->getMediaTypes();
  266. }
  267. return in_array($this->getMediaType(), $mediaTypes, true);
  268. }
  269. /**
  270. * @return string
  271. */
  272. protected function getTemplatePageFromMediaType(): string
  273. {
  274. return $this->getMediaType() ?: static::MEDIA_TYPE_DEFAULT;
  275. }
  276. /**
  277. * Return all mediaTypes
  278. * @return array
  279. */
  280. final protected function getMediaTypes(): array
  281. {
  282. $reflection = new ReflectionClass(__CLASS__);
  283. $constants = $reflection->getConstants();
  284. $result = [];
  285. foreach($constants as $name => $value)
  286. {
  287. if(mb_strpos($name, 'MEDIA_TYPE_') === 0)
  288. {
  289. $result[$name] = $value;
  290. }
  291. }
  292. return $result;
  293. }
  294. /**
  295. * @return string
  296. */
  297. protected function getMediaType(): string
  298. {
  299. return $this->mediaType;
  300. }
  301. /**
  302. * @return bool
  303. */
  304. final protected function hasTemplateFolder(): bool
  305. {
  306. static $checkedTemplateFolders = [];
  307. if(
  308. !array_key_exists($this->getMode(), $checkedTemplateFolders)
  309. ||
  310. $checkedTemplateFolders[$this->getMode()] === null
  311. )
  312. {
  313. $this->setTemplateName($this->getTemplateNameFromMode());
  314. $this->componentTemplate->Init($this);
  315. $checkedTemplateFolders[$this->getMode()] = $this->componentTemplate->hasTemplate();
  316. }
  317. return $checkedTemplateFolders[$this->getMode()];
  318. }
  319. /**
  320. * Returning first correct template folder from array of availables modes
  321. * @return null|string
  322. */
  323. final protected function getAvailableTemplateFolder(): ?string
  324. {
  325. $availableMethodsKey = $this->generateAvailableModesHash();
  326. static $availableMode = [];
  327. if(
  328. !array_key_exists($availableMethodsKey, $availableMode)
  329. ||
  330. $availableMode[$availableMethodsKey] === null
  331. )
  332. {
  333. foreach($this->getAvailableModes() as $mode)
  334. {
  335. $this->setMode($mode);
  336. if($this->hasTemplateFolder())
  337. {
  338. $availableMode[$availableMethodsKey] = $this->getMode();
  339. break;
  340. }
  341. }
  342. }
  343. return $availableMode[$availableMethodsKey];
  344. }
  345. /**
  346. * @return string
  347. */
  348. final protected function generateAvailableModesHash(): string
  349. {
  350. return md5(static::getUserTypeId() . json_encode($this->getAvailableModes()));
  351. }
  352. /**
  353. * @param string $templatePage
  354. * @return bool
  355. */
  356. final protected function hasTemplatePage(string $templatePage): bool
  357. {
  358. static $isCheckedTemplatePage = null;
  359. if($isCheckedTemplatePage === null)
  360. {
  361. $this->componentTemplate->Init($this, $this->getTemplateNameFromMode());
  362. $isCheckedTemplatePage = $this->componentTemplate->hasTemplatePage($templatePage);
  363. }
  364. return $isCheckedTemplatePage;
  365. }
  366. /**
  367. * @return string
  368. */
  369. protected function getFieldName(): string
  370. {
  371. if(
  372. (!$this->userField || empty($this->userField['FIELD_NAME']))
  373. &&
  374. (!$this->additionalParameters || !isset($this->additionalParameters['NAME']))
  375. )
  376. {
  377. return '';
  378. }
  379. $fieldName = $this->additionalParameters['NAME'] ?? $this->userField['FIELD_NAME'];
  380. if($this->userField['MULTIPLE'] === 'Y' && !mb_substr_count($fieldName, '[]'))
  381. {
  382. $fieldName .= '[]';
  383. }
  384. return $fieldName;
  385. }
  386. /**
  387. * @return array
  388. */
  389. protected function getFieldValue(): array
  390. {
  391. if(
  392. !$this->additionalParameters['bVarsFromForm']
  393. &&
  394. !isset($this->additionalParameters['VALUE'])
  395. )
  396. {
  397. $value = (
  398. isset($this->userField['ENTITY_VALUE_ID'])
  399. &&
  400. $this->userField['ENTITY_VALUE_ID'] <= 0
  401. ?
  402. $this->userField['SETTINGS']['DEFAULT_VALUE'] : $this->userField['VALUE']
  403. );
  404. }
  405. elseif(isset($this->additionalParameters['VALUE']))
  406. {
  407. $value = $this->additionalParameters['VALUE'];
  408. }
  409. else
  410. {
  411. $value = Context::getCurrent()->getRequest()->get($this->userField['FIELD_NAME']);
  412. }
  413. return self::normalizeFieldValue($value);
  414. }
  415. /**
  416. * @param mixed $value
  417. * @return array
  418. */
  419. final protected static function normalizeFieldValue($value): array
  420. {
  421. if(!is_array($value))
  422. {
  423. $value = array($value);
  424. }
  425. if(empty($value))
  426. {
  427. $value = array(null);
  428. }
  429. return $value;
  430. }
  431. /**
  432. * @return HtmlBuilder
  433. */
  434. final public function getHtmlBuilder()
  435. {
  436. if(!array_key_exists(static::getUserTypeId(), self::$htmlBuilder))
  437. {
  438. $this->setHtmlBuilder(new HtmlBuilder(static::getUserTypeId()));
  439. }
  440. return self::$htmlBuilder[static::getUserTypeId()];
  441. }
  442. /**
  443. * @param HtmlBuilder $htmlBuilder
  444. */
  445. final public function setHtmlBuilder(HtmlBuilder $htmlBuilder): void
  446. {
  447. self::$htmlBuilder[static::getUserTypeId()] = $htmlBuilder;
  448. }
  449. /**
  450. * @return bool
  451. */
  452. final public function isDefaultMode(): bool
  453. {
  454. return ($this->getMediaType() === static::MEDIA_TYPE_DEFAULT);
  455. }
  456. /**
  457. * @return bool
  458. */
  459. final public function isMobileMode(): bool
  460. {
  461. return ($this->getMediaType() === static::MEDIA_TYPE_MOBILE);
  462. }
  463. /**
  464. * @return bool
  465. */
  466. final public function isAjaxRequest(): bool
  467. {
  468. return Context::getCurrent()->getRequest()->isAjaxRequest();
  469. }
  470. /**
  471. * @return string
  472. */
  473. abstract protected static function getUserTypeId(): string;
  474. }