PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.36.0
MailPoet – Newsletters, Email Marketing, and Automation v5.36.0
5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor / woocommerce / email-editor / src / Engine / class-site-style-sync-controller.php
mailpoet / vendor / woocommerce / email-editor / src / Engine Last commit date
Logger 11 months ago Patterns 11 months ago PersonalizationTags 2 days ago Renderer 2 days ago Templates 4 months ago class-assets-manager.php 5 months ago class-dependency-check.php 11 months ago class-email-api-controller.php 6 months ago class-email-editor.php 2 days ago class-email-styles-schema.php 11 months ago class-personalizer.php 2 days ago class-send-preview-email.php 5 months ago class-settings-controller.php 3 months ago class-site-style-sync-controller.php 4 months ago class-theme-controller.php 3 months ago class-user-theme.php 11 months ago content-editor.css 2 weeks ago content-shared.css 11 months ago index.php 11 months ago theme.json 3 months ago
class-site-style-sync-controller.php
295 lines
1 <?php
2 declare(strict_types = 1);
3 namespace Automattic\WooCommerce\EmailEditor\Engine;
4 if (!defined('ABSPATH')) exit;
5 use WP_Theme_JSON;
6 use WP_Theme_JSON_Resolver;
7 use Automattic\WooCommerce\EmailEditor\Integrations\Utils\Styles_Helper;
8 class Site_Style_Sync_Controller {
9 private ?WP_Theme_JSON $site_theme = null;
10 private ?array $base_theme_data = null;
11 private $email_safe_fonts = array();
12 public function __construct() {
13 add_action( 'init', array( $this, 'initialize' ), 20 );
14 }
15 public function initialize(): void {
16 add_action( 'switch_theme', array( $this, 'invalidate_site_theme_cache' ) );
17 add_action( 'customize_save_after', array( $this, 'invalidate_site_theme_cache' ) );
18 }
19 public function sync_site_styles( ?WP_Theme_JSON $base_theme = null ): array {
20 // Store base theme data for fallback lookups.
21 $this->base_theme_data = $base_theme ? $base_theme->get_data() : null;
22 $site_theme = $this->get_site_theme();
23 $site_data = $site_theme->get_data();
24 $synced_data = array(
25 'version' => 3,
26 'settings' => $this->sync_settings_data( $site_data['settings'] ?? array() ),
27 'styles' => $this->sync_styles_data( $site_data['styles'] ?? array() ),
28 );
29 $synced_data = apply_filters( 'woocommerce_email_editor_synced_site_styles', $synced_data, $site_data );
30 return $synced_data;
31 }
32 public function get_theme( ?WP_Theme_JSON $base_theme = null ): ?WP_Theme_JSON {
33 if ( ! $this->is_sync_enabled() ) {
34 return null;
35 }
36 $synced_data = $this->sync_site_styles( $base_theme );
37 if ( empty( $synced_data ) || ! isset( $synced_data['version'] ) ) {
38 return null;
39 }
40 return new WP_Theme_JSON( $synced_data, 'theme' );
41 }
42 public function is_sync_enabled(): bool {
43 return apply_filters( 'woocommerce_email_editor_site_style_sync_enabled', true );
44 }
45 public function invalidate_site_theme_cache(): void {
46 if ( ! $this->is_sync_enabled() ) {
47 return;
48 }
49 $this->site_theme = null;
50 }
51 private function get_site_theme(): WP_Theme_JSON {
52 if ( null === $this->site_theme ) {
53 // Get only the theme and user customizations (e.g. from site editor).
54 $this->site_theme = new WP_Theme_JSON();
55 $this->site_theme->merge( WP_Theme_JSON_Resolver::get_theme_data() );
56 $this->site_theme->merge( WP_Theme_JSON_Resolver::get_user_data() );
57 $this->site_theme = apply_filters( 'woocommerce_email_editor_site_theme', $this->site_theme );
58 if ( isset( $this->site_theme->get_raw_data()['styles'] ) ) {
59 $this->site_theme = WP_Theme_JSON::resolve_variables( $this->site_theme );
60 }
61 }
62 return $this->site_theme;
63 }
64 private function sync_settings_data( array $site_settings ): array {
65 $email_settings = array();
66 // Sync color palette.
67 if ( isset( $site_settings['color']['palette'] ) ) {
68 $email_settings['color']['palette'] = $site_settings['color']['palette'];
69 }
70 return $email_settings;
71 }
72 private function sync_styles_data( array $site_styles ): array {
73 $email_styles = array();
74 // Sync color styles.
75 if ( ! empty( $site_styles['color'] ) ) {
76 $email_styles['color'] = $this->convert_color_styles( $site_styles['color'] );
77 }
78 // Sync typography styles.
79 if ( ! empty( $site_styles['typography'] ) ) {
80 $email_styles['typography'] = $this->convert_typography_styles( $site_styles['typography'] );
81 }
82 // Sync spacing styles.
83 if ( ! empty( $site_styles['spacing'] ) ) {
84 $email_styles['spacing'] = $this->convert_spacing_styles( $site_styles['spacing'] );
85 }
86 // Sync element styles.
87 if ( ! empty( $site_styles['elements'] ) ) {
88 $email_styles['elements'] = $this->convert_element_styles( $site_styles['elements'] );
89 }
90 return $email_styles;
91 }
92 public function get_email_safe_fonts(): array {
93 if ( empty( $this->email_safe_fonts ) ) {
94 $theme_data = (array) json_decode( (string) file_get_contents( __DIR__ . '/theme.json' ), true );
95 $font_families = $theme_data['settings']['typography']['fontFamilies'] ?? array();
96 if ( ! empty( $font_families ) ) {
97 foreach ( $font_families as $font_family ) {
98 $this->email_safe_fonts[ strtolower( $font_family['slug'] ) ] = $font_family['fontFamily'];
99 }
100 }
101 }
102 return $this->email_safe_fonts;
103 }
104 private function convert_color_styles( array $color_styles ): array {
105 $email_colors = array();
106 $this->resolve_and_assign( $color_styles, 'background', $email_colors );
107 $this->resolve_and_assign( $color_styles, 'text', $email_colors );
108 return $email_colors;
109 }
110 private function convert_typography_styles( array $typography_styles, string $element = '' ): array {
111 $email_typography = array();
112 // Handle special cases with processors.
113 $this->resolve_and_assign( $typography_styles, 'fontFamily', $email_typography, array( $this, 'convert_to_email_safe_font' ) );
114 $this->resolve_and_assign(
115 $typography_styles,
116 'fontSize',
117 $email_typography,
118 function ( $value ) use ( $element ) {
119 // Try element-specific fallback first, then global fallback.
120 $fallback = null;
121 if ( $element ) {
122 $fallback = $this->get_base_theme_value( array( 'styles', 'elements', $element, 'typography', 'fontSize' ) );
123 }
124 if ( ! $fallback ) {
125 $fallback = $this->get_base_theme_value( array( 'styles', 'typography', 'fontSize' ) );
126 }
127 return $this->convert_to_px_size( $value, $fallback );
128 }
129 );
130 // Handle compatible properties without processing.
131 $compatible_props = array( 'fontWeight', 'fontStyle', 'lineHeight', 'letterSpacing', 'textTransform', 'textDecoration' );
132 foreach ( $compatible_props as $prop ) {
133 $this->resolve_and_assign( $typography_styles, $prop, $email_typography );
134 }
135 return $email_typography;
136 }
137 private function convert_spacing_styles( array $spacing_styles ): array {
138 $email_spacing = array();
139 $this->resolve_and_assign(
140 $spacing_styles,
141 'padding',
142 $email_spacing,
143 function ( $value ) {
144 return $this->convert_spacing_values( $value, array( 'styles', 'spacing', 'padding' ) );
145 }
146 );
147 $this->resolve_and_assign(
148 $spacing_styles,
149 'blockGap',
150 $email_spacing,
151 function ( $value ) {
152 $fallback = $this->get_base_theme_value( array( 'styles', 'spacing', 'blockGap' ) );
153 return $this->convert_to_px_size( $value, $fallback );
154 }
155 );
156 // Note: We intentionally skip margin as it's not supported in email renderer.
157 return $email_spacing;
158 }
159 private function convert_element_styles( array $element_styles ): array {
160 $email_elements = array();
161 // Process supported elements.
162 $supported_elements = array( 'heading', 'button', 'link', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' );
163 foreach ( $supported_elements as $element ) {
164 if ( isset( $element_styles[ $element ] ) ) {
165 $email_elements[ $element ] = $this->convert_element_style( $element_styles[ $element ], $element );
166 }
167 }
168 return $email_elements;
169 }
170 private function convert_element_style( array $element_style, string $element_name = '' ): array {
171 $email_element = array();
172 // Convert typography if present.
173 if ( isset( $element_style['typography'] ) ) {
174 $email_element['typography'] = $this->convert_typography_styles( $element_style['typography'], $element_name );
175 }
176 // Convert color if present.
177 if ( isset( $element_style['color'] ) ) {
178 $email_element['color'] = $this->convert_color_styles( $element_style['color'] );
179 }
180 // Convert spacing if present.
181 if ( isset( $element_style['spacing'] ) ) {
182 $email_element['spacing'] = $this->convert_spacing_styles( $element_style['spacing'] );
183 }
184 return $email_element;
185 }
186 private function resolve_and_assign( array $styles, string $property, array &$target, ?callable $processor = null ): bool {
187 if ( ! isset( $styles[ $property ] ) ) {
188 return false;
189 }
190 $resolved = $this->resolve_style_value( $styles[ $property ] );
191 if ( ! $resolved ) {
192 return false;
193 }
194 $target[ $property ] = $processor ? $processor( $resolved ) : $resolved;
195 return true;
196 }
197 private function resolve_style_value( $style_value ) {
198 // Check if this is a reference array.
199 if ( is_array( $style_value ) && isset( $style_value['ref'] ) ) {
200 $ref = $style_value['ref'];
201 if ( ! is_string( $ref ) || empty( $ref ) ) {
202 return null;
203 }
204 $path = explode( '.', $ref );
205 return _wp_array_get( $this->get_site_theme()->get_data(), $path, null );
206 }
207 return $style_value;
208 }
209 private function convert_to_email_safe_font( string $font_family ): string {
210 // Get email-safe fonts.
211 $email_safe_fonts = $this->get_email_safe_fonts();
212 // Map common web fonts to email-safe alternatives.
213 $font_map = array(
214 'helvetica' => $email_safe_fonts['arial'], // Arial fallback.
215 'times' => $email_safe_fonts['georgia'], // Georgia fallback.
216 'courier' => $email_safe_fonts['courier-new'], // Courier New.
217 'trebuchet' => $email_safe_fonts['trebuchet-ms'],
218 );
219 $email_safe_fonts = array_merge( $email_safe_fonts, $font_map );
220 $get_font_family = function ( $font_name ) use ( $email_safe_fonts ) {
221 $font_name_lower = strtolower( $font_name );
222 // First check for match in the email-safe slug.
223 if ( isset( $email_safe_fonts[ $font_name_lower ] ) ) {
224 return $email_safe_fonts[ $font_name_lower ];
225 }
226 // If no match in the slug, check for match in the font family name.
227 foreach ( $email_safe_fonts as $safe_font_slug => $safe_font ) {
228 if ( stripos( $safe_font, $font_name_lower ) !== false ) {
229 return $safe_font;
230 }
231 }
232 return null;
233 };
234 // Check if it's already an email-safe font.
235 $font_family_array = explode( ',', $font_family );
236 $first_font = trim( $font_family_array[0] );
237 $first_font = trim( $first_font, "\"'" );
238 $safe_font_family = $get_font_family( $first_font );
239 if ( $safe_font_family ) {
240 return $safe_font_family;
241 }
242 // Default to arial font if no match found.
243 return $email_safe_fonts['arial'];
244 }
245 private function convert_to_px_size( string $size, ?string $fallback = null ): string {
246 $converted = null;
247 // Replace clamp() with its minimum value. We use min because it's emails are most likely to be viewed on smaller screens.
248 if ( stripos( $size, 'clamp(' ) !== false ) {
249 $converted = Styles_Helper::clamp_to_static_px( $size, 'min' );
250 // If clamp_to_static_px returns the original value, it failed to convert.
251 if ( $converted === $size ) {
252 $converted = null;
253 }
254 }
255 // Try standard conversion.
256 if ( is_null( $converted ) ) {
257 $converted = Styles_Helper::convert_to_px( $size, false );
258 }
259 // If all conversions failed, use fallback if provided.
260 if ( is_null( $converted ) && $fallback ) {
261 return $fallback;
262 }
263 // Return converted value or original if conversion failed.
264 return $converted ?? $size;
265 }
266 private function convert_spacing_values( $spacing_values, array $base_path ) {
267 if ( ! is_string( $spacing_values ) && ! is_array( $spacing_values ) ) {
268 return $spacing_values;
269 }
270 if ( is_string( $spacing_values ) ) {
271 $fallback = $this->get_base_theme_value( $base_path );
272 return $this->convert_to_px_size( $spacing_values, $fallback );
273 }
274 $px_values = array();
275 foreach ( $spacing_values as $side => $value ) {
276 if ( is_string( $value ) ) {
277 // Build path for side-specific fallback (e.g., ['styles', 'spacing', 'padding', 'top']).
278 $side_path = array_merge( $base_path, array( $side ) );
279 $fallback = $this->get_base_theme_value( $side_path );
280 $px_values[ $side ] = $this->convert_to_px_size( $value, $fallback );
281 } else {
282 $px_values[ $side ] = $value;
283 }
284 }
285 return $px_values;
286 }
287 private function get_base_theme_value( array $path ): ?string {
288 if ( ! $this->base_theme_data ) {
289 return null;
290 }
291 $value = _wp_array_get( $this->base_theme_data, $path );
292 return is_string( $value ) ? $value : null;
293 }
294 }
295