Nessuna descrizione

DisplayFormInWPContent.php 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. namespace MailPoet\Form;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\API\JSON\API;
  5. use MailPoet\Config\Renderer as TemplateRenderer;
  6. use MailPoet\Entities\FormEntity;
  7. use MailPoet\Util\Security;
  8. use MailPoet\WP\Functions as WPFunctions;
  9. class DisplayFormInWPContent {
  10. const NO_FORM_TRANSIENT_KEY = 'no_forms_displayed_bellow_content';
  11. const TYPES = [
  12. FormEntity::DISPLAY_TYPE_BELOW_POST,
  13. FormEntity::DISPLAY_TYPE_POPUP,
  14. FormEntity::DISPLAY_TYPE_FIXED_BAR,
  15. FormEntity::DISPLAY_TYPE_SLIDE_IN,
  16. ];
  17. const SUPPORTED_POST_TYPES = [
  18. 'post',
  19. 'product',
  20. 'job_listing',
  21. ];
  22. /** @var WPFunctions */
  23. private $wp;
  24. /** @var FormsRepository */
  25. private $formsRepository;
  26. /** @var Renderer */
  27. private $formRenderer;
  28. /** @var AssetsController */
  29. private $assetsController;
  30. /** @var TemplateRenderer */
  31. private $templateRenderer;
  32. public function __construct(
  33. WPFunctions $wp,
  34. FormsRepository $formsRepository,
  35. Renderer $formRenderer,
  36. AssetsController $assetsController,
  37. TemplateRenderer $templateRenderer
  38. ) {
  39. $this->wp = $wp;
  40. $this->formsRepository = $formsRepository;
  41. $this->formRenderer = $formRenderer;
  42. $this->assetsController = $assetsController;
  43. $this->templateRenderer = $templateRenderer;
  44. }
  45. /**
  46. * This takes input from an action and any plugin or theme can pass anything.
  47. * We return string for regular content otherwise we just pass thru what comes.
  48. * @param mixed $content
  49. * @return string|mixed
  50. */
  51. public function display($content = null) {
  52. if (!is_string($content) || !$this->shouldDisplay()) return $content;
  53. $forms = $this->getForms();
  54. if (count($forms) === 0) {
  55. return $content;
  56. }
  57. $this->assetsController->setupFrontEndDependencies();
  58. $result = $content;
  59. foreach ($forms as $displayType => $form) {
  60. $result .= $this->getContentBellow($form, $displayType);
  61. }
  62. return $result;
  63. }
  64. private function shouldDisplay(): bool {
  65. $result = true;
  66. // This is a fix Yoast plugin and Shapely theme compatibility
  67. // This is to make sure we only display once for each page
  68. // Yast plugin calls `get_the_excerpt` which also triggers hook `the_content` we don't want to include our form in that
  69. // Shapely calls the hook `the_content` multiple times on the page as well and we would display popup multiple times - not ideal
  70. if (!$this->wp->inTheLoop() || !$this->wp->isMainQuery()) {
  71. $result = $this->wp->applyFilters('mailpoet_display_form_is_main_loop', false);
  72. }
  73. // this code ensures that we display the form only on a page which is related to single post
  74. if (!$this->wp->isSingle() && !$this->wp->isPage()) $result = $this->wp->applyFilters('mailpoet_display_form_is_single', false);
  75. $noFormsCache = $this->wp->getTransient(DisplayFormInWPContent::NO_FORM_TRANSIENT_KEY);
  76. if ($noFormsCache === '1') $result = false;
  77. return $result;
  78. }
  79. private function saveNoForms() {
  80. $this->wp->setTransient(DisplayFormInWPContent::NO_FORM_TRANSIENT_KEY, '1');
  81. }
  82. /**
  83. * @return array<string, FormEntity>
  84. */
  85. private function getForms(): array {
  86. $forms = $this->formsRepository->findBy([
  87. 'deletedAt' => null,
  88. 'status' => FormEntity::STATUS_ENABLED,
  89. ], ['updatedAt' => 'ASC']);
  90. if (count($forms) === 0) {
  91. $this->saveNoForms();
  92. }
  93. $forms = $this->filterOneFormInEachDisplayType($forms);
  94. return $forms;
  95. }
  96. /**
  97. * @param FormEntity[] $forms
  98. * @return array<string, FormEntity>
  99. */
  100. private function filterOneFormInEachDisplayType($forms): array {
  101. $formsFiltered = [];
  102. foreach ($forms as $form) {
  103. foreach (self::TYPES as $displayType) {
  104. if ($this->shouldDisplayFormType($form, $displayType)) {
  105. $formsFiltered[$displayType] = $form;
  106. }
  107. }
  108. }
  109. return $formsFiltered;
  110. }
  111. private function getContentBellow(FormEntity $form, string $displayType): string {
  112. if (!$this->shouldDisplayFormType($form, $displayType)) return '';
  113. $formSettings = $form->getSettings();
  114. if (!is_array($formSettings)) return '';
  115. $htmlId = 'mp_form_' . $displayType . $form->getId();
  116. $templateData = [
  117. 'form_html_id' => $htmlId,
  118. 'form_id' => $form->getId(),
  119. 'form_success_message' => $formSettings['success_message'] ?? null,
  120. 'form_type' => $displayType,
  121. 'styles' => $this->formRenderer->renderStyles($form, '#' . $htmlId, $displayType),
  122. 'html' => $this->formRenderer->renderHTML($form),
  123. 'close_button_icon' => $formSettings['close_button'] ?? 'round_white',
  124. ];
  125. // (POST) non ajax success/error variables
  126. $templateData['success'] = (
  127. (isset($_GET['mailpoet_success']))
  128. &&
  129. ((int)$_GET['mailpoet_success'] === $form->getId())
  130. );
  131. $templateData['error'] = (
  132. (isset($_GET['mailpoet_error']))
  133. &&
  134. ((int)$_GET['mailpoet_error'] === $form->getId())
  135. );
  136. $templateData['delay'] = $formSettings['form_placement'][$displayType]['delay'] ?? 0;
  137. $templateData['position'] = $formSettings['form_placement'][$displayType]['position'] ?? '';
  138. $templateData['animation'] = $formSettings['form_placement'][$displayType]['animation'] ?? '';
  139. $templateData['fontFamily'] = $formSettings['font_family'] ?? '';
  140. $templateData['enableExitIntent'] = false;
  141. if (
  142. isset($formSettings['form_placement'][$displayType]['exit_intent_enabled'])
  143. && ($formSettings['form_placement'][$displayType]['exit_intent_enabled'] === '1')
  144. ) {
  145. $templateData['enableExitIntent'] = true;
  146. }
  147. // generate security token
  148. $templateData['token'] = Security::generateToken();
  149. // add API version
  150. $templateData['api_version'] = API::CURRENT_VERSION;
  151. return $this->templateRenderer->render('form/front_end_form.html', $templateData);
  152. }
  153. private function shouldDisplayFormType(FormEntity $form, string $formType): bool {
  154. $settings = $form->getSettings();
  155. // check the structure just to be sure
  156. if (!is_array($settings)
  157. || !isset($settings['form_placement'][$formType])
  158. || !is_array($settings['form_placement'][$formType])
  159. ) return false;
  160. $setup = $settings['form_placement'][$formType];
  161. if ($setup['enabled'] !== '1') {
  162. return false;
  163. }
  164. if ($this->wp->isSingular($this->wp->applyFilters('mailpoet_display_form_supported_post_types', self::SUPPORTED_POST_TYPES))) {
  165. if ($this->shouldDisplayFormOnPost($setup, 'posts')) return true;
  166. if ($this->shouldDisplayFormOnCategory($setup)) return true;
  167. if ($this->shouldDisplayFormOnTag($setup)) return true;
  168. return false;
  169. }
  170. if ($this->wp->isPage() && $this->shouldDisplayFormOnPost($setup, 'pages')) {
  171. return true;
  172. }
  173. return false;
  174. }
  175. private function shouldDisplayFormOnPost(array $setup, string $postsKey): bool {
  176. if (!isset($setup[$postsKey])) {
  177. return false;
  178. }
  179. if (isset($setup[$postsKey]['all']) && $setup[$postsKey]['all'] === '1') {
  180. return true;
  181. }
  182. $post = $this->wp->getPost(null, ARRAY_A);
  183. if (isset($setup[$postsKey]['selected']) && in_array($post['ID'], $setup[$postsKey]['selected'])) {
  184. return true;
  185. }
  186. return false;
  187. }
  188. private function shouldDisplayFormOnCategory(array $setup): bool {
  189. if (!isset($setup['categories'])) return false;
  190. if ($this->wp->hasCategory($setup['categories'])) return true;
  191. if ($this->wp->hasTerm($setup['categories'], 'product_cat')) return true;
  192. return false;
  193. }
  194. private function shouldDisplayFormOnTag(array $setup): bool {
  195. if (!isset($setup['tags'])) return false;
  196. if ($this->wp->hasTag($setup['tags'])) return true;
  197. if ($this->wp->hasTerm($setup['tags'], 'product_tag')) return true;
  198. return false;
  199. }
  200. }