PluginProbe
Qi Blocks / trunk
Qi Blocks vtrunk
1.5.3 1.5.2 1.5.1 trunk 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 All 45 releases
qi-blocks / inc / admin / global-styles / class-qi-blocks-framework-global-styles.php

class-qi-blocks-framework-global-styles.php in Qi Blocks trunk, at inc/admin/global-styles/class-qi-blocks-framework-global-styles.php

746 lines 27.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 // Exit if accessed directly.
5 exit;
6 }
7
8 if ( ! class_exists( 'Qi_Blocks_Framework_Global_Styles' ) ) {
9 class Qi_Blocks_Framework_Global_Styles {
10 private static $instance;
11
12 public function __construct() {
13 // Create global options.
14 add_action( 'init', array( $this, 'add_options' ) );
15
16 // Extend main rest api routes with new case.
17 add_filter( 'qi_blocks_filter_rest_api_routes', array( $this, 'add_rest_api_routes' ) );
18
19 // Localize main editor js script with additional variables.
20 add_filter( 'qi_blocks_filter_localize_main_editor_js', array( $this, 'localize_script' ) );
21
22 // Add page inline styles.
23 // permission 20 is set in order to be after the main css file and after the global typography styles (@see Qi_Blocks_Global_Settings_Typography).
24 add_action( 'wp_enqueue_scripts', array( $this, 'add_page_inline_style' ), 20 );
25
26 // Set Qode themes Gutenberg styles.
27 add_action( 'enqueue_block_editor_assets', array( $this, 'set_themes_gutenberg_styles' ), 15 );
28 }
29
30 /**
31 * Module class instance
32 *
33 * @return Qi_Blocks_Framework_Global_Styles
34 */
35 public static function get_instance() {
36 if ( is_null( self::$instance ) ) {
37 self::$instance = new self();
38 }
39
40 return self::$instance;
41 }
42
43 public function localize_script( $global ) {
44 $global['currentPageID'] = 0;
45 $global['currentPageStyles'] = array();
46
47 return $global;
48 }
49
50 public function sanitize_block_selector( $selector ) {
51 return isset( $selector ) && is_string( $selector ) ? preg_replace( '/[^a-zA-Z0-9\-_.,:>\s\[\]*~=\"()#\'+]/', '', $selector ) : '';
52 }
53
54 public function add_options() {
55 if ( ! get_option( 'qi_blocks_global_styles' ) ) {
56 add_option(
57 'qi_blocks_global_styles',
58 array(
59 'posts' => array(),
60 'widgets' => array(),
61 'templates' => array(),
62 'undefined' => array(),
63 )
64 );
65 }
66 }
67
68 public function add_rest_api_routes( $routes ) {
69 $routes['get-global-styles'] = array(
70 'route' => 'get-styles',
71 'methods' => WP_REST_Server::READABLE,
72 'callback' => array( $this, 'get_global_styles_callback' ),
73 'permission_callback' => function () {
74 return current_user_can( 'edit_posts' );
75 },
76 );
77
78 $routes['update-global-styles'] = array(
79 'route' => 'update-styles',
80 'methods' => WP_REST_Server::CREATABLE,
81 'callback' => array( $this, 'update_global_styles_callback' ),
82 'permission_callback' => function () {
83 return current_user_can( 'edit_posts' ) && current_user_can( 'publish_posts' );
84 },
85 'args' => array(
86 'options' => array(
87 'required' => true,
88 'validate_callback' => function ( $param ) {
89 // Simple solution for validation can be 'is_array' value instead of callback function.
90 return is_array( $param ) ? $param : (array) $param;
91 },
92 ),
93 ),
94 );
95
96 return $routes;
97 }
98
99 public function get_global_styles_callback() {
100
101 if ( empty( $_GET ) ) {
102 qi_blocks_get_ajax_status( 'error', esc_html__( 'Get method is invalid', 'qi-blocks' ), array() );
103 } else {
104 $options = get_option( 'qi_blocks_global_styles' );
105 $page_id = isset( $_GET['page_id'] ) && ! empty( $_GET['page_id'] ) ? sanitize_text_field( $_GET['page_id'] ) : '';
106
107 if ( ! $this->user_can_manage_global_styles_target( $page_id ) ) {
108 qi_blocks_get_ajax_status( 'error', esc_html__( 'You are not allowed to access these options.', 'qi-blocks' ), array() );
109 }
110
111 if ( isset( $options ) ) {
112
113 if ( isset( $options['widgets'] ) && 'widget' === $page_id ) {
114 qi_blocks_get_ajax_status( 'success', esc_html__( 'Options are successfully returned', 'qi-blocks' ), $options['widgets'] );
115 } elseif ( isset( $options['templates'] ) && 'template' === $page_id ) {
116 qi_blocks_get_ajax_status( 'success', esc_html__( 'Options are successfully returned', 'qi-blocks' ), $options['templates'] );
117 } elseif ( isset( $options['posts'] ) && '' !== $page_id && isset( $options['posts'][ $page_id ] ) ) {
118 qi_blocks_get_ajax_status( 'success', esc_html__( 'Options are successfully returned', 'qi-blocks' ), $options['posts'][ $page_id ] );
119 } else {
120 qi_blocks_get_ajax_status( 'success', esc_html__( 'Options are successfully returned', 'qi-blocks' ), $options['undefined'] );
121 }
122 } else {
123 qi_blocks_get_ajax_status( 'error', esc_html__( 'Options are invalid', 'qi-blocks' ), array() );
124 }
125 }
126 }
127
128 public function update_global_styles_callback( $response ) {
129
130 if ( ! isset( $response ) || empty( $response->get_body() ) ) {
131 qi_blocks_get_ajax_status( 'error', esc_html__( 'Options can\'t be updated', 'qi-blocks' ) );
132 } else {
133 $response_data = json_decode( $response->get_body() );
134 $global_options = get_option( 'qi_blocks_global_styles' );
135
136 if ( ! empty( $response_data->options ) && isset( $global_options ) ) {
137 $options = $this->sanitize_global_options( $response_data->options );
138 $page_id = isset( $response_data->page_id ) && ! empty( $response_data->page_id ) ? esc_attr( $response_data->page_id ) : '';
139
140 if ( ! $this->user_can_manage_global_styles_target( $page_id ) ) {
141 qi_blocks_get_ajax_status( 'error', esc_html__( 'You are not allowed to update these options.', 'qi-blocks' ) );
142 }
143
144 // Sanitize and validate CSS options.
145 if ( isset( $global_options['widgets'] ) && 'widget' === $page_id ) {
146 $global_options['widgets'] = $options;
147 } elseif ( isset( $global_options['templates'] ) && 'template' === $page_id ) {
148 $global_options['templates'] = $options;
149 } elseif ( isset( $global_options['posts'] ) && '' !== $page_id ) {
150 $global_options['posts'][ $page_id ] = $options;
151 } else {
152 $global_options['undefined'] = $options;
153 }
154
155 update_option( 'qi_blocks_global_styles', $global_options );
156
157 qi_blocks_get_ajax_status( 'success', esc_html__( 'Options are saved', 'qi-blocks' ) );
158 } else {
159 qi_blocks_get_ajax_status( 'error', esc_html__( 'Options are invalid', 'qi-blocks' ) );
160 }
161 }
162 }
163
164 /**
165 * Verify object-level authorization for a global styles target.
166 *
167 * @param string $page_id Numeric post ID, or one of: widget, template.
168 *
169 * @return bool
170 */
171 private function user_can_manage_global_styles_target( $page_id ) {
172 if ( 'widget' === $page_id || 'template' === $page_id ) {
173 return current_user_can( 'edit_theme_options' );
174 }
175
176 if ( '' === $page_id ) {
177 return current_user_can( 'edit_theme_options' );
178 }
179
180 if ( is_numeric( $page_id ) ) {
181 $post_id = (int) $page_id;
182
183 if ( $post_id <= 0 ) {
184 return false;
185 }
186
187 return current_user_can( 'edit_post', $post_id );
188 }
189
190 return false;
191 }
192
193 /**
194 * Sanitize qi-blocks $options array received via AJAX.
195 *
196 * @param mixed $raw_options The raw options (decoded JSON -> array/object).
197 *
198 * @return object Sanitized options array safe to save.
199 */
200 private function sanitize_global_options( $raw_options ) {
201 // Ensure we have an array.
202 $options = is_array( $raw_options ) ? $raw_options : ( is_object( $raw_options ) ? (array) $raw_options : array() );
203
204 // Helper: reject if value contains dangerous tokens.
205 $contains_danger = function ( $value ) {
206 if ( ! is_string( $value ) ) {
207 return true;
208 }
209
210 $v = strtolower( $value );
211
212 // Dangerous patterns.
213 $danger_patterns = array(
214 '@import',
215 // old IE.
216 'expression(',
217 // javascript urls.
218 'javascript:',
219 // data URIs (can be used for exfiltration).
220 'data:',
221 // legacy.
222 'vbscript:',
223 // XBL.
224 '-moz-binding',
225 // IE behaviors.
226 'behavior:',
227 'onerror',
228 'onclick',
229 'onload',
230 'onfocus',
231 'onblur',
232 'onchange',
233 'onmouseover',
234 'onmouseout',
235 'onkeydown',
236 'onkeyup',
237 'oninput',
238 );
239
240 foreach ( $danger_patterns as $pat ) {
241 if ( strpos( $v, $pat ) !== false ) {
242 return true;
243 }
244 }
245
246 return false;
247 };
248
249 // Helper: sanitize a single CSS declaration block string -> returns cleaned css or empty string.
250 $sanitize_css_block = function ( $css_text ) use ( $contains_danger ) {
251 if ( ! is_string( $css_text ) ) {
252 return '';
253 }
254
255 // Normalize semicolons and remove HTML tags.
256 $css_text = wp_strip_all_tags( $css_text );
257 $css_text = trim( $css_text );
258
259 // Split into declarations by semicolon.
260 $declarations = preg_split( '/\s*;\s*/', $css_text, -1, PREG_SPLIT_NO_EMPTY );
261 $safe_declarations = array();
262
263 foreach ( $declarations as $decl ) {
264 // split only on first colon (property: value).
265 if ( strpos( $decl, ':' ) === false ) {
266 continue;
267 }
268
269 list( $prop, $val ) = array_map( 'trim', explode( ':', $decl, 2 ) );
270
271 // lowercase property for checking.
272 $prop_lc = strtolower( $prop );
273
274 // Reject content property outright (injection vector).
275 if ( 'content' === $prop_lc ) {
276 continue;
277 }
278
279 // If value contains dangerous tokens, skip.
280 if ( $contains_danger( $val ) ) {
281 continue;
282 }
283
284 // Basic safe cleanup of value.
285 // remove any control characters.
286 $val = preg_replace( '/[\\x00-\\x1F\\x7F]+/u', '', $val );
287 // escape quotes and trim.
288 $val = trim( $val );
289 // sanitize text field but keep some chars like parentheses for transform().
290 $val = sanitize_text_field( $val );
291
292 // re-add declaration.
293 $safe_declarations[] = $prop_lc . ': ' . $val;
294 }
295
296 if ( empty( $safe_declarations ) ) {
297 return '';
298 }
299
300 return implode( '; ', $safe_declarations ) . ';';
301 };
302
303 $sanitized_options = new stdClass();
304
305 foreach ( $options as $entry ) {
306 // Ensure entry is arrayed.
307 $entry = is_array( $entry ) ? $entry : ( is_object( $entry ) ? (array) $entry : array() );
308
309 $san_entry = new stdClass();
310
311 // Sanitize key.
312 $san_entry->key = isset( $entry['key'] ) ? sanitize_text_field( wp_strip_all_tags( $entry['key'] ) ) : '';
313
314 // Sanitize fonts if present (family, weight, style are arrays).
315 if ( isset( $entry['fonts'] ) && ( is_array( $entry['fonts'] ) || is_object( $entry['fonts'] ) ) ) {
316 $fonts_array = is_object( $entry['fonts'] ) ? (array) $entry['fonts'] : $entry['fonts'];
317
318 $san_fonts = new stdClass();
319 $allowed_font_keys = [ 'family', 'weight', 'style' ];
320
321 foreach ( $allowed_font_keys as $fkey ) {
322
323 if ( isset( $fonts_array[ $fkey ] ) && is_array( $fonts_array[ $fkey ] ) ) {
324
325 // sanitize each element of the array.
326 $san_fonts->$fkey = array_map(
327 function ( $val ) {
328 // number? → clean number.
329 if ( is_numeric( $val ) ) {
330 return intval( $val );
331 }
332
333 // string? → clean string.
334 return sanitize_text_field( wp_strip_all_tags( (string) $val ) );
335 },
336 $fonts_array[ $fkey ]
337 );
338
339 } else {
340 $san_fonts->$fkey = array();
341 }
342 }
343
344 $san_entry->fonts = $san_fonts;
345
346 } else {
347 $san_entry->fonts = (object) array(
348 'family' => array(),
349 'weight' => array(),
350 'style' => array(),
351 );
352 }
353
354 // Sanitize values (array of selector/styles objects).
355 $san_entry->values = array();
356 if ( isset( $entry['values'] ) && is_array( $entry['values'] ) ) {
357 foreach ( $entry['values'] as $val_item ) {
358 $val_item = is_array( $val_item ) ? $val_item : ( is_object( $val_item ) ? (array) $val_item : array() );
359
360 $san_val = new stdClass();
361 $san_val->selector = $this->sanitize_block_selector( $val_item['selector'] );
362
363 // Sanitize styles using parser/whitelist.
364 $san_val->styles = isset( $val_item['styles'] ) ? $sanitize_css_block( $val_item['styles'] ) : '';
365 $san_val->tablet_styles = isset( $val_item['tablet_styles'] ) ? $sanitize_css_block( $val_item['tablet_styles'] ) : '';
366 $san_val->mobile_styles = isset( $val_item['mobile_styles'] ) ? $sanitize_css_block( $val_item['mobile_styles'] ) : '';
367 $san_val->custom_styles = isset( $val_item['custom_styles'] ) ? $val_item['custom_styles'] : new stdClass();
368
369 // include only if selector or styles remain.
370 if ( '' !== $san_val->selector || '' !== $san_val->styles ) {
371 $san_entry->values[] = $san_val;
372 }
373 }
374 }
375
376 $sanitized_options->{$san_entry->key} = $san_entry;
377 }
378
379 return $sanitized_options;
380 }
381
382 public function has_configured_global_styles( $global_styles ) {
383 if ( empty( $global_styles ) || ! is_array( $global_styles ) ) {
384 return false;
385 }
386
387 foreach ( $global_styles as $section ) {
388 if ( empty( $section ) || ! is_array( $section ) ) {
389 continue;
390 }
391
392 foreach ( $section as $item ) {
393 if ( ! empty( $item ) ) {
394 return true;
395 }
396 }
397 }
398
399 return false;
400 }
401
402 public function add_page_inline_style() {
403 $global_styles = get_option( 'qi_blocks_global_styles' );
404
405 if ( $this->has_configured_global_styles( $global_styles ) ) {
406 $page_id = apply_filters( 'qi_blocks_filter_page_inline_style_page_id', get_queried_object_id() );
407 $styles = array();
408 $fonts = array(
409 'family' => array(),
410 'weight' => array(),
411 'style' => array(),
412 );
413
414 $update = false;
415 $include_italic_fonts = false;
416 $templates_selector = 'body ';
417
418 // Widgets blocks.
419 if ( isset( $global_styles['widgets'] ) && ! empty( $global_styles['widgets'] ) ) {
420 foreach ( $global_styles['widgets'] as $block_style ) {
421 // Skin styles loading if item is invalid.
422 if ( empty( $block_style ) ) {
423 continue;
424 }
425
426 // If the selector key is not allowed skip that styles.
427 if ( strpos( $block_style->key, 'qodef-widget-block' ) === false ) {
428 continue;
429 }
430
431 $block_style_fonts = (array) $block_style->fonts;
432 foreach ( array_keys( $fonts ) as $font_key ) {
433 if ( array_key_exists( $font_key, $block_style_fonts ) ) {
434 $fonts[ $font_key ] = array_merge( $fonts[ $font_key ], $block_style_fonts[ $font_key ] );
435 }
436 }
437
438 foreach ( $block_style->values as $block_element ) {
439 $block_selector = $this->sanitize_block_selector( $block_element->selector );
440
441 if ( ! empty( $block_element->styles ) ) {
442 $styles[] = $templates_selector . $block_selector . '{' . $block_element->styles . '}';
443 }
444
445 if ( isset( $block_element->tablet_styles ) && ! empty( $block_element->tablet_styles ) ) {
446 $styles[] = '@media (max-width: 1024px) { ' . $templates_selector . $block_selector . '{' . $block_element->tablet_styles . '} }';
447 }
448
449 if ( isset( $block_element->mobile_styles ) && ! empty( $block_element->mobile_styles ) ) {
450 $styles[] = '@media (max-width: 680px) { ' . $templates_selector . $block_selector . '{' . $block_element->mobile_styles . '} }';
451 }
452
453 if ( isset( $block_element->custom_styles ) && ! empty( $block_element->custom_styles ) ) {
454 foreach ( $block_element->custom_styles as $custom_style ) {
455 if ( isset( $custom_style->value ) && ! empty( $custom_style->value ) ) {
456 $styles[] = '@media (max-width: ' . $custom_style->key . 'px) { ' . $templates_selector . $block_selector . '{' . $custom_style->value . '} }';
457 }
458 }
459 }
460 }
461 }
462 }
463
464 // Template blocks.
465 if ( isset( $global_styles['templates'] ) && ! empty( $global_styles['templates'] ) ) {
466 foreach ( $global_styles['templates'] as $block_style ) {
467 // Skin styles loading if item is invalid.
468 if ( empty( $block_style ) ) {
469 continue;
470 }
471
472 // If the selector key is not allowed skip that styles.
473 if ( empty( $block_style->key ) || strpos( $block_style->key, 'qodef-template-block' ) === false ) {
474 continue;
475 }
476
477 $block_style_fonts = (array) $block_style->fonts;
478 foreach ( array_keys( $fonts ) as $font_key ) {
479 if ( array_key_exists( $font_key, $block_style_fonts ) ) {
480 $fonts[ $font_key ] = array_merge( $fonts[ $font_key ], $block_style_fonts[ $font_key ] );
481 }
482 }
483
484 foreach ( $block_style->values as $block_element ) {
485 $block_selector = $this->sanitize_block_selector( $block_element->selector );
486
487 if ( ! empty( $block_element->styles ) ) {
488 $styles[] = $templates_selector . $block_selector . '{' . $block_element->styles . '}';
489 }
490
491 if ( isset( $block_element->tablet_styles ) && ! empty( $block_element->tablet_styles ) ) {
492 $styles[] = '@media (max-width: 1024px) { ' . $templates_selector . $block_selector . '{' . $block_element->tablet_styles . '} }';
493 }
494
495 if ( isset( $block_element->mobile_styles ) && ! empty( $block_element->mobile_styles ) ) {
496 $styles[] = '@media (max-width: 680px) { ' . $templates_selector . $block_selector . '{' . $block_element->mobile_styles . '} }';
497 }
498
499 if ( isset( $block_element->custom_styles ) && ! empty( $block_element->custom_styles ) ) {
500 foreach ( $block_element->custom_styles as $custom_style ) {
501 if ( isset( $custom_style->value ) && ! empty( $custom_style->value ) ) {
502 $styles[] = '@media (max-width: ' . $custom_style->key . 'px) { ' . $templates_selector . $block_selector . '{' . $custom_style->value . '} }';
503 }
504 }
505 }
506 }
507 }
508 }
509
510 // Post blocks.
511 if ( isset( $global_styles['posts'][ $page_id ] ) && ! empty( $global_styles['posts'][ $page_id ] ) ) {
512 $get_page_content = trim( get_the_content( null, false, $page_id ) );
513
514 // Check if the content contains reusable blocks and expand the page content with the real reusable content.
515 preg_match_all( '({[\s]?[\'\"]ref[\'\"]:(.*?)[\s]?})', $get_page_content, $reusable_matches );
516
517 if ( ! empty( $reusable_matches ) && isset( $reusable_matches[1] ) && ! empty( $reusable_matches[1] ) ) {
518 $usable_content = '';
519
520 if ( is_array( $reusable_matches[1] ) ) {
521 foreach ( $reusable_matches[1] as $reusable_match_item ) {
522 $usable_content .= get_the_content( null, false, intval( $reusable_match_item ) );
523 }
524 } else {
525 $usable_content = get_the_content( null, false, intval( $reusable_matches[1] ) );
526 }
527
528 if ( ! empty( $usable_content ) ) {
529 $get_page_content .= $usable_content;
530 }
531 }
532
533 // Check if the content contains italic html selector and include corresponding font weights and styles for it.
534 if ( strpos( $get_page_content, '<em>' ) !== false ) {
535 $include_italic_fonts = true;
536 }
537
538 foreach ( $global_styles['posts'][ $page_id ] as $block_index => $block_style ) {
539 // Skin styles loading if item is invalid.
540 if ( empty( $block_style ) ) {
541 continue;
542 }
543
544 $block_style_fonts = ! empty( $block_style->fonts ) ? (array) $block_style->fonts : array();
545
546 foreach ( array_keys( $fonts ) as $font_key ) {
547
548 if ( array_key_exists( $font_key, $block_style_fonts ) ) {
549 $fonts[ $font_key ] = array_merge( $fonts[ $font_key ], $block_style_fonts[ $font_key ] );
550 }
551 }
552
553 foreach ( $block_style->values as $block_element ) {
554 $block_selector = $this->sanitize_block_selector( $block_element->selector );
555
556 if ( ! empty( $block_element->styles ) ) {
557 $styles[] = $block_selector . '{' . $block_element->styles . '}';
558 }
559
560 if ( isset( $block_element->tablet_styles ) && ! empty( $block_element->tablet_styles ) ) {
561 $styles[] = '@media (max-width: 1024px) { ' . $block_selector . '{' . $block_element->tablet_styles . '} }';
562 }
563
564 if ( isset( $block_element->mobile_styles ) && ! empty( $block_element->mobile_styles ) ) {
565 $styles[] = '@media (max-width: 680px) { ' . $block_selector . '{' . $block_element->mobile_styles . '} }';
566 }
567
568 if ( isset( $block_element->custom_styles ) && ! empty( $block_element->custom_styles ) ) {
569 foreach ( $block_element->custom_styles as $custom_style ) {
570 if ( isset( $custom_style->value ) && ! empty( $custom_style->value ) ) {
571 $styles[] = '@media (max-width: ' . $custom_style->key . 'px) { ' . $block_selector . '{' . $custom_style->value . '} }';
572 }
573 }
574 }
575 }
576 }
577 }
578
579 // Enqueue Google Fonts.
580 if ( isset( $fonts['family'] ) && ! empty( $fonts['family'] ) ) {
581 $this->include_google_fonts( $fonts, $include_italic_fonts );
582 }
583
584 // Load styles.
585 if ( ! empty( $styles ) ) {
586 wp_add_inline_style( 'qi-blocks-main', wp_strip_all_tags( implode( ' ', $styles ) ) );
587 }
588 }
589 }
590
591 public function include_google_fonts( $fonts, $include_italic_fonts = false ) {
592 $general_options = get_option( QI_BLOCKS_GENERAL_OPTIONS, array() );
593 $disable_google_fonts = ! empty( $general_options ) && isset( $general_options['disable_google_fonts'] );
594
595 // Disable Google Fonts loading if global option is on.
596 if ( $disable_google_fonts ) {
597 return;
598 }
599
600 if ( ! isset( $fonts['family'] ) || empty( $fonts['family'] ) || ! is_array( $fonts['family'] ) ) {
601 return;
602 }
603
604 $default_font_weight = isset( $fonts['weight'] ) && is_array( $fonts['weight'] ) ? array_unique( $fonts['weight'] ) : array();
605
606 if ( ! empty( $default_font_weight ) && ( ( isset( $fonts['style'] ) && is_array( $fonts['style'] ) && in_array( 'italic', $fonts['style'], true ) ) || $include_italic_fonts ) ) {
607 foreach ( $default_font_weight as $font_weight_value ) {
608 $default_font_weight[] = $font_weight_value . 'i';
609 }
610 }
611
612 $fonts_array = array_unique( apply_filters( 'qi_blocks_filter_google_fonts_list', $fonts['family'] ) );
613 $font_weight_str = implode( ',', array_unique( apply_filters( 'qi_blocks_filter_google_fonts_weight_list', $default_font_weight ) ) );
614 $font_subset_str = implode( ',', array_unique( apply_filters( 'qi_blocks_filter_google_fonts_subset_list', array() ) ) );
615
616 if ( ! empty( $fonts_array ) ) {
617 $modified_default_font_family = array();
618
619 foreach ( $fonts_array as $font ) {
620 $modified_default_font_family[] = $font . ':' . $font_weight_str;
621 }
622
623 $default_font_string = implode( '|', $modified_default_font_family );
624
625 $fonts_full_list_args = array(
626 'family' => rawurlencode( $default_font_string ),
627 'subset' => rawurlencode( $font_subset_str ),
628 'display' => 'swap',
629 );
630
631 $google_fonts_url = add_query_arg( $fonts_full_list_args, 'https://fonts.googleapis.com/css' );
632
633 wp_enqueue_style( 'qi-blocks-google-fonts', esc_url_raw( $google_fonts_url ), array(), '1.0.0' );
634 }
635 }
636
637 public function set_themes_gutenberg_styles() {
638 $upload_dir = wp_upload_dir( null, false );
639
640 if ( ! empty( $upload_dir ) && ini_get( 'allow_url_fopen' ) ) {
641 // Set default plugin folder path.
642 $uploads_qi_dir = $upload_dir['basedir'] . '/qi-blocks';
643 $uploads_qi_dir_url = $upload_dir['baseurl'] . '/qi-blocks';
644
645 // Create a new folder inside uploads.
646 if ( ! file_exists( trailingslashit( $uploads_qi_dir ) ) ) {
647 wp_mkdir_p( $uploads_qi_dir );
648 }
649
650 // Get all loaded Styles.
651 global $wp_styles;
652
653 foreach ( $wp_styles->queue as $style ) :
654 // Check style handle is our latest framework or previous one.
655 if ( 'qi-gutenberg-blocks-style' !== $style && strpos( $style, '-gutenberg-blocks-style' ) !== false || strpos( $style, '-modules-admin-styles' ) !== false ) {
656 $current_style = $wp_styles->registered[ $style ];
657
658 // Check the current style.
659 if ( ! empty( $current_style ) ) {
660 // Get activated theme data.
661 $current_theme = wp_get_theme();
662 $current_theme_name = esc_attr( str_replace( ' ', '-', strtolower( $current_theme->get( 'Name' ) ) ) );
663
664 // Get current file location.
665 $current_style_src = $current_style->src;
666 $current_style_info = pathinfo( $current_style_src );
667 $current_style_name = $current_style_info['filename'];
668 $current_style_extension = $current_style_info['extension'];
669 $current_style_full_name = $current_theme_name . '-' . $current_style_name . '.' . $current_style_extension;
670
671 // Set new file location.
672 $filename = $uploads_qi_dir . '/' . $current_style_full_name;
673 $filename_url = $uploads_qi_dir_url . '/' . $current_style_full_name;
674
675 // If a new file does not exist, create it.
676 if ( ! file_exists( $filename ) ) {
677 copy( $current_style_src, $filename ); // @codingStandardsIgnoreLine.
678
679 // Get current style content.
680 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
681 $current_style_content = @file_get_contents( $current_style_src );
682
683 if ( ! empty( $current_style_content ) ) {
684 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fopen
685 $file_handle = @fopen( $filename, 'w+' );
686
687 if ( $file_handle ) {
688 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fwrite, WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
689 @fwrite( $file_handle, str_replace( '!important', '', $current_style_content ) );
690 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fclose, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
691 @fclose( $file_handle );
692 }
693
694 // Dequeue current theme styles.
695 wp_dequeue_style( $style );
696
697 // Enqueue new theme styles.
698 wp_enqueue_style( $style . '-qi-blocks', $filename_url, array(), QI_BLOCKS_VERSION );
699 }
700 } else {
701
702 // Check if the theme style updated.
703 if ( ! get_transient( 'qi_blocks_check_theme_gutenberg_style' ) ) {
704 // Set time until expiration in seconds. Default value is 1 day.
705 set_transient( 'qi_blocks_check_theme_gutenberg_style', 1, 86400 );
706
707 // Get current style content.
708 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
709 $current_style_content = @file_get_contents( $current_style_src );
710
711 if ( ! empty( $current_style_content ) ) {
712 // Get new files data.
713 $new_style_size = filesize( $filename );
714 $current_theme_style = str_replace( '!important', '', $current_style_content );
715 $current_theme_style_size = strlen( $current_theme_style );
716
717 if ( $current_theme_style_size > $new_style_size ) {
718 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen, WordPress.WP.AlternativeFunctions.file_system_operations_fopen
719 $file_handle = @fopen( $filename, 'w+' );
720
721 if ( $file_handle ) {
722 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fwrite, WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
723 @fwrite( $file_handle, $current_theme_style );
724 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fclose, WordPress.WP.AlternativeFunctions.file_system_operations_fclose
725 @fclose( $file_handle );
726 }
727 }
728 }
729 }
730
731 // Dequeue current theme styles.
732 wp_dequeue_style( $style );
733
734 // Enqueue new theme styles.
735 wp_enqueue_style( $style . '-qi-blocks', $filename_url, array(), QI_BLOCKS_VERSION );
736 }
737 }
738 }
739 endforeach;
740 }
741 }
742 }
743
744 Qi_Blocks_Framework_Global_Styles::get_instance();
745 }
746