PluginProbe
YayMail – WooCommerce Email Customizer / 4.4.7
YayMail – WooCommerce Email Customizer v4.4.7
4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 trunk 1.9.6 2.1.4 2.1.5 3.2.2 3.2.6 3.2.7.1 3.2.8.1 3.2.9 3.3 3.3.1 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4 All 58 releases
yaymail / src / Ajax.php

Ajax.php in YayMail – WooCommerce Email Customizer 4.4.7, at src/Ajax.php

1,027 lines 45.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace YayMail;
4
5 use YayMail\Utils\SingletonTrait;
6 use YayMail\Models\SettingModel;
7 use YayMail\Models\TemplateModel;
8 use YayMail\Models\RevisionModel;
9 use YayMail\Migrations\MainMigration;
10 use YayMail\Utils\Helpers;
11 use YayMail\Utils\SafeUnserializer;
12 use YayMail\Utils\Logger;
13 use YayMail\Models\PatternTemplateModel;
14 use YayMail\Utils\TemplateHelpers;
15
16 /**
17 * I18n Logic
18 *
19 * @method static Ajax get_instance()
20 */
21 class Ajax {
22 use SingletonTrait;
23
24 protected function __construct() {
25 $this->init_hooks();
26 }
27
28 protected function init_hooks() {
29 add_action( 'wp_ajax_yaymail_preview_mail', [ $this, 'preview_mail' ] );
30 add_action( 'wp_ajax_yaymail_preview_mail_for_woo', [ $this, 'preview_mail_for_woo' ] );
31 add_action( 'wp_ajax_yaymail_send_test_mail', [ $this, 'send_test_mail' ] );
32 add_action( 'wp_ajax_yaymail_install_yaysmtp', [ $this, 'install_yaysmtp' ] );
33 add_action( 'wp_ajax_yaymail_get_custom_hook_html', [ $this, 'get_custom_hook_html' ] );
34 add_action( 'wp_ajax_yaymail_get_template_data_onload', [ $this, 'get_template_data_onload' ] );
35 add_action( 'wp_ajax_yaymail_export_templates', [ $this, 'export_templates' ] );
36 add_action( 'wp_ajax_yaymail_import_templates', [ $this, 'import_templates' ] );
37 add_action( 'wp_ajax_yaymail_review', [ $this, 'yaymail_review' ] );
38 add_action( 'wp_ajax_yaymail_change_ghf_tour', [ $this, 'change_ghf_tour' ] );
39 add_action( 'wp_ajax_yaymail_dismiss_multi_select_notice', [ $this, 'dismiss_multi_select_notice' ] );
40 add_action( 'wp_ajax_yaymail_export_state', [ $this, 'export_state' ] );
41 add_action( 'wp_ajax_yaymail_import_state', [ $this, 'import_state' ] );
42 add_action( 'wp_ajax_yaymail_dismiss_new_element_notification', [ $this, 'dismiss_new_element_notification' ] );
43 add_action( 'wp_ajax_yaymail_log_client_error', [ $this, 'log_client_error' ] );
44 }
45
46 /**
47 * Records a runtime error reported by the admin SPA (see apps/customizer/src/utils/client-error-reporter.ts).
48 *
49 * Goes to the WooCommerce log (WooCommerce > Status > Logs, source "yaymail-client") so support can ask the
50 * customer to copy it from wp-admin; falls back to the yaymail-logs file when WooCommerce is absent.
51 */
52 public function log_client_error() {
53 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
54 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) || ! current_user_can( 'manage_woocommerce' ) ) {
55 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ], 403 );
56 }
57
58 // A render loop in one browser must not be able to fill the log.
59 $rate_key = 'yaymail_client_error_rate_' . get_current_user_id();
60 $count = (int) get_transient( $rate_key );
61 if ( $count >= 20 ) {
62 return wp_send_json_success( [ 'logged' => false ] );
63 }
64 set_transient( $rate_key, $count + 1, MINUTE_IN_SECONDS );
65
66 $text = function ( $key, $max = 8000 ) {
67 $value = isset( $_POST[ $key ] ) ? sanitize_textarea_field( wp_unslash( $_POST[ $key ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
68 return mb_substr( $value, 0, $max );
69 };
70
71 $lines = [
72 'CLIENT ERROR [' . $text( 'source', 20 ) . ']: ' . $text( 'message', 1000 ),
73 'Version: ' . $text( 'version', 20 ) . ' | Platform: ' . $text( 'platform', 40 ) . ' | Template: ' . $text( 'template', 200 ),
74 'URL: ' . $text( 'url', 2000 ),
75 'User agent: ' . $text( 'user_agent', 500 ),
76 ];
77 foreach ( [ 'hint' => 'Hint', 'stack' => 'Stack', 'component_stack' => 'Component stack' ] as $key => $label ) {
78 $value = $text( $key );
79 if ( '' !== $value ) {
80 $lines[] = $label . ':' . PHP_EOL . $value;
81 }
82 }
83 $message = implode( PHP_EOL, $lines );
84
85 if ( function_exists( 'wc_get_logger' ) ) {
86 wc_get_logger()->error( $message, [ 'source' => 'yaymail-client' ] );
87 } else {
88 ( new Logger() )->log( $message );
89 }
90
91 wp_send_json_success( [ 'logged' => true ] );
92 }
93
94 public function import_state() {
95 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
96 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
97 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
98 }
99 try {
100 $import_file = isset( $_FILES['import_file'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_FILES['import_file'] ) ) : null;
101 if ( ! $import_file ) {
102 return wp_send_json_error( [ 'mess' => __( 'Can\'t find import file', 'yaymail' ) ] );
103 }
104
105 if ( $import_file['type'] !== 'application/zip' ) {
106 return wp_send_json_error( [ 'mess' => __( 'Invalid file type. Please upload a ZIP file.', 'yaymail' ) ] );
107 }
108
109 $zip = new \ZipArchive();
110 if ( $zip->open( $import_file['tmp_name'] ) !== true ) {
111 return wp_send_json_error( [ 'mess' => __( 'Cannot open ZIP file.', 'yaymail' ) ] );
112 }
113
114 $state_data = null;
115 for ( $i = 0; $i < $zip->numFiles; $i++ ) {
116 $filename = $zip->getNameIndex( $i );
117 // Matches both "yaymail_backup.json" (YayMail) and "yaymagic_email_builder_backup.json"
118 // (Email Builder) -- the zip always holds exactly this one entry, named per
119 // getBrandName() in backup/index.tsx.
120 if ( 1 === preg_match( '/_backup\.json$/', (string) $filename ) ) {
121 $state_data = $zip->getFromIndex( $i );
122 break;
123 }
124 }
125
126 $zip->close();
127
128 if ( ! $state_data ) {
129 return wp_send_json_error( [ 'mess' => __( 'Cannot find the backup JSON file in the ZIP file.', 'yaymail' ) ] );
130 }
131
132 $imported_data = json_decode( $state_data );
133 if ( json_last_error() !== JSON_ERROR_NONE ) {
134 return wp_send_json_error( [ 'mess' => __( 'Invalid JSON data in the state file.', 'yaymail' ) ] );
135 }
136
137 if ( empty( $imported_data->posts ) || empty( $imported_data->postmeta ) || empty( $imported_data->options ) ) {
138 return wp_send_json_error( [ 'mess' => __( 'Invalid state file structure.', 'yaymail' ) ] );
139 }
140
141 // Reject serialized gadgets before any DB writes. stdClass from YayMail exports is allowed.
142 if ( $this->has_serialized_object( $imported_data->postmeta, 'meta_value' )
143 || $this->has_serialized_object( $imported_data->options, 'option_value' ) ) {
144 return wp_send_json_error( [ 'mess' => __( 'Invalid backup data: serialized PHP objects are not allowed.', 'yaymail' ) ] );
145 }
146
147 $migration_model = \YayMail\Models\MigrationModel::get_instance();
148
149 $source_version = $imported_data->version;
150
151 $imported_posts = array_values(
152 array_filter(
153 $imported_data->posts,
154 function( $post ) {
155 return isset( $post->post_type ) && 'yaymail_template' === $post->post_type;
156 }
157 )
158 );
159
160 $imported_postmeta = array_values(
161 array_filter(
162 $imported_data->postmeta,
163 function( $postmeta ) use ( $imported_posts ) {
164 if ( empty( $imported_posts ) ) {
165 return false;
166 }
167 if ( ! isset( $postmeta->post_id ) || ! isset( $postmeta->meta_key ) ) {
168 return false;
169 }
170 if ( strpos( (string) $postmeta->meta_key, 'yaymail' ) === false ) {
171 return false;
172 }
173 foreach ( $imported_posts as $post ) {
174 if ( isset( $post->ID ) && (int) $post->ID === (int) $postmeta->post_id ) {
175 return true;
176 }
177 }
178 return false;
179 }
180 )
181 );
182
183 $imported_options = array_values(
184 array_filter(
185 $imported_data->options,
186 function( $option ) {
187 return isset( $option->option_name ) && strpos( (string) $option->option_name, 'yaymail' ) !== false;
188 }
189 )
190 );
191
192 $backup_data = [
193 'posts' => $imported_posts,
194 'postmeta' => $imported_postmeta,
195 'options' => $imported_options,
196 'created_date' => $imported_data->created_date ?? current_datetime()->format( 'Y-m-d H:i:s' ),
197 'name' => '_yaymail_import_backup_' . $source_version,
198 'version' => $source_version,
199 ];
200
201 $migration_model->reset( $backup_data );
202
203 wp_send_json_success(
204 [
205 'message' => __( 'Import state successfully', 'yaymail' ),
206 ]
207 );
208 } catch ( \Error $error ) {
209 yaymail_get_logger( $error );
210 wp_send_json_error( [ 'mess' => __( 'Import failed: ', 'yaymail' ) . $error->getMessage() ] );
211 } catch ( \Exception $exception ) {
212 yaymail_get_logger( $exception );
213 wp_send_json_error( [ 'mess' => __( 'Import failed: ', 'yaymail' ) . $exception->getMessage() ] );
214 }//end try
215 }
216
217 public function export_state() {
218 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
219 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
220 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
221 }
222
223 try {
224 global $wpdb;
225
226 /**
227 * Backup posts and postmeta
228 */
229 $query_posts = "
230 SELECT *
231 FROM {$wpdb->posts}
232 WHERE post_type = 'yaymail_template'
233 ";
234 $yaymail_template_posts = $wpdb->get_results( $query_posts );// phpcs:ignore
235
236 $query_postmeta = "
237 SELECT *
238 FROM {$wpdb->postmeta}
239 WHERE meta_key LIKE '%yaymail%'
240 ";
241 $yaymail_template_postmeta = $wpdb->get_results( $query_postmeta );// phpcs:ignore
242
243 $backup_data = [
244 'posts' => $yaymail_template_posts,
245 'postmeta' => $yaymail_template_postmeta,
246 ];
247 /** ****************************** */
248
249 /**
250 * Backup options
251 */
252 $query_options = "
253 SELECT *
254 FROM {$wpdb->options}
255 WHERE option_name LIKE '%yaymail%'
256 ";
257 $yaymail_options = $wpdb->get_results( $query_options ); // phpcs:ignore
258 $backup_data['options'] = $yaymail_options;
259
260 $backup_data['created_date'] = current_datetime()->format( 'Y-m-d H:i:s' );
261
262 // Fall back through running version / YAYWP_VERSION / '1.0' when the
263 // backup option hasn't been set yet (e.g. init_modules() hasn't
264 // recorded it), otherwise a falsy 'version' gets baked into the
265 // exported ZIP and MigrationModel::reset() rejects it on import with
266 // "Back up does not exist".
267 $version_backup = get_option( 'yaymail_version_backup' );
268 if ( ! empty( $version_backup ) ) {
269 $backup_data['version'] = $version_backup;
270 } elseif ( ! empty( yaymail_version() ) ) {
271 $backup_data['version'] = yaymail_version();
272 } else {
273 $backup_data['version'] = defined( 'YAYWP_VERSION' ) ? YAYWP_VERSION : '1.0';
274 }
275
276 $backup_data = apply_filters( 'yaymail_backup_state_data', $backup_data );
277
278 wp_send_json_success(
279 [
280 'message' => 'success',
281 'data' => $backup_data,
282 'file_name' => $this->export_file_name_prefix() . '_export_backup_' . gmdate( 'm-d-Y' ),
283 ]
284 );
285
286 } catch ( \Error $error ) {
287 yaymail_get_logger( $error );
288 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
289 } catch ( \Exception $exception ) {
290 yaymail_get_logger( $exception );
291 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
292 }//end try
293 }
294
295 public function sanitize( $array ) {
296
297 return wp_kses_post_deep( $array );
298 }
299
300 /**
301 * File name prefix for exported/backup downloads, matching the frontend's
302 * brand name per platform (constants/theme.ts WP_COLORS vs the free "yaymail"
303 * default). Reads the "platform" the frontend sends alongside these AJAX
304 * requests (see common/ajax's getPlatform() calls) since these admin-ajax.php
305 * requests don't reliably expose the calling screen via get_current_screen().
306 */
307 private function export_file_name_prefix() {
308 $platform = isset( $_POST['platform'] ) ? sanitize_text_field( wp_unslash( $_POST['platform'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Missing
309 return 'yaymagic-email-builder' === $platform ? 'yaymagic_email_builder' : 'yaymail';
310 }
311
312 /**
313 * Whether imported rows contain a serialized non-stdClass object.
314 *
315 * @param mixed $rows Imported rows, as decoded from the backup JSON.
316 * @param string $column Name of the column holding the serialized value.
317 * @return bool
318 */
319 private function has_serialized_object( $rows, string $column ): bool {
320 foreach ( (array) $rows as $row ) {
321 if ( ! isset( $row->{$column} ) ) {
322 continue;
323 }
324
325 if ( SafeUnserializer::has_object_payload( $row->{$column} ) ) {
326 return true;
327 }
328 }
329
330 return false;
331 }
332
333 public function process_plugin_installer( $slug ) {
334 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
335 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
336 require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
337 require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
338
339 $api = plugins_api(
340 'plugin_information',
341 [
342 'slug' => $slug,
343 'fields' => [
344 'short_description' => false,
345 'sections' => false,
346 'requires' => false,
347 'rating' => false,
348 'ratings' => false,
349 'downloaded' => false,
350 'last_updated' => false,
351 'added' => false,
352 'tags' => false,
353 'compatibility' => false,
354 'homepage' => false,
355 'donate_link' => false,
356 ],
357 ]
358 );
359
360 $skin = new \WP_Ajax_Upgrader_Skin();
361
362 $plugin_upgrader = new \Plugin_Upgrader( $skin );
363
364 try {
365 $result = $plugin_upgrader->install( $api->download_link );
366
367 if ( is_wp_error( $result ) ) {
368 yaymail_get_logger( $result );
369 }
370
371 return true;
372 } catch ( \Exception $exception ) {
373 yaymail_get_logger( $exception );
374 }
375
376 return false;
377 }
378
379 public function install_yaysmtp() {
380 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
381 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
382 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
383 }
384
385 if ( ! current_user_can( 'install_plugins' ) && ! current_user_can( 'activate_plugins' ) ) {
386 return wp_send_json_error( [ 'mess' => __( 'You do not have permission to install plugins', 'yaymail' ) ] );
387 }
388
389 try {
390 $is_installed = $this->process_plugin_installer( 'yaysmtp' );
391
392 if ( false === $is_installed ) {
393 wp_send_json_error( [ 'message' => $is_installed ] );
394 }
395
396 $result = activate_plugin( 'yaysmtp/yay-smtp.php' );
397
398 if ( is_wp_error( $result ) ) {
399 return wp_send_json_error( [ 'mess' => esc_html( $result->get_error_message() ) ] );
400 }
401
402 wp_send_json_success(
403 [
404 'installed' => null === $result,
405 ]
406 );
407
408 } catch ( \Error $error ) {
409 yaymail_get_logger( $error );
410 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
411 } catch ( \Exception $exception ) {
412 yaymail_get_logger( $exception );
413 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
414 }//end try
415 }
416
417 public function send_test_mail() {
418 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
419 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
420 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
421 }
422 try {
423 $template_name = isset( $_POST['template_name'] ) ? sanitize_text_field( wp_unslash( $_POST['template_name'] ) ) : '';
424 $order_id = isset( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : 'sample_order';
425 $email = isset( $_POST['email'] ) ? sanitize_text_field( $_POST['email'] ) : '';
426 $is_wc_email = strpos( $template_name, 'wp-core-' ) !== 0;
427
428 if ( empty( $template_name ) ) {
429 return wp_send_json_error( [ 'mess' => __( 'Can\'t find template', 'yaymail' ) ] );
430 }
431
432 if ( empty( $order_id ) && $is_wc_email ) {
433 return wp_send_json_error( [ 'mess' => __( 'Can\'t find order', 'yaymail' ) ] );
434 }
435
436 if ( empty( $email ) ) {
437 return wp_send_json_error( [ 'mess' => __( 'Can\'t find email', 'yaymail' ) ] );
438 }
439
440 $variant = yaymail_sanitize_template_variant( isset( $_POST['variant'] ) ? sanitize_text_field( wp_unslash( $_POST['variant'] ) ) : '' );
441 $template = new YayMailTemplate( $template_name, '', $variant );
442
443 $render_data = [];
444
445 if ( empty( $order_id ) || ( 'sample_order' === $order_id ) ) {
446 $render_data['is_sample'] = true;
447 } else {
448 $render_data['order'] = wc_get_order( $order_id );
449 }
450
451 $render_data['is_customized_preview'] = true;
452 // check if email template on preview and send test mail
453
454 update_option( 'yaymail_default_email_test', $email );
455
456 $html = $template->get_content( $render_data );
457
458 $headers = "Content-Type: text/html\r\n";
459 $subject = __( 'Email Test', 'yaymail' );
460
461 if ( $is_wc_email ) {
462 $class_wc_email = \WC_Emails::instance();
463 $send_mail = $class_wc_email->send( $email, $subject, $html, $headers, [] );
464 } else {
465 // $html is already the fully-rendered wp-core-mail template output.
466 // Without this marker, WpMail::custom_wp_mail_body() (hooked on the
467 // 'wp_mail' filter, meant to wrap *raw* WP core messages like a
468 // password-reset email) can't tell it apart from unrendered content
469 // and wraps it in the template a second time -- the same marker
470 // AddonWpMailController::woocommerce_mail_content() uses for real
471 // WooCommerce sends.
472 $html = '<!-- yaymail:wp-has-template-email-content -->' . $html;
473 $send_mail = wp_mail( $email, $subject, $html, $headers, [] );
474 }
475 if ( ! $send_mail ) {
476 return wp_send_json_error( [ 'mess' => __( 'Can\'t send email', 'yaymail' ) ] );
477 }
478
479 wp_send_json_success(
480 [
481 'email' => $email,
482 'send_mail_success' => $send_mail,
483 ]
484 );
485 } catch ( \Error $error ) {
486 yaymail_get_logger( $error );
487 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
488 } catch ( \Exception $exception ) {
489 yaymail_get_logger( $exception );
490 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
491 }//end try
492 }
493
494 public function preview_mail() {
495 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
496 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
497 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
498 }
499 try {
500 $order_id = ! empty( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : 'sample_order';
501 $template_data = isset( $_POST['template_data'] ) ? $this->sanitize( json_decode( wp_unslash( $_POST['template_data'] ), true ) ) : []; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
502 $unsaved_settings = isset( $_POST['unsaved_settings'] ) ? $this->sanitize( wp_unslash( $_POST['unsaved_settings'] ) ) : []; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
503
504 if ( empty( $template_data ) ) {
505 return wp_send_json_error( [ 'mess' => __( 'Can\'t find template', 'yaymail' ) ] );
506 }
507
508 if ( empty( $order_id ) ) {
509 return wp_send_json_error( [ 'mess' => __( 'Can\'t find order', 'yaymail' ) ] );
510 }
511
512 $template = new YayMailTemplate( $template_data['name'] );
513
514 if ( ! empty( $unsaved_settings ) ) {
515 global $yaymail_unsaved_settings;
516 $yaymail_unsaved_settings = $unsaved_settings;
517 }
518
519 $template->set_background_color( $template_data['background_color'] );
520 $template->set_text_link_color( $template_data['text_link_color'] );
521 $template->set_global_header_settings( $template_data['global_header_settings'] );
522 $template->set_global_footer_settings( $template_data['global_footer_settings'] );
523 $template->set_elements( $template_data['elements'] );
524
525 $render_data = [];
526
527 if ( empty( $order_id ) || ( 'sample_order' === $order_id ) ) {
528 $render_data['is_sample'] = true;
529 } else {
530 $render_data['order'] = wc_get_order( $order_id );
531 }
532
533 $render_data['is_customized_preview'] = true;
534 // check if email template on preview and send test mail
535
536 $html = $template->get_content( $render_data );
537
538 if ( strpos( $template->get_name(), 'wp-core-' ) !== 0 ) {
539 // TODO: render with passing settings
540 $current_email = null;
541 $subject = 'Sample Subject';
542 $emails = wc()->mailer()->emails;
543 foreach ( $emails as $email ) {
544 if ( $email->id === $template_data['name'] ) {
545 $current_email = $email;
546 if ( method_exists( $current_email, 'set_object' ) ) {
547 if ( ! empty( $render_data['order'] ) && is_a( $render_data['order'], '\WC_Order' ) ) {
548 $current_email->set_object( $render_data['order'] );
549 } else {
550 $current_email->set_object( Helpers::get_dummy_order() );
551 }
552 }
553 break;
554 }
555 }
556 }
557
558 if ( ! empty( $current_email ) ) {
559 $subject = $current_email->get_subject();
560 }
561 $email_address = wp_get_current_user()->user_email ?? '[email protected]';
562
563 wp_send_json_success(
564 [
565 'html' => $html,
566 'subject' => $subject,
567 'email_address' => $email_address,
568 ]
569 );
570 } catch ( \Error $error ) {
571 yaymail_get_logger( $error );
572 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
573 } catch ( \Exception $exception ) {
574 yaymail_get_logger( $exception );
575 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
576 }//end try
577 }
578
579 public function preview_mail_for_woo() {
580 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
581 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
582 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
583 }
584 try {
585 $template_name = isset( $_POST['template_name'] ) ? sanitize_text_field( wp_unslash( $_POST['template_name'] ) ) : '';
586 $search_order_id = isset( $_POST['search_order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['search_order_id'] ) ) : null;
587 $email_address = isset( $_POST['email_address'] ) ? sanitize_text_field( wp_unslash( $_POST['email_address'] ) ) : '';
588 $variant = yaymail_sanitize_template_variant( isset( $_POST['variant'] ) ? sanitize_text_field( wp_unslash( $_POST['variant'] ) ) : '' );
589
590 $email_preview_output = PreviewEmail\PreviewEmailWoo::email_preview_output( $search_order_id, $template_name, $email_address, $variant );
591
592 $email_preview_output = apply_filters( 'yaymail_preview_email', $email_preview_output, $search_order_id, $template_name, $email_address, $variant );
593
594 $send_mail = false;
595
596 if ( ! empty( $email_address ) && ! empty( $email_preview_output['html'] ) ) {
597 $headers = "Content-Type: text/html\r\n";
598 $class_wc_email = \WC_Emails::instance();
599 $subject = __( 'Email Preview', 'yaymail' );
600 $send_mail = $class_wc_email->send( $email_address, $subject, $email_preview_output['html'], $headers, [] );
601 if ( ! $send_mail ) {
602 return wp_send_json_error( [ 'mess' => __( 'Can\'t send email', 'yaymail' ) ] );
603 }
604 }
605
606 wp_send_json_success(
607 [
608 'html' => ! empty( $email_preview_output['html'] ) ? $email_preview_output['html'] : __( 'No email content found', 'yaymail' ),
609 'subject' => ! empty( $email_preview_output['subject'] ) ? $email_preview_output['subject'] : __( 'No subject found', 'yaymail' ),
610 'is_disabled_send_mail' => ! empty( $email_preview_output['is_disabled_send_mail'] ) ? $email_preview_output['is_disabled_send_mail'] : false,
611 'send_mail_success' => $send_mail,
612 ]
613 );
614 } catch ( \Error $error ) {
615 yaymail_get_logger( $error );
616 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
617 } catch ( \Exception $exception ) {
618 yaymail_get_logger( $exception );
619 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
620 }//end try
621 }
622
623 public function export_templates() {
624 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
625 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
626 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
627 }
628 try {
629 $templates = isset( $_POST['templates'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['templates'] ) ) : [];
630 // TODO: sanitize
631 $default = [
632 'post_type' => 'yaymail_template',
633 'post_status' => [ 'publish', 'pending', 'future' ],
634 'posts_per_page' => '-1',
635 'meta_query' => [
636 [
637 'key' => YayMailTemplate::META_KEYS['name'],
638 'value' => $templates,
639 'compare' => 'IN',
640 ],
641 // Export the default design only; import keys on name and would collapse variants onto it.
642 [
643 'key' => YayMailTemplate::META_KEYS['variant'],
644 'compare' => 'NOT EXISTS',
645 ],
646 ],
647 ];
648 $export_data = [];
649 $query = new \WP_Query( $default );
650 if ( $query->have_posts() ) {
651 $posts = $query->get_posts();
652 foreach ( $posts as $post ) {
653 $template_name = get_post_meta( $post->ID, YayMailTemplate::META_KEYS['name'], true );
654 $elements = get_post_meta( $post->ID, YayMailTemplate::META_KEYS['elements'], true );
655 $language = get_post_meta( $post->ID, YayMailTemplate::META_KEYS['language'], true );
656 $text_link_color = get_post_meta( $post->ID, YayMailTemplate::META_KEYS['text_link_color'], true );
657 $background_color = get_post_meta( $post->ID, YayMailTemplate::META_KEYS['background_color'], true );
658 $content_background_color = get_post_meta( $post->ID, YayMailTemplate::META_KEYS['content_background_color'], true );
659 $content_text_color = get_post_meta( $post->ID, YayMailTemplate::META_KEYS['content_text_color'], true );
660 $title_color = get_post_meta( $post->ID, YayMailTemplate::META_KEYS['title_color'], true );
661 $file_name = "{$template_name}.json";
662 // Every WP-core template (e.g. wp-core-mail) is saved with a
663 // non-empty language meta (the site locale), unlike WooCommerce
664 // templates -- gating on "no language" here used to silently
665 // drop every WP-core template from the export with no error.
666 // find_by_name() below only ever looks templates up by name, so
667 // there's no separate per-language row to disambiguate against.
668 $export_data[] = [
669 'file_name' => $file_name,
670 'templates_data' => [
671 'template' => $template_name,
672 'elements' => $elements,
673 'language' => $language,
674 'text_link_color' => $text_link_color,
675 'background_color' => $background_color,
676 'content_background_color' => $content_background_color,
677 'content_text_color' => $content_text_color,
678 'title_color' => $title_color,
679 ],
680 ];
681 }//end foreach
682 }//end if
683 wp_reset_postdata();
684 wp_send_json_success(
685 [
686 'message' => __( 'Export successfully', 'yaymail' ),
687 'data' => $export_data,
688 'file_name' => $this->export_file_name_prefix() . '_customizer_templates_' . gmdate( 'm-d-Y' ),
689 ]
690 );
691 } catch ( \Error $error ) {
692 yaymail_get_logger( $error );
693 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
694 } catch ( \Exception $exception ) {
695 yaymail_get_logger( $exception );
696 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
697 }//end try
698 }
699
700 public function import_templates() {
701 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
702 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
703 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
704 }
705 try {
706 if ( ! empty( $_FILES ) ) {
707 $result = $this->process_import( $_FILES );
708 wp_send_json_success(
709 [
710 'imported_data' => $result,
711 ]
712 );
713 } else {
714 wp_send_json_error( [ 'message' => __( 'Can\'t find import files.', 'yaymail' ) ] );
715 }
716 } catch ( \Error $error ) {
717 yaymail_get_logger( $error );
718 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
719 } catch ( \Exception $exception ) {
720 yaymail_get_logger( $exception );
721 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
722 }
723 }
724
725 public function process_import( $files ) {
726 global $wp_filesystem;
727 if ( empty( $wp_filesystem ) ) {
728 require_once ABSPATH . '/wp-admin/includes/file.php';
729 WP_Filesystem();
730 }
731 $imported_templates = [];
732 $is_legacy = false;
733 foreach ( $files as $file ) {
734 if ( isset( $file['type'] ) ) {
735 if ( 'application/json' === $file['type'] ) {
736 if ( ! empty( $file['tmp_name'] ) ) {
737 $file_tmp_name = sanitize_text_field( $file['tmp_name'] );
738 $file_content = $wp_filesystem->get_contents( $file_tmp_name );
739 $file_content = json_decode( $file_content, true );
740 if ( ! isset( $file_content['template'] ) ) {
741 if ( isset( $file_content['yaymailTemplateExport'] ) ) {
742 $is_legacy = true;
743 $imported_templates = array_merge( $imported_templates, $this->process_import_legacy( $file_content ) );
744 } else {
745 continue;
746 }
747 } else {
748 $update_result = $this->processing_import_update_data( $file_content );
749 if ( ! empty( $update_result ) ) {
750 $imported_templates[] = $update_result;
751 }
752 }
753 }//end if
754 }//end if
755 }//end if
756 }//end foreach
757 if ( $is_legacy ) {
758 MainMigration::get_instance()->migrate( true );
759 }
760 return $imported_templates;
761 }
762
763 public function process_import_legacy( $file_content ) {
764 $updated_templates = [];
765 // Import templates
766 foreach ( $file_content['yaymailTemplateExport'] as $template ) {
767 $updated_result = $this->processing_import_update_data( $template, true );
768 if ( ! empty( $updated_result ) ) {
769 $updated_templates[] = $updated_result;
770 }
771 }//end foreach
772 // Import settings
773 $import_settings = isset( $file_content['yaymail_settings'] ) ? $file_content['yaymail_settings'] : [];
774 if ( ! empty( $import_settings ) ) {
775 update_option( 'yaymail_settings', $import_settings );
776 }
777 return $updated_templates;
778 }
779
780 public function processing_import_update_data( $data, $is_legacy = false ) {
781 $template_name = $data['template'] ?? null;
782 $elements = $data['elements'] ?? null;
783 $text_link_color = $data['text_link_color'] ?? null;
784 $background_color = $data['background_color'] ?? null;
785 $title_color = $data['title_color'] ?? null;
786 $content_background_color = $data['content_background_color'] ?? null;
787 $content_text_color = $data['content_text_color'] ?? null;
788
789 if ( empty( $template_name ) ) {
790 return null;
791 }
792
793 $template = new YayMailTemplate( $template_name );
794 if ( ! $template->is_exists() ) {
795 return null;
796 }
797
798 $template->set_elements( $elements );
799 $template->set_text_link_color( $text_link_color );
800 $template->set_background_color( $background_color );
801 $template->set_title_color( $title_color );
802 $template->set_content_background_color( $content_background_color );
803 $template->set_content_text_color( $content_text_color );
804 if ( $is_legacy ) {
805 $template->set_status( 'inactive' );
806 }
807
808 $template->save();
809
810 wp_reset_postdata();
811
812 return [
813 'template_name' => $template_name,
814 ];
815 }
816
817 /**
818 * Process a custom hook request and generate HTML content.
819 *
820 * This function handles a custom hook request, generates HTML content based on the provided data and attributes.
821 * It is designed to be used as an AJAX callback.
822 *
823 * @example $_POST['data'] =
824 * [
825 * 'template_data' => YayMail\YayMailTemplate,
826 * 'order_id' => 'sample_order',
827 * 'attributes' => [
828 * [
829 * 'name' => 'hook',
830 * 'value' => 'your_hook'
831 * ],
832 * [
833 * 'name' => 'background_color',
834 * 'value' => '#ffffff'
835 * ]
836 * ]
837 * ]
838 *
839 * @return void This function sends a JSON response with HTML content or error messages.
840 */
841 public function get_custom_hook_html() {
842 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
843 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
844 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
845 }
846 try {
847 $attributes = isset( $_POST['data']['attributes'] ) ? $_POST['data']['attributes'] : []; // phpcs:ignore
848 if ( empty( $attributes ) ) {
849 return wp_send_json_error( [ 'mess' => __( 'Attributes empty', 'yaymail' ) ] );
850 }
851
852 /**
853 * Build data for shortcode
854 */
855 $template_model = \YayMail\Models\TemplateModel::get_instance();
856 $data = [];
857 if ( ! empty( $_POST['data']['template_data'] ) ) {
858 $data = \YayMail\Models\TemplateModel::get_shortcode_executor_data( sanitize_text_field( wp_unslash( $_POST['data']['template_data'] ) ), sanitize_text_field( wp_unslash( $_POST['data']['order_id'] ) ) );
859
860 $data['template']->set_props( sanitize_text_field( wp_unslash( $_POST['data']['template_data'] ) ) );
861 }
862
863 $hook_shortcodes = \YayMail\Shortcodes\HookShortcodes::get_instance();
864 $html = $hook_shortcodes->yaymail_handle_custom_hook_shortcode( $data, $attributes );
865 wp_send_json_success(
866 [
867 'html' => $html,
868 ]
869 );
870 } catch ( \Error $error ) {
871 yaymail_get_logger( $error );
872 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
873 } catch ( \Exception $exception ) {
874 yaymail_get_logger( $exception );
875 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
876 }//end try
877 }
878
879 /**
880 * Get all needed data when load YayMail template to customizer.
881 */
882 public function get_template_data_onload() {
883 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
884 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
885 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
886 }
887 try {
888 $setting_model = SettingModel::get_instance();
889 $settings_data = $setting_model->find_all();
890
891 $template_name = isset( $_POST['data']['template_name'] ) ? sanitize_text_field( wp_unslash( $_POST['data']['template_name'] ) ) : 'new_order';
892 $order_id = isset( $_POST['data']['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['data']['order_id'] ) ) : 'sample_order';
893 $variant = yaymail_sanitize_template_variant( isset( $_POST['data']['variant'] ) ? sanitize_text_field( wp_unslash( $_POST['data']['variant'] ) ) : '' );
894
895 $template_model = TemplateModel::get_instance();
896
897 $shortcodes_data = $template_model->get_shortcodes_by_template_name_and_order_id( $template_name, $order_id );
898
899 $templates_data = apply_filters( 'yaymail_get_all_templates', $template_model->find_all() );
900
901 $selected_template_data = $template_model->find_by_name( $template_name, '', $variant );
902
903 $elements_data = TemplateModel::get_elements_for_template( $template_name );
904
905 $revision_model = RevisionModel::get_instance();
906 $revisions_data = $revision_model->get_by_template( $template_name, $variant );
907
908 wp_send_json_success(
909 [
910 'settings_data' => $settings_data,
911 'templates_data' => $templates_data,
912 'selected_template_data' => $selected_template_data,
913 'elements_data' => $elements_data,
914 'revisions_data' => $revisions_data,
915 'shortcodes_data' => $shortcodes_data,
916 ]
917 );
918 } catch ( \Error $error ) {
919 yaymail_get_logger( $error );
920 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
921 } catch ( \Exception $exception ) {
922 yaymail_get_logger( $exception );
923 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
924 } catch ( \Throwable $throwable ) {
925 yaymail_get_logger( $throwable );
926 wp_send_json_error( [ 'mess' => $throwable->getMessage() ] );
927 }//end try
928 }
929
930 public function yaymail_review() {
931 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
932 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
933 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
934 }
935 try {
936
937 $yaymail_review = update_option( 'yaymail_review', true );
938
939 wp_send_json_success(
940 [
941 'reviewed' => $yaymail_review,
942 ]
943 );
944
945 } catch ( \Error $error ) {
946 yaymail_get_logger( $error );
947 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
948 } catch ( \Exception $exception ) {
949 yaymail_get_logger( $exception );
950 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
951 }
952 }
953
954 public function change_ghf_tour() {
955 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
956 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
957 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
958 }
959
960 try {
961 $next_move = isset( $_POST['next_move'] ) ? sanitize_text_field( wp_unslash( $_POST['next_move'] ) ) : 'initial';
962 $ghf_tour = update_option( 'yaymail_ghf_tour', $next_move );
963
964 wp_send_json_success(
965 [
966 'ghf_tour' => $ghf_tour,
967 ]
968 );
969 } catch ( \Error $error ) {
970 yaymail_get_logger( $error );
971 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
972 } catch ( \Exception $exception ) {
973 yaymail_get_logger( $exception );
974 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
975 }
976 }
977
978 public function dismiss_multi_select_notice() {
979 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
980 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
981 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
982 }
983
984 try {
985 update_option( 'yaymail_show_multi_select_notice', 'no' );
986
987 wp_send_json_success(
988 [
989 'show_multi_select_notice' => 'no',
990 ]
991 );
992 } catch ( \Error $error ) {
993 yaymail_get_logger( $error );
994 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
995 } catch ( \Exception $exception ) {
996 yaymail_get_logger( $exception );
997 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
998 }
999 }
1000
1001 public function dismiss_new_element_notification() {
1002 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
1003 if ( ! wp_verify_nonce( $nonce, 'yaymail_frontend_nonce' ) ) {
1004 return wp_send_json_error( [ 'mess' => __( 'Verify nonce failed', 'yaymail' ) ] );
1005 }
1006
1007 $elements = isset( $_POST['elements'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['elements'] ) ) : [];
1008
1009 $viewed_new_elements = get_option( 'yaymail_viewed_new_elements', [] );
1010 $viewed_new_elements = array_unique( array_merge( $viewed_new_elements, $elements ) );
1011
1012 try {
1013 update_option( 'yaymail_viewed_new_elements', $viewed_new_elements );
1014
1015 wp_send_json_success(
1016 [
1017 'viewed_new_elements' => $viewed_new_elements,
1018 ]
1019 );
1020 } catch ( \Error $error ) {
1021 wp_send_json_error( [ 'mess' => $error->getMessage() ] );
1022 } catch ( \Exception $exception ) {
1023 yaymail_get_logger( $exception );
1024 wp_send_json_error( [ 'mess' => $exception->getMessage() ] );
1025 }
1026 }
1027 }