PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.4
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.4
4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 All 202 releases
← All changes | includes/Modules/StyleHandler.php +276 -94 4.9.3 → 4.9.4 View file →
@@ -54,9 +54,19 @@
54 54
55 55 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_assets' ), 99 );
56 56 add_action( 'save_post', array( $this, 'on_save_post' ), 10, 3 );
57 57 add_action( 'wp', array( $this, 'generate_post_content' ) );
58 +
59 + // Synced patterns in block widgets render on every page; track them
60 + // once the widget/sidebar changes of the request are all saved.
61 + add_action( 'rest_after_save_widget', array( $this, 'schedule_widget_patterns_refresh' ) );
62 + add_action( 'rest_delete_widget', array( $this, 'schedule_widget_patterns_refresh' ) );
63 + add_action( 'rest_save_sidebar', array( $this, 'schedule_widget_patterns_refresh' ) );
64 + add_action( 'after_switch_theme', array( $this, 'schedule_widget_patterns_refresh' ) );
58 65 add_action( 'rest_after_save_widget', array( $this, 'after_save_widget' ), 10, 4 );
66 + add_action( 'rest_delete_widget', array( $this, 'after_save_widget' ) );
67 + add_action( 'rest_save_sidebar', array( $this, 'after_save_widget' ) );
68 + add_action( 'after_switch_theme', array( $this, 'after_save_widget' ) );
59 69
60 70 // FSE assets generation
61 71 add_action(
62 72 'init',
@@ -125,12 +135,12 @@
125 135 */
126 136 public function write_css_from_content( $post, $post_id, $parsed_content ) {
127 137 $betterdocs_blocks = array( );
128 138 $recursive_response = CSSParser::betterdocs_block_style_recursive( $parsed_content, $betterdocs_blocks );
129 - $reusable_Blocks = ! empty( $recursive_response[ 'reusableBlocks' ] ) ? $recursive_response[ 'reusableBlocks' ] : array( );
139 + $all_reusable_blocks = ! empty( $recursive_response[ 'reusableBlocks' ] ) ? $recursive_response[ 'reusableBlocks' ] : array( );
130 140 // remove empty reusable blocks
131 141 $reusable_Blocks = array_filter(
132 - $reusable_Blocks,
142 + $all_reusable_blocks,
133 143 function ( $v ) {
134 144 return ! empty( $v );
135 145 }
136 146 );
@@ -136,40 +146,111 @@
136 146 );
137 147 unset( $recursive_response[ 'reusableBlocks' ] );
138 148 $style = CSSParser::blocks_to_style_array( $recursive_response );
139 149 $reusableIds = $reusable_Blocks ? array_keys( $reusable_Blocks ) : array( );
140 - if ( ! empty( $reusableIds ) ) {
150 + // The option is merged into every page's enqueue, so only FSE
151 + // templates / template parts (rendered around every page) may set
152 + // it. Ordinary posts keep their ids in post meta below; they used to
153 + // overwrite the option too, so whatever the last post with synced
154 + // patterns referenced was loaded site-wide.
155 + if ( ! empty( $reusableIds ) && $this->is_fse_template( $post ) ) {
141 156 update_option( '_betterdocs_reusable_block_ids', $reusableIds );
142 157 }
143 158 update_post_meta( $post_id, '_betterdocs_reusable_block_ids', $reusableIds );
144 159 $this->write_block_css( $style, $post ); //Write CSS file for this page
145 160
146 - if ( ! empty( $reusable_Blocks ) ) {
147 - foreach ( $reusable_Blocks as $blockId => $block ) {
148 - $style = CSSParser::blocks_to_style_array( $block );
149 - $this->write_reusable_block_css( $style, $blockId );
150 - }
161 + // Every referenced reusable block, including those with no BetterDocs
162 + // styles, so their stale files (e.g. pre-#174 copies of Essential
163 + // Blocks' CSS) are removed.
164 + foreach ( $all_reusable_blocks as $blockId => $block ) {
165 + $style = CSSParser::blocks_to_style_array( $block );
166 + $this->write_reusable_block_css( $style, $blockId );
151 167 }
152 168 }
153 169
154 170 /**
155 - * Save Widget CSS when Widget is saved
171 + * Whether a post is an FSE template or template part.
172 + *
173 + * @param \WP_Post|null $post Post.
174 + * @return bool
175 + */
176 + private function is_fse_template( $post ) {
177 + return isset( $post->post_type ) && ( 'wp_template_part' === $post->post_type || 'wp_template' === $post->post_type );
178 + }
179 +
180 + /**
181 + * Rebuild the widget CSS when a widget or sidebar is saved or a widget is
182 + * deleted — once, at the end of the request: core fires
183 + * rest_after_save_widget before it assigns the widget to its sidebar.
156 184 * @return void
157 185 * @since 3.5.3
158 186 */
159 - public function after_save_widget( $id, $sidebar_id, $request, $creating ) {
160 - $parsed_content = isset( $request[ 'instance' ][ 'raw' ][ 'content' ] ) ? parse_blocks( $request[ 'instance' ][ 'raw' ][ 'content' ] ) : array( );
161 - if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
187 + public function after_save_widget() {
188 + if ( false === has_action( 'shutdown', array( $this, 'rebuild_widget_css' ) ) ) {
189 + add_action( 'shutdown', array( $this, 'rebuild_widget_css' ) );
190 + }
191 + }
192 +
193 + /**
194 + * Write the widget CSS file from every active block widget's BetterDocs
195 + * blocks, or remove it when there are none. It used to hold only the
196 + * widget saved last, and was never removed (e.g. stale Essential Blocks
197 + * CSS collected before #174 stayed loaded on every page).
198 + * @return void
199 + */
200 + public function rebuild_widget_css() {
201 + $styles = array( );
202 + foreach ( $this->active_block_widget_contents() as $content ) {
203 + if ( false === strpos( $content, '<!-- wp:betterdocs/' ) ) {
204 + continue;
205 + }
162 206 $betterdocs_blocks = array( );
163 - $recursive_response = CSSParser::betterdocs_block_style_recursive( $parsed_content, $betterdocs_blocks );
207 + $recursive_response = CSSParser::betterdocs_block_style_recursive( parse_blocks( $content ), $betterdocs_blocks );
164 208 unset( $recursive_response[ 'reusableBlocks' ] );
165 - $style = CSSParser::blocks_to_style_array( $recursive_response );
166 - //Write CSS file for Widget
167 - $this->single_file_css_generator( $style, $this->style_dir, $this->prefix . '-widget.min.css' );
209 + $styles = array_merge( $styles, CSSParser::blocks_to_style_array( $recursive_response ) );
168 210 }
211 +
212 + $file = $this->style_dir . $this->prefix . '-widget.min.css';
213 + $css = empty( $styles ) ? '' : CSSParser::build_css( $styles );
214 + if ( empty( $css ) ) {
215 + if ( file_exists( $file ) ) {
216 + wp_delete_file( $file );
217 + }
218 + return;
219 + }
220 +
221 + // Unchanged CSS: leave the file (and its mtime-based version) alone.
222 + if ( file_exists( $file ) && file_get_contents( $file ) === $css ) {
223 + return;
224 + }
225 +
226 + if ( ! file_exists( $this->style_dir ) ) {
227 + wp_mkdir_p( $this->style_dir );
228 + }
229 + file_put_contents( $file, $css );
169 230 }
170 231
171 232 /**
233 + * Content of the block widgets placed in a sidebar (not inactive).
234 + * @return string[]
235 + */
236 + private function active_block_widget_contents() {
237 + $contents = array( );
238 + $instances = get_option( 'widget_block', array( ) );
239 + foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
240 + if ( 'wp_inactive_widgets' === $sidebar_id || ! is_array( $widget_ids ) ) {
241 + continue;
242 + }
243 + foreach ( $widget_ids as $widget_id ) {
244 + if ( preg_match( '/^block-(\d+)$/', (string) $widget_id, $matches ) && ! empty( $instances[ $matches[1] ]['content'] ) ) {
245 + $contents[] = (string) $instances[ $matches[1] ]['content'];
246 + }
247 + }
248 + }
249 + return $contents;
250 + }
251 +
252 + /**
172 253 * Load Dependencies
173 254 */
174 255 private function load_style_handler_dependencies() {
175 256 require_once plugin_dir_path( __FILE__ ) . 'includes/class-parse-css.php';
@@ -190,9 +271,9 @@
190 271 $gp_elements = get_posts( array( 'post_type' => 'gp_elements' ) );
191 272 if ( is_array( $gp_elements ) && ! empty( $gp_elements ) ) {
192 273 foreach ( $gp_elements as $element ) {
193 274 if ( file_exists( $this->style_dir . $this->prefix . '-' . $element->ID . '.min.css' ) ) {
194 - wp_enqueue_style( 'betterdocs-block-style-' . $element->ID, $this->style_url . $this->prefix . '-' . $element->ID . '.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
275 + wp_enqueue_style( 'betterdocs-block-style-' . $element->ID, $this->style_url . $this->prefix . '-' . $element->ID . '.min.css', $deps, $this->file_version( $this->style_dir . $this->prefix . '-' . $element->ID . '.min.css' ) );
195 276 }
196 277 }
197 278 }
198 279 }
@@ -197,11 +278,16 @@
197 278 }
198 279 }
199 280
200 281 if ( ! empty( $post ) && ! empty( $post->ID ) ) {
201 - //Page/Post Style Enqueue
202 - if ( file_exists( $this->style_dir . $this->prefix . '-' . $post->ID . '.min.css' ) ) {
203 - wp_enqueue_style( 'betterdocs-block-style-' . $post->ID, $this->style_url . $this->prefix . '-' . $post->ID . '.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
282 + //Page/Post Style Enqueue — only for posts that hold BetterDocs
283 + // blocks. Until #174 the parser also collected Essential Blocks'
284 + // blocks, so files written then for pages without BetterDocs
285 + // blocks are stale duplicates of Essential Blocks' CSS; they are
286 + // skipped here and removed on the post's next save (#174).
287 + $post_css = $this->style_dir . $this->prefix . '-' . $post->ID . '.min.css';
288 + if ( file_exists( $post_css ) && false !== strpos( (string) $post->post_content, '<!-- wp:betterdocs/' ) ) {
289 + wp_enqueue_style( 'betterdocs-block-style-' . $post->ID, $this->style_url . $this->prefix . '-' . $post->ID . '.min.css', $deps, $this->file_version( $post_css ) );
204 290 }
205 291
206 292 // Reusable block Style Enqueues
207 293 $reusableIds = get_post_meta( $post->ID, '_betterdocs_reusable_block_ids', true );
@@ -207,22 +293,30 @@
207 293 $reusableIds = get_post_meta( $post->ID, '_betterdocs_reusable_block_ids', true );
208 294 $reusableIds = ! empty( $reusableIds ) ? $reusableIds : array( );
209 295 $templateReusableIds = get_option( '_betterdocs_reusable_block_ids', array( ) );
210 296 $reusableIds = array_unique( array_merge( $reusableIds, $templateReusableIds ) );
211 - if ( ! empty( $reusableIds ) ) {
212 - foreach ( $reusableIds as $reusableId ) {
213 - if ( file_exists( $this->style_dir . 'reusable-blocks/betterdocs-reusable-' . $reusableId . '.min.css' ) ) {
214 - wp_enqueue_style( 'betterdocs-reusable-block-style-' . $reusableId, $this->style_url . 'reusable-blocks/betterdocs-reusable-' . $reusableId . '.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
215 - }
216 - }
297 + foreach ( $reusableIds as $reusableId ) {
298 + $this->enqueue_reusable_block_style( $reusableId, $deps );
217 299 }
218 300 }
219 301
220 - //Widget Style Enqueue
221 - if ( file_exists( $this->style_dir . $this->prefix . '-widget.min.css' ) ) {
222 - wp_enqueue_style( 'betterdocs-widget-style', $this->style_url . $this->prefix . '-widget.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
302 + //Widget Style Enqueue — only while an active block widget holds
303 + // BetterDocs blocks, so a stale file (see rebuild_widget_css()) is
304 + // not loaded site-wide until the next widget save removes it.
305 + if ( file_exists( $this->style_dir . $this->prefix . '-widget.min.css' ) && false !== strpos( implode( '', $this->active_block_widget_contents() ), '<!-- wp:betterdocs/' ) ) {
306 + wp_enqueue_style( 'betterdocs-widget-style', $this->style_url . $this->prefix . '-widget.min.css', $deps, $this->file_version( $this->style_dir . $this->prefix . '-widget.min.css' ) );
223 307 }
224 308
309 + // Synced patterns in block widgets, on every page. Built once on
310 + // sites whose widgets were saved before this list existed.
311 + $widgetReusableIds = get_option( '_betterdocs_widget_reusable_block_ids', null );
312 + if ( null === $widgetReusableIds ) {
313 + $widgetReusableIds = $this->refresh_widget_patterns();
314 + }
315 + foreach ( (array) $widgetReusableIds as $reusableId ) {
316 + $this->enqueue_reusable_block_style( $reusableId, $deps );
317 + }
318 +
225 319 //FSE Style Enqueue — one file per FSE template/template-part post so
226 320 // styles do not bleed across templates (issue #53).
227 321 if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() && betterdocs()->helper->is_templates() && ! empty( self::$_fse_template_post_ids ) ) {
228 322 foreach ( array_unique( self::$_fse_template_post_ids ) as $template_post_id ) {
@@ -231,9 +325,9 @@
231 325 wp_enqueue_style(
232 326 'betterdocs-fullsite-style-' . abs( $template_post_id ),
233 327 $this->style_url . $file_name,
234 328 $deps,
235 - substr( md5( microtime( true ) ), 0, 10 )
329 + $this->file_version( $this->style_dir . $file_name )
236 330 );
237 331 }
238 332 }
239 333 }
@@ -271,8 +365,18 @@
271 365 }
272 366
273 367 $parsed_content = $this->get_parsed_content( $post_id, $post, $post_type );
274 368
369 + // A synced pattern is styled through its reusable-block file, which
370 + // pages referencing it enqueue. Refresh (or remove) that file here so
371 + // edits to the pattern take effect without re-saving those pages.
372 + if ( 'wp_block' === $post_type ) {
373 + $betterdocs_blocks = array( );
374 + $recursive_response = is_array( $parsed_content ) ? CSSParser::betterdocs_block_style_recursive( $parsed_content, $betterdocs_blocks ) : array( );
375 + $this->write_reusable_block_css( CSSParser::blocks_to_style_array( $recursive_response ), $post_id );
376 + return;
377 + }
378 +
275 379 if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
276 380 $this->write_css_from_content( $post, $post_id, $parsed_content );
277 381 }
278 382 }
@@ -297,8 +401,90 @@
297 401
298 402 /**
299 403 * Get post content when page is load in frontend
300 404 */
405 + /**
406 + * Cheap pre-check: can this post's content hold BetterDocs blocks,
407 + * directly or through a reusable block (`core/block` ref)?
408 + *
409 + * @param \WP_Post|null $post Post.
410 + * @return bool
411 + */
412 + public static function may_have_betterdocs_blocks( $post ) {
413 + $content = isset( $post->post_content ) ? (string) $post->post_content : '';
414 + return false !== strpos( $content, '<!-- wp:betterdocs/' ) || false !== strpos( $content, '<!-- wp:block ' );
415 + }
416 +
417 + /**
418 + * Whether a synced pattern (wp_block) holds BetterDocs blocks.
419 + * get_post() is object-cached, so this is cheap per enqueue.
420 + *
421 + * @param int $id wp_block post ID.
422 + * @return bool
423 + */
424 + private function reusable_block_has_betterdocs_blocks( $id ) {
425 + $block = get_post( (int) $id );
426 + return isset( $block->post_content ) && false !== strpos( (string) $block->post_content, '<!-- wp:betterdocs/' );
427 + }
428 +
429 + /**
430 + * Refresh the widget pattern list at the end of the request: core fires
431 + * rest_after_save_widget before it assigns the widget to its sidebar.
432 + * @return void
433 + */
434 + public function schedule_widget_patterns_refresh() {
435 + if ( false === has_action( 'shutdown', array( $this, 'refresh_widget_patterns' ) ) ) {
436 + add_action( 'shutdown', array( $this, 'refresh_widget_patterns' ) );
437 + }
438 + }
439 +
440 + /**
441 + * Write the CSS of the synced patterns placed in active block widgets and
442 + * record their ids, which every page enqueues.
443 + * @return array Pattern ids.
444 + */
445 + public function refresh_widget_patterns() {
446 + $ids = array( );
447 + $instances = get_option( 'widget_block', array( ) );
448 + foreach ( wp_get_sidebars_widgets() as $sidebar_id => $widget_ids ) {
449 + if ( 'wp_inactive_widgets' === $sidebar_id || ! is_array( $widget_ids ) ) {
450 + continue;
451 + }
452 + foreach ( $widget_ids as $widget_id ) {
453 + if ( ! preg_match( '/^block-(\d+)$/', (string) $widget_id, $matches ) || empty( $instances[ $matches[1] ]['content'] ) ) {
454 + continue;
455 + }
456 + $betterdocs_blocks = array( );
457 + $recursive_response = CSSParser::betterdocs_block_style_recursive( parse_blocks( $instances[ $matches[1] ]['content'] ), $betterdocs_blocks );
458 + $reusable_blocks = ! empty( $recursive_response['reusableBlocks'] ) ? $recursive_response['reusableBlocks'] : array( );
459 + foreach ( $reusable_blocks as $block_id => $block ) {
460 + $this->write_reusable_block_css( CSSParser::blocks_to_style_array( $block ), $block_id );
461 + if ( ! empty( $block ) ) {
462 + $ids[] = (int) $block_id;
463 + }
464 + }
465 + }
466 + }
467 +
468 + $ids = array_values( array_unique( $ids ) );
469 + update_option( '_betterdocs_widget_reusable_block_ids', $ids );
470 + return $ids;
471 + }
472 +
473 + /**
474 + * Enqueue a synced pattern's CSS file, if it holds BetterDocs blocks.
475 + * @return void
476 + */
477 + private function enqueue_reusable_block_style( $reusableId, $deps ) {
478 + // Only for synced patterns that hold BetterDocs blocks: files written
479 + // before #174 for patterns built with other plugins' blocks are stale
480 + // duplicates of their CSS (#174).
481 + $file = $this->style_dir . 'reusable-blocks/betterdocs-reusable-' . $reusableId . '.min.css';
482 + if ( file_exists( $file ) && $this->reusable_block_has_betterdocs_blocks( $reusableId ) ) {
483 + wp_enqueue_style( 'betterdocs-reusable-block-style-' . $reusableId, $this->style_url . 'reusable-blocks/betterdocs-reusable-' . $reusableId . '.min.css', $deps, $this->file_version( $file ) );
484 + }
485 + }
486 +
301 487 public function generate_post_content() {
302 488 $post_id = get_the_ID();
303 489 if ( $post_id ) {
304 490 $post_type = get_post_type( $post_id );
@@ -312,8 +498,16 @@
312 498 if ( false !== wp_is_post_revision( $post_id ) ) {
313 499 return null;
314 500 }
315 501
502 + // This runs on every uncached front-end request. Skip the parse
503 + // and the file/meta/option writes for content with nothing for
504 + // BetterDocs to style — on a large page built with other blocks
505 + // that was a full parse_blocks() plus a CSS rewrite per view.
506 + if ( ! self::may_have_betterdocs_blocks( $post ) ) {
507 + return;
508 + }
509 +
316 510 $parsed_content = $this->get_parsed_content( $post_id, $post, $post_type );
317 511
318 512 if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
319 513 $this->write_css_from_content( $post, $post_id, $parsed_content );
@@ -321,8 +515,20 @@
321 515 }
322 516 }
323 517
324 518 /**
519 + * Cache-busting version for a generated stylesheet: its modification time.
520 + * Was a random value per render, which stopped browsers caching the file.
521 + *
522 + * @param string $path File path.
523 + * @return string|false
524 + */
525 + private function file_version( $path ) {
526 + $mtime = @filemtime( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
527 + return $mtime ? (string) $mtime : false;
528 + }
529 +
530 + /**
325 531 * Ajax callback to write css in upload directory
326 532 * @retun void
327 533 * @since 1.0.2
328 534 */
@@ -330,24 +536,33 @@
330 536 // Write CSS for FSE templates / template parts — one file per post so
331 537 // styles from different templates do not overwrite each other in a
332 538 // shared file (issue #53).
333 539 if ( isset( $post->post_type ) && ( 'wp_template_part' === $post->post_type || 'wp_template' === $post->post_type ) ) {
334 - if ( empty( $block_styles ) ) {
335 - return;
540 + $file = $this->style_dir . $this->prefix . '-edit-site-' . abs( $post->ID ) . '.min.css';
541 + } else { // Page/Posts
542 + $file = $this->style_dir . $this->prefix . '-' . abs( $post->ID ) . '.min.css';
543 + }
544 +
545 + $css = empty( $block_styles ) ? '' : CSSParser::build_css( $block_styles );
546 + if ( empty( $css ) ) {
547 + // No BetterDocs blocks left: drop the file so a stale one is not
548 + // enqueued forever (#174).
549 + if ( file_exists( $file ) ) {
550 + wp_delete_file( $file );
336 551 }
337 - if ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
338 - if ( ! file_exists( $this->style_dir ) ) {
339 - wp_mkdir_p( $this->style_dir );
340 - }
341 - file_put_contents( $this->style_dir . $this->prefix . '-edit-site-' . abs( $post->ID ) . '.min.css', $css );
342 - }
343 - } // Write CSS for Page/Posts
344 - elseif ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
345 - if ( ! file_exists( $this->style_dir ) ) {
346 - wp_mkdir_p( $this->style_dir );
347 - }
348 - file_put_contents( $this->style_dir . $this->prefix . '-' . abs( $post->ID ) . '.min.css', $css );
552 + return;
349 553 }
554 +
555 + // Unchanged CSS: leave the file (and its mtime-based version) alone,
556 + // so browsers keep their cached copy.
557 + if ( file_exists( $file ) && file_get_contents( $file ) === $css ) {
558 + return;
559 + }
560 +
561 + if ( ! file_exists( $this->style_dir ) ) {
562 + wp_mkdir_p( $this->style_dir );
563 + }
564 + file_put_contents( $file, $css );
350 565 }
351 566
352 567 /**
353 568 * Write css for Reusable block
@@ -354,63 +569,30 @@
354 569 * @retun void
355 570 * @since 3.4.0
356 571 */
357 572 private function write_reusable_block_css( $block_styles, $id ) {
358 - if ( isset( $block_styles ) && is_array( $block_styles ) ) {
359 - if ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
360 - $upload_dir = $this->style_dir . 'reusable-blocks/';
361 - if ( ! file_exists( $upload_dir ) ) {
362 - wp_mkdir_p( $upload_dir );
363 - }
364 - file_put_contents( $upload_dir . '/' . 'betterdocs-reusable-' . abs( $id ) . '.min.css', $css );
573 + $upload_dir = $this->style_dir . 'reusable-blocks/';
574 + $file = $upload_dir . 'betterdocs-reusable-' . abs( $id ) . '.min.css';
575 +
576 + $css = empty( $block_styles ) || ! is_array( $block_styles ) ? '' : CSSParser::build_css( $block_styles );
577 + if ( empty( $css ) ) {
578 + // No BetterDocs blocks in this synced pattern: drop the file so a
579 + // stale one is not enqueued forever (#174).
580 + if ( file_exists( $file ) ) {
581 + wp_delete_file( $file );
365 582 }
583 + return;
366 584 }
367 - }
368 585
369 - /**
370 - * Single file css generator
371 - * @retun void
372 - * @since 3.5.3
373 - */
374 - private function single_file_css_generator( $block_styles, $upload_dir, $filename ) {
375 - $editSiteCssPath = $upload_dir . $filename;
376 - if ( file_exists( $editSiteCssPath ) ) {
377 - $existingCss = file_get_contents( $editSiteCssPath );
378 - $pattern = '~\/\*(.*?)\*\/~';
379 - preg_match_all( $pattern, $existingCss, $result, PREG_PATTERN_ORDER );
380 - $allComments = $result[ 0 ];
381 - $seperatedIds = array( );
382 - foreach ( $allComments as $comment ) {
383 - $id = preg_replace( '/[^A-Za-z0-9\-]|Ends|Starts/', '', $comment );
586 + // Unchanged CSS: leave the file (and its mtime-based version) alone.
587 + if ( file_exists( $file ) && file_get_contents( $file ) === $css ) {
588 + return;
589 + }
384 590
385 - if ( strpos( $comment, 'Starts' ) ) {
386 - $seperatedIds[ $id ][ 'start' ] = $comment;
387 - } elseif ( strpos( $comment, 'Ends' ) ) {
388 - $seperatedIds[ $id ][ 'end' ] = $comment;
389 - }
390 - }
391 -
392 - $seperateStyles = array( );
393 - foreach ( $seperatedIds as $key => $ids ) {
394 - $seperateStyles[ ][ $key ] = isset( $block_styles[ $key ] ) ? $block_styles[ $key ] : array( );
395 - }
396 -
397 - self::$_block_styles = array_merge( self::$_block_styles, $block_styles );
398 -
399 - if ( ! empty( $css = CSSParser::build_css( self::$_block_styles ) ) ) {
400 - if ( ! file_exists( $upload_dir ) ) {
401 - wp_mkdir_p( $upload_dir );
402 - }
403 -
404 - file_put_contents( $editSiteCssPath, $css );
405 - }
406 - } elseif ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
407 - if ( ! file_exists( $this->style_dir ) ) {
408 - wp_mkdir_p( $this->style_dir );
409 - }
410 -
411 - file_put_contents( $editSiteCssPath, $css );
591 + if ( ! file_exists( $upload_dir ) ) {
592 + wp_mkdir_p( $upload_dir );
412 593 }
594 + file_put_contents( $file, $css );
413 595 }
414 596
415 597 /**
416 598 * Get post id by post_name for template