Plugin Directory

Changeset 3423300


Ignore:
Timestamp:
12/19/2025 02:42:32 AM (7 days ago)
Author:
courane01
Message:

Release 1.1.3: Critical performance fix and pattern simplification

Performance:

  • Fix revision query performance issue on sites with many synced patterns
  • Add transient-based caching for pattern registration
  • Skip pattern registration on front-end
  • Add revision limiter for wp_block posts (3 revisions)

Bug Fixes:

  • Fix duplicate pattern insertion when selecting format
  • Fix character counter appearing twice in Status format
  • Fix Aside format icon missing in admin
  • Fix JavaScript postCategories undefined error

Improvements:

  • Simplify all format patterns (remove wrapper Groups)
  • Add Settings link on Plugins page
  • Add domReady wrapper and fallback icon for safer initialization
Location:
post-formats-for-block-themes
Files:
2 added
4 deleted
34 edited
1 copied

Legend:

Unmodified
Added
Removed
  • post-formats-for-block-themes/tags/1.1.3/blocks/post-format-block/index.js

    r3413728 r3423300  
    1818import { __ } from '@wordpress/i18n';
    1919
    20 // Icon
     20// DOM Ready
     21import domReady from '@wordpress/dom-ready';
     22
     23// Icon - use optional chaining to prevent errors if icons not loaded yet
    2124import { postCategories as icon } from '@wordpress/icons';
    2225
     
    2932 * @see https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/post-terms/variations.js
    3033 */
    31 const variation = {
    32     name: 'post_format',
    33     title: __( 'Post Format', 'post-formats-for-block-themes' ),
    34     description: __( "Display a post's format", 'post-formats-for-block-themes' ),
    35     icon,
    36     isDefault: false,
    37     attributes: { term: 'post_format' },
    38     isActive: ( blockAttributes ) => blockAttributes.term === 'post_format',
    39     scope: [ 'inserter', 'transform' ],
    40 };
     34domReady( () => {
     35    // Fallback icon if postCategories isn't available
     36    const variationIcon = icon || 'tag';
    4137
    42 registerBlockVariation( 'core/post-terms', variation );
     38    const variation = {
     39        name: 'post_format',
     40        title: __( 'Post Format', 'post-formats-for-block-themes' ),
     41        description: __( "Display a post's format", 'post-formats-for-block-themes' ),
     42        icon: variationIcon,
     43        isDefault: false,
     44        attributes: { term: 'post_format' },
     45        isActive: ( blockAttributes ) => blockAttributes.term === 'post_format',
     46        scope: [ 'inserter', 'transform' ],
     47    };
     48
     49    registerBlockVariation( 'core/post-terms', variation );
     50} );
  • post-formats-for-block-themes/tags/1.1.3/blocks/post-format-block/index.min.js

    r3413728 r3423300  
    33 * @package PostFormatsBlockThemes
    44 */
    5 (function(){var e=window.wp.blocks,t=window.wp.i18n,n=window.wp.icons;const r={name:"post_format",title:(0,t.__)("Post Format","post-formats-for-block-themes"),description:(0,t.__)("Display a post's format","post-formats-for-block-themes"),icon:n.postCategories,isDefault:false,attributes:{term:"post_format"},isActive:function(e){return e.term==="post_format"},scope:["inserter","transform"]};(0,e.registerBlockVariation)("core/post-terms",r)})();
     5(function(){var e=window.wp.blocks,t=window.wp.i18n,n=window.wp.icons,d=window.wp.domReady;d(function(){var i=(n&&n.postCategories)?n.postCategories:"tag";var r={name:"post_format",title:(0,t.__)("Post Format","post-formats-for-block-themes"),description:(0,t.__)("Display a post's format","post-formats-for-block-themes"),icon:i,isDefault:false,attributes:{term:"post_format"},isActive:function(e){return e.term==="post_format"},scope:["inserter","transform"]};(0,e.registerBlockVariation)("core/post-terms",r)})})();
  • post-formats-for-block-themes/tags/1.1.3/build/index.asset.php

    r3416925 r3423300  
    1 <?php return array('dependencies' => array('react', 'wp-a11y', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => 'aee0db3c8e30a508c90e');
     1<?php return array('dependencies' => array('react', 'wp-a11y', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => '5add7157ad1a69c9a4cc');
  • post-formats-for-block-themes/tags/1.1.3/build/index.js

    r3416925 r3423300  
    1 (()=>{"use strict";const t=window.React,e=window.wp.plugins,a=window.wp.components,n=window.wp.data,r=window.wp.element,o=window.wp.i18n,s=window.wp.a11y,i=window.wp.hooks,p=window.wp.blocks,l=()=>{const[e,i]=(0,r.useState)(!1),[l,m]=(0,r.useState)(!1),{isNewPost:c,currentFormat:d,postType:u}=(0,n.useSelect)(t=>{const e=t("core/editor"),a=e.getCurrentPost();return{isNewPost:!a.id||"auto-draft"===a.status,currentFormat:e.getEditedPostAttribute("format")||"standard",postType:a.type}},[]),{editPost:f}=(0,n.useDispatch)("core/editor"),{insertBlocks:b}=(0,n.useDispatch)("core/block-editor");(0,r.useEffect)(()=>{c&&"post"===u&&!l&&window.pfbtData&&setTimeout(()=>{i(!0),m(!0)},500)},[c,u,l]);if(!e||!window.pfbtData)return null;const h=window.pfbtData.formats,g=Object.entries(h).map(([t,e])=>"standard"===t?[t,{...e,name:(0,o.__)("Standard (Single Template)","post-formats-for-block-themes"),description:(0,o.__)("Default post format using the Single template","post-formats-for-block-themes")}]:[t,e]).sort((t,e)=>"standard"===t[0]?-1:"standard"===e[0]?1:t[1].name.localeCompare(e[1].name));return(0,t.createElement)(a.Modal,{title:(0,o.__)("Choose Post Format","post-formats-for-block-themes"),onRequestClose:()=>i(!1),className:"pfpu-format-modal"},(0,t.createElement)("div",{className:"pfpu-format-grid"},g.map(([e,n])=>(0,t.createElement)(a.Card,{key:e,className:"pfpu-format-card"},(0,t.createElement)(a.CardBody,null,(0,t.createElement)(a.Button,{onClick:()=>(t=>{if(f({format:t,template:"standard"===t?"":`single-format-${t}`}),"standard"!==t&&window.pfbtData.formats[t]){const e=window.pfbtData.formats[t];((t,e)=>{if(!window.pfbtData.patterns||!window.pfbtData.patterns[t])return;const a=window.pfbtData.patterns[t],n=(0,p.parse)(a);n.length>0&&e(n,0,void 0,!1)})(t,b),(0,s.speak)(sprintf(/* translators: %s: Format name */ /* translators: %s: Format name */
    2 (0,o.__)("Selected %s format. Pattern inserted.","post-formats-for-block-themes"),e.name),"polite")}i(!1)})(e),className:"pfpu-format-button",variant:"standard"===e?"primary":"secondary"},(0,t.createElement)("span",{className:`dashicons dashicons-${n.icon}`,"aria-hidden":"true"}),(0,t.createElement)("span",{className:"pfpu-format-name"},n.name)),(0,t.createElement)("p",{className:"pfpu-format-description"},n.description))))))},m=()=>{const{createNotice:t,removeNotice:e}=(0,n.useDispatch)("core/notices"),{currentFormat:a,blocks:s}=(0,n.useSelect)(t=>{const e=t("core/editor"),a=t("core/block-editor");return{currentFormat:e.getEditedPostAttribute("format")||"standard",blocks:a.getBlocks()}},[]);return(0,r.useEffect)(()=>{if("status"!==a)return void e("pfbt-status-char-count");const n=s.find(t=>"core/paragraph"===t.name&&t.attributes.className?.includes("status-paragraph"));if(!n)return void e("pfbt-status-char-count");const r=(n.attributes.content||"").replace(/<[^>]*>/g,"").length;r>280?t("error",sprintf(/* translators: %d: Current character count */ /* translators: %d: Current character count */
    3 (0,o.__)("%d / 280 characters - Status updates should be 280 characters or less","post-formats-for-block-themes"),r),{id:"pfbt-status-char-count",isDismissible:!1}):r>=260?t("warning",sprintf(/* translators: %d: Current character count */ /* translators: %d: Current character count */
    4 (0,o.__)("%d / 280 characters - Approaching limit","post-formats-for-block-themes"),r),{id:"pfbt-status-char-count",isDismissible:!1}):e("pfbt-status-char-count")},[a,s,t,e]),null};if((0,e.registerPlugin)("post-formats-for-block-themes",{render:()=>(0,t.createElement)(t.Fragment,null,(0,t.createElement)(l,null),(0,t.createElement)(m,null))}),(0,i.addFilter)("editor.BlockEdit","pfpu/status-paragraph-counter",e=>a=>{const{name:n,attributes:r,setAttributes:s}=a;if("core/paragraph"!==n||!r.className?.includes("status-paragraph"))return(0,t.createElement)(e,{...a});const i=280-(r.content||"").replace(/<[^>]*>/g,"").length,p=i<0;return(0,t.createElement)("div",{className:"pfpu-status-paragraph-wrapper"},(0,t.createElement)(e,{...a}),(0,t.createElement)("div",{className:`pfpu-char-counter ${p?"is-over-limit":""} ${i<=20?"is-warning":""}`,"aria-live":"polite","aria-atomic":"true"},(0,t.createElement)("span",null,sprintf(/* translators: %d: Remaining characters */ /* translators: %d: Remaining characters */
    5 (0,o.__)("%d characters remaining","post-formats-for-block-themes"),i))))}),"undefined"!=typeof document){const t=document.createElement("style");t.textContent='\n\t.pfpu-format-modal {\n\t\tmax-width: 800px;\n\t}\n\n\t.pfpu-format-grid {\n\t\tdisplay: grid;\n\t\tgrid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n\t\tgap: 1rem;\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-format-card {\n\t\ttext-align: center;\n\t}\n\n\t.pfpu-format-button {\n\t\twidth: 100%;\n\t\theight: auto;\n\t\tpadding: 1rem;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tgap: 0.5rem;\n\t}\n\n\t.pfpu-format-button .dashicons {\n\t\tfont-size: 2rem;\n\t\twidth: 2rem;\n\t\theight: 2rem;\n\t}\n\n\t.pfpu-format-name {\n\t\tfont-weight: 600;\n\t}\n\n\t.pfpu-format-description {\n\t\tfont-size: 0.875rem;\n\t\tcolor: #757575;\n\t\tmargin-top: 0.5rem;\n\t}\n\n\t.pfpu-format-change-actions {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 0.5rem;\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-status-validator {\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-status-paragraph-wrapper {\n\t\tposition: relative;\n\t}\n\n\t.pfpu-char-counter {\n\t\tposition: absolute;\n\t\tbottom: -30px;\n\t\tright: 0;\n\t\tfont-size: 0.875rem;\n\t\tcolor: #757575;\n\t}\n\n\t.pfpu-char-counter.is-warning {\n\t\tcolor: #f0b849;\n\t\tfont-weight: 600;\n\t}\n\n\t.pfpu-char-counter.is-over-limit {\n\t\tcolor: #d63638;\n\t\tfont-weight: 600;\n\t}\n\n\t/* Template Modal - Visual Separation for Format Templates */\n\n\t/* Target template items in the template selection modal */\n\t/* Format templates have "Format" in their title (e.g., "Gallery Format", "Aside Format") */\n\n\t/* Template card styling - targets the button/card elements */\n\t.edit-post-template__panel .components-panel__body button[aria-label*="Format"],\n\t.edit-site-template-card button[aria-label*="Format"],\n\t.block-editor-block-card button[aria-label*="Format"] {\n\t\tborder-left: 4px solid var(--wp-admin-theme-color, #2271b1) !important;\n\t\tpadding-left: 12px !important;\n\t\tbackground: rgba(34, 113, 177, 0.05) !important;\n\t\tposition: relative;\n\t}\n\n\t/* Add emoji badge to format templates */\n\t.edit-post-template__panel .components-panel__body button[aria-label*="Format"]::before,\n\t.edit-site-template-card button[aria-label*="Format"]::before,\n\t.block-editor-block-card button[aria-label*="Format"]::before {\n\t\tcontent: "🎨 ";\n\t\tmargin-right: 4px;\n\t\tfont-size: 14px;\n\t}\n\n\t/* Template dropdown/modal items */\n\t.edit-post-template-dropdown__content button[aria-label*="Format"],\n\t.components-dropdown-menu__menu button[aria-label*="Format"] {\n\t\tborder-left: 4px solid var(--wp-admin-theme-color, #2271b1);\n\t\tpadding-left: 12px;\n\t\tbackground: rgba(34, 113, 177, 0.05);\n\t}\n\n\t/* Add visual indicator to currently selected format template */\n\t.edit-post-template__panel .components-panel__body .is-selected[aria-label*="Format"],\n\tbutton[aria-checked="true"][aria-label*="Format"] {\n\t\tbackground: rgba(34, 113, 177, 0.1) !important;\n\t\tfont-weight: 600;\n\t}\n\n\t/* Template name in sidebar - show when auto-applied */\n\t.edit-post-template__panel .components-panel__body .components-base-control__help {\n\t\tfont-style: italic;\n\t\tcolor: #757575;\n\t}\n',document.head.appendChild(t)}})();
     1(()=>{"use strict";const t=window.React,e=window.wp.plugins,n=window.wp.components,r=window.wp.data,a=window.wp.element,o=window.wp.i18n,s=window.wp.a11y,i=window.wp.hooks,p=window.wp.blocks,c=window.wp.compose,m={},l=(t,e,n=null,r=!1)=>{if(m[t])return!1;if(!window.pfbtData?.patterns?.[t])return!1;const a=window.pfbtData.patterns[t],o=(0,p.parse)(a);return o.length>0&&(r&&n?n(o):e(o,0,void 0,!1),m[t]=!0,!0)},d=()=>{const t=(0,a.useRef)(null),{currentFormat:e,postType:n,blocks:i}=(0,r.useSelect)(t=>{const e=t("core/editor"),n=t("core/block-editor");return{currentFormat:e.getEditedPostAttribute("format")||"standard",postType:e.getCurrentPostType(),blocks:n.getBlocks()}},[]),{insertBlocks:p,resetBlocks:c}=(0,r.useDispatch)("core/block-editor");return(0,a.useEffect)(()=>{if("post"!==n)return;if(t.current===e)return;const r=null===t.current;if(t.current=e,r)return;if("standard"===e)return;const a=0===i.length||1===i.length&&"core/paragraph"===i[0].name&&!i[0].attributes.content;if(window.pfbtData?.patterns?.[e]&&l(e,p,c,a)){const t=window.pfbtData.formats?.[e]?.name||e;(0,s.speak)(sprintf((0,o.__)("Switched to %s format. Pattern inserted.","post-formats-for-block-themes"),t),"polite")}},[e,n,i,p,c]),null},u=()=>{const[e,i]=(0,a.useState)(!1),[p,c]=(0,a.useState)(!1),{isNewPost:m,currentFormat:d,postType:u}=(0,r.useSelect)(t=>{const e=t("core/editor"),n=e.getCurrentPost();return{isNewPost:!n.id||"auto-draft"===n.status,currentFormat:e.getEditedPostAttribute("format")||"standard",postType:n.type}},[]),{editPost:f}=(0,r.useDispatch)("core/editor"),{insertBlocks:w,resetBlocks:g}=(0,r.useDispatch)("core/block-editor");if((0,a.useEffect)(()=>{m&&"post"===u&&!p&&window.pfbtData&&setTimeout(()=>{i(!0),c(!0)},500)},[m,u,p]),!e||!window.pfbtData)return null;const h=window.pfbtData.formats,b=Object.entries(h).map(([t,e])=>"standard"===t?[t,{...e,name:(0,o.__)("Standard (Single Template)","post-formats-for-block-themes"),description:(0,o.__)("Default post format using the Single template","post-formats-for-block-themes")}]:[t,e]).sort((t,e)=>"standard"===t[0]?-1:"standard"===e[0]?1:t[1].name.localeCompare(e[1].name));return(0,t.createElement)(n.Modal,{title:(0,o.__)("Choose Post Format","post-formats-for-block-themes"),onRequestClose:()=>i(!1),className:"pfpu-format-modal"},(0,t.createElement)("div",{className:"pfpu-format-grid"},b.map(([e,r])=>(0,t.createElement)(n.Card,{key:e,className:"pfpu-format-card"},(0,t.createElement)(n.CardBody,null,(0,t.createElement)(n.Button,{onClick:()=>(t=>{if(f({format:t}),"standard"!==t&&window.pfbtData?.formats?.[t]){const e=window.pfbtData.formats[t];l(t,w,g,!0),(0,s.speak)(sprintf(/* translators: %s: Format name */ /* translators: %s: Format name */
     2(0,o.__)("Selected %s format. Pattern inserted.","post-formats-for-block-themes"),e.name),"polite")}i(!1)})(e),className:"pfpu-format-button",variant:"standard"===e?"primary":"secondary"},(0,t.createElement)("span",{className:`dashicons dashicons-${r.icon}`,"aria-hidden":"true"}),(0,t.createElement)("span",{className:"pfpu-format-name"},r.name)),(0,t.createElement)("p",{className:"pfpu-format-description"},r.description))))))};(0,e.registerPlugin)("post-formats-for-block-themes",{render:()=>(0,t.createElement)(t.Fragment,null,(0,t.createElement)(u,null),(0,t.createElement)(d,null))});const f=(0,c.createHigherOrderComponent)(e=>n=>{const{name:a,attributes:s}=n,i=((0,r.useSelect)(t=>{const e=t("core/editor");return e?e.getEditedPostAttribute("format"):null},[]),s.className?.includes("status-paragraph"));if("core/paragraph"!==a||!i)return(0,t.createElement)(e,{...n});const p=280-(s.content||"").replace(/<[^>]*>/g,"").length,c=p<0;return(0,t.createElement)("div",{className:"pfpu-status-paragraph-wrapper"},(0,t.createElement)(e,{...n}),(0,t.createElement)("div",{className:`pfpu-char-counter ${c?"is-over-limit":""} ${p<=20?"is-warning":""}`,"aria-live":"polite","aria-atomic":"true"},(0,t.createElement)("span",null,sprintf(/* translators: %d: Remaining characters */ /* translators: %d: Remaining characters */
     3(0,o.__)("%d characters remaining","post-formats-for-block-themes"),p))))},"withStatusCharacterCounter");if((0,i.addFilter)("editor.BlockEdit","pfpu/status-paragraph-counter",f),"undefined"!=typeof document){const t=document.createElement("style");t.textContent="\n\t.pfpu-format-modal {\n\t\tmax-width: 800px;\n\t}\n\n\t.pfpu-format-grid {\n\t\tdisplay: grid;\n\t\tgrid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n\t\tgap: 1rem;\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-format-card {\n\t\ttext-align: center;\n\t}\n\n\t.pfpu-format-button {\n\t\twidth: 100%;\n\t\theight: auto;\n\t\tpadding: 1rem;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tgap: 0.5rem;\n\t}\n\n\t.pfpu-format-button .dashicons {\n\t\tfont-size: 2rem;\n\t\twidth: 2rem;\n\t\theight: 2rem;\n\t}\n\n\t.pfpu-format-name {\n\t\tfont-weight: 600;\n\t}\n\n\t.pfpu-format-description {\n\t\tfont-size: 0.875rem;\n\t\tcolor: #757575;\n\t\tmargin-top: 0.5rem;\n\t}\n\n\t.pfpu-format-change-actions {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 0.5rem;\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-status-validator {\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-status-paragraph-wrapper {\n\t\tposition: relative;\n\t\tmargin-bottom: 30px;\n\t}\n\n\t.pfpu-char-counter {\n\t\tposition: absolute;\n\t\tbottom: -25px;\n\t\tright: 0;\n\t\tfont-size: 0.875rem;\n\t\tcolor: #757575;\n\t\tbackground: #fff;\n\t\tpadding: 2px 8px;\n\t\tborder-radius: 3px;\n\t\tbox-shadow: 0 1px 3px rgba(0,0,0,0.1);\n\t}\n\n\t.pfpu-char-counter.is-warning {\n\t\tcolor: #f0b849;\n\t\tfont-weight: 600;\n\t}\n\n\t.pfpu-char-counter.is-over-limit {\n\t\tcolor: #d63638;\n\t\tfont-weight: 600;\n\t\tbackground: #fcf0f1;\n\t}\n",document.head.appendChild(t)}})();
  • post-formats-for-block-themes/tags/1.1.3/includes/class-admin-columns.php

    r3413728 r3423300  
    170170        $icons = array(
    171171            'standard' => 'dashicons-admin-post',
    172             'aside'    => 'dashicons-aside',
     172            'aside'    => 'dashicons-format-aside',
    173173            'gallery'  => 'dashicons-format-gallery',
    174174            'link'     => 'dashicons-admin-links',
  • post-formats-for-block-themes/tags/1.1.3/includes/class-pattern-manager.php

    r3413728 r3423300  
    8989     * Only synced blocks are created to avoid duplicates.
    9090     *
     91     * PERFORMANCE FIX: Only runs on plugin activation or when patterns
     92     * are missing. Uses a transient to avoid checking on every page load.
     93     *
    9194     * @since 1.0.0
    9295     */
    9396    public static function register_all_patterns() {
     97        // PERFORMANCE: Skip on front-end entirely.
     98        if ( ! is_admin() && ! wp_doing_ajax() && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
     99            return;
     100        }
     101
     102        // PERFORMANCE: Use transient to avoid running on every admin page load.
     103        // Patterns only need to be created once, then they persist in the database.
     104        $patterns_version = PFBT_VERSION;
     105        $cached_version   = get_transient( 'pfbt_patterns_registered' );
     106
     107        // Skip if patterns were already registered for this plugin version.
     108        if ( $cached_version === $patterns_version ) {
     109            return;
     110        }
     111
    94112        $instance = self::instance();
    95113        $formats  = PFBT_Format_Registry::get_all_formats();
     
    99117            $instance->create_synced_pattern( $slug, $format );
    100118        }
     119
     120        // Mark patterns as registered for 1 week (they persist in database anyway).
     121        set_transient( 'pfbt_patterns_registered', $patterns_version, WEEK_IN_SECONDS );
     122    }
     123
     124    /**
     125     * Force re-registration of patterns
     126     *
     127     * Called on plugin activation to ensure patterns are up to date.
     128     *
     129     * @since 1.1.3
     130     */
     131    public static function force_register_patterns() {
     132        delete_transient( 'pfbt_patterns_registered' );
     133        self::register_all_patterns();
    101134    }
    102135
     
    157190     * all instances update automatically.
    158191     *
     192     * PERFORMANCE FIX: Only updates existing blocks when content has
     193     * actually changed to avoid creating unnecessary revisions.
     194     *
    159195     * @since 1.0.0
    160196     *
     
    188224        );
    189225
    190         $block_data = array(
    191             'post_title'   => $block_title,
    192             'post_content' => $pattern_content,
    193             'post_status'  => 'publish',
    194             'post_type'    => 'wp_block',
    195             'post_name'    => $block_slug,
    196         );
    197 
    198226        if ( $existing_block ) {
    199             // Update existing reusable block.
    200             $block_data['ID'] = $existing_block->ID;
    201             $block_id         = wp_update_post( $block_data );
     227            // PERFORMANCE: Only update if content has actually changed.
     228            // This prevents creating revisions on every admin page load.
     229            $content_changed = ( trim( $existing_block->post_content ) !== trim( $pattern_content ) );
     230            $title_changed   = ( $existing_block->post_title !== $block_title );
     231
     232            if ( ! $content_changed && ! $title_changed ) {
     233                // Content unchanged - skip update to avoid revision creation.
     234                $block_id = $existing_block->ID;
     235            } else {
     236                // Content changed - update the block.
     237                $block_data = array(
     238                    'ID'           => $existing_block->ID,
     239                    'post_title'   => $block_title,
     240                    'post_content' => $pattern_content,
     241                    'post_status'  => 'publish',
     242                    'post_type'    => 'wp_block',
     243                    'post_name'    => $block_slug,
     244                );
     245                $block_id   = wp_update_post( $block_data );
     246            }
    202247        } else {
    203248            // Create new reusable block.
    204             $block_id = wp_insert_post( $block_data );
     249            $block_data = array(
     250                'post_title'   => $block_title,
     251                'post_content' => $pattern_content,
     252                'post_status'  => 'publish',
     253                'post_type'    => 'wp_block',
     254                'post_name'    => $block_slug,
     255            );
     256            $block_id   = wp_insert_post( $block_data );
    205257
    206258            // Add metadata to identify this as a post format pattern.
     
    210262        }
    211263
    212         // Assign to Post formats category.
     264        // Assign to Post formats category (only for new blocks or if category missing).
    213265        if ( $block_id && ! is_wp_error( $block_id ) ) {
    214             // Get or create the category term.
    215             $category_term = get_term_by( 'slug', 'post-formats', 'wp_pattern_category' );
    216 
    217             if ( ! $category_term ) {
    218                 $category_term = wp_insert_term(
    219                     __( 'Post formats', 'post-formats-for-block-themes' ),
    220                     'wp_pattern_category',
    221                     array( 'slug' => 'post-formats' )
    222                 );
    223             }
    224 
    225             // Assign the category to the block.
    226             if ( $category_term && ! is_wp_error( $category_term ) ) {
    227                 $term_id = is_array( $category_term ) ? $category_term['term_id'] : $category_term->term_id;
    228                 wp_set_object_terms( $block_id, $term_id, 'wp_pattern_category' );
     266            // PERFORMANCE: Check if block already has the correct category.
     267            $existing_terms = wp_get_object_terms( $block_id, 'wp_pattern_category', array( 'fields' => 'slugs' ) );
     268            $has_category   = ! is_wp_error( $existing_terms ) && in_array( 'post-formats', $existing_terms, true );
     269
     270            if ( ! $has_category ) {
     271                // Get or create the category term.
     272                $category_term = get_term_by( 'slug', 'post-formats', 'wp_pattern_category' );
     273
     274                if ( ! $category_term ) {
     275                    $category_term = wp_insert_term(
     276                        __( 'Post formats', 'post-formats-for-block-themes' ),
     277                        'wp_pattern_category',
     278                        array( 'slug' => 'post-formats' )
     279                    );
     280                }
     281
     282                // Assign the category to the block.
     283                if ( $category_term && ! is_wp_error( $category_term ) ) {
     284                    $term_id = is_array( $category_term ) ? $category_term['term_id'] : $category_term->term_id;
     285                    wp_set_object_terms( $block_id, $term_id, 'wp_pattern_category' );
     286                }
    229287            }
    230288        }
  • post-formats-for-block-themes/tags/1.1.3/patterns/aside.php

    r3413728 r3423300  
    1414 */
    1515?>
    16 <!-- wp:group {"layout":{"type":"constrained"}} -->
    17 <div class="wp-block-group">
    18     <!-- wp:paragraph -->
    19     <p></p>
    20     <!-- /wp:paragraph -->
    21 </div>
    22 <!-- /wp:group -->
     16<!-- wp:paragraph -->
     17<p></p>
     18<!-- /wp:paragraph -->
  • post-formats-for-block-themes/tags/1.1.3/patterns/audio.php

    r3413728 r3423300  
    77 * Audio Post Format Pattern
    88 *
    9  * Audio file or embed. Starts with a core audio block.
     9 * Audio file or embed. Starts with a core audio block followed by a paragraph.
    1010 * Can be swapped with Podlove Podcast Publisher or Able Player blocks.
    1111 *
     
    1414 */
    1515?>
    16 <!-- wp:group {"layout":{"type":"constrained"}} -->
    17 <div class="wp-block-group">
    18     <!-- wp:audio -->
    19     <figure class="wp-block-audio"><audio controls></audio></figure>
    20     <!-- /wp:audio -->
    21 </div>
    22 <!-- /wp:group -->
     16<!-- wp:audio -->
     17<figure class="wp-block-audio"><audio controls></audio></figure>
     18<!-- /wp:audio -->
    2319
     20<!-- wp:paragraph -->
     21<p></p>
     22<!-- /wp:paragraph -->
  • post-formats-for-block-themes/tags/1.1.3/patterns/chat.php

    r3413728 r3423300  
    77 * Chat Post Format Pattern
    88 *
    9  * Chat transcript or conversation log. Uses the Chat Log block.
     9 * Chat transcript or conversation log. Uses the Chat Log block followed by a paragraph.
    1010 *
    1111 * Note: Requires the Chat Log plugin to be active.
     
    1616 */
    1717?>
    18 <!-- wp:group {"layout":{"type":"constrained"}} -->
    19 <div class="wp-block-group">
    20     <!-- wp:chatlog/conversation /-->
    21 </div>
    22 <!-- /wp:group -->
     18<!-- wp:chatlog/conversation /-->
    2319
     20<!-- wp:paragraph -->
     21<p></p>
     22<!-- /wp:paragraph -->
  • post-formats-for-block-themes/tags/1.1.3/patterns/gallery.php

    r3413728 r3423300  
    77 * Gallery Post Format Pattern
    88 *
    9  * Image gallery post. Starts with a gallery block.
     9 * Image gallery post. Starts with a gallery block followed by a paragraph.
    1010 *
    1111 * @package PostFormatsBlockThemes
     
    1313 */
    1414?>
    15 <!-- wp:group {"layout":{"type":"constrained"}} -->
    16 <div class="wp-block-group">
    17     <!-- wp:gallery {"linkTo":"none"} -->
    18     <figure class="wp-block-gallery has-nested-images columns-default is-cropped">
    19     <!-- wp:image -->
    20     <figure class="wp-block-image"><img alt=""/></figure>
    21     <!-- /wp:image -->
    22     </figure>
    23     <!-- /wp:gallery -->
    24 </div>
    25 <!-- /wp:group -->
     15<!-- wp:gallery {"linkTo":"none"} -->
     16<figure class="wp-block-gallery has-nested-images columns-default is-cropped"></figure>
     17<!-- /wp:gallery -->
    2618
     19<!-- wp:paragraph -->
     20<p></p>
     21<!-- /wp:paragraph -->
  • post-formats-for-block-themes/tags/1.1.3/patterns/image.php

    r3413728 r3423300  
    77 * Image Post Format Pattern
    88 *
    9  * Single image post. Starts with an image block.
     9 * Single image post. Starts with an image block followed by a paragraph.
    1010 *
    1111 * @package PostFormatsBlockThemes
     
    1313 */
    1414?>
    15 <!-- wp:group {"layout":{"type":"constrained"}} -->
    16 <div class="wp-block-group">
    17     <!-- wp:image {"sizeSlug":"large"} -->
    18     <figure class="wp-block-image size-large"><img alt=""/></figure>
    19     <!-- /wp:image -->
    20 </div>
    21 <!-- /wp:group -->
     15<!-- wp:image {"sizeSlug":"large"} -->
     16<figure class="wp-block-image size-large"><img alt=""/></figure>
     17<!-- /wp:image -->
    2218
     19<!-- wp:paragraph -->
     20<p></p>
     21<!-- /wp:paragraph -->
  • post-formats-for-block-themes/tags/1.1.3/patterns/link.php

    r3413728 r3423300  
    99 * Link to external content. Uses Bookmark Card plugin if available,
    1010 * otherwise falls back to paragraph with link styling.
     11 * Followed by a paragraph for additional commentary.
    1112 *
    1213 * @package PostFormatsBlockThemes
     
    1617// Check if Bookmark Card plugin is available.
    1718$pfbt_has_bookmark_card = function_exists( 'bookmark_card_register_block' ) || has_block( 'bookmark-card/bookmark-card' );
    18 ?>
    19 <!-- wp:group {"layout":{"type":"constrained"}} -->
    20 <div class="wp-block-group">
    21 <?php
     19
    2220if ( $pfbt_has_bookmark_card ) {
    2321    // Use Bookmark Card block.
    2422    ?>
    25     <!-- wp:bookmark-card/bookmark-card /-->
     23<!-- wp:bookmark-card/bookmark-card /-->
     24
     25<!-- wp:paragraph -->
     26<p></p>
     27<!-- /wp:paragraph -->
    2628    <?php
    2729} else {
    2830    // Fallback to paragraph with link placeholder.
    2931    ?>
    30     <!-- wp:paragraph {"className":"link-format-fallback","fontSize":"large"} -->
    31     <p class="link-format-fallback has-large-font-size"><a href="#"></a></p>
    32     <!-- /wp:paragraph -->
     32<!-- wp:paragraph {"className":"link-format-fallback","fontSize":"large"} -->
     33<p class="link-format-fallback has-large-font-size"><a href="#"></a></p>
     34<!-- /wp:paragraph -->
     35
     36<!-- wp:paragraph -->
     37<p></p>
     38<!-- /wp:paragraph -->
    3339    <?php
    3440}
    3541?>
    36 </div>
    37 <!-- /wp:group -->
    38 
  • post-formats-for-block-themes/tags/1.1.3/patterns/quote.php

    r3413728 r3423300  
    77 * Quote Post Format Pattern
    88 *
    9  * Quotation or citation. Starts with a quote block.
     9 * Quotation or citation. Starts with a quote block followed by a paragraph.
    1010 *
    1111 * @package PostFormatsBlockThemes
     
    1313 */
    1414?>
    15 <!-- wp:group {"layout":{"type":"constrained"}} -->
    16 <div class="wp-block-group">
    17     <!-- wp:quote -->
    18     <blockquote class="wp-block-quote">
    19     <!-- wp:paragraph -->
    20     <p></p>
    21     <!-- /wp:paragraph -->
    22     </blockquote>
    23     <!-- /wp:quote -->
    24 </div>
    25 <!-- /wp:group -->
     15<!-- wp:quote -->
     16<blockquote class="wp-block-quote">
     17<!-- wp:paragraph -->
     18<p></p>
     19<!-- /wp:paragraph -->
     20</blockquote>
     21<!-- /wp:quote -->
    2622
     23<!-- wp:paragraph -->
     24<p></p>
     25<!-- /wp:paragraph -->
  • post-formats-for-block-themes/tags/1.1.3/patterns/status.php

    r3413728 r3423300  
    1414 */
    1515
    16 // Paragraph with status-paragraph class for validation.
     16// Single paragraph with status-paragraph class for character counter.
    1717?>
    18 <!-- wp:group {"layout":{"type":"constrained"}} -->
    19 <div class="wp-block-group">
    20     <!-- wp:paragraph {"className":"status-paragraph","fontSize":"large"} -->
    21     <p class="status-paragraph has-large-font-size"></p>
    22     <!-- /wp:paragraph -->
    23 </div>
    24 <!-- /wp:group -->
     18<!-- wp:paragraph {"className":"status-paragraph","fontSize":"large"} -->
     19<p class="status-paragraph has-large-font-size"></p>
     20<!-- /wp:paragraph -->
    2521
  • post-formats-for-block-themes/tags/1.1.3/patterns/video.php

    r3413728 r3423300  
    77 * Video Post Format Pattern
    88 *
    9  * Video file or embed. Starts with a video block.
     9 * Video file or embed. Starts with a video block followed by a paragraph.
    1010 *
    1111 * @package PostFormatsBlockThemes
     
    1313 */
    1414?>
    15 <!-- wp:group {"layout":{"type":"constrained"}} -->
    16 <div class="wp-block-group">
    17     <!-- wp:video -->
    18     <figure class="wp-block-video"><video controls></video></figure>
    19     <!-- /wp:video -->
    20 </div>
    21 <!-- /wp:group -->
     15<!-- wp:video -->
     16<figure class="wp-block-video"><video controls></video></figure>
     17<!-- /wp:video -->
    2218
     19<!-- wp:paragraph -->
     20<p></p>
     21<!-- /wp:paragraph -->
  • post-formats-for-block-themes/tags/1.1.3/post-formats-for-block-themes.php

    r3416925 r3423300  
    44 * Plugin URI: https://wordpress.org/plugins/post-formats-for-block-themes/
    55 * Description: Modernizes WordPress post formats for block themes with format-specific patterns, auto-detection, and enhanced editor experience.
    6  * Version: 1.1.2
     6 * Version: 1.1.3
    77 * Requires at least: 6.8
    88 * Tested up to: 6.9
     
    3939 * Plugin constants
    4040 */
    41 define( 'PFBT_VERSION', '1.1.2' );
     41define( 'PFBT_VERSION', '1.1.3' );
    4242define( 'PFBT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    4343define( 'PFBT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
     
    378378    delete_transient( 'pfbt_bookmark_card_available' );
    379379    delete_transient( 'pfbt_chatlog_block_available' );
     380    delete_transient( 'pfbt_patterns_registered' );
    380381}
    381382register_deactivation_hook( __FILE__, 'pfbt_deactivate' );
     383
     384/**
     385 * Add plugin action links
     386 *
     387 * Adds a "Settings" link to the plugin's row on the Plugins page
     388 * that links to the Post Format Repair tool.
     389 *
     390 * @since 1.1.3
     391 *
     392 * @param array $links Existing plugin action links.
     393 * @return array Modified plugin action links.
     394 */
     395function pfbt_plugin_action_links( $links ) {
     396    $settings_link = sprintf(
     397        '<a href="%s">%s</a>',
     398        esc_url( admin_url( 'tools.php?page=pfbt-repair-tool' ) ),
     399        esc_html__( 'Settings', 'post-formats-for-block-themes' )
     400    );
     401    array_unshift( $links, $settings_link );
     402    return $links;
     403}
     404add_filter( 'plugin_action_links_' . PFBT_PLUGIN_BASENAME, 'pfbt_plugin_action_links' );
     405
     406/**
     407 * Limit revisions for wp_block post type
     408 *
     409 * Synced patterns are stored as wp_block posts. Limit revisions
     410 * to prevent database bloat from pattern updates.
     411 *
     412 * @since 1.1.3
     413 *
     414 * @param int     $num  Number of revisions to keep.
     415 * @param WP_Post $post The post object.
     416 * @return int Modified number of revisions.
     417 */
     418function pfbt_limit_wp_block_revisions( $num, $post ) {
     419    if ( 'wp_block' === $post->post_type ) {
     420        return 3;
     421    }
     422    return $num;
     423}
     424add_filter( 'wp_revisions_to_keep', 'pfbt_limit_wp_block_revisions', 10, 2 );
  • post-formats-for-block-themes/tags/1.1.3/readme.txt

    r3416925 r3423300  
    66Tested up to: 6.9
    77Requires PHP: 7.4
    8 Stable tag: 1.1.2
     8Stable tag: 1.1.3
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    347347
    348348== Changelog ==
     349
     350= 1.1.3 - 2025-12-18 =
     351
     352**Performance**
     353
     354* **Fixed:** Critical performance issue with revision queries on sites with many synced patterns
     355* **Added:** Transient-based caching for pattern registration to avoid unnecessary database operations
     356* **Added:** Pattern registration now skipped entirely on front-end for better performance
     357* **Added:** Pattern updates only occur when content has actually changed
     358* **Added:** Revision limiter for wp_block post type (limits to 3 revisions to prevent database bloat)
     359
     360**New Features**
     361
     362* **Added:** "Settings" link on Plugins page that links to Post Format Repair tool for easy access
     363
     364**Bug Fixes**
     365
     366* **Fixed:** Duplicate pattern insertion when selecting format from modal (patterns were being inserted twice)
     367* **Fixed:** Status format character counter appearing twice in editor
     368* **Fixed:** Aside format icon not displaying in Posts admin list (changed from `dashicons-aside` to `dashicons-format-aside`)
     369* **Fixed:** JavaScript error "Cannot read properties of undefined (reading 'postCategories')" in block editor
     370* **Fixed:** Pattern transient now cleared on plugin deactivation to ensure fresh patterns on reactivation
     371
     372**Improvements**
     373
     374* **Changed:** Simplified all format patterns by removing unnecessary wrapper Group blocks
     375* **Changed:** Status pattern now uses single paragraph with `status-paragraph` class
     376* **Changed:** Aside pattern now uses single paragraph (no wrapper)
     377* **Changed:** All other format patterns now use primary block + paragraph structure for cleaner editing
     378
     379**Security**
     380
     381* **Added:** `domReady` wrapper for Post Format Block to prevent race conditions
     382* **Added:** Null check with fallback icon for safer script initialization
     383* **Added:** Asset file for Post Format Block script dependencies
    349384
    350385= 1.1.2 - 2025-12-11 =
     
    460495== Upgrade Notice ==
    461496
     497= 1.1.3 =
     498Critical performance fix: Resolves database performance issue with revision queries. Also fixes duplicate pattern insertion, character counter appearing twice, and JavaScript errors. Simplified patterns for cleaner editing. All users should upgrade.
     499
     500= 1.1.2 =
     501Editor UI improvements: Simplified format selection, better template chooser, fixed standard posts incorrectly showing format templates.
     502
     503= 1.1.1 =
     504Critical fix: Format templates no longer appear in Template dropdown, fixing template selection issues.
     505
    462506= 1.1.0 =
    463507Major update: Adds Post Format Block for frontend display, post format admin column with filtering, template improvements with category/tag/format display, and critical bug fixes for template assignment. All users should upgrade.
  • post-formats-for-block-themes/trunk/blocks/post-format-block/index.js

    r3413728 r3423300  
    1818import { __ } from '@wordpress/i18n';
    1919
    20 // Icon
     20// DOM Ready
     21import domReady from '@wordpress/dom-ready';
     22
     23// Icon - use optional chaining to prevent errors if icons not loaded yet
    2124import { postCategories as icon } from '@wordpress/icons';
    2225
     
    2932 * @see https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/post-terms/variations.js
    3033 */
    31 const variation = {
    32     name: 'post_format',
    33     title: __( 'Post Format', 'post-formats-for-block-themes' ),
    34     description: __( "Display a post's format", 'post-formats-for-block-themes' ),
    35     icon,
    36     isDefault: false,
    37     attributes: { term: 'post_format' },
    38     isActive: ( blockAttributes ) => blockAttributes.term === 'post_format',
    39     scope: [ 'inserter', 'transform' ],
    40 };
     34domReady( () => {
     35    // Fallback icon if postCategories isn't available
     36    const variationIcon = icon || 'tag';
    4137
    42 registerBlockVariation( 'core/post-terms', variation );
     38    const variation = {
     39        name: 'post_format',
     40        title: __( 'Post Format', 'post-formats-for-block-themes' ),
     41        description: __( "Display a post's format", 'post-formats-for-block-themes' ),
     42        icon: variationIcon,
     43        isDefault: false,
     44        attributes: { term: 'post_format' },
     45        isActive: ( blockAttributes ) => blockAttributes.term === 'post_format',
     46        scope: [ 'inserter', 'transform' ],
     47    };
     48
     49    registerBlockVariation( 'core/post-terms', variation );
     50} );
  • post-formats-for-block-themes/trunk/blocks/post-format-block/index.min.js

    r3413728 r3423300  
    33 * @package PostFormatsBlockThemes
    44 */
    5 (function(){var e=window.wp.blocks,t=window.wp.i18n,n=window.wp.icons;const r={name:"post_format",title:(0,t.__)("Post Format","post-formats-for-block-themes"),description:(0,t.__)("Display a post's format","post-formats-for-block-themes"),icon:n.postCategories,isDefault:false,attributes:{term:"post_format"},isActive:function(e){return e.term==="post_format"},scope:["inserter","transform"]};(0,e.registerBlockVariation)("core/post-terms",r)})();
     5(function(){var e=window.wp.blocks,t=window.wp.i18n,n=window.wp.icons,d=window.wp.domReady;d(function(){var i=(n&&n.postCategories)?n.postCategories:"tag";var r={name:"post_format",title:(0,t.__)("Post Format","post-formats-for-block-themes"),description:(0,t.__)("Display a post's format","post-formats-for-block-themes"),icon:i,isDefault:false,attributes:{term:"post_format"},isActive:function(e){return e.term==="post_format"},scope:["inserter","transform"]};(0,e.registerBlockVariation)("core/post-terms",r)})})();
  • post-formats-for-block-themes/trunk/build/index.asset.php

    r3416925 r3423300  
    1 <?php return array('dependencies' => array('react', 'wp-a11y', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => 'aee0db3c8e30a508c90e');
     1<?php return array('dependencies' => array('react', 'wp-a11y', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => '5add7157ad1a69c9a4cc');
  • post-formats-for-block-themes/trunk/build/index.js

    r3416925 r3423300  
    1 (()=>{"use strict";const t=window.React,e=window.wp.plugins,a=window.wp.components,n=window.wp.data,r=window.wp.element,o=window.wp.i18n,s=window.wp.a11y,i=window.wp.hooks,p=window.wp.blocks,l=()=>{const[e,i]=(0,r.useState)(!1),[l,m]=(0,r.useState)(!1),{isNewPost:c,currentFormat:d,postType:u}=(0,n.useSelect)(t=>{const e=t("core/editor"),a=e.getCurrentPost();return{isNewPost:!a.id||"auto-draft"===a.status,currentFormat:e.getEditedPostAttribute("format")||"standard",postType:a.type}},[]),{editPost:f}=(0,n.useDispatch)("core/editor"),{insertBlocks:b}=(0,n.useDispatch)("core/block-editor");(0,r.useEffect)(()=>{c&&"post"===u&&!l&&window.pfbtData&&setTimeout(()=>{i(!0),m(!0)},500)},[c,u,l]);if(!e||!window.pfbtData)return null;const h=window.pfbtData.formats,g=Object.entries(h).map(([t,e])=>"standard"===t?[t,{...e,name:(0,o.__)("Standard (Single Template)","post-formats-for-block-themes"),description:(0,o.__)("Default post format using the Single template","post-formats-for-block-themes")}]:[t,e]).sort((t,e)=>"standard"===t[0]?-1:"standard"===e[0]?1:t[1].name.localeCompare(e[1].name));return(0,t.createElement)(a.Modal,{title:(0,o.__)("Choose Post Format","post-formats-for-block-themes"),onRequestClose:()=>i(!1),className:"pfpu-format-modal"},(0,t.createElement)("div",{className:"pfpu-format-grid"},g.map(([e,n])=>(0,t.createElement)(a.Card,{key:e,className:"pfpu-format-card"},(0,t.createElement)(a.CardBody,null,(0,t.createElement)(a.Button,{onClick:()=>(t=>{if(f({format:t,template:"standard"===t?"":`single-format-${t}`}),"standard"!==t&&window.pfbtData.formats[t]){const e=window.pfbtData.formats[t];((t,e)=>{if(!window.pfbtData.patterns||!window.pfbtData.patterns[t])return;const a=window.pfbtData.patterns[t],n=(0,p.parse)(a);n.length>0&&e(n,0,void 0,!1)})(t,b),(0,s.speak)(sprintf(/* translators: %s: Format name */ /* translators: %s: Format name */
    2 (0,o.__)("Selected %s format. Pattern inserted.","post-formats-for-block-themes"),e.name),"polite")}i(!1)})(e),className:"pfpu-format-button",variant:"standard"===e?"primary":"secondary"},(0,t.createElement)("span",{className:`dashicons dashicons-${n.icon}`,"aria-hidden":"true"}),(0,t.createElement)("span",{className:"pfpu-format-name"},n.name)),(0,t.createElement)("p",{className:"pfpu-format-description"},n.description))))))},m=()=>{const{createNotice:t,removeNotice:e}=(0,n.useDispatch)("core/notices"),{currentFormat:a,blocks:s}=(0,n.useSelect)(t=>{const e=t("core/editor"),a=t("core/block-editor");return{currentFormat:e.getEditedPostAttribute("format")||"standard",blocks:a.getBlocks()}},[]);return(0,r.useEffect)(()=>{if("status"!==a)return void e("pfbt-status-char-count");const n=s.find(t=>"core/paragraph"===t.name&&t.attributes.className?.includes("status-paragraph"));if(!n)return void e("pfbt-status-char-count");const r=(n.attributes.content||"").replace(/<[^>]*>/g,"").length;r>280?t("error",sprintf(/* translators: %d: Current character count */ /* translators: %d: Current character count */
    3 (0,o.__)("%d / 280 characters - Status updates should be 280 characters or less","post-formats-for-block-themes"),r),{id:"pfbt-status-char-count",isDismissible:!1}):r>=260?t("warning",sprintf(/* translators: %d: Current character count */ /* translators: %d: Current character count */
    4 (0,o.__)("%d / 280 characters - Approaching limit","post-formats-for-block-themes"),r),{id:"pfbt-status-char-count",isDismissible:!1}):e("pfbt-status-char-count")},[a,s,t,e]),null};if((0,e.registerPlugin)("post-formats-for-block-themes",{render:()=>(0,t.createElement)(t.Fragment,null,(0,t.createElement)(l,null),(0,t.createElement)(m,null))}),(0,i.addFilter)("editor.BlockEdit","pfpu/status-paragraph-counter",e=>a=>{const{name:n,attributes:r,setAttributes:s}=a;if("core/paragraph"!==n||!r.className?.includes("status-paragraph"))return(0,t.createElement)(e,{...a});const i=280-(r.content||"").replace(/<[^>]*>/g,"").length,p=i<0;return(0,t.createElement)("div",{className:"pfpu-status-paragraph-wrapper"},(0,t.createElement)(e,{...a}),(0,t.createElement)("div",{className:`pfpu-char-counter ${p?"is-over-limit":""} ${i<=20?"is-warning":""}`,"aria-live":"polite","aria-atomic":"true"},(0,t.createElement)("span",null,sprintf(/* translators: %d: Remaining characters */ /* translators: %d: Remaining characters */
    5 (0,o.__)("%d characters remaining","post-formats-for-block-themes"),i))))}),"undefined"!=typeof document){const t=document.createElement("style");t.textContent='\n\t.pfpu-format-modal {\n\t\tmax-width: 800px;\n\t}\n\n\t.pfpu-format-grid {\n\t\tdisplay: grid;\n\t\tgrid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n\t\tgap: 1rem;\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-format-card {\n\t\ttext-align: center;\n\t}\n\n\t.pfpu-format-button {\n\t\twidth: 100%;\n\t\theight: auto;\n\t\tpadding: 1rem;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tgap: 0.5rem;\n\t}\n\n\t.pfpu-format-button .dashicons {\n\t\tfont-size: 2rem;\n\t\twidth: 2rem;\n\t\theight: 2rem;\n\t}\n\n\t.pfpu-format-name {\n\t\tfont-weight: 600;\n\t}\n\n\t.pfpu-format-description {\n\t\tfont-size: 0.875rem;\n\t\tcolor: #757575;\n\t\tmargin-top: 0.5rem;\n\t}\n\n\t.pfpu-format-change-actions {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 0.5rem;\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-status-validator {\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-status-paragraph-wrapper {\n\t\tposition: relative;\n\t}\n\n\t.pfpu-char-counter {\n\t\tposition: absolute;\n\t\tbottom: -30px;\n\t\tright: 0;\n\t\tfont-size: 0.875rem;\n\t\tcolor: #757575;\n\t}\n\n\t.pfpu-char-counter.is-warning {\n\t\tcolor: #f0b849;\n\t\tfont-weight: 600;\n\t}\n\n\t.pfpu-char-counter.is-over-limit {\n\t\tcolor: #d63638;\n\t\tfont-weight: 600;\n\t}\n\n\t/* Template Modal - Visual Separation for Format Templates */\n\n\t/* Target template items in the template selection modal */\n\t/* Format templates have "Format" in their title (e.g., "Gallery Format", "Aside Format") */\n\n\t/* Template card styling - targets the button/card elements */\n\t.edit-post-template__panel .components-panel__body button[aria-label*="Format"],\n\t.edit-site-template-card button[aria-label*="Format"],\n\t.block-editor-block-card button[aria-label*="Format"] {\n\t\tborder-left: 4px solid var(--wp-admin-theme-color, #2271b1) !important;\n\t\tpadding-left: 12px !important;\n\t\tbackground: rgba(34, 113, 177, 0.05) !important;\n\t\tposition: relative;\n\t}\n\n\t/* Add emoji badge to format templates */\n\t.edit-post-template__panel .components-panel__body button[aria-label*="Format"]::before,\n\t.edit-site-template-card button[aria-label*="Format"]::before,\n\t.block-editor-block-card button[aria-label*="Format"]::before {\n\t\tcontent: "🎨 ";\n\t\tmargin-right: 4px;\n\t\tfont-size: 14px;\n\t}\n\n\t/* Template dropdown/modal items */\n\t.edit-post-template-dropdown__content button[aria-label*="Format"],\n\t.components-dropdown-menu__menu button[aria-label*="Format"] {\n\t\tborder-left: 4px solid var(--wp-admin-theme-color, #2271b1);\n\t\tpadding-left: 12px;\n\t\tbackground: rgba(34, 113, 177, 0.05);\n\t}\n\n\t/* Add visual indicator to currently selected format template */\n\t.edit-post-template__panel .components-panel__body .is-selected[aria-label*="Format"],\n\tbutton[aria-checked="true"][aria-label*="Format"] {\n\t\tbackground: rgba(34, 113, 177, 0.1) !important;\n\t\tfont-weight: 600;\n\t}\n\n\t/* Template name in sidebar - show when auto-applied */\n\t.edit-post-template__panel .components-panel__body .components-base-control__help {\n\t\tfont-style: italic;\n\t\tcolor: #757575;\n\t}\n',document.head.appendChild(t)}})();
     1(()=>{"use strict";const t=window.React,e=window.wp.plugins,n=window.wp.components,r=window.wp.data,a=window.wp.element,o=window.wp.i18n,s=window.wp.a11y,i=window.wp.hooks,p=window.wp.blocks,c=window.wp.compose,m={},l=(t,e,n=null,r=!1)=>{if(m[t])return!1;if(!window.pfbtData?.patterns?.[t])return!1;const a=window.pfbtData.patterns[t],o=(0,p.parse)(a);return o.length>0&&(r&&n?n(o):e(o,0,void 0,!1),m[t]=!0,!0)},d=()=>{const t=(0,a.useRef)(null),{currentFormat:e,postType:n,blocks:i}=(0,r.useSelect)(t=>{const e=t("core/editor"),n=t("core/block-editor");return{currentFormat:e.getEditedPostAttribute("format")||"standard",postType:e.getCurrentPostType(),blocks:n.getBlocks()}},[]),{insertBlocks:p,resetBlocks:c}=(0,r.useDispatch)("core/block-editor");return(0,a.useEffect)(()=>{if("post"!==n)return;if(t.current===e)return;const r=null===t.current;if(t.current=e,r)return;if("standard"===e)return;const a=0===i.length||1===i.length&&"core/paragraph"===i[0].name&&!i[0].attributes.content;if(window.pfbtData?.patterns?.[e]&&l(e,p,c,a)){const t=window.pfbtData.formats?.[e]?.name||e;(0,s.speak)(sprintf((0,o.__)("Switched to %s format. Pattern inserted.","post-formats-for-block-themes"),t),"polite")}},[e,n,i,p,c]),null},u=()=>{const[e,i]=(0,a.useState)(!1),[p,c]=(0,a.useState)(!1),{isNewPost:m,currentFormat:d,postType:u}=(0,r.useSelect)(t=>{const e=t("core/editor"),n=e.getCurrentPost();return{isNewPost:!n.id||"auto-draft"===n.status,currentFormat:e.getEditedPostAttribute("format")||"standard",postType:n.type}},[]),{editPost:f}=(0,r.useDispatch)("core/editor"),{insertBlocks:w,resetBlocks:g}=(0,r.useDispatch)("core/block-editor");if((0,a.useEffect)(()=>{m&&"post"===u&&!p&&window.pfbtData&&setTimeout(()=>{i(!0),c(!0)},500)},[m,u,p]),!e||!window.pfbtData)return null;const h=window.pfbtData.formats,b=Object.entries(h).map(([t,e])=>"standard"===t?[t,{...e,name:(0,o.__)("Standard (Single Template)","post-formats-for-block-themes"),description:(0,o.__)("Default post format using the Single template","post-formats-for-block-themes")}]:[t,e]).sort((t,e)=>"standard"===t[0]?-1:"standard"===e[0]?1:t[1].name.localeCompare(e[1].name));return(0,t.createElement)(n.Modal,{title:(0,o.__)("Choose Post Format","post-formats-for-block-themes"),onRequestClose:()=>i(!1),className:"pfpu-format-modal"},(0,t.createElement)("div",{className:"pfpu-format-grid"},b.map(([e,r])=>(0,t.createElement)(n.Card,{key:e,className:"pfpu-format-card"},(0,t.createElement)(n.CardBody,null,(0,t.createElement)(n.Button,{onClick:()=>(t=>{if(f({format:t}),"standard"!==t&&window.pfbtData?.formats?.[t]){const e=window.pfbtData.formats[t];l(t,w,g,!0),(0,s.speak)(sprintf(/* translators: %s: Format name */ /* translators: %s: Format name */
     2(0,o.__)("Selected %s format. Pattern inserted.","post-formats-for-block-themes"),e.name),"polite")}i(!1)})(e),className:"pfpu-format-button",variant:"standard"===e?"primary":"secondary"},(0,t.createElement)("span",{className:`dashicons dashicons-${r.icon}`,"aria-hidden":"true"}),(0,t.createElement)("span",{className:"pfpu-format-name"},r.name)),(0,t.createElement)("p",{className:"pfpu-format-description"},r.description))))))};(0,e.registerPlugin)("post-formats-for-block-themes",{render:()=>(0,t.createElement)(t.Fragment,null,(0,t.createElement)(u,null),(0,t.createElement)(d,null))});const f=(0,c.createHigherOrderComponent)(e=>n=>{const{name:a,attributes:s}=n,i=((0,r.useSelect)(t=>{const e=t("core/editor");return e?e.getEditedPostAttribute("format"):null},[]),s.className?.includes("status-paragraph"));if("core/paragraph"!==a||!i)return(0,t.createElement)(e,{...n});const p=280-(s.content||"").replace(/<[^>]*>/g,"").length,c=p<0;return(0,t.createElement)("div",{className:"pfpu-status-paragraph-wrapper"},(0,t.createElement)(e,{...n}),(0,t.createElement)("div",{className:`pfpu-char-counter ${c?"is-over-limit":""} ${p<=20?"is-warning":""}`,"aria-live":"polite","aria-atomic":"true"},(0,t.createElement)("span",null,sprintf(/* translators: %d: Remaining characters */ /* translators: %d: Remaining characters */
     3(0,o.__)("%d characters remaining","post-formats-for-block-themes"),p))))},"withStatusCharacterCounter");if((0,i.addFilter)("editor.BlockEdit","pfpu/status-paragraph-counter",f),"undefined"!=typeof document){const t=document.createElement("style");t.textContent="\n\t.pfpu-format-modal {\n\t\tmax-width: 800px;\n\t}\n\n\t.pfpu-format-grid {\n\t\tdisplay: grid;\n\t\tgrid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n\t\tgap: 1rem;\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-format-card {\n\t\ttext-align: center;\n\t}\n\n\t.pfpu-format-button {\n\t\twidth: 100%;\n\t\theight: auto;\n\t\tpadding: 1rem;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tgap: 0.5rem;\n\t}\n\n\t.pfpu-format-button .dashicons {\n\t\tfont-size: 2rem;\n\t\twidth: 2rem;\n\t\theight: 2rem;\n\t}\n\n\t.pfpu-format-name {\n\t\tfont-weight: 600;\n\t}\n\n\t.pfpu-format-description {\n\t\tfont-size: 0.875rem;\n\t\tcolor: #757575;\n\t\tmargin-top: 0.5rem;\n\t}\n\n\t.pfpu-format-change-actions {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 0.5rem;\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-status-validator {\n\t\tmargin-top: 1rem;\n\t}\n\n\t.pfpu-status-paragraph-wrapper {\n\t\tposition: relative;\n\t\tmargin-bottom: 30px;\n\t}\n\n\t.pfpu-char-counter {\n\t\tposition: absolute;\n\t\tbottom: -25px;\n\t\tright: 0;\n\t\tfont-size: 0.875rem;\n\t\tcolor: #757575;\n\t\tbackground: #fff;\n\t\tpadding: 2px 8px;\n\t\tborder-radius: 3px;\n\t\tbox-shadow: 0 1px 3px rgba(0,0,0,0.1);\n\t}\n\n\t.pfpu-char-counter.is-warning {\n\t\tcolor: #f0b849;\n\t\tfont-weight: 600;\n\t}\n\n\t.pfpu-char-counter.is-over-limit {\n\t\tcolor: #d63638;\n\t\tfont-weight: 600;\n\t\tbackground: #fcf0f1;\n\t}\n",document.head.appendChild(t)}})();
  • post-formats-for-block-themes/trunk/includes/class-admin-columns.php

    r3413728 r3423300  
    170170        $icons = array(
    171171            'standard' => 'dashicons-admin-post',
    172             'aside'    => 'dashicons-aside',
     172            'aside'    => 'dashicons-format-aside',
    173173            'gallery'  => 'dashicons-format-gallery',
    174174            'link'     => 'dashicons-admin-links',
  • post-formats-for-block-themes/trunk/includes/class-pattern-manager.php

    r3413728 r3423300  
    8989     * Only synced blocks are created to avoid duplicates.
    9090     *
     91     * PERFORMANCE FIX: Only runs on plugin activation or when patterns
     92     * are missing. Uses a transient to avoid checking on every page load.
     93     *
    9194     * @since 1.0.0
    9295     */
    9396    public static function register_all_patterns() {
     97        // PERFORMANCE: Skip on front-end entirely.
     98        if ( ! is_admin() && ! wp_doing_ajax() && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
     99            return;
     100        }
     101
     102        // PERFORMANCE: Use transient to avoid running on every admin page load.
     103        // Patterns only need to be created once, then they persist in the database.
     104        $patterns_version = PFBT_VERSION;
     105        $cached_version   = get_transient( 'pfbt_patterns_registered' );
     106
     107        // Skip if patterns were already registered for this plugin version.
     108        if ( $cached_version === $patterns_version ) {
     109            return;
     110        }
     111
    94112        $instance = self::instance();
    95113        $formats  = PFBT_Format_Registry::get_all_formats();
     
    99117            $instance->create_synced_pattern( $slug, $format );
    100118        }
     119
     120        // Mark patterns as registered for 1 week (they persist in database anyway).
     121        set_transient( 'pfbt_patterns_registered', $patterns_version, WEEK_IN_SECONDS );
     122    }
     123
     124    /**
     125     * Force re-registration of patterns
     126     *
     127     * Called on plugin activation to ensure patterns are up to date.
     128     *
     129     * @since 1.1.3
     130     */
     131    public static function force_register_patterns() {
     132        delete_transient( 'pfbt_patterns_registered' );
     133        self::register_all_patterns();
    101134    }
    102135
     
    157190     * all instances update automatically.
    158191     *
     192     * PERFORMANCE FIX: Only updates existing blocks when content has
     193     * actually changed to avoid creating unnecessary revisions.
     194     *
    159195     * @since 1.0.0
    160196     *
     
    188224        );
    189225
    190         $block_data = array(
    191             'post_title'   => $block_title,
    192             'post_content' => $pattern_content,
    193             'post_status'  => 'publish',
    194             'post_type'    => 'wp_block',
    195             'post_name'    => $block_slug,
    196         );
    197 
    198226        if ( $existing_block ) {
    199             // Update existing reusable block.
    200             $block_data['ID'] = $existing_block->ID;
    201             $block_id         = wp_update_post( $block_data );
     227            // PERFORMANCE: Only update if content has actually changed.
     228            // This prevents creating revisions on every admin page load.
     229            $content_changed = ( trim( $existing_block->post_content ) !== trim( $pattern_content ) );
     230            $title_changed   = ( $existing_block->post_title !== $block_title );
     231
     232            if ( ! $content_changed && ! $title_changed ) {
     233                // Content unchanged - skip update to avoid revision creation.
     234                $block_id = $existing_block->ID;
     235            } else {
     236                // Content changed - update the block.
     237                $block_data = array(
     238                    'ID'           => $existing_block->ID,
     239                    'post_title'   => $block_title,
     240                    'post_content' => $pattern_content,
     241                    'post_status'  => 'publish',
     242                    'post_type'    => 'wp_block',
     243                    'post_name'    => $block_slug,
     244                );
     245                $block_id   = wp_update_post( $block_data );
     246            }
    202247        } else {
    203248            // Create new reusable block.
    204             $block_id = wp_insert_post( $block_data );
     249            $block_data = array(
     250                'post_title'   => $block_title,
     251                'post_content' => $pattern_content,
     252                'post_status'  => 'publish',
     253                'post_type'    => 'wp_block',
     254                'post_name'    => $block_slug,
     255            );
     256            $block_id   = wp_insert_post( $block_data );
    205257
    206258            // Add metadata to identify this as a post format pattern.
     
    210262        }
    211263
    212         // Assign to Post formats category.
     264        // Assign to Post formats category (only for new blocks or if category missing).
    213265        if ( $block_id && ! is_wp_error( $block_id ) ) {
    214             // Get or create the category term.
    215             $category_term = get_term_by( 'slug', 'post-formats', 'wp_pattern_category' );
    216 
    217             if ( ! $category_term ) {
    218                 $category_term = wp_insert_term(
    219                     __( 'Post formats', 'post-formats-for-block-themes' ),
    220                     'wp_pattern_category',
    221                     array( 'slug' => 'post-formats' )
    222                 );
    223             }
    224 
    225             // Assign the category to the block.
    226             if ( $category_term && ! is_wp_error( $category_term ) ) {
    227                 $term_id = is_array( $category_term ) ? $category_term['term_id'] : $category_term->term_id;
    228                 wp_set_object_terms( $block_id, $term_id, 'wp_pattern_category' );
     266            // PERFORMANCE: Check if block already has the correct category.
     267            $existing_terms = wp_get_object_terms( $block_id, 'wp_pattern_category', array( 'fields' => 'slugs' ) );
     268            $has_category   = ! is_wp_error( $existing_terms ) && in_array( 'post-formats', $existing_terms, true );
     269
     270            if ( ! $has_category ) {
     271                // Get or create the category term.
     272                $category_term = get_term_by( 'slug', 'post-formats', 'wp_pattern_category' );
     273
     274                if ( ! $category_term ) {
     275                    $category_term = wp_insert_term(
     276                        __( 'Post formats', 'post-formats-for-block-themes' ),
     277                        'wp_pattern_category',
     278                        array( 'slug' => 'post-formats' )
     279                    );
     280                }
     281
     282                // Assign the category to the block.
     283                if ( $category_term && ! is_wp_error( $category_term ) ) {
     284                    $term_id = is_array( $category_term ) ? $category_term['term_id'] : $category_term->term_id;
     285                    wp_set_object_terms( $block_id, $term_id, 'wp_pattern_category' );
     286                }
    229287            }
    230288        }
  • post-formats-for-block-themes/trunk/patterns/aside.php

    r3413728 r3423300  
    1414 */
    1515?>
    16 <!-- wp:group {"layout":{"type":"constrained"}} -->
    17 <div class="wp-block-group">
    18     <!-- wp:paragraph -->
    19     <p></p>
    20     <!-- /wp:paragraph -->
    21 </div>
    22 <!-- /wp:group -->
     16<!-- wp:paragraph -->
     17<p></p>
     18<!-- /wp:paragraph -->
  • post-formats-for-block-themes/trunk/patterns/audio.php

    r3413728 r3423300  
    77 * Audio Post Format Pattern
    88 *
    9  * Audio file or embed. Starts with a core audio block.
     9 * Audio file or embed. Starts with a core audio block followed by a paragraph.
    1010 * Can be swapped with Podlove Podcast Publisher or Able Player blocks.
    1111 *
     
    1414 */
    1515?>
    16 <!-- wp:group {"layout":{"type":"constrained"}} -->
    17 <div class="wp-block-group">
    18     <!-- wp:audio -->
    19     <figure class="wp-block-audio"><audio controls></audio></figure>
    20     <!-- /wp:audio -->
    21 </div>
    22 <!-- /wp:group -->
     16<!-- wp:audio -->
     17<figure class="wp-block-audio"><audio controls></audio></figure>
     18<!-- /wp:audio -->
    2319
     20<!-- wp:paragraph -->
     21<p></p>
     22<!-- /wp:paragraph -->
  • post-formats-for-block-themes/trunk/patterns/chat.php

    r3413728 r3423300  
    77 * Chat Post Format Pattern
    88 *
    9  * Chat transcript or conversation log. Uses the Chat Log block.
     9 * Chat transcript or conversation log. Uses the Chat Log block followed by a paragraph.
    1010 *
    1111 * Note: Requires the Chat Log plugin to be active.
     
    1616 */
    1717?>
    18 <!-- wp:group {"layout":{"type":"constrained"}} -->
    19 <div class="wp-block-group">
    20     <!-- wp:chatlog/conversation /-->
    21 </div>
    22 <!-- /wp:group -->
     18<!-- wp:chatlog/conversation /-->
    2319
     20<!-- wp:paragraph -->
     21<p></p>
     22<!-- /wp:paragraph -->
  • post-formats-for-block-themes/trunk/patterns/gallery.php

    r3413728 r3423300  
    77 * Gallery Post Format Pattern
    88 *
    9  * Image gallery post. Starts with a gallery block.
     9 * Image gallery post. Starts with a gallery block followed by a paragraph.
    1010 *
    1111 * @package PostFormatsBlockThemes
     
    1313 */
    1414?>
    15 <!-- wp:group {"layout":{"type":"constrained"}} -->
    16 <div class="wp-block-group">
    17     <!-- wp:gallery {"linkTo":"none"} -->
    18     <figure class="wp-block-gallery has-nested-images columns-default is-cropped">
    19     <!-- wp:image -->
    20     <figure class="wp-block-image"><img alt=""/></figure>
    21     <!-- /wp:image -->
    22     </figure>
    23     <!-- /wp:gallery -->
    24 </div>
    25 <!-- /wp:group -->
     15<!-- wp:gallery {"linkTo":"none"} -->
     16<figure class="wp-block-gallery has-nested-images columns-default is-cropped"></figure>
     17<!-- /wp:gallery -->
    2618
     19<!-- wp:paragraph -->
     20<p></p>
     21<!-- /wp:paragraph -->
  • post-formats-for-block-themes/trunk/patterns/image.php

    r3413728 r3423300  
    77 * Image Post Format Pattern
    88 *
    9  * Single image post. Starts with an image block.
     9 * Single image post. Starts with an image block followed by a paragraph.
    1010 *
    1111 * @package PostFormatsBlockThemes
     
    1313 */
    1414?>
    15 <!-- wp:group {"layout":{"type":"constrained"}} -->
    16 <div class="wp-block-group">
    17     <!-- wp:image {"sizeSlug":"large"} -->
    18     <figure class="wp-block-image size-large"><img alt=""/></figure>
    19     <!-- /wp:image -->
    20 </div>
    21 <!-- /wp:group -->
     15<!-- wp:image {"sizeSlug":"large"} -->
     16<figure class="wp-block-image size-large"><img alt=""/></figure>
     17<!-- /wp:image -->
    2218
     19<!-- wp:paragraph -->
     20<p></p>
     21<!-- /wp:paragraph -->
  • post-formats-for-block-themes/trunk/patterns/link.php

    r3413728 r3423300  
    99 * Link to external content. Uses Bookmark Card plugin if available,
    1010 * otherwise falls back to paragraph with link styling.
     11 * Followed by a paragraph for additional commentary.
    1112 *
    1213 * @package PostFormatsBlockThemes
     
    1617// Check if Bookmark Card plugin is available.
    1718$pfbt_has_bookmark_card = function_exists( 'bookmark_card_register_block' ) || has_block( 'bookmark-card/bookmark-card' );
    18 ?>
    19 <!-- wp:group {"layout":{"type":"constrained"}} -->
    20 <div class="wp-block-group">
    21 <?php
     19
    2220if ( $pfbt_has_bookmark_card ) {
    2321    // Use Bookmark Card block.
    2422    ?>
    25     <!-- wp:bookmark-card/bookmark-card /-->
     23<!-- wp:bookmark-card/bookmark-card /-->
     24
     25<!-- wp:paragraph -->
     26<p></p>
     27<!-- /wp:paragraph -->
    2628    <?php
    2729} else {
    2830    // Fallback to paragraph with link placeholder.
    2931    ?>
    30     <!-- wp:paragraph {"className":"link-format-fallback","fontSize":"large"} -->
    31     <p class="link-format-fallback has-large-font-size"><a href="#"></a></p>
    32     <!-- /wp:paragraph -->
     32<!-- wp:paragraph {"className":"link-format-fallback","fontSize":"large"} -->
     33<p class="link-format-fallback has-large-font-size"><a href="#"></a></p>
     34<!-- /wp:paragraph -->
     35
     36<!-- wp:paragraph -->
     37<p></p>
     38<!-- /wp:paragraph -->
    3339    <?php
    3440}
    3541?>
    36 </div>
    37 <!-- /wp:group -->
    38 
  • post-formats-for-block-themes/trunk/patterns/quote.php

    r3413728 r3423300  
    77 * Quote Post Format Pattern
    88 *
    9  * Quotation or citation. Starts with a quote block.
     9 * Quotation or citation. Starts with a quote block followed by a paragraph.
    1010 *
    1111 * @package PostFormatsBlockThemes
     
    1313 */
    1414?>
    15 <!-- wp:group {"layout":{"type":"constrained"}} -->
    16 <div class="wp-block-group">
    17     <!-- wp:quote -->
    18     <blockquote class="wp-block-quote">
    19     <!-- wp:paragraph -->
    20     <p></p>
    21     <!-- /wp:paragraph -->
    22     </blockquote>
    23     <!-- /wp:quote -->
    24 </div>
    25 <!-- /wp:group -->
     15<!-- wp:quote -->
     16<blockquote class="wp-block-quote">
     17<!-- wp:paragraph -->
     18<p></p>
     19<!-- /wp:paragraph -->
     20</blockquote>
     21<!-- /wp:quote -->
    2622
     23<!-- wp:paragraph -->
     24<p></p>
     25<!-- /wp:paragraph -->
  • post-formats-for-block-themes/trunk/patterns/status.php

    r3413728 r3423300  
    1414 */
    1515
    16 // Paragraph with status-paragraph class for validation.
     16// Single paragraph with status-paragraph class for character counter.
    1717?>
    18 <!-- wp:group {"layout":{"type":"constrained"}} -->
    19 <div class="wp-block-group">
    20     <!-- wp:paragraph {"className":"status-paragraph","fontSize":"large"} -->
    21     <p class="status-paragraph has-large-font-size"></p>
    22     <!-- /wp:paragraph -->
    23 </div>
    24 <!-- /wp:group -->
     18<!-- wp:paragraph {"className":"status-paragraph","fontSize":"large"} -->
     19<p class="status-paragraph has-large-font-size"></p>
     20<!-- /wp:paragraph -->
    2521
  • post-formats-for-block-themes/trunk/patterns/video.php

    r3413728 r3423300  
    77 * Video Post Format Pattern
    88 *
    9  * Video file or embed. Starts with a video block.
     9 * Video file or embed. Starts with a video block followed by a paragraph.
    1010 *
    1111 * @package PostFormatsBlockThemes
     
    1313 */
    1414?>
    15 <!-- wp:group {"layout":{"type":"constrained"}} -->
    16 <div class="wp-block-group">
    17     <!-- wp:video -->
    18     <figure class="wp-block-video"><video controls></video></figure>
    19     <!-- /wp:video -->
    20 </div>
    21 <!-- /wp:group -->
     15<!-- wp:video -->
     16<figure class="wp-block-video"><video controls></video></figure>
     17<!-- /wp:video -->
    2218
     19<!-- wp:paragraph -->
     20<p></p>
     21<!-- /wp:paragraph -->
  • post-formats-for-block-themes/trunk/post-formats-for-block-themes.php

    r3416925 r3423300  
    44 * Plugin URI: https://wordpress.org/plugins/post-formats-for-block-themes/
    55 * Description: Modernizes WordPress post formats for block themes with format-specific patterns, auto-detection, and enhanced editor experience.
    6  * Version: 1.1.2
     6 * Version: 1.1.3
    77 * Requires at least: 6.8
    88 * Tested up to: 6.9
     
    3939 * Plugin constants
    4040 */
    41 define( 'PFBT_VERSION', '1.1.2' );
     41define( 'PFBT_VERSION', '1.1.3' );
    4242define( 'PFBT_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    4343define( 'PFBT_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
     
    378378    delete_transient( 'pfbt_bookmark_card_available' );
    379379    delete_transient( 'pfbt_chatlog_block_available' );
     380    delete_transient( 'pfbt_patterns_registered' );
    380381}
    381382register_deactivation_hook( __FILE__, 'pfbt_deactivate' );
     383
     384/**
     385 * Add plugin action links
     386 *
     387 * Adds a "Settings" link to the plugin's row on the Plugins page
     388 * that links to the Post Format Repair tool.
     389 *
     390 * @since 1.1.3
     391 *
     392 * @param array $links Existing plugin action links.
     393 * @return array Modified plugin action links.
     394 */
     395function pfbt_plugin_action_links( $links ) {
     396    $settings_link = sprintf(
     397        '<a href="%s">%s</a>',
     398        esc_url( admin_url( 'tools.php?page=pfbt-repair-tool' ) ),
     399        esc_html__( 'Settings', 'post-formats-for-block-themes' )
     400    );
     401    array_unshift( $links, $settings_link );
     402    return $links;
     403}
     404add_filter( 'plugin_action_links_' . PFBT_PLUGIN_BASENAME, 'pfbt_plugin_action_links' );
     405
     406/**
     407 * Limit revisions for wp_block post type
     408 *
     409 * Synced patterns are stored as wp_block posts. Limit revisions
     410 * to prevent database bloat from pattern updates.
     411 *
     412 * @since 1.1.3
     413 *
     414 * @param int     $num  Number of revisions to keep.
     415 * @param WP_Post $post The post object.
     416 * @return int Modified number of revisions.
     417 */
     418function pfbt_limit_wp_block_revisions( $num, $post ) {
     419    if ( 'wp_block' === $post->post_type ) {
     420        return 3;
     421    }
     422    return $num;
     423}
     424add_filter( 'wp_revisions_to_keep', 'pfbt_limit_wp_block_revisions', 10, 2 );
  • post-formats-for-block-themes/trunk/readme.txt

    r3416925 r3423300  
    66Tested up to: 6.9
    77Requires PHP: 7.4
    8 Stable tag: 1.1.2
     8Stable tag: 1.1.3
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    347347
    348348== Changelog ==
     349
     350= 1.1.3 - 2025-12-18 =
     351
     352**Performance**
     353
     354* **Fixed:** Critical performance issue with revision queries on sites with many synced patterns
     355* **Added:** Transient-based caching for pattern registration to avoid unnecessary database operations
     356* **Added:** Pattern registration now skipped entirely on front-end for better performance
     357* **Added:** Pattern updates only occur when content has actually changed
     358* **Added:** Revision limiter for wp_block post type (limits to 3 revisions to prevent database bloat)
     359
     360**New Features**
     361
     362* **Added:** "Settings" link on Plugins page that links to Post Format Repair tool for easy access
     363
     364**Bug Fixes**
     365
     366* **Fixed:** Duplicate pattern insertion when selecting format from modal (patterns were being inserted twice)
     367* **Fixed:** Status format character counter appearing twice in editor
     368* **Fixed:** Aside format icon not displaying in Posts admin list (changed from `dashicons-aside` to `dashicons-format-aside`)
     369* **Fixed:** JavaScript error "Cannot read properties of undefined (reading 'postCategories')" in block editor
     370* **Fixed:** Pattern transient now cleared on plugin deactivation to ensure fresh patterns on reactivation
     371
     372**Improvements**
     373
     374* **Changed:** Simplified all format patterns by removing unnecessary wrapper Group blocks
     375* **Changed:** Status pattern now uses single paragraph with `status-paragraph` class
     376* **Changed:** Aside pattern now uses single paragraph (no wrapper)
     377* **Changed:** All other format patterns now use primary block + paragraph structure for cleaner editing
     378
     379**Security**
     380
     381* **Added:** `domReady` wrapper for Post Format Block to prevent race conditions
     382* **Added:** Null check with fallback icon for safer script initialization
     383* **Added:** Asset file for Post Format Block script dependencies
    349384
    350385= 1.1.2 - 2025-12-11 =
     
    460495== Upgrade Notice ==
    461496
     497= 1.1.3 =
     498Critical performance fix: Resolves database performance issue with revision queries. Also fixes duplicate pattern insertion, character counter appearing twice, and JavaScript errors. Simplified patterns for cleaner editing. All users should upgrade.
     499
     500= 1.1.2 =
     501Editor UI improvements: Simplified format selection, better template chooser, fixed standard posts incorrectly showing format templates.
     502
     503= 1.1.1 =
     504Critical fix: Format templates no longer appear in Template dropdown, fixing template selection issues.
     505
    462506= 1.1.0 =
    463507Major update: Adds Post Format Block for frontend display, post format admin column with filtering, template improvements with category/tag/format display, and critical bug fixes for template assignment. All users should upgrade.
Note: See TracChangeset for help on using the changeset viewer.