PluginProbe
YayMail – WooCommerce Email Customizer / 4.3.4
YayMail – WooCommerce Email Customizer v4.3.4
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 3.4.2 3.4.3 All 55 releases
yaymail / src / Models / TemplateModel.php

TemplateModel.php in YayMail – WooCommerce Email Customizer 4.3.4, at src/Models/TemplateModel.php

546 lines 23.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace YayMail\Models;
4
5 use YayMail\Abstracts\BaseElement;
6 use YayMail\Elements\ElementsHelper;
7 use YayMail\Elements\Heading;
8 use YayMail\Elements\Logo;
9 use YayMail\Elements\Text;
10 use YayMail\Elements\Footer;
11 use YayMail\YayMailTemplate;
12 use YayMail\Utils\SingletonTrait;
13 use YayMail\PostTypes\TemplatePostType;
14 use YayMail\Shortcodes\ShortcodesExecutor;
15 use YayMail\SupportedPlugins;
16 use YayMail\Utils\TemplateHelpers;
17
18 /**
19 * Template Model
20 *
21 * @method static TemplateModel get_instance()
22 */
23 class TemplateModel {
24 use SingletonTrait;
25
26 private const POST_TABLE = 'posts';
27 private const POST_META_TABLE = 'postmeta';
28 private static $meta_keys = YayMailTemplate::META_KEYS;
29
30 /**
31 * Query post with given arguments.
32 * Build query string base on given arguments
33 * then use $wpdb to query it.
34 *
35 * Returns post id or null if not found
36 *
37 * @param array $args Given args.
38 */
39 private static function query_template( $args = [] ) {
40
41 if ( ! is_array( $args ) ) {
42 return null;
43 }
44
45 global $wpdb;
46 $posts = $wpdb->prefix . self::POST_TABLE;
47 $post_meta = $wpdb->prefix . self::POST_META_TABLE;
48 $post_type = TemplatePostType::POST_TYPE;
49
50 $clauses = [
51 'select' => "SELECT posts.ID FROM $posts AS posts",
52 'join' => "JOIN $post_meta AS postmeta ON ( posts.ID = postmeta.post_id )",
53 'where' => "WHERE posts.post_type = '$post_type' AND posts.post_status IN ('publish', 'pending', 'future')",
54 ];
55
56 if ( isset( $args['name'] ) ) {
57 $template_name = $args['name'];
58 $template_meta_key = self::$meta_keys['name'];
59 $clauses['where'] .= $wpdb->prepare(
60 ' AND ( postmeta.meta_key=%s AND postmeta.meta_value = %s )',
61 $template_meta_key,
62 $template_name
63 );
64 }
65
66 $query_string = implode( ' ', $clauses );
67 $post_id = $wpdb->get_var( $query_string );
68
69 return $post_id;
70 }
71
72 /**
73 * Finds all YayMail templates and retrieves relevant information about each template.
74 *
75 * @return array An array of YayMail templates with their corresponding data.
76 * Each template is represented as an associative array containing the following keys:
77 * - 'key': The unique identifier for the template (same as 'id').
78 * - 'template_title': The title of the template.
79 * - 'status': The status of the template.
80 * - 'recipient': The recipient associated with the template.
81 * - 'source': The plugin name that the template originates from.
82 * - 'last_updated': The date and time of the last modification to the template
83 * (formatted according to the WordPress date and time settings) or 'N/A' if not available.
84 * - 'id': The unique identifier for the template (same as 'key').
85 * - '...': Other template-specific data retrieved from YayMail.
86 */
87 public static function find_all() {
88 $templates = [];
89
90 $excluded_templates = apply_filters( 'yaymail_excluded_templates', [ 'yaymail_global_header_footer' ] );
91
92 /* Wc Emails, may or may not be supported by us */
93 $wc_emails = \WC_Emails::instance()->get_emails();
94
95 foreach ( $wc_emails as $wc_email ) {
96 $template_id = $wc_email->id ?? null;
97
98 if ( ! isset( $template_id ) ) {
99 continue;
100 }
101
102 if ( in_array( $template_id, $excluded_templates, true ) ) {
103 continue;
104 }
105
106 if ( SupportedPlugins::get_instance()->get_support_info( $template_id )['status'] !== 'already_supported' ) {
107 // Templates is currently not editable, but could be supported by pro/addon
108 $template_data = self::get_uneditable_template( $wc_email, $templates );
109 $templates[] = $template_data;
110 continue;
111 }
112
113 $email_data = SupportedPlugins::get_instance()->get_yaymail_template_data( $template_id );
114 $template_data = self::get_yaymail_template( $email_data );
115 if ( isset( $template_data ) ) {
116 unset( $template_data['elements'] );
117 $templates[] = $template_data;
118 }
119 }//end foreach
120
121 // Make sure it will show templates for Automatewoo ...
122 // Because those templates don't appear in wc_emails
123 $template_ids = array_map( fn( $template ) => $template['name'], $templates );
124 foreach ( yaymail_get_emails() as $yaymail_email ) {
125 $template_id = $yaymail_email->get_id();
126 if ( in_array( $template_id, $excluded_templates, true ) ) {
127 continue;
128 }
129 if ( in_array( $template_id, $template_ids, true ) ) {
130 continue;
131 }
132
133 $email_data = SupportedPlugins::get_instance()->get_yaymail_template_data( $template_id );
134 $template_data = self::get_yaymail_template( $email_data );
135 if ( isset( $template_data ) ) {
136 unset( $template_data['elements'] );
137 $templates[] = $template_data;
138 }
139 }
140
141 return $templates;
142 }
143
144 private static function get_yaymail_template( $email_data ) {
145 /* Template is supported and ready to be edited */
146 $template = new YayMailTemplate( $email_data->get_id() );
147 if ( ! $template->is_exists() || empty( $email_data ) ) {
148 return null;
149 }
150
151 $template_data = $template->get_data();
152 $template_data['elements'] = $template->get_elements();
153 $template_data['key'] = $template_data['id'];
154 $template_data['template_title'] = $email_data->get_title();
155 $template_data['status'] = $template_data['status'];
156 $template_data['recipient'] = $email_data->get_recipient();
157 $template_data['source'] = $email_data->get_source()['plugin_name'] ?? '';
158 $template_data['last_updated'] = isset( get_post( $template_data['id'] )->post_modified ) ? date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( get_post( $template_data['id'] )->post_modified ) ) : esc_html__( 'N/A', 'yaymail' );
159 return $template_data;
160 }
161
162 /**
163 * Get template data for template that is currently not editable by YayMail and YayMail addons
164 * (It might or might not be supported by YayMail)
165 *
166 * @param object $wc_email The WooCommerce email object.
167 * @param array $templates The list of templates.
168 * @return array The template data.
169 */
170 private static function get_uneditable_template( $wc_email, $templates ): array {
171
172 $existed_post_ids = array_map( fn( $template ) => $template['id'], $templates );
173 do {
174 // Create a mockup post id
175 $mock_post_id = wp_rand( 1000, 10000 );
176 } while ( in_array(
177 $mock_post_id,
178 $existed_post_ids,
179 true
180 ) );
181
182 $template_id = $wc_email->id;
183 $support_info = SupportedPlugins::get_instance()->get_support_info( $template_id );
184 $support_status = $support_info['status'];
185 $source = $wc_email->source ?? $support_info['addon']['plugin_name'] ?? '';
186
187 // Get title
188
189 $support_status_labels = [
190 'pro_needed' => __( 'Pro', 'yaymail' ),
191 'addon_needed' => __( 'Addon', 'yaymail' ),
192 'not_supported' => __( 'N/A', 'yaymail' ),
193 ];
194
195 $template_title = $wc_email->title;
196 if ( isset( $support_status_labels[ $support_status ] ) ) {
197 $template_title .= ' (' . $support_status_labels[ $support_status ] . ')';
198 }
199
200 // Get last_updated
201 $post = get_post( $template_id );
202 $last_updated = $post && isset( $post->post_modified )
203 ? date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $post->post_modified ) )
204 : esc_html__( 'N/A', 'yaymail' );
205
206 return [
207 'id' => $mock_post_id,
208 'key' => $mock_post_id,
209 'name' => $template_id,
210 'support_status' => $support_status,
211 'addon_info' => $support_info['addon'],
212 'template_title' => $template_title,
213 'status' => 'inactive',
214 'recipient' => yaymail_get_email_recipient_zone( $wc_email ),
215 'source' => $source,
216 'last_updated' => $last_updated,
217 ];
218 }
219
220 /**
221 * Find template by given name
222 * Returns null if not found
223 *
224 * @param string $name
225 */
226 public static function find_by_name( $name ) {
227 $template_id = self::query_template(
228 [
229 'name' => $name,
230 ]
231 );
232 if ( empty( $template_id ) ) {
233 $support_info = SupportedPlugins::get_instance()->get_support_info( $name );
234 return [
235 'support_status' => $support_info['status'] ?? '',
236 'addon_info' => $support_info['addon'] ?? '',
237 ];
238 }
239 return self::get_meta_data( $template_id, $name );
240 }
241
242 /**
243 * Find template by post id
244 * Returns null if not found
245 *
246 * @param number $id
247 */
248 public static function find_by_id( $id ) {
249 if ( ! empty( $id ) && ! is_null( get_post( $id ) ) ) {
250 return self::get_meta_data( $id );
251 }
252 return null;
253 // Returns null on failure
254 }
255
256 public static function insert( $args = false ) {
257 // Insert template data to posts table
258 $template_args = [
259 'post_content' => '',
260 'post_type' => TemplatePostType::POST_TYPE,
261 'post_title' => '',
262 'post_status' => 'publish',
263 ];
264 $new_template_id = wp_insert_post( $template_args );
265
266 // Insert data to post meta table
267 update_post_meta( $new_template_id, self::$meta_keys['name'], $args['name'] );
268 update_post_meta( $new_template_id, self::$meta_keys['elements'], $args['elements'] );
269 update_post_meta( $new_template_id, self::$meta_keys['status'], 'inactive' );
270 update_post_meta( $new_template_id, self::$meta_keys['background_color'], YAYMAIL_COLOR_BACKGROUND_DEFAULT );
271 update_post_meta( $new_template_id, self::$meta_keys['is_v4_supported'], true );
272 if ( ! empty( $args['text_link_color'] ) ) {
273 update_post_meta( $new_template_id, self::$meta_keys['text_link_color'], YAYMAIL_COLOR_WC_DEFAULT );
274 }
275 if ( ! empty( $args['content_background_color'] ) ) {
276 update_post_meta( $new_template_id, self::$meta_keys['content_background_color'], '#ffffff' );
277 }
278 if ( ! empty( $args['content_text_color'] ) ) {
279 update_post_meta( $new_template_id, self::$meta_keys['content_text_color'], '#000000' );
280 }
281 if ( ! empty( $args['title_color'] ) ) {
282 update_post_meta( $new_template_id, self::$meta_keys['title_color'], '#000000' );
283 }
284 if ( ! empty( $args['global_header_settings'] ) ) {
285 update_post_meta( $new_template_id, self::$meta_keys['global_header_settings'], $args['global_header_settings'] );
286 }
287 if ( ! empty( $args['global_footer_settings'] ) ) {
288 update_post_meta( $new_template_id, self::$meta_keys['global_footer_settings'], $args['global_footer_settings'] );
289 }
290
291 // Hook insert data of integrations
292
293 return self::get_meta_data( $new_template_id );
294 }
295
296 public static function update( $template_id, $data, $is_save_revision = false ) {
297 if ( isset( $data['elements'] ) && is_array( $data['elements'] ) && isset( self::$meta_keys['elements'] ) ) {
298 $elements_data = TemplateHelpers::sanitize_elements_recursive( $data['elements'] );
299 update_post_meta( $template_id, self::$meta_keys['elements'], $elements_data );
300 }
301
302 if ( ! empty( $data['background_color'] ) && isset( self::$meta_keys['background_color'] ) ) {
303 update_post_meta( $template_id, self::$meta_keys['background_color'], $data['background_color'] );
304 }
305
306 if ( ! empty( $data['text_link_color'] ) && isset( self::$meta_keys['text_link_color'] ) ) {
307 update_post_meta( $template_id, self::$meta_keys['text_link_color'], $data['text_link_color'] );
308 }
309
310 if ( ! empty( $data['content_background_color'] ) && isset( self::$meta_keys['content_background_color'] ) ) {
311 update_post_meta( $template_id, self::$meta_keys['content_background_color'], $data['content_background_color'] );
312 }
313
314 if ( ! empty( $data['content_text_color'] ) && isset( self::$meta_keys['content_text_color'] ) ) {
315 update_post_meta( $template_id, self::$meta_keys['content_text_color'], $data['content_text_color'] );
316 }
317
318 if ( ! empty( $data['title_color'] ) && isset( self::$meta_keys['title_color'] ) ) {
319 update_post_meta( $template_id, self::$meta_keys['title_color'], $data['title_color'] );
320 }
321
322 if ( isset( $data['status'] ) && isset( self::$meta_keys['status'] ) ) {
323 update_post_meta( $template_id, self::$meta_keys['status'], $data['status'] );
324 }
325
326 if ( ! empty( $data['global_header_settings'] ) && isset( self::$meta_keys['global_header_settings'] ) ) {
327 update_post_meta( $template_id, self::$meta_keys['global_header_settings'], $data['global_header_settings'] );
328 }
329
330 if ( ! empty( $data['global_footer_settings'] ) && isset( self::$meta_keys['global_footer_settings'] ) ) {
331 update_post_meta( $template_id, self::$meta_keys['global_footer_settings'], $data['global_footer_settings'] );
332 }
333
334 // Update post_modified
335 $post_data = [
336 'ID' => $template_id,
337 'post_modified' => current_time( 'mysql' ),
338 'post_modified_gmt' => current_time( 'mysql', 1 ),
339 ];
340 wp_update_post( $post_data, true );
341
342 if ( $is_save_revision ) {
343 try {
344 $revision_model = RevisionModel::get_instance();
345 $new_revision = $revision_model->save( $template_id, $data );
346 } catch ( \Throwable $th ) {
347 $new_revision = null;
348 }
349
350 return [
351 'updated_data' => self::get_meta_data( $template_id ),
352 'new_revision' => $new_revision,
353 ];
354 }
355
356 return self::get_meta_data( $template_id );
357 }
358
359 public static function delete( $template_id ) {
360 wp_delete_post( $template_id, true );
361 // TODO: remove relatives.
362 }
363
364 /**
365 * Retrieve the global header and footer elements.
366 *
367 * @return array An array containing the global header and footer elements:
368 * - 'global_header_elements': array.
369 * - 'global_footer_elements': array.
370 */
371 public static function get_global_header_and_footer() {
372 $query_args = [
373 'name' => 'yaymail_global_header_footer',
374 ];
375 $template_id = self::query_template( $query_args );
376 if ( empty( $template_id ) ) {
377 self::insert(
378 [
379 'name' => 'yaymail_global_header_footer',
380 'elements' => yaymail_get_default_elements( 'yaymail_global_header_footer' ),
381 ]
382 );
383 $template_id = self::query_template( $query_args );
384 }
385 $retrieved_meta_data = self::get_meta_data( $template_id );
386 $elements = isset( $retrieved_meta_data['elements'] ) ? $retrieved_meta_data['elements'] : [];
387
388 if ( empty( $elements ) ) {
389 /**
390 * Get default elements
391 */
392 $global_header_footer_instance = \YayMail\Emails\GlobalHeaderFooter::get_instance();
393 $elements = $global_header_footer_instance->get_default_elements();
394 }
395
396 $divider_index = array_search( 'skeleton_divider', array_column( $elements, 'type' ), true );
397
398 $global_header_elements = [];
399 $global_footer_elements = [];
400
401 if ( false !== $divider_index ) {
402 $global_header_elements = array_slice( $elements, 0, $divider_index );
403 $global_footer_elements = array_slice( $elements, $divider_index + 1 );
404 }
405
406 return [
407 'global_header_elements' => $global_header_elements,
408 'global_footer_elements' => $global_footer_elements,
409 ];
410 }
411
412 /**
413 * Get template meta data from Database
414 *
415 * @param number $template_post_id
416 * @param string $template_name
417 */
418 private static function get_meta_data( $template_post_id, $template_name = null ) {
419
420 if ( empty( $template_name ) ) {
421 $template_name = self::query_meta_data( $template_post_id, self::$meta_keys['name'], '' );
422 }
423 $status = self::query_meta_data( $template_post_id, self::$meta_keys['status'], 'inactive' );
424
425 $post = isset( $template_post_id ) ? get_post( $template_post_id ) : null;
426 $post_modified = isset( $post ) ? $post->post_modified : '';
427
428 // /** For editable templates */
429 $template_elements = self::query_meta_data( $template_post_id, self::$meta_keys['elements'], [] );
430
431 $support_info = SupportedPlugins::get_instance()->get_support_info( $template_name );
432 if ( $support_info['status'] !== 'already_supported' ) {
433 // Template is not editable
434 $template_elements = self::get_uneditable_template_placeholder_elements( $support_info );
435 } elseif ( isset( $post ) ) {
436 // TODO: how to store global default value
437 $background_color = self::query_meta_data( $template_post_id, self::$meta_keys['background_color'], YayMailTemplate::DEFAULT_DATA['background_color'] );
438 $text_link_color = self::query_meta_data( $template_post_id, self::$meta_keys['text_link_color'], YayMailTemplate::DEFAULT_DATA['text_link_color'] );
439 $title_color = self::query_meta_data( $template_post_id, self::$meta_keys['title_color'], YayMailTemplate::DEFAULT_DATA['title_color'] );
440 $content_background_color = self::query_meta_data( $template_post_id, self::$meta_keys['content_background_color'], YayMailTemplate::DEFAULT_DATA['content_background_color'] );
441 $content_text_color = self::query_meta_data( $template_post_id, self::$meta_keys['content_text_color'], YayMailTemplate::DEFAULT_DATA['content_text_color'] );
442 $global_header_settings = self::query_meta_data( $template_post_id, self::$meta_keys['global_header_settings'], YayMailTemplate::DEFAULT_DATA['global_header_settings'] );
443 $global_footer_settings = self::query_meta_data( $template_post_id, self::$meta_keys['global_footer_settings'], YayMailTemplate::DEFAULT_DATA['global_footer_settings'] );
444 }
445
446 return [
447 'id' => $template_post_id,
448 'key' => $template_post_id,
449 'name' => $template_name,
450 'elements' => ElementsHelper::filter_available_elements( $template_elements, $template_name ),
451 'status' => $status,
452 'title_color' => $title_color ?? '#000000',
453 'background_color' => $background_color ?? YAYMAIL_COLOR_BACKGROUND_DEFAULT,
454 'text_link_color' => $text_link_color ?? YAYMAIL_COLOR_WC_DEFAULT,
455 'content_background_color' => $content_background_color ?? '#ffffff',
456 'content_text_color' => $content_text_color ?? '#000000',
457 'support_status' => $support_info['status'] ?? 'already_supported',
458 'addon_info' => $support_info['addon'] ?? '',
459 'post_modified' => $post_modified,
460 'global_header_settings' => $global_header_settings,
461 'global_footer_settings' => $global_footer_settings,
462 ];
463 }
464
465 private static function get_uneditable_template_placeholder_elements( array $support_info ): array {
466 $elements = [];
467
468 $elements[] = BaseElement::reduce_new_element( Logo::get_data() );
469 $elements[] = BaseElement::reduce_new_element( Heading::get_data() );
470 $elements[] = BaseElement::reduce_new_element( Text::get_data() );
471 $elements[] = BaseElement::reduce_new_element( Footer::get_data( [ 'rich_text' => '<p style="font-size: 14px;margin: 0px 0px 16px; text-align: center;">' . esc_html( get_bloginfo( 'name' ) ) . '&nbsp;- Built with <a style="color: ' . esc_attr( YAYMAIL_COLOR_WC_DEFAULT ) . '; font-weight: normal; text-decoration: underline;" href="https://woocommerce.com" target="_blank" rel="noopener">WooCommerce</a></p>' ] ) );
472
473 return $elements;
474 }
475
476
477 private static function query_meta_data( $post_id, $meta_name, $default ) {
478 if ( ! isset( $post_id ) ) {
479 return $default;
480 }
481
482 $meta_value = get_post_meta( $post_id, $meta_name, true );
483
484 if ( empty( $meta_value ) ) {
485 return $default;
486 }
487 return $meta_value;
488 }
489
490 public static function get_shortcodes_by_template_name_and_order_id( $template_name, $order_id ) {
491 do_action( 'yaymail_before_order_id_changed', $order_id );
492
493 $shortcodes = yaymail_get_email_shortcodes( $template_name );
494
495 // TODO: check shortcodes
496 // $shortcodes = apply_filters( 'yaymail_extra_shortcodes', $shortcodes, $template_name, $order_id );
497
498 $executor_data = self::get_shortcode_executor_data( $template_name, $order_id );
499 $executor = new ShortcodesExecutor( $shortcodes, $executor_data );
500 return $executor->get_shortcodes_content();
501 }
502
503 /**
504 * Gets data needed for executing a YayMail shortcode.
505 *
506 * @param string $template_name The template name.
507 * @param string $order_id The order ID. Uses sample data if empty or 'sample_order'.
508 * @return array [template, render_data, settings, is_placeholder]
509 */
510 public static function get_shortcode_executor_data( $template_name, $order_id ) {
511
512 return [
513 'template' => new YayMailTemplate( $template_name ),
514 'render_data' => empty( $order_id ) || ( ! empty( $order_id ) && 'sample_order' === $order_id ) ? [ 'is_sample' => true ] : [ 'order' => wc_get_order( $order_id ) ],
515 'settings' => yaymail_settings(),
516 'is_placeholder' => true,
517 ];
518 }
519
520 public static function get_elements_for_template( $template_id ): array {
521 $support_info = SupportedPlugins::get_instance()->get_support_info( $template_id );
522 if ( $support_info['status'] !== 'already_supported' ) {
523 $elements = array_map(
524 function( $element ) {
525 $element['available'] = false;
526 return $element;
527 },
528 yaymail_get_email_elements_data( 'new_order' )
529 );
530 return $elements;
531 }
532 return yaymail_get_email_elements_data( $template_id );
533 }
534
535 public static function get_short_data_by_name( $name ) {
536 $template_id = self::query_template( [ 'name' => $name ] );
537 if ( empty( $template_id ) ) {
538 return null;
539 }
540 return [
541 'id' => $template_id,
542 'status' => self::query_meta_data( $template_id, self::$meta_keys['status'], 'inactive' ),
543 ];
544 }
545 }
546