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