| @@ -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 ] ); |