PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / trunk
MailPoet – Newsletters, Email Marketing, and Automation vtrunk
5.37.0 5.36.1 5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / EmailEditor / Integrations / MailPoet / EmailEditor.php
mailpoet / lib / EmailEditor / Integrations / MailPoet Last commit date
Blocks 1 day ago Coupons 2 months ago Endpoints 3 months ago Patterns 2 months ago PersonalizationTags 1 day ago ProductCollection 2 months ago Templates 2 weeks ago AutomationEmailContextProvider.php 3 months ago AutomationEmailPreviewOrderProvider.php 3 months ago BlockEmailContentDetector.php 3 months ago Cli.php 1 month ago DependencyNotice.php 1 year ago EditorPageRenderer.php 1 day ago EmailApiController.php 1 month ago EmailEditor.php 1 day ago EmailEditorPreviewEmail.php 10 months ago Logger.php 11 months ago MailPoetCssInliner.php 9 months ago MailpoetCssInlinerFactory.php 1 year ago PersonalizationTagManager.php 1 day ago index.php 2 years ago
EmailEditor.php
198 lines
1 <?php declare(strict_types = 1);
2
3 namespace MailPoet\EmailEditor\Integrations\MailPoet;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\API\REST\API;
9 use MailPoet\EmailEditor\Integrations\MailPoet\Endpoints\GenerateSubjectSuggestionsEndpoint;
10 use MailPoet\EmailEditor\Integrations\MailPoet\Patterns\PatternsController;
11 use MailPoet\EmailEditor\Integrations\MailPoet\Templates\TemplatesController;
12 use MailPoet\Newsletter\NewslettersRepository;
13 use MailPoet\WP\Functions as WPFunctions;
14 use MailPoet\WPCOM\DotcomHelperFunctions;
15
16 class EmailEditor {
17 const MAILPOET_EMAIL_POST_TYPE = 'mailpoet_email';
18
19 private WPFunctions $wp;
20
21 private EmailApiController $emailApiController;
22
23 private EditorPageRenderer $editorPageRenderer;
24
25 private PatternsController $patternsController;
26
27 private Cli $cli;
28
29 private EmailEditorPreviewEmail $emailEditorPreviewEmail;
30
31 private PersonalizationTagManager $personalizationTagManager;
32
33 private TemplatesController $templatesController;
34
35 private DotcomHelperFunctions $dotcomHelperFunctions;
36
37 private NewslettersRepository $newslettersRepository;
38
39 private bool $editorAlreadyRendered = false;
40
41 public function __construct(
42 WPFunctions $wp,
43 EmailApiController $emailApiController,
44 EditorPageRenderer $editorPageRenderer,
45 EmailEditorPreviewEmail $emailEditorPreviewEmail,
46 PatternsController $patternsController,
47 TemplatesController $templatesController,
48 Cli $cli,
49 DotcomHelperFunctions $dotcomHelperFunctions,
50 PersonalizationTagManager $personalizationTagManager,
51 NewslettersRepository $newslettersRepository
52 ) {
53 $this->wp = $wp;
54 $this->emailApiController = $emailApiController;
55 $this->editorPageRenderer = $editorPageRenderer;
56 $this->patternsController = $patternsController;
57 $this->templatesController = $templatesController;
58 $this->cli = $cli;
59 $this->dotcomHelperFunctions = $dotcomHelperFunctions;
60 $this->emailEditorPreviewEmail = $emailEditorPreviewEmail;
61 $this->personalizationTagManager = $personalizationTagManager;
62 $this->newslettersRepository = $newslettersRepository;
63 }
64
65 public function initialize(): void {
66 $this->cli->initialize();
67 $this->wp->addFilter('woocommerce_email_editor_post_types', [$this, 'addEmailPostType']);
68 $this->wp->addAction('rest_delete_mailpoet_email', [$this->emailApiController, 'trashEmail'], 10, 1);
69 $this->wp->addFilter('woocommerce_is_email_editor_page', [$this, 'isEditorPage'], 10, 1);
70 $this->wp->addFilter('replace_editor', [$this, 'replaceEditor'], 10, 2);
71 $this->wp->addFilter('woocommerce_email_editor_send_preview_email', [$this->emailEditorPreviewEmail, 'sendPreviewEmail'], 10, 1);
72 $this->wp->addFilter('woocommerce_email_editor_send_preview_email_personalizer_context', [$this, 'extendPreviewPersonalizerContext'], 10, 1);
73 $this->wp->addFilter('rest_pre_insert_mailpoet_email', [$this, 'preserveAutomationEmailStatus'], 10, 2);
74 $this->patternsController->registerPatterns();
75 // Skip classic templates in Garden environment.
76 if (!$this->dotcomHelperFunctions->isGarden()) {
77 $this->templatesController->initialize();
78 }
79 $this->extendEmailPostApi();
80 $this->registerApiRoutes();
81 $this->personalizationTagManager->initialize();
82 }
83
84 public function addEmailPostType(array $postTypes): array {
85 $postTypes[] = [
86 'name' => self::MAILPOET_EMAIL_POST_TYPE,
87 'args' => [
88 'labels' => [
89 'name' => __('Emails', 'mailpoet'),
90 'singular_name' => __('Email', 'mailpoet'),
91 ],
92 'rewrite' => ['slug' => self::MAILPOET_EMAIL_POST_TYPE],
93 ],
94 ];
95 return $postTypes;
96 }
97
98 public function isEditorPage(bool $isEditorPage): bool {
99 if ($isEditorPage) {
100 return $isEditorPage;
101 }
102 // We need to check early if we are on the email editor page. The check runs early so we can't use current_screen() here.
103 if ($this->wp->isAdmin() && isset($_GET['post']) && isset($_GET['action']) && $_GET['action'] === 'edit' && is_numeric($_GET['post'])) {
104 $post = $this->wp->getPost((int)$_GET['post']);
105 return $post && $post->post_type === self::MAILPOET_EMAIL_POST_TYPE; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
106 }
107 return false;
108 }
109
110 private function registerApiRoutes(): void {
111 $this->wp->addAction(API::REST_API_INIT_ACTION, function (API $api) {
112 $api->registerPostRoute(
113 'email/generate-subject-suggestions',
114 GenerateSubjectSuggestionsEndpoint::class
115 );
116 });
117 }
118
119 public function extendEmailPostApi() {
120 $this->wp->registerRestField(self::MAILPOET_EMAIL_POST_TYPE, 'mailpoet_data', [
121 'get_callback' => [$this->emailApiController, 'getEmailData'],
122 'update_callback' => [$this->emailApiController, 'saveEmailData'],
123 'schema' => $this->emailApiController->getEmailDataSchema(),
124 ]);
125 }
126
127 public function replaceEditor($replace, $post) {
128 $currentScreen = $this->wp->getCurrentScreen();
129 if ($post->post_type === self::MAILPOET_EMAIL_POST_TYPE && $currentScreen) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
130 // The no-arg WP_Screen::get() re-fires this filter, and some plugins (e.g. Yoast SEO
131 // Premium) call it during admin_enqueue_scripts — while render() is still printing
132 // the admin header. Rendering again would echo a second editor container into <head>
133 // and leave the editor unresponsive, so only report that the editor was replaced.
134 if ($this->editorAlreadyRendered) {
135 return true;
136 }
137 $this->editorAlreadyRendered = true;
138 $this->editorPageRenderer->render();
139 return true;
140 }
141 return $replace;
142 }
143
144 /**
145 * Extend preview personalizer context with automation sample data.
146 * This is called by WooCommerce Email Editor when rendering preview in browser.
147 *
148 * @param array<string, mixed> $context
149 * @return array<string, mixed>
150 */
151 public function extendPreviewPersonalizerContext(array $context): array {
152 // Get the current post being previewed
153 $post = $this->wp->getPost();
154 if (!$post || $post->post_type !== self::MAILPOET_EMAIL_POST_TYPE) { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
155 return $context;
156 }
157
158 $newsletter = $this->newslettersRepository->findOneBy(['wpPost' => $post->ID]);
159 if (!$newsletter || (!$newsletter->isAutomation() && !$newsletter->isAutomationTransactional())) {
160 return $context;
161 }
162
163 // Get automation ID and extend tags
164 $automationId = $newsletter->getOptionValue('automationId');
165 if (!$automationId) {
166 return $context;
167 }
168
169 // Extend personalization tags based on automation subjects
170 $this->personalizationTagManager->extendPersonalizationTagsByAutomationSubjects((int)$automationId);
171
172 // Context is populated via woocommerce_email_editor_send_preview_email_personalizer_context filter
173 // which extensions can hook into to add their sample data
174 return $context;
175 }
176
177 /**
178 * Preserve the 'private' status for automation emails when updating via REST API.
179 * This prevents the block editor from changing the status to 'draft' during autosave.
180 *
181 * @param \stdClass $preparedPost The prepared post data for insertion.
182 * @param \WP_REST_Request $request The REST request object.
183 * @return \stdClass The modified post data.
184 */
185 public function preserveAutomationEmailStatus($preparedPost, $request) {
186 if (empty($preparedPost->ID)) {
187 return $preparedPost;
188 }
189
190 $newsletter = $this->newslettersRepository->findOneBy(['wpPost' => $preparedPost->ID]);
191 if ($newsletter && ($newsletter->isAutomation() || $newsletter->isAutomationTransactional())) {
192 $preparedPost->post_status = 'private'; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
193 }
194
195 return $preparedPost;
196 }
197 }
198