PluginProbe
YayMail – WooCommerce Email Customizer / 4.3.3
YayMail – WooCommerce Email Customizer v4.3.3
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.3, at src/Models/TemplateModel.php

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