Block
1 week ago
Listing
1 month ago
RestApi
1 month ago
Templates
2 years ago
Util
2 months ago
ApiDataSanitizer.php
2 weeks ago
AssetsController.php
2 months ago
BlockStylesRenderer.php
3 years ago
BlockWrapperRenderer.php
3 years ago
BlocksRenderer.php
1 week ago
DisplayFormInWPContent.php
2 months ago
FormHtmlSanitizer.php
1 month ago
FormMessageController.php
4 years ago
FormSaveController.php
1 year ago
FormsRepository.php
1 year ago
PreviewPage.php
2 months ago
PreviewWidget.php
1 year ago
Renderer.php
2 months ago
Widget.php
2 months ago
index.php
3 years ago
Widget.php
320 lines
| 1 | <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing |
| 2 | |
| 3 | namespace MailPoet\Form; |
| 4 | |
| 5 | if (!defined('ABSPATH')) exit; |
| 6 | |
| 7 | |
| 8 | use MailPoet\API\JSON\API; |
| 9 | use MailPoet\Config\Env; |
| 10 | use MailPoet\Config\RendererFactory; |
| 11 | use MailPoet\DI\ContainerWrapper; |
| 12 | use MailPoet\Entities\FormEntity; |
| 13 | use MailPoet\Form\Renderer as FormRenderer; |
| 14 | use MailPoet\Form\Util\CustomFonts; |
| 15 | use MailPoet\Settings\SettingsController; |
| 16 | use MailPoet\WP\Functions as WPFunctions; |
| 17 | |
| 18 | // phpcs:disable Generic.Files.InlineHTML |
| 19 | class Widget extends \WP_Widget { |
| 20 | private $renderer; |
| 21 | private $wp; |
| 22 | |
| 23 | /** @var AssetsController */ |
| 24 | private $assetsController; |
| 25 | |
| 26 | /** @var FormRenderer */ |
| 27 | private $formRenderer; |
| 28 | |
| 29 | /** @var FormsRepository */ |
| 30 | private $formsRepository; |
| 31 | |
| 32 | /** @var CustomFonts */ |
| 33 | private $customFonts; |
| 34 | |
| 35 | /** @var SettingsController */ |
| 36 | private $settings; |
| 37 | |
| 38 | public function __construct() { |
| 39 | parent::__construct( |
| 40 | 'mailpoet_form', |
| 41 | __('MailPoet Form', 'mailpoet'), |
| 42 | ['description' => __('Add a newsletter subscription form', 'mailpoet')] |
| 43 | ); |
| 44 | $this->wp = new WPFunctions; |
| 45 | |
| 46 | $this->renderer = (new RendererFactory())->getRenderer(); |
| 47 | $this->settings = SettingsController::getInstance(); |
| 48 | $this->assetsController = new AssetsController($this->wp, $this->renderer, $this->settings); |
| 49 | $this->formRenderer = ContainerWrapper::getInstance()->get(FormRenderer::class); |
| 50 | $this->formsRepository = ContainerWrapper::getInstance()->get(FormsRepository::class); |
| 51 | $this->customFonts = ContainerWrapper::getInstance()->get(CustomFonts::class); |
| 52 | |
| 53 | if (!$this->wp->isAdmin()) { |
| 54 | $this->setupIframe(); |
| 55 | } else { |
| 56 | WPFunctions::get()->addAction('widgets_admin_page', [ |
| 57 | $this->assetsController, |
| 58 | 'setupAdminWidgetPageDependencies', |
| 59 | ]); |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | public function setupIframe() { |
| 64 | $formId = isset($_GET['mailpoet_form_iframe']) && is_numeric($_GET['mailpoet_form_iframe']) ? (int)$_GET['mailpoet_form_iframe'] : 0; |
| 65 | if (!$formId || !$this->formsRepository->findOneById($formId)) return; |
| 66 | |
| 67 | $formHtml = $this->widget( |
| 68 | [ |
| 69 | 'form' => $formId, |
| 70 | 'form_type' => 'iframe', |
| 71 | ] |
| 72 | ); |
| 73 | |
| 74 | $scripts = $this->assetsController->printScripts(); |
| 75 | |
| 76 | // language attributes |
| 77 | $languageAttributes = []; |
| 78 | $isRtl = (bool)(function_exists('is_rtl') && WPFunctions::get()->isRtl()); |
| 79 | |
| 80 | if ($isRtl) { |
| 81 | $languageAttributes[] = 'dir="rtl"'; |
| 82 | } |
| 83 | |
| 84 | if ($this->wp->getOption('html_type') === 'text/html') { |
| 85 | $languageAttributes[] = sprintf('lang="%s"', WPFunctions::get()->getBloginfo('language')); |
| 86 | } |
| 87 | |
| 88 | $languageAttributes = WPFunctions::get()->applyFilters( |
| 89 | 'language_attributes', |
| 90 | implode(' ', $languageAttributes) |
| 91 | ); |
| 92 | |
| 93 | $data = [ |
| 94 | 'language_attributes' => $languageAttributes, |
| 95 | 'scripts' => $scripts, |
| 96 | 'form' => $formHtml, |
| 97 | 'mailpoet_form' => [ |
| 98 | 'ajax_url' => WPFunctions::get()->adminUrl('admin-ajax.php', 'absolute'), |
| 99 | 'is_rtl' => $isRtl, |
| 100 | 'collect_subscriber_timezones' => $this->settings->isSettingEnabled('collect_subscriber_timezones.enabled'), |
| 101 | ], |
| 102 | 'fonts_link' => $this->customFonts->generateHtmlCustomFontLink(), |
| 103 | 'mailpoet_public_css_url' => Env::$assetsUrl . '/dist/css/' . $this->renderer->getCssAsset('mailpoet-public.css'), |
| 104 | ]; |
| 105 | |
| 106 | try { |
| 107 | // We control the template and the data is sanitized |
| 108 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 109 | echo $this->renderer->render('form/iframe.html', $data); |
| 110 | } catch (\Exception $e) { |
| 111 | echo esc_html($e->getMessage()); |
| 112 | } |
| 113 | |
| 114 | exit(); |
| 115 | } |
| 116 | |
| 117 | /** |
| 118 | * Save the new widget's title. |
| 119 | */ |
| 120 | public function update($newInstance, $oldInstance) { |
| 121 | $instance = $oldInstance; |
| 122 | $instance['title'] = strip_tags(is_string($newInstance['title']) ? $newInstance['title'] : ''); |
| 123 | $instance['form'] = is_numeric($newInstance['form']) ? (int)$newInstance['form'] : null; |
| 124 | return $instance; |
| 125 | } |
| 126 | |
| 127 | /** |
| 128 | * Output the widget's option form. |
| 129 | */ |
| 130 | public function form($instance) { |
| 131 | $instance = WPFunctions::get()->wpParseArgs( |
| 132 | (array)$instance, |
| 133 | [ |
| 134 | 'title' => __('Subscribe to Our Newsletter', 'mailpoet'), |
| 135 | ] |
| 136 | ); |
| 137 | |
| 138 | $formEditUrl = WPFunctions::get()->adminUrl('admin.php?page=mailpoet-form-editor-template-selection'); |
| 139 | |
| 140 | // set title |
| 141 | $title = isset($instance['title']) ? strip_tags($instance['title']) : ''; |
| 142 | |
| 143 | // set form |
| 144 | $selectedForm = isset($instance['form']) ? (int)($instance['form']) : 0; |
| 145 | |
| 146 | // get forms list |
| 147 | $forms = $this->formsRepository->findBy(['deletedAt' => null], ['name' => 'asc']); |
| 148 | ?><p> |
| 149 | <label for="<?php echo esc_attr($this->get_field_id('title')) ?>"><?php echo esc_html(__('Title:', 'mailpoet')); ?></label> |
| 150 | <input |
| 151 | type="text" |
| 152 | class="widefat" |
| 153 | id="<?php echo esc_attr($this->get_field_id('title')) ?>" |
| 154 | name="<?php echo esc_attr($this->get_field_name('title')); ?>" |
| 155 | value="<?php echo esc_attr($title); ?>" |
| 156 | /> |
| 157 | </p> |
| 158 | <p> |
| 159 | <select class="widefat" id="<?php echo esc_attr($this->get_field_id('form')) ?>" name="<?php echo esc_attr($this->get_field_name('form')); ?>"> |
| 160 | <?php |
| 161 | // Select the first one from the list if none selected |
| 162 | if ($selectedForm === 0 && !empty($forms)) $selectedForm = $forms[0]->getId(); |
| 163 | foreach ($forms as $form) { |
| 164 | $formName = $form->getName() ? $this->wp->escHtml($form->getName()) : '(' . _x('no name', 'fallback for forms without a name in a form list', 'mailpoet') . ')'; |
| 165 | $formName .= $form->getStatus() === FormEntity::STATUS_DISABLED ? ' (' . __('inactive', 'mailpoet') . ')' : ''; |
| 166 | ?> |
| 167 | <option value="<?php echo esc_attr((string)$form->getId()); ?>" <?php echo ($selectedForm === $form->getId()) ? 'selected="selected"' : ''; ?>><?php echo esc_html($formName); ?></option> |
| 168 | <?php } ?> |
| 169 | </select> |
| 170 | </p> |
| 171 | <p> |
| 172 | <a href="<?php echo esc_url($formEditUrl); ?>" target="_blank" class="mailpoet_form_new"><?php echo esc_html(__('Create a new form', 'mailpoet')); ?></a> |
| 173 | </p> |
| 174 | <?php |
| 175 | return ''; |
| 176 | } |
| 177 | |
| 178 | /** |
| 179 | * @param array{form?: int|string, form_type?: string, before_widget?: string, after_widget?: string, before_title?: string, after_title?: string } $args Widget arguments. |
| 180 | * Output the widget itself. |
| 181 | */ |
| 182 | public function widget($args, $instance = null) { |
| 183 | $this->assetsController->setupFrontEndDependencies(); |
| 184 | |
| 185 | $beforeWidget = !empty($args['before_widget']) ? $args['before_widget'] : ''; |
| 186 | $afterWidget = !empty($args['after_widget']) ? $args['after_widget'] : ''; |
| 187 | $beforeTitle = !empty($args['before_title']) ? $args['before_title'] : ''; |
| 188 | $afterTitle = !empty($args['after_title']) ? $args['after_title'] : ''; |
| 189 | |
| 190 | if ($instance === null) { |
| 191 | $instance = $args; |
| 192 | } |
| 193 | |
| 194 | $title = $this->wp->applyFilters( |
| 195 | 'widget_title', |
| 196 | !empty($instance['title']) ? $instance['title'] : '', |
| 197 | $instance, |
| 198 | $this->id_base // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps |
| 199 | ); |
| 200 | |
| 201 | // get form |
| 202 | if (!empty($instance['form'])) { |
| 203 | $form = $this->formsRepository->findOneById($instance['form']); |
| 204 | } else { |
| 205 | // Backwards compatibility for MAILPOET-3847 |
| 206 | // Get first non deleted form |
| 207 | $forms = $this->formsRepository->findBy(['deletedAt' => null], ['name' => 'asc']); |
| 208 | if (empty($forms)) return ''; |
| 209 | $form = $forms[0]; |
| 210 | } |
| 211 | |
| 212 | if (!$form) return ''; |
| 213 | if ($form->getDeletedAt()) return ''; |
| 214 | if ($form->getStatus() !== FormEntity::STATUS_ENABLED) return ''; |
| 215 | |
| 216 | $formType = 'widget'; |
| 217 | if ( |
| 218 | isset($instance['form_type']) && in_array( |
| 219 | $instance['form_type'], |
| 220 | [ |
| 221 | 'html', |
| 222 | 'php', |
| 223 | 'iframe', |
| 224 | 'shortcode', |
| 225 | ] |
| 226 | ) |
| 227 | ) { |
| 228 | $formType = $instance['form_type']; |
| 229 | } |
| 230 | |
| 231 | $body = (!empty($form->getBody()) ? $form->getBody() : []); |
| 232 | $output = ''; |
| 233 | $settings = $form->getSettings(); |
| 234 | |
| 235 | if (!empty($body) && is_array($settings)) { |
| 236 | $idBase = is_string($this->id_base) ? $this->id_base : 'mailpoet_form'; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps |
| 237 | $formId = $idBase . '_' . $form->getId(); |
| 238 | $data = [ |
| 239 | 'form_html_id' => $formId, |
| 240 | 'form_id' => $form->getId(), |
| 241 | 'form_type' => $formType, |
| 242 | 'form_success_message' => $settings['success_message'], |
| 243 | 'title' => $title, |
| 244 | 'styles' => $this->formRenderer->renderStyles($form, '#' . $formId, FormEntity::DISPLAY_TYPE_OTHERS), |
| 245 | 'html' => $this->formRenderer->renderHTML($form), |
| 246 | 'before_widget' => $beforeWidget, |
| 247 | 'after_widget' => $afterWidget, |
| 248 | 'before_title' => $beforeTitle, |
| 249 | 'after_title' => $afterTitle, |
| 250 | ]; |
| 251 | |
| 252 | // (POST) non ajax success/error variables |
| 253 | $data['success'] = ( |
| 254 | isset($_GET['mailpoet_success']) |
| 255 | && is_numeric($_GET['mailpoet_success']) |
| 256 | && (int)$_GET['mailpoet_success'] === $form->getId() |
| 257 | ); |
| 258 | $data['error'] = ( |
| 259 | isset($_GET['mailpoet_error']) |
| 260 | && is_numeric($_GET['mailpoet_error']) |
| 261 | && (int)$_GET['mailpoet_error'] === $form->getId() |
| 262 | ); |
| 263 | |
| 264 | // generate security token |
| 265 | $data['token'] = $this->wp->wpCreateNonce('mailpoet_token'); |
| 266 | |
| 267 | // add API version |
| 268 | $data['api_version'] = API::CURRENT_VERSION; |
| 269 | |
| 270 | // render form |
| 271 | $renderer = (new RendererFactory())->getRenderer(); |
| 272 | try { |
| 273 | $output = $renderer->render('form/front_end_form.html', $data); |
| 274 | $output = WPFunctions::get()->doShortcode($output); |
| 275 | |
| 276 | // Define the exact keys we want to keep for the filter context |
| 277 | $allowed_keys = [ |
| 278 | 'form_html_id' => null, |
| 279 | 'form_id' => null, |
| 280 | 'form_type' => null, |
| 281 | 'form_success_message' => null, |
| 282 | 'title' => null, |
| 283 | 'styles' => null, |
| 284 | 'html' => null, |
| 285 | 'before_widget' => null, |
| 286 | 'after_widget' => null, |
| 287 | 'before_title' => null, |
| 288 | 'after_title' => null, |
| 289 | ]; |
| 290 | |
| 291 | // Create a new context array for the filter, containing only the allowed keys. |
| 292 | // This automatically excludes 'success', 'error', 'token', and 'api_version'. |
| 293 | $filter_context_data = array_intersect_key($data, $allowed_keys); |
| 294 | |
| 295 | /** |
| 296 | * Filters the rendered MailPoet form HTML after shortcodes are processed. |
| 297 | * |
| 298 | * @since TBD Added the $data context parameter. |
| 299 | * |
| 300 | * @param string $output Rendered form HTML. |
| 301 | * @param array<string, mixed> $filter_context_data Rendering context (form id, type, styles, and more). |
| 302 | * @return string Filtered HTML. |
| 303 | */ |
| 304 | $output = $this->wp->applyFilters('mailpoet_form_widget_post_process', $output, $filter_context_data); |
| 305 | } catch (\Exception $e) { |
| 306 | $output = $e->getMessage(); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | if ($formType === 'widget') { |
| 311 | /** @var string $output */ |
| 312 | // We control the template and the data is sanitized |
| 313 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 314 | echo $output; |
| 315 | } else { |
| 316 | return $output; |
| 317 | } |
| 318 | } |
| 319 | } |
| 320 |