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