PageRenderTime 53ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/landing/lib/subtype/form.php

https://gitlab.com/alexprowars/bitrix
PHP | 945 lines | 714 code | 93 blank | 138 comment | 78 complexity | 699db27175930832ddb0205fba1ad39d MD5 | raw file
  1. <?php
  2. namespace Bitrix\Landing\Subtype;
  3. use Bitrix\Crm\Integration\UserConsent;
  4. use Bitrix\Crm\Settings\LeadSettings;
  5. use Bitrix\Crm\UI\Webpack;
  6. use Bitrix\Crm\WebForm;
  7. use Bitrix\Landing\Landing;
  8. use Bitrix\Landing\Block;
  9. use Bitrix\Landing\Internals\BlockTable;
  10. use Bitrix\Landing\Manager;
  11. use Bitrix\Landing\Site;
  12. use Bitrix\Main\Loader;
  13. use Bitrix\Main\Localization\Loc;
  14. use Bitrix\Socialservices\ApClient;
  15. Loc::loadMessages(__FILE__);
  16. /**
  17. * Subtype for blocks with CRM-forms
  18. * @package Bitrix\Landing\Subtype
  19. */
  20. class Form
  21. {
  22. protected const ATTR_FORM_PARAMS = 'data-b24form';
  23. protected const ATTR_FORM_EMBED = 'data-b24form-embed';
  24. protected const ATTR_FORM_STYLE = 'data-b24form-design';
  25. protected const ATTR_FORM_USE_STYLE = 'data-b24form-use-style';
  26. protected const ATTR_FORM_FROM_CONNECTOR = 'data-b24form-connector';
  27. protected const ATTR_FORM_OLD_DOMAIN = 'data-b24form-original-domain';
  28. protected const ATTR_FORM_OLD_HEADER = 'data-b24form-show-header';
  29. protected const SELECTOR_FORM_NODE = '.bitrix24forms';
  30. protected const SELECTOR_OLD_STYLE_NODE = '.landing-block-form-styles';
  31. protected const STYLE_SETTING = 'crm-form';
  32. protected const REGEXP_FORM_STYLE = '/data-b24form-design *= *[\'"](\{.+\})[\'"]/i';
  33. protected const REGEXP_FORM_ID_INLINE = '/data-b24form=["\']#crmFormInline(?<id>[\d]+)["\']/i';
  34. public const INLINE_MARKER_PREFIX = '#crmFormInline';
  35. public const POPUP_MARKER_PREFIX = '#crmFormPopup';
  36. protected const AVAILABLE_FORM_FIELDS = [
  37. 'ID',
  38. 'NAME',
  39. 'SECURITY_CODE',
  40. 'IS_CALLBACK_FORM',
  41. 'ACTIVE',
  42. 'XML_ID',
  43. ];
  44. // region replaces for view and public
  45. /**
  46. * Replace form markers in block, put true scripts. Run on publication action
  47. * @param string $content - content of block
  48. * @return string - replaced content
  49. */
  50. public static function prepareFormsToPublication(string $content): string
  51. {
  52. // change - replace markers always, not only if connector
  53. $content = self::replaceFormMarkers($content);
  54. return $content;
  55. }
  56. /**
  57. * Replace form markers in block, put true scripts. Run on view in public mode
  58. * @param string $content - content of block
  59. * @return string - replaced content
  60. */
  61. public static function prepareFormsToView(string $content): string
  62. {
  63. if (self::isCrm())
  64. {
  65. $content = self::replaceFormMarkers($content);
  66. }
  67. return $content;
  68. }
  69. /**
  70. * Replaces and returns all #crmForm-link to the popup codes or in inline forms
  71. * For CP - every hit (cached), for SMN - on public
  72. * @param string $content Some content.
  73. * @return string
  74. */
  75. protected static function replaceFormMarkers(string $content): string
  76. {
  77. $replace = preg_replace_callback(
  78. '/(?<pre><a[^>]+href=|data-b24form=)["\']#crmForm(?<type>Inline|Popup)(?<id>[\d]+)["\']/i',
  79. static function ($matches)
  80. {
  81. if (
  82. !(int)$matches['id']
  83. || !($form = self::getFormById((int)$matches['id']))
  84. )
  85. {
  86. return $matches[0];
  87. }
  88. if (strtolower($matches['type']) === 'inline')
  89. {
  90. $param = "{$form['ID']}|{$form['SECURITY_CODE']}|{$form['URL']}";
  91. return $matches['pre'] . "\"{$param}\"";
  92. }
  93. if (strtolower($matches['type']) === 'popup')
  94. {
  95. $script = "<script data-b24-form=\"click/{$matches['id']}/{$form['SECURITY_CODE']}\" data-skip-moving=\"true\">
  96. (function(w,d,u){
  97. var s=d.createElement('script');s.async=true;s.src=u+'?'+(Date.now()/180000|0);
  98. var h=d.getElementsByTagName('script')[0];h.parentNode.insertBefore(s,h);
  99. })(window,document,'{$form['URL']}');
  100. </script>";
  101. return $script . $matches['pre'] . "\"#\" onclick=\"BX.PreventDefault();\"";
  102. }
  103. return $matches[0];
  104. },
  105. $content
  106. );
  107. return $replace ?? $content;
  108. }
  109. /**
  110. * Clears cache all sites with blocks.
  111. * @return void
  112. */
  113. public static function clearCache(): void
  114. {
  115. $sites = [];
  116. $res = BlockTable::getList(
  117. [
  118. 'select' => [
  119. 'SITE_ID' => 'LANDING.SITE_ID',
  120. ],
  121. 'filter' => [
  122. '=LANDING.ACTIVE' => 'Y',
  123. '=LANDING.SITE.ACTIVE' => 'Y',
  124. '=PUBLIC' => 'Y',
  125. '=DELETED' => 'N',
  126. 'CONTENT' => '%bitrix24forms%',
  127. ],
  128. 'group' => [
  129. 'LANDING.SITE_ID',
  130. ],
  131. ]
  132. );
  133. while ($row = $res->fetch())
  134. {
  135. if (!in_array($row['SITE_ID'], $sites))
  136. {
  137. $sites[] = $row['SITE_ID'];
  138. }
  139. }
  140. foreach ($sites as $site)
  141. {
  142. Site::update($site, [
  143. 'DATE_MODIFY' => false
  144. ]);
  145. }
  146. }
  147. // endregion
  148. // region get forms
  149. /**
  150. * Gets web forms in system.
  151. * @param bool $force - if true - get forms forcibly w/o cache
  152. * @return array
  153. */
  154. public static function getForms(bool $force = false): array
  155. {
  156. static $forms = [];
  157. if ($forms && !$force)
  158. {
  159. return $forms;
  160. }
  161. if (self::isCrm())
  162. {
  163. $forms = self::getFormsForPortal();
  164. }
  165. elseif (Manager::isB24Connector())
  166. {
  167. $forms = self::getFormsViaConnector();
  168. }
  169. return $forms;
  170. }
  171. /**
  172. * Check if b24 or box portal
  173. * @return bool
  174. */
  175. protected static function isCrm(): bool
  176. {
  177. return Loader::includeModule('crm');
  178. }
  179. protected static function getFormsForPortal(array $filter = []): array
  180. {
  181. $res = Webform\Internals\FormTable::getList(
  182. [
  183. 'select' => self::AVAILABLE_FORM_FIELDS,
  184. 'filter' => $filter,
  185. 'order' => [
  186. 'ID' => 'ASC',
  187. ],
  188. ]
  189. );
  190. $forms = [];
  191. while ($form = $res->fetch())
  192. {
  193. $form['ID'] = (int)$form['ID'];
  194. $webpack = Webpack\Form::instance($form['ID']);
  195. if (!$webpack->isBuilt())
  196. {
  197. $webpack->build();
  198. $webpack = Webpack\Form::instance($form['ID']);
  199. }
  200. $form['URL'] = $webpack->getEmbeddedFileUrl();
  201. $forms[$form['ID']] = $form;
  202. }
  203. return $forms;
  204. }
  205. protected static function getFormsViaConnector(): array
  206. {
  207. $forms = [];
  208. $client = ApClient::init();
  209. if ($client)
  210. {
  211. $res = $client->call('crm.webform.list', ['GET_INACTIVE' => 'Y']);
  212. if (isset($res['result']) && is_array($res['result']))
  213. {
  214. foreach ($res['result'] as $form)
  215. {
  216. $form['ID'] = (int)$form['ID'];
  217. $forms[$form['ID']] = $form;
  218. }
  219. }
  220. }
  221. return $forms;
  222. }
  223. /**
  224. * Find just one form by ID. Return array of form fields, or empty array if not found
  225. * @return array
  226. */
  227. public static function getFormById(int $id): array
  228. {
  229. $forms = self::getFormsByFilter(['ID' => $id]);
  230. return !empty($forms) ? array_shift($forms) : [];
  231. }
  232. /**
  233. * Find only callback forms. Return array of form arrays, or empty array if not found
  234. * @return array
  235. */
  236. public static function getCallbackForms(): array
  237. {
  238. return self::getFormsByFilter(['IS_CALLBACK_FORM' => 'Y', 'ACTIVE' => 'Y']);
  239. }
  240. protected static function getFormsByFilter(array $filter): array
  241. {
  242. $filter = array_filter(
  243. $filter,
  244. static function ($key)
  245. {
  246. return in_array($key, self::AVAILABLE_FORM_FIELDS, true);
  247. },
  248. ARRAY_FILTER_USE_KEY
  249. );
  250. $forms = [];
  251. if (self::isCrm())
  252. {
  253. $forms = self::getFormsForPortal($filter);
  254. }
  255. elseif (Manager::isB24Connector())
  256. {
  257. foreach (self::getFormsViaConnector() as $form)
  258. {
  259. $filtred = true;
  260. foreach ($filter as $key => $value)
  261. {
  262. if (!$form[$key] || $form[$key] !== $value)
  263. {
  264. $filtred = false;
  265. break;
  266. }
  267. }
  268. if ($filtred)
  269. {
  270. $forms[$form['ID']] = $form;
  271. }
  272. }
  273. }
  274. return $forms;
  275. }
  276. // endregion
  277. // region prepare manifest
  278. /**
  279. * Prepare manifest.
  280. * @param array $manifest Block's manifest.
  281. * @param Block|null $block Block instance.
  282. * @param array $params Additional params.
  283. * @return array
  284. */
  285. public static function prepareManifest(array $manifest, Block $block = null, array $params = []): array
  286. {
  287. // add extension
  288. if (!isset($manifest['assets']) || !is_array($manifest['assets']))
  289. {
  290. $manifest['assets'] = [];
  291. }
  292. if (!isset($manifest['assets']['ext']))
  293. {
  294. $manifest['assets']['ext'] = [];
  295. }
  296. if (!is_array($manifest['assets']['ext']))
  297. {
  298. $manifest['assets']['ext'] = [$manifest['assets']['ext']];
  299. }
  300. if (!in_array('landing_form', $manifest['assets']['ext'], true))
  301. {
  302. $manifest['assets']['ext'][] = 'landing_form';
  303. }
  304. // style setting
  305. if (!is_array($manifest['style']['block']) && !is_array($manifest['style']['nodes']))
  306. {
  307. $manifest['style'] = [
  308. 'block' => Block::DEFAULT_WRAPPER_STYLE,
  309. 'nodes' => $manifest['style'],
  310. ];
  311. }
  312. $manifest['style']['nodes'][self::SELECTOR_FORM_NODE] = [
  313. 'type' => self::STYLE_SETTING,
  314. ];
  315. if (Manager::isB24())
  316. {
  317. $link = '/crm/webform/';
  318. }
  319. else if (Manager::isB24Connector())
  320. {
  321. $link = '/bitrix/admin/b24connector_crm_forms.php?lang=' . LANGUAGE_ID;
  322. }
  323. if (isset($link))
  324. {
  325. $manifest['block']['attrsFormDescription'] = '<a href="' . $link . '" target="_blank">' .
  326. Loc::getMessage('LANDING_BLOCK_FORM_CONFIG') .
  327. '</a>';
  328. }
  329. // add callbacks
  330. $manifest['callbacks'] = [
  331. 'afterAdd' => function (Block &$block)
  332. {
  333. $dom = $block->getDom();
  334. if (!($node = $dom->querySelector(self::SELECTOR_FORM_NODE)))
  335. {
  336. return;
  337. }
  338. $attrsToSet = [self::ATTR_FORM_EMBED => ''];
  339. if (!self::isCrm())
  340. {
  341. $attrsToSet[self::ATTR_FORM_FROM_CONNECTOR] = 'Y';
  342. }
  343. // if block copy - not update params
  344. if (
  345. ($attrsExists = $node->getAttributes())
  346. && $attrsExists[self::ATTR_FORM_PARAMS]
  347. && $formParamsExists = $attrsExists[self::ATTR_FORM_PARAMS]->getValue()
  348. )
  349. {
  350. $attrsToSet[self::ATTR_FORM_PARAMS] = $formParamsExists;
  351. }
  352. else
  353. {
  354. // try to get 1) default callback form 2) last added form 3) create new form
  355. $forms = self::getFormsByFilter([
  356. 'XML_ID' => 'crm_preset_fb'
  357. ]);
  358. $forms = self::prepareFormsToAttrs($forms);
  359. if (empty($forms))
  360. {
  361. $forms = self::getForms(true); // force to preserve cycle when create form landing block
  362. $forms = self::prepareFormsToAttrs($forms);
  363. if (empty($forms))
  364. {
  365. $forms = self::createDefaultForm();
  366. $forms = self::prepareFormsToAttrs($forms);
  367. }
  368. }
  369. if (!empty($forms))
  370. {
  371. self::setFormIdParam(
  372. $block,
  373. str_replace(self::INLINE_MARKER_PREFIX, '', $forms[0]['value'])
  374. );
  375. }
  376. }
  377. // preload alert
  378. $node->setInnerHTML(
  379. '<div class="g-landing-alert">'
  380. . Loc::getMessage('LANDING_BLOCK_WEBFORM_PRELOADER')
  381. . '</div>'
  382. );
  383. $block->saveContent($dom->saveHTML());
  384. // save
  385. $block->setAttributes([self::SELECTOR_FORM_NODE => $attrsToSet]);
  386. $block->save();
  387. },
  388. ];
  389. // add attrs
  390. if (
  391. !array_key_exists('attrs', $manifest)
  392. || !is_array($manifest['attrs'])
  393. )
  394. {
  395. $manifest['attrs'] = [];
  396. }
  397. // hard operation getAttrs is only FOR EDITOR, in public set fake array for saveAttributes later
  398. $manifest['attrs'][self::SELECTOR_FORM_NODE] =
  399. Landing::getEditMode()
  400. ? self::getAttrs()
  401. : [['attribute' => self::ATTR_FORM_PARAMS]];
  402. return $manifest;
  403. }
  404. /**
  405. * Gets attrs for form.
  406. * @return array
  407. */
  408. protected static function getAttrs(): array
  409. {
  410. static $attrs = [];
  411. if ($attrs)
  412. {
  413. return $attrs;
  414. }
  415. // get from CRM or via connector
  416. $forms = self::getForms();
  417. $forms = self::prepareFormsToAttrs($forms);
  418. $attrs = [
  419. $attrs[] = [
  420. 'name' => 'Embed form flag',
  421. 'attribute' => self::ATTR_FORM_EMBED,
  422. 'type' => 'string',
  423. 'hidden' => true,
  424. ],
  425. [
  426. 'name' => 'Form design',
  427. 'attribute' => self::ATTR_FORM_STYLE,
  428. 'type' => 'string',
  429. 'hidden' => true,
  430. ],
  431. [
  432. 'name' => 'Form from connector flag',
  433. 'attribute' => self::ATTR_FORM_FROM_CONNECTOR,
  434. 'type' => 'string',
  435. 'hidden' => true,
  436. ],
  437. ];
  438. if (!empty($forms))
  439. {
  440. // get forms list
  441. $attrs[] = [
  442. 'name' => Loc::getMessage('LANDING_BLOCK_WEBFORM'),
  443. 'attribute' => self::ATTR_FORM_PARAMS,
  444. 'items' => $forms,
  445. 'type' => 'list',
  446. ];
  447. // show header
  448. // use custom design
  449. $attrs[] = [
  450. 'name' => Loc::getMessage('LANDING_BLOCK_WEBFORM_USE_STYLE'),
  451. 'attribute' => self::ATTR_FORM_USE_STYLE,
  452. 'type' => 'list',
  453. 'items' => [
  454. [
  455. 'name' => Loc::getMessage('LANDING_BLOCK_WEBFORM_USE_STYLE_Y'),
  456. 'value' => 'Y',
  457. ],
  458. [
  459. 'name' => Loc::getMessage('LANDING_BLOCK_WEBFORM_USE_STYLE_N'),
  460. 'value' => 'N',
  461. ],
  462. ],
  463. ];
  464. }
  465. // no form - no settings, just message for user
  466. else
  467. {
  468. // portal or SMN with b24connector
  469. if (Manager::isB24() || Manager::isB24Connector())
  470. {
  471. // todo:need alert?
  472. $attrs[] = [
  473. 'name' => Loc::getMessage('LANDING_BLOCK_WEBFORM'),
  474. 'attribute' => self::ATTR_FORM_PARAMS,
  475. 'type' => 'list',
  476. 'items' => [
  477. [
  478. 'name' => Loc::getMessage('LANDING_BLOCK_WEBFORM_NO_FORM'),
  479. 'value' => false,
  480. ],
  481. ],
  482. ];
  483. }
  484. // siteman
  485. else
  486. {
  487. // todo: need?
  488. $attrs[] = [
  489. 'name' => Loc::getMessage('LANDING_BLOCK_WEBFORM'),
  490. 'attribute' => self::ATTR_FORM_PARAMS,
  491. 'type' => 'list',
  492. 'items' => [
  493. [
  494. 'name' => Loc::getMessage('LANDING_BLOCK_WEBFORM_NO_FORM'),
  495. 'value' => false,
  496. ],
  497. ],
  498. ];
  499. }
  500. }
  501. return $attrs;
  502. }
  503. /**
  504. * Move callback form to end.
  505. * @param array $forms Forms array.
  506. * @return array
  507. */
  508. protected static function prepareFormsToAttrs(array $forms): array
  509. {
  510. $sorted = [];
  511. foreach ($forms as $form)
  512. {
  513. if (array_key_exists('ACTIVE', $form) && $form['ACTIVE'] !== 'Y')
  514. {
  515. continue;
  516. }
  517. $item = [
  518. 'name' => $form['NAME'],
  519. 'value' => self::INLINE_MARKER_PREFIX . $form['ID'],
  520. ];
  521. if ($form['IS_CALLBACK_FORM'] === 'Y')
  522. {
  523. $sorted[] = $item;
  524. }
  525. else
  526. {
  527. array_unshift($sorted, $item);
  528. }
  529. }
  530. return $sorted;
  531. }
  532. // endregion
  533. // region actions with blocks and forms
  534. /**
  535. * @param int|array $landingIds - int or [int] of landing IDs
  536. * @return array of all block with CRM-forms at this page
  537. * @throws \Bitrix\Main\ArgumentException
  538. * @throws \Bitrix\Main\ObjectPropertyException
  539. * @throws \Bitrix\Main\SystemException
  540. */
  541. public static function getLandingFormBlocks($landingIds): array
  542. {
  543. if (empty($landingIds))
  544. {
  545. return [];
  546. }
  547. if (!is_array($landingIds))
  548. {
  549. $landingIds = [$landingIds];
  550. }
  551. return BlockTable::getList(
  552. [
  553. 'select' => ['ID', 'LID'],
  554. 'filter' => [
  555. '=LID' => $landingIds,
  556. '=DELETED' => 'N',
  557. 'CONTENT' => '%data-b24form=%',
  558. ],
  559. ]
  560. )->fetchAll()
  561. ;
  562. }
  563. /**
  564. * Return CRM-form ID from block, if exists. Else return null;
  565. * @param int $blockId
  566. * @return int|null
  567. */
  568. public static function getFormByBlock(int $blockId): ?int
  569. {
  570. $block = new Block($blockId);
  571. if (preg_match(self::REGEXP_FORM_ID_INLINE, $block->getContent(), $matches))
  572. {
  573. return (int)$matches[1];
  574. }
  575. return null;
  576. }
  577. /**
  578. * Save form params in block for current form
  579. * @param int $blockId - from landing block table
  580. * @param int $formId - from webform table
  581. * @return bool - true if success, false if errors
  582. */
  583. public static function setFormIdToBlock(int $blockId, int $formId): bool
  584. {
  585. $block = new Block($blockId);
  586. self::setFormIdParam($block, $formId);
  587. $block->save();
  588. return $block->getError()->isEmpty();
  589. }
  590. /**
  591. * Encapsulates the form params save logic
  592. * @param Block $block
  593. * @param int $formId - from webform table
  594. */
  595. protected static function setFormIdParam(Block $block, int $formId): void
  596. {
  597. if (($form = self::getFormById($formId)))
  598. {
  599. // todo: can add force public flag for replaces, when we know exactly that block is public
  600. $newParam = self::INLINE_MARKER_PREFIX . $form['ID'];
  601. $block->setAttributes([
  602. self::SELECTOR_FORM_NODE => [self::ATTR_FORM_PARAMS => $newParam],
  603. ]);
  604. }
  605. }
  606. /**
  607. * Create form with default params
  608. * @return array - array with once item, fields equal getForms(). Or empty array if not created
  609. */
  610. protected static function createDefaultForm(): array
  611. {
  612. if ($formId = self::createForm([]))
  613. {
  614. return self::getFormsByFilter(['ID' => $formId]);
  615. }
  616. return [];
  617. }
  618. /**
  619. * @param array $formData
  620. * @return int|null - id of created form or null if errors
  621. */
  622. protected static function createForm(array $formData): ?int
  623. {
  624. if (self::isCrm())
  625. {
  626. $form = new WebForm\Form;
  627. $defaultData = WebForm\Preset::getById('crm_preset_cd');
  628. $defaultData['XML_ID'] = '';
  629. $defaultData['ACTIVE'] = 'Y';
  630. $defaultData['IS_SYSTEM'] = 'N';
  631. $defaultData['IS_CALLBACK_FORM'] = 'N';
  632. $defaultData['BUTTON_CAPTION'] = $form->getButtonCaption();
  633. $agreementId = UserConsent::getDefaultAgreementId();
  634. $defaultData['USE_LICENCE'] = $agreementId ? 'Y' : 'N';
  635. if ($agreementId)
  636. {
  637. $defaultData['LICENCE_BUTTON_IS_CHECKED'] = 'Y';
  638. $defaultData['AGREEMENT_ID'] = $agreementId;
  639. }
  640. $isLeadEnabled = LeadSettings::getCurrent()->isEnabled();
  641. $defaultData['ENTITY_SCHEME'] = (string)(
  642. $isLeadEnabled
  643. ? WebForm\Entity::ENUM_ENTITY_SCHEME_LEAD
  644. : WebForm\Entity::ENUM_ENTITY_SCHEME_DEAL
  645. );
  646. $currentUserId = is_object($GLOBALS['USER']) ? $GLOBALS['USER']->getId() : null;
  647. $defaultData['ACTIVE_CHANGE_BY'] = $currentUserId;
  648. $defaultData['ASSIGNED_BY_ID'] = $currentUserId;
  649. $formData = array_merge($defaultData, $formData);
  650. $form->merge($formData);
  651. $form->save();
  652. return !$form->hasErrors() ? $form->getId() : null;
  653. }
  654. return null;
  655. }
  656. /**
  657. * @param Block $block
  658. * @param string $xmlId
  659. */
  660. public static function setSpecialFormToBlock(Block $block, string $xmlId): void
  661. {
  662. if (($formData = self::getSpecialFormsData()[$xmlId]))
  663. {
  664. $formId = null;
  665. foreach (self::getForms() as $form)
  666. {
  667. if (
  668. array_key_exists('XML_ID', $form)
  669. && $form['XML_ID'] === $xmlId
  670. )
  671. {
  672. $formId = $form['ID'];
  673. break;
  674. }
  675. }
  676. if (!$formId)
  677. {
  678. $formId = self::createForm($formData);
  679. }
  680. if ($formId)
  681. {
  682. self::setFormIdParam($block, $formId);
  683. $block->save();
  684. }
  685. }
  686. }
  687. protected static function getSpecialFormsData(): ?array
  688. {
  689. if (self::isCrm())
  690. {
  691. $data = [
  692. 'crm_preset_store_v3' => [
  693. 'XML_ID' => 'crm_preset_store_v3',
  694. 'NAME' => Loc::getMessage('LANDING_FORM_SPECIAL_STOREV3_NAME'),
  695. 'IS_SYSTEM' => 'N',
  696. 'ACTIVE' => 'Y',
  697. 'RESULT_SUCCESS_TEXT' => Loc::getMessage('LANDING_FORM_SPECIAL_STOREV3_RESULT_SUCCESS'),
  698. 'RESULT_FAILURE_TEXT' => Loc::getMessage('LANDING_FORM_SPECIAL_STOREV3_RESULT_FAILURE'),
  699. 'COPYRIGHT_REMOVED' => 'N',
  700. 'IS_PAY' => 'N',
  701. 'FORM_SETTINGS' => [
  702. 'DEAL_DC_ENABLED' => 'Y',
  703. ],
  704. 'BUTTON_CAPTION' => '',
  705. 'FIELDS' => [
  706. [
  707. 'TYPE' => 'string',
  708. 'CODE' => 'CONTACT_NAME',
  709. 'CAPTION' => Loc::getMessage('LANDING_FORM_SPECIAL_STOREV3_FIELD_NAME'),
  710. 'SORT' => 100,
  711. 'REQUIRED' => 'N',
  712. 'MULTIPLE' => 'N',
  713. 'PLACEHOLDER' => '',
  714. ],
  715. [
  716. 'TYPE' => 'phone',
  717. 'CODE' => 'CONTACT_PHONE',
  718. 'CAPTION' => Loc::getMessage('LANDING_FORM_SPECIAL_STOREV3_FIELD_PHONE'),
  719. 'SORT' => 200,
  720. 'REQUIRED' => 'N',
  721. 'MULTIPLE' => 'N',
  722. 'PLACEHOLDER' => '',
  723. ],
  724. [
  725. 'TYPE' => 'text',
  726. 'CODE' => 'DEAL_COMMENTS',
  727. 'CAPTION' => Loc::getMessage('LANDING_FORM_SPECIAL_STOREV3_FIELD_COMMENT'),
  728. 'SORT' => 300,
  729. 'REQUIRED' => 'N',
  730. 'MULTIPLE' => 'N',
  731. 'PLACEHOLDER' => '',
  732. ],
  733. ],
  734. ],
  735. ];
  736. $isLeadEnabled = LeadSettings::getCurrent()->isEnabled();
  737. foreach ($data as $id => $form)
  738. {
  739. if ($isLeadEnabled)
  740. {
  741. foreach ($data[$id]['FIELDS'] as $key => $field)
  742. {
  743. $field['CODE'] = str_replace(['CONTACT', 'DEAL'], 'LEAD', $field['CODE']);
  744. $data[$id]['FIELDS'][$key] = $field;
  745. }
  746. }
  747. }
  748. return $data;
  749. }
  750. return null;
  751. }
  752. // endregion
  753. // region update
  754. /**
  755. * Find old forms blocks and update to embed format
  756. * @param int $landingId
  757. */
  758. public static function updateLandingToEmbedForms(int $landingId): void
  759. {
  760. $res = BlockTable::getList(
  761. [
  762. 'select' => [
  763. 'ID',
  764. ],
  765. 'filter' => [
  766. 'LID' => $landingId,
  767. '=DELETED' => 'N',
  768. ],
  769. ]
  770. );
  771. while ($row = $res->fetch())
  772. {
  773. $block = new Block($row['ID']);
  774. self::updateBlockToEmbed($block);
  775. }
  776. }
  777. /**
  778. * Migrate from old form to new embed, adjust block params, remove old style nodes
  779. * @param Block $block
  780. */
  781. protected static function updateBlockToEmbed(Block $block): void
  782. {
  783. // check if update needed
  784. $manifest = $block->getManifest();
  785. if (
  786. !$manifest['block']['subtype']
  787. || (!is_array($manifest['block']['subtype']) && $manifest['block']['subtype'] !== 'form')
  788. || (is_array($manifest['block']['subtype']) && !in_array('form', $manifest['block']['subtype'], true))
  789. )
  790. {
  791. return;
  792. }
  793. $dom = $block->getDom();
  794. if (
  795. !($resultNode = $dom->querySelector(self::SELECTOR_FORM_NODE))
  796. || !($attrs = $resultNode->getAttributes())
  797. || !array_key_exists(self::ATTR_FORM_PARAMS, $attrs))
  798. {
  799. return;
  800. }
  801. $formParams = explode('|', $attrs[self::ATTR_FORM_PARAMS]->getValue());
  802. if (count($formParams) !== 2 || !(int)$formParams[0])
  803. {
  804. return;
  805. }
  806. // update
  807. $forms = self::getForms();
  808. if (array_key_exists($formParams[0], $forms))
  809. {
  810. $form = $forms[$formParams[0]];
  811. self::setFormIdParam($block, $form['ID']);
  812. $resultNode->setAttribute(self::ATTR_FORM_EMBED, '');
  813. $resultNode->removeAttribute(self::ATTR_FORM_OLD_DOMAIN);
  814. $resultNode->removeAttribute(self::ATTR_FORM_OLD_HEADER);
  815. if (
  816. !array_key_exists(self::ATTR_FORM_STYLE, $attrs)
  817. || !$attrs[self::ATTR_FORM_STYLE]->getValue()
  818. )
  819. {
  820. // find new styles
  821. $contentFromRepo = Block::getContentFromRepository($block->getCode());
  822. if (
  823. $contentFromRepo
  824. && preg_match(self::REGEXP_FORM_STYLE, $contentFromRepo, $style)
  825. )
  826. {
  827. $resultNode->setAttribute(self::ATTR_FORM_STYLE, $style[1]);
  828. }
  829. }
  830. }
  831. if (($oldStyleNode = $dom->querySelector(self::SELECTOR_OLD_STYLE_NODE)))
  832. {
  833. $oldStyleNode->getParentNode()->removeChild($oldStyleNode);
  834. }
  835. $block->saveContent($dom->saveHTML());
  836. $block->save();
  837. }
  838. /**
  839. * Get original domain for web-forms.
  840. * @return string
  841. * @deprecated
  842. */
  843. public static function getOriginalFormDomain(): string
  844. {
  845. trigger_error(
  846. "Now using embedded forms, no need domain",
  847. E_USER_WARNING
  848. );
  849. return '';
  850. }
  851. // endregion
  852. }