PluginProbe ʕ •ᴥ•ʔ
GenerateBlocks / 1.3.5
GenerateBlocks v1.3.5
trunk 1.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 2.0.0 2.0.1 2.0.2 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.3.0
generateblocks / includes / class-enqueue-css.php
generateblocks / includes Last commit date
class-do-css.php 5 years ago class-enqueue-css.php 4 years ago class-plugin-update.php 5 years ago class-render-blocks.php 5 years ago class-rest.php 5 years ago class-settings.php 5 years ago dashboard.php 5 years ago defaults.php 5 years ago functions.php 4 years ago general.php 4 years ago generate-css.php 5 years ago
class-enqueue-css.php
468 lines
1 <?php
2 /**
3 * Handles the CSS Output.
4 *
5 * @package GenerateBlocks
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit; // Exit if accessed directly.
10 }
11
12 /**
13 * Enqueue our block CSS to the current page.
14 */
15 class GenerateBlocks_Enqueue_CSS {
16 /**
17 * Instance.
18 *
19 * @access private
20 * @var object Instance
21 * @since 0.1
22 */
23 private static $instance;
24
25 /**
26 * Initiator.
27 *
28 * @since 0.1
29 * @return object initialized object of class.
30 */
31 public static function get_instance() {
32 if ( ! isset( self::$instance ) ) {
33 self::$instance = new self();
34 }
35
36 return self::$instance;
37 }
38
39 /**
40 * Constructor.
41 */
42 public function __construct() {
43 $this->add_options();
44
45 add_action( 'save_post', array( $this, 'post_update_option' ), 10, 2 );
46 add_action( 'save_post_wp_block', array( $this, 'wp_block_update' ), 10, 2 );
47 add_action( 'init', array( $this, 'enqueue_assets' ) );
48 add_filter( 'widget_update_callback', array( $this, 'force_file_regen_on_widget_save' ) );
49 add_action( 'customize_save_after', array( $this, 'force_file_regen_on_customizer_save' ) );
50 }
51
52 /**
53 * Enqueue our front-end assets.
54 */
55 public function enqueue_assets() {
56 $dynamic_css_priority = apply_filters( 'generateblocks_dynamic_css_priority', 10 );
57
58 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_dynamic_css' ), $dynamic_css_priority );
59 add_action( 'wp_head', array( $this, 'print_inline_css' ), $dynamic_css_priority );
60 }
61
62 /**
63 * Get the current page ID.
64 */
65 public function page_id() {
66 global $post;
67
68 $id = isset( $post ) ? $post->ID : false;
69 $id = ( ! is_singular() ) ? false : $id;
70 $id = ( function_exists( 'is_shop' ) && is_shop() ) ? get_option( 'woocommerce_shop_page_id' ) : $id;
71 $id = ( is_home() ) ? get_option( 'page_for_posts' ) : $id;
72
73 return $id;
74 }
75
76 /**
77 * Determine if we're using file mode or inline mode.
78 */
79 public function mode() {
80 // Check if we're using file mode or inline mode.
81 // Default to file mode and fallback to inline if file mode is not possible.
82 $mode = apply_filters( 'generateblocks_css_print_method', 'file' );
83
84 if (
85 ( function_exists( 'is_customize_preview' ) && is_customize_preview() )
86 ||
87 is_preview()
88 ||
89 // AMP inlines all CSS, so inlining from the start improves CSS processing performance.
90 ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() )
91 ) {
92 return 'inline';
93 }
94
95 // Additional checks for file mode.
96 if ( 'file' === $mode && $this->needs_update() ) {
97 // Only allow processing 1 file every 5 seconds.
98 $current_time = (int) time();
99 $last_time = (int) get_option( 'generateblocks_dynamic_css_time' );
100
101 if ( 5 <= ( $current_time - $last_time ) ) {
102 // Attempt to write to the file.
103 $mode = ( $this->can_write() && $this->make_css() ) ? 'file' : 'inline';
104
105 // Does again if the file exists.
106 if ( 'file' === $mode ) {
107 $mode = ( file_exists( $this->file( 'path' ) ) ) ? 'file' : 'inline';
108 }
109 }
110 }
111
112 return $mode;
113 }
114
115 /**
116 * Enqueue the dynamic CSS.
117 */
118 public function enqueue_dynamic_css() {
119 $page_id = $this->page_id();
120
121 if ( ! $page_id ) {
122 return;
123 }
124
125 $has_generateblocks = get_post_meta( $page_id, '_generateblocks_dynamic_css_version', true );
126
127 if ( empty( $has_generateblocks ) ) {
128 return;
129 }
130
131 if ( 'file' === $this->mode() ) {
132 wp_enqueue_style( 'generateblocks', esc_url( $this->file( 'uri' ) ), array(), null ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
133 }
134 }
135
136 /**
137 * Print our inline CSS.
138 */
139 public function print_inline_css() {
140 if ( 'inline' === $this->mode() || ! wp_style_is( 'generateblocks', 'enqueued' ) ) {
141 $css = generateblocks_get_frontend_block_css();
142
143 if ( empty( $css ) ) {
144 return;
145 }
146
147 printf(
148 '<style id="generateblocks-css">%s</style>',
149 wp_strip_all_tags( $css ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
150 );
151 }
152 }
153
154 /**
155 * Make our CSS.
156 */
157 public function make_css() {
158 $page_id = $this->page_id();
159
160 if ( ! $page_id ) {
161 return false;
162 }
163
164 $has_generateblocks = get_post_meta( $page_id, '_generateblocks_dynamic_css_version', true );
165
166 if ( empty( $has_generateblocks ) ) {
167 return false;
168 }
169
170 $content = generateblocks_get_frontend_block_css();
171
172 if ( ! $content ) {
173 return false;
174 }
175
176 // If we only have a little CSS, we should inline it.
177 $css_size = strlen( $content );
178
179 if ( $css_size < (int) apply_filters( 'generateblocks_css_inline_length', 500 ) ) {
180 return false;
181 }
182
183 global $wp_filesystem;
184
185 // Initialize the WordPress filesystem.
186 if ( empty( $wp_filesystem ) ) {
187 require_once ABSPATH . '/wp-admin/includes/file.php';
188 WP_Filesystem();
189 }
190
191 // Take care of domain mapping.
192 if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
193 if ( function_exists( 'domain_mapping_siteurl' ) && function_exists( 'get_original_url' ) ) {
194 $mapped_domain = domain_mapping_siteurl( false );
195 $original_domain = get_original_url( 'siteurl' );
196
197 $content = str_replace( $original_domain, $mapped_domain, $content );
198 }
199 }
200
201 if ( is_writable( $this->file( 'path' ) ) || ( ! file_exists( $this->file( 'path' ) ) && is_writable( dirname( $this->file( 'path' ) ) ) ) ) {
202
203 if ( ! $wp_filesystem->put_contents( $this->file( 'path' ), wp_strip_all_tags( $content ), FS_CHMOD_FILE ) ) {
204
205 // Fail!
206 return false;
207
208 } else {
209
210 $option = get_option( 'generateblocks_dynamic_css_posts', array() );
211 $option[ $page_id ] = true;
212 update_option( 'generateblocks_dynamic_css_posts', $option );
213
214 // Update the 'generateblocks_dynamic_css_time' option.
215 $this->update_saved_time();
216
217 // Success!
218 return true;
219
220 }
221 }
222 }
223
224 /**
225 * Determines if the CSS file is writable.
226 */
227 public function can_write() {
228 global $blog_id;
229
230 // Get the upload directory for this site.
231 $upload_dir = wp_get_upload_dir();
232
233 // If this is a multisite installation, append the blogid to the filename.
234 $css_blog_id = ( is_multisite() && $blog_id > 1 ) ? '_blog-' . $blog_id : null;
235 $page_id = $this->page_id();
236
237 if ( ! $page_id ) {
238 return false;
239 }
240
241 $file_name = '/style' . $css_blog_id . '-' . $page_id . '.css';
242 $folder_path = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'generateblocks';
243
244 // Does the folder exist?
245 if ( file_exists( $folder_path ) ) {
246 // Folder exists, but is the folder writable?
247 if ( ! is_writable( $folder_path ) ) {
248 // Folder is not writable.
249 // Does the file exist?
250 if ( ! file_exists( $folder_path . $file_name ) ) {
251 // File does not exist, therefore it can't be created
252 // since the parent folder is not writable.
253 return false;
254 } else {
255 // File exists, but is it writable?
256 if ( ! is_writable( $folder_path . $file_name ) ) {
257 // Nope, it's not writable.
258 return false;
259 }
260 }
261 } else {
262 // The folder is writable.
263 // Does the file exist?
264 if ( file_exists( $folder_path . $file_name ) ) {
265 // File exists.
266 // Is it writable?
267 if ( ! is_writable( $folder_path . $file_name ) ) {
268 // Nope, it's not writable.
269 return false;
270 }
271 }
272 }
273 } else {
274 // Can we create the folder?
275 // returns true if yes and false if not.
276 return wp_mkdir_p( $folder_path );
277 }
278
279 // all is well!
280 return true;
281 }
282
283 /**
284 * Gets the css path or url to the stylesheet
285 *
286 * @param string $target path/url.
287 */
288 public function file( $target = 'path' ) {
289 global $blog_id;
290
291 // Get the upload directory for this site.
292 $upload_dir = wp_get_upload_dir();
293
294 // If this is a multisite installation, append the blogid to the filename.
295 $css_blog_id = ( is_multisite() && $blog_id > 1 ) ? '_blog-' . $blog_id : null;
296 $page_id = $this->page_id();
297
298 $file_name = 'style' . $css_blog_id . '-' . $page_id . '.css';
299 $folder_path = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'generateblocks';
300
301 // The complete path to the file.
302 $file_path = $folder_path . DIRECTORY_SEPARATOR . $file_name;
303
304 // Get the URL directory of the stylesheet.
305 $css_uri_folder = $upload_dir['baseurl'];
306
307 $css_uri = trailingslashit( $css_uri_folder ) . 'generateblocks/' . $file_name;
308
309 // Take care of domain mapping.
310 if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ) {
311 if ( function_exists( 'domain_mapping_siteurl' ) && function_exists( 'get_original_url' ) ) {
312 $mapped_domain = domain_mapping_siteurl( false );
313 $original_domain = get_original_url( 'siteurl' );
314 $css_uri = str_replace( $original_domain, $mapped_domain, $css_uri );
315 }
316 }
317
318 $css_uri = set_url_scheme( $css_uri );
319
320 if ( 'path' === $target ) {
321 return $file_path;
322 } elseif ( 'url' === $target || 'uri' === $target ) {
323 $timestamp = ( file_exists( $file_path ) ) ? '?ver=' . filemtime( $file_path ) : '';
324 return $css_uri . $timestamp;
325 }
326 }
327
328 /**
329 * Create settings.
330 */
331 public function add_options() {
332 /**
333 * The 'generateblocks_dynamic_css_posts' option will hold an array of posts that have had their css generated.
334 * We can use that to keep track of which pages need their CSS to be recreated and which don't.
335 */
336 add_option( 'generateblocks_dynamic_css_posts', array(), '', 'yes' );
337
338 /**
339 * The 'generateblocks_dynamic_css_time' option holds the time the file writer was last used.
340 */
341 add_option( 'generateblocks_dynamic_css_time', time(), '', 'yes' );
342 }
343
344 /**
345 * Update the generateblocks_dynamic_css_posts option when a post is saved.
346 *
347 * @param int $post_id The current post ID.
348 * @param object $post The current post.
349 */
350 public function post_update_option( $post_id, $post ) {
351 $is_autosave = wp_is_post_autosave( $post_id );
352 $is_revision = wp_is_post_revision( $post_id );
353
354 if ( $is_autosave || $is_revision || ! current_user_can( 'edit_post', $post_id ) ) {
355 return $post_id;
356 }
357
358 if ( isset( $post->post_content ) ) {
359 if ( strpos( $post->post_content, 'wp:generateblocks' ) !== false ) {
360 update_post_meta( $post_id, '_generateblocks_dynamic_css_version', sanitize_text_field( GENERATEBLOCKS_VERSION ) );
361 } else {
362 delete_post_meta( $post_id, '_generateblocks_dynamic_css_version' );
363 }
364
365 // Store any re-usable block IDs on the page. We need this to regenerate CSS files later if the re-usable block is changed.
366 $reusable_blocks = preg_match_all( '/wp:block {"ref":([^}]*)}/', $post->post_content, $matches );
367 $stored_reusable_blocks = array();
368
369 foreach ( $matches[1] as $match ) {
370 $stored_reusable_blocks[] = $match;
371 }
372
373 if ( ! empty( $stored_reusable_blocks ) ) {
374 $stored_reusable_blocks = array_map( 'intval', $stored_reusable_blocks );
375 update_post_meta( $post_id, '_generateblocks_reusable_blocks', $stored_reusable_blocks );
376 } else {
377 delete_post_meta( $post_id, '_generateblocks_reusable_blocks' );
378 }
379 }
380
381 // Make a new CSS file for this post on next load.
382 $option = get_option( 'generateblocks_dynamic_css_posts', array() );
383 unset( $option[ $post_id ] );
384
385 update_option( 'generateblocks_dynamic_css_posts', $option );
386 }
387
388 /**
389 * Force regeneration of CSS files attached to pages with this re-usable block.
390 *
391 * @param int $post_id The current post ID.
392 * @param object $post The current post.
393 */
394 public function wp_block_update( $post_id, $post ) {
395 $is_autosave = wp_is_post_autosave( $post_id );
396 $is_revision = wp_is_post_revision( $post_id );
397
398 if ( $is_autosave || $is_revision || ! current_user_can( 'edit_post', $post_id ) ) {
399 return $post_id;
400 }
401
402 if ( isset( $post->post_content ) ) {
403 if ( strpos( $post->post_content, 'wp:generateblocks' ) !== false ) {
404 global $wpdb;
405
406 $option = get_option( 'generateblocks_dynamic_css_posts', array() );
407
408 $posts = $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_generateblocks_reusable_blocks'" );
409
410 foreach ( (array) $posts as $id ) {
411 unset( $option[ $id ] );
412 }
413
414 update_option( 'generateblocks_dynamic_css_posts', $option );
415 }
416 }
417 }
418
419 /**
420 * Do we need to update the CSS file?
421 */
422 public function needs_update() {
423 $option = get_option( 'generateblocks_dynamic_css_posts', array() );
424 $page_id = $this->page_id();
425
426 // If the CSS file does not exist then we definitely need to regenerate the CSS.
427 if ( ! file_exists( $this->file( 'path' ) ) ) {
428 return true;
429 }
430
431 return ( ! isset( $option[ $page_id ] ) || ! $option[ $page_id ] ) ? true : false;
432 }
433
434 /**
435 * Update the 'generateblocks_dynamic_css_time' option.
436 */
437 public function update_saved_time() {
438 update_option( 'generateblocks_dynamic_css_time', time() );
439 }
440
441 /**
442 * Force CSS files to regenerate after a widget has been saved.
443 *
444 * @param array $instance The current widget instance's settings.
445 */
446 public function force_file_regen_on_widget_save( $instance ) {
447 if ( function_exists( 'wp_use_widgets_block_editor' ) && wp_use_widgets_block_editor() ) {
448 update_option( 'generateblocks_dynamic_css_posts', array() );
449 }
450
451 return $instance;
452 }
453
454 /**
455 * Force CSS files to regenerate after the Customizer has been saved.
456 * This is necessary because force_file_regen_on_widget_save() doesn't fire in the Customizer for some reason.
457 *
458 * @todo Make this only happen when the widgets have been changed.
459 */
460 public function force_file_regen_on_customizer_save() {
461 if ( function_exists( 'wp_use_widgets_block_editor' ) && wp_use_widgets_block_editor() ) {
462 update_option( 'generateblocks_dynamic_css_posts', array() );
463 }
464 }
465 }
466
467 GenerateBlocks_Enqueue_CSS::get_instance();
468