PluginProbe
YayMail – WooCommerce Email Customizer / 4.4.5
YayMail – WooCommerce Email Customizer v4.4.5
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 3.4.1 All 57 releases
← All changes | src/Models/TemplateModel.php +213 -13 4.4.1 → 4.4.5 View file →
@@ -7,8 +7,9 @@
7 7 use YayMail\Elements\Heading;
8 8 use YayMail\Elements\Logo;
9 9 use YayMail\Elements\Text;
10 10 use YayMail\Elements\Footer;
11 +use YayMail\Integrations\TranslationModule;
11 12 use YayMail\YayMailTemplate;
12 13 use YayMail\Utils\SingletonTrait;
13 14 use YayMail\PostTypes\TemplatePostType;
14 15 use YayMail\Shortcodes\ShortcodesExecutor;
@@ -25,8 +26,11 @@
25 26 private const POST_TABLE = 'posts';
26 27 private const POST_META_TABLE = 'postmeta';
27 28 private static $meta_keys = YayMailTemplate::META_KEYS;
28 29
30 + /** @var string[]|null Per-request memo for get_active_default_template_names(). */
31 + private static $active_default_template_names = null;
32 +
29 33 /**
30 34 * Query post with given arguments.
31 35 * Build query string base on given arguments
32 36 * then use $wpdb to query it.
@@ -61,11 +65,29 @@
61 65 $template_name
62 66 );
63 67 }
64 68
65 - $query_string = implode( ' ', $clauses );
66 - $post_id = $wpdb->get_var( $query_string );
69 + // Variant clause is always present so default lookups never return a variant post.
70 + $variant_meta_key = self::$meta_keys['variant'];
71 + if ( isset( $args['variant'] ) && '' !== $args['variant'] ) {
72 + $clauses['join'] .= " JOIN $post_meta AS postmeta3 ON ( posts.ID = postmeta3.post_id )";
73 + $clauses['where'] .= $wpdb->prepare(
74 + ' AND ( postmeta3.meta_key=%s AND postmeta3.meta_value = %s )',
75 + $variant_meta_key,
76 + $args['variant']
77 + );
78 + } else {
79 + $clauses['join'] .= $wpdb->prepare(
80 + " LEFT JOIN $post_meta AS postmeta3 ON ( posts.ID = postmeta3.post_id AND postmeta3.meta_key = %s )", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
81 + $variant_meta_key
82 + );
83 + $clauses['where'] .= " AND ( postmeta3.meta_value IS NULL OR postmeta3.meta_value = '' )";
84 + }
67 85
86 + // Oldest post wins when duplicates exist so customizer and send target the same row.
87 + $query_string = implode( ' ', $clauses ) . ' ORDER BY posts.ID ASC LIMIT 1';
88 + $post_id = $wpdb->get_var( $query_string ); //phpcs:ignore
89 +
68 90 return $post_id;
69 91 }
70 92
71 93 /**
@@ -225,12 +247,18 @@
225 247 * Returns null if not found
226 248 *
227 249 * @param string $name
228 250 */
229 - public static function find_by_name( $name ) {
251 + /**
252 + * @param string $name
253 + * @param string $language Unused in Lite; kept so callers match the Pro signature.
254 + * @param string $variant Variant slug, '' for the default design.
255 + */
256 + public static function find_by_name( $name, $language = '', $variant = '' ) {
230 257 $template_id = self::query_template(
231 258 [
232 - 'name' => $name,
259 + 'name' => $name,
260 + 'variant' => $variant,
233 261 ]
234 262 );
235 263 if ( empty( $template_id ) ) {
236 264 $support_info = SupportedPlugins::get_instance()->get_support_info( $name );
@@ -289,8 +317,11 @@
289 317 }
290 318 if ( ! empty( $args['global_footer_settings'] ) ) {
291 319 update_post_meta( $new_template_id, self::$meta_keys['global_footer_settings'], $args['global_footer_settings'] );
292 320 }
321 + if ( isset( $args['variant'] ) && '' !== $args['variant'] ) {
322 + update_post_meta( $new_template_id, self::$meta_keys['variant'], $args['variant'] );
323 + }
293 324
294 325 // Hook insert data of integrations
295 326
296 327 return self::get_meta_data( $new_template_id );
@@ -333,8 +364,12 @@
333 364 if ( ! empty( $data['global_footer_settings'] ) && isset( self::$meta_keys['global_footer_settings'] ) ) {
334 365 update_post_meta( $template_id, self::$meta_keys['global_footer_settings'], $data['global_footer_settings'] );
335 366 }
336 367
368 + if ( isset( $data['preheader'] ) && isset( self::$meta_keys['preheader'] ) ) {
369 + update_post_meta( $template_id, self::$meta_keys['preheader'], sanitize_text_field( $data['preheader'] ) );
370 + }
371 +
337 372 // Update post_modified
338 373 $post_data = [
339 374 'ID' => $template_id,
340 375 'post_modified' => current_time( 'mysql' ),
@@ -370,18 +405,20 @@
370 405 * @return array An array containing the global header and footer elements:
371 406 * - 'global_header_elements': array.
372 407 * - 'global_footer_elements': array.
373 408 */
374 - public static function get_global_header_and_footer() {
409 + public static function get_global_header_and_footer( $language = '', $template_name = 'yaymail_global_header_footer' ) {
375 410 $query_args = [
376 - 'name' => 'yaymail_global_header_footer',
411 + 'name' => $template_name,
412 + 'language' => in_array( $language, [ 'en', 'en_US' ], true ) ? '' : $language,
377 413 ];
378 414 $template_id = self::query_template( $query_args );
379 415 if ( empty( $template_id ) ) {
380 416 self::insert(
381 417 [
382 - 'name' => 'yaymail_global_header_footer',
383 - 'elements' => yaymail_get_default_elements( 'yaymail_global_header_footer' ),
418 + 'name' => $template_name,
419 + 'elements' => yaymail_get_default_elements( $template_name ),
420 + 'language' => $language,
384 421 ]
385 422 );
386 423 $template_id = self::query_template( $query_args );
387 424 }
@@ -411,8 +448,43 @@
411 448 'global_footer_elements' => $global_footer_elements,
412 449 ];
413 450 }
414 451
452 + public static function get_global_headers_and_footers_for_all_available_languages( $template_name = 'yaymail_global_header_footer' ) {
453 + $multi_languages_instances = TranslationModule::get_instance()->current_integration;
454 +
455 + if ( ! empty( $multi_languages_instances ) ) {
456 + $languages = $multi_languages_instances->get_available_languages();
457 + } else {
458 + $languages = [ [ 'code' => 'en' ] ];
459 + }
460 +
461 + /**
462 + * Each language should have 3 fields: code, name, flag
463 + *
464 + * @Example [
465 + * code: 'en',
466 + * name: 'English',
467 + * flag: 'http://localhost:8080/wp-content/plugins/sitepress-multilingual-cms/res/flags/en.svg'
468 + * ]
469 + */
470 + $result = array_map(
471 + function ( $language ) use ( $template_name ) {
472 + $code = isset( $language['code'] ) ? $language['code'] : '';
473 + $global_header_and_footer = self::get_global_header_and_footer( 'en' !== $code ? $code : '', $template_name );
474 +
475 + return [
476 + 'language' => $code,
477 + 'global_header_elements' => $global_header_and_footer['global_header_elements'] ? $global_header_and_footer['global_header_elements'] : [],
478 + 'global_footer_elements' => $global_header_and_footer['global_footer_elements'] ? $global_header_and_footer['global_footer_elements'] : [],
479 + ];
480 + },
481 + $languages
482 + );
483 +
484 + return array_values( $result );
485 + }
486 +
415 487 /**
416 488 * Get template meta data from Database
417 489 *
418 490 * @param number $template_post_id
@@ -441,10 +513,11 @@
441 513 $text_link_color = self::query_meta_data( $template_post_id, self::$meta_keys['text_link_color'], YayMailTemplate::DEFAULT_DATA['text_link_color'] );
442 514 $title_color = self::query_meta_data( $template_post_id, self::$meta_keys['title_color'], YayMailTemplate::DEFAULT_DATA['title_color'] );
443 515 $content_background_color = self::query_meta_data( $template_post_id, self::$meta_keys['content_background_color'], YayMailTemplate::DEFAULT_DATA['content_background_color'] );
444 516 $content_text_color = self::query_meta_data( $template_post_id, self::$meta_keys['content_text_color'], YayMailTemplate::DEFAULT_DATA['content_text_color'] );
445 - $global_header_settings = self::query_meta_data( $template_post_id, self::$meta_keys['global_header_settings'], YayMailTemplate::DEFAULT_DATA['global_header_settings'] );
446 - $global_footer_settings = self::query_meta_data( $template_post_id, self::$meta_keys['global_footer_settings'], YayMailTemplate::DEFAULT_DATA['global_footer_settings'] );
517 + $global_header_settings = self::query_meta_data( $template_post_id, self::$meta_keys['global_header_settings'], self::get_default_global_header_settings() );
518 + $global_footer_settings = self::query_meta_data( $template_post_id, self::$meta_keys['global_footer_settings'], self::get_default_global_footer_settings() );
519 + $preheader = self::query_meta_data( $template_post_id, self::$meta_keys['preheader'], YayMailTemplate::DEFAULT_DATA['preheader'] );
447 520 }
448 521
449 522 return [
450 523 'id' => $template_post_id,
@@ -451,8 +524,9 @@
451 524 'key' => $template_post_id,
452 525 'name' => $template_name,
453 526 'elements' => ElementsHelper::filter_available_elements( $template_elements, $template_name ),
454 527 'status' => $status,
528 + 'variant' => self::query_meta_data( $template_post_id, self::$meta_keys['variant'], '' ),
455 529 'title_color' => $title_color ?? '#000000',
456 530 'background_color' => $background_color ?? YAYMAIL_COLOR_BACKGROUND_DEFAULT,
457 531 'text_link_color' => $text_link_color ?? YAYMAIL_COLOR_WC_DEFAULT,
458 532 'content_background_color' => $content_background_color ?? '#ffffff',
@@ -461,11 +535,57 @@
461 535 'addon_info' => $support_info['addon'] ?? '',
462 536 'post_modified' => $post_modified,
463 537 'global_header_settings' => isset( $global_header_settings ) ? $global_header_settings : [],
464 538 'global_footer_settings' => isset( $global_footer_settings ) ? $global_footer_settings : [],
539 + 'preheader' => isset( $preheader ) ? $preheader : YayMailTemplate::DEFAULT_DATA['preheader'],
540 + 'email_subject' => self::get_email_subject( $template_name ),
465 541 ];
466 542 }
467 543
544 + /**
545 + * Get email subject from WooCommerce email settings.
546 + *
547 + * @param string $template_name Email / template id.
548 + * @return string|null
549 + */
550 + private static function get_email_subject( $template_name ) {
551 + $settings = self::get_wc_email_settings( $template_name );
552 + return is_array( $settings ) && isset( $settings['subject'] ) ? (string) $settings['subject'] : null;
553 + }
554 +
555 + /**
556 + * Save email subject to WooCommerce email settings (same option as Woo admin).
557 + *
558 + * @param string $template_name Email / template id.
559 + * @param string $subject Subject line.
560 + */
561 + public static function save_email_subject( $template_name, $subject ) {
562 + if ( ! is_string( $template_name ) || '' === $template_name ) {
563 + return;
564 + }
565 +
566 + $settings = self::get_wc_email_settings( $template_name );
567 + $settings['subject'] = sanitize_text_field( $subject );
568 + update_option( self::get_wc_email_settings_option_name( $template_name ), $settings );
569 + }
570 +
571 + /**
572 + * @param string $template_name Email / template id.
573 + * @return string
574 + */
575 + private static function get_wc_email_settings_option_name( $template_name ) {
576 + return "woocommerce_{$template_name}_settings";
577 + }
578 +
579 + /**
580 + * @param string $template_name Email / template id.
581 + * @return array
582 + */
583 + private static function get_wc_email_settings( $template_name ) {
584 + $settings = get_option( self::get_wc_email_settings_option_name( $template_name ), [] );
585 + return is_array( $settings ) ? $settings : [];
586 + }
587 +
468 588 private static function get_uneditable_template_placeholder_elements( array $support_info ): array {
469 589 $elements = [];
470 590
471 591 $elements[] = BaseElement::reduce_new_element( Logo::get_data() );
@@ -476,9 +596,25 @@
476 596 return $elements;
477 597 }
478 598
479 599
480 - private static function query_meta_data( $post_id, $meta_name, $default ) {
600 + private static function get_default_global_footer_settings(): array {
601 + $default = YayMailTemplate::DEFAULT_DATA['global_footer_settings'];
602 + if ( ! function_exists( 'WC' ) ) {
603 + $default['footer_content'] = '<p style="font-size: 14px;margin: 0px 0px 16px; text-align: center;">[yaymail_site_name]</p>';
604 + }
605 + return apply_filters( 'yaymail_default_global_footer_settings', $default );
606 + }
607 +
608 + private static function get_default_global_header_settings(): array {
609 + $default = YayMailTemplate::DEFAULT_DATA['global_header_settings'];
610 + if ( ! function_exists( 'WC' ) ) {
611 + $default['heading_content'] = '<h1 style="font-size: 30px; font-weight: 300; line-height: normal; margin: 0px; color: inherit;">[yaymail_site_name]</h1>';
612 + }
613 + return apply_filters( 'yaymail_default_global_header_settings', $default );
614 + }
615 +
616 + protected static function query_meta_data( $post_id, $meta_name, $default ) {
481 617 if ( ! isset( $post_id ) ) {
482 618 return $default;
483 619 }
484 620
@@ -522,18 +658,82 @@
522 658
523 659 public static function get_elements_for_template( $template_id ): array {
524 660 $support_info = SupportedPlugins::get_instance()->get_support_info( $template_id );
525 661 if ( $support_info['status'] !== 'already_supported' ) {
526 - $elements = array_map(
662 + // 'new_order' only exists on WooCommerce sites; fall back to the WP-core
663 + // email so unsupported templates still resolve an elements catalog.
664 + $fallback_email_id = yaymail_get_email( 'new_order' ) ? 'new_order' : 'wp-core-mail';
665 + $elements = array_map(
527 666 function( $element ) {
528 667 $element['available'] = false;
529 668 return $element;
530 669 },
531 - yaymail_get_email_elements_data( 'new_order' )
670 + yaymail_get_email_elements_data( $fallback_email_id )
532 671 );
533 672 return $elements;
534 673 }
535 674 return yaymail_get_email_elements_data( $template_id );
675 + }
676 +
677 + /**
678 + * Names of every template whose default post is active, from one query.
679 + *
680 + * "Default post" mirrors query_template( [ 'name' => $name ] ) followed by the
681 + * status meta read: no variant, default language ('' / unset / en_US), oldest
682 + * post wins, missing status counts as inactive. Memoised for the request so the
683 + * core emails can all consult it at init without one lookup each.
684 + *
685 + * @return string[]
686 + */
687 + public static function get_active_default_template_names() {
688 + if ( null !== self::$active_default_template_names ) {
689 + return self::$active_default_template_names;
690 + }
691 +
692 + global $wpdb;
693 + $posts = $wpdb->prefix . self::POST_TABLE;
694 + $post_meta = $wpdb->prefix . self::POST_META_TABLE;
695 +
696 + $rows = $wpdb->get_results( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
697 + $wpdb->prepare(
698 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
699 + "SELECT posts.ID, name.meta_value AS name, status.meta_value AS status, lang.meta_value AS language, variant.meta_value AS variant
700 + FROM $posts AS posts
701 + JOIN $post_meta AS name ON ( posts.ID = name.post_id AND name.meta_key = %s )
702 + LEFT JOIN $post_meta AS status ON ( posts.ID = status.post_id AND status.meta_key = %s )
703 + LEFT JOIN $post_meta AS lang ON ( posts.ID = lang.post_id AND lang.meta_key = %s )
704 + LEFT JOIN $post_meta AS variant ON ( posts.ID = variant.post_id AND variant.meta_key = %s )
705 + WHERE posts.post_type = %s AND posts.post_status IN ('publish', 'pending', 'future')
706 + ORDER BY posts.ID ASC",
707 + self::$meta_keys['name'],
708 + self::$meta_keys['status'],
709 + self::$meta_keys['language'],
710 + self::$meta_keys['variant'],
711 + TemplatePostType::POST_TYPE
712 + ),
713 + ARRAY_A
714 + );
715 +
716 + $seen = [];
717 + $active = [];
718 + foreach ( (array) $rows as $row ) {
719 + if ( '' !== (string) $row['variant'] ) {
720 + continue;
721 + }
722 + if ( ! in_array( (string) $row['language'], [ '', 'en_US' ], true ) ) {
723 + continue;
724 + }
725 + if ( isset( $seen[ $row['name'] ] ) ) {
726 + continue;
727 + }
728 + $seen[ $row['name'] ] = true;
729 + if ( 'active' === $row['status'] ) {
730 + $active[] = $row['name'];
731 + }
732 + }
733 +
734 + self::$active_default_template_names = $active;
735 + return $active;
536 736 }
537 737
538 738 public static function get_short_data_by_name( $name ) {
539 739 $template_id = self::query_template( [ 'name' => $name ] );