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