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