<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Matt Watson's Blog]]></title><description><![CDATA[Welcome to the home of my WordPress, Agile, and Leadership escapades! Expect fresh content, new projects and watch as I'll be solving (and occasionally committi]]></description><link>https://mattwatson.blog</link><generator>RSS for Node</generator><lastBuildDate>Thu, 16 Apr 2026 17:29:01 GMT</lastBuildDate><atom:link href="https://mattwatson.blog/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Introducing Dynamic Template Parts: Supercharge Your WordPress Design Workflow]]></title><description><![CDATA[Tired of duplicating templates just to make small design changes for individual posts or pages? Say hello to Dynamic Template Parts, the WordPress plugin that redefines how you manage templates in the Full Site Editor (FSE). With this powerful tool, ...]]></description><link>https://mattwatson.blog/introducing-dynamic-template-parts-supercharge-your-wordpress-design-workflow</link><guid isPermaLink="true">https://mattwatson.blog/introducing-dynamic-template-parts-supercharge-your-wordpress-design-workflow</guid><category><![CDATA[Template Parts]]></category><category><![CDATA[Full Site Editor]]></category><category><![CDATA[Dynamic Template Parts]]></category><category><![CDATA[WordPress]]></category><category><![CDATA[plugin]]></category><category><![CDATA[wordpress plugins]]></category><category><![CDATA[WordPress Full Site Editing]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Tue, 03 Dec 2024 19:45:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731969388480/37253674-2956-47dc-9f2a-4a6a85a1bf68.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Tired of duplicating templates just to make small design changes for individual posts or pages? Say hello to <strong>Dynamic Template Parts</strong>, the WordPress plugin that redefines how you manage templates in the Full Site Editor (FSE). With this powerful tool, you can dynamically swap Template Parts like headers, footers, or sidebars on a per-content basis—no coding required.</p>
<p>Dynamic Template Parts brings unparalleled flexibility to your WordPress site. Want a unique header for a specific post or a custom footer for a landing page? Now you can do it all directly in the block editor, without the hassle of creating multiple templates or touching theme files.</p>
<hr />
<h2 id="heading-why-youll-love-dynamic-template-parts">Why You’ll Love Dynamic Template Parts</h2>
<ul>
<li><p><strong>Simplified Template Management</strong><br />  No more juggling dozens of templates—use one and swap parts dynamically.</p>
</li>
<li><p><strong>Customisation Without Code</strong><br />  Select and preview Template Parts in real time using an intuitive interface.</p>
</li>
<li><p><strong>Versatile Design Options</strong><br />  Works seamlessly with posts, pages, and custom post types for tailored layouts.</p>
</li>
<li><p><strong>Full Site Editing Integration</strong><br />  Built to enhance the native WordPress Full Site Editor experience.</p>
</li>
</ul>
<hr />
<h2 id="heading-see-it-in-action">See It in Action</h2>
<p>Want to see how easy it is to use? Watch the demo video to learn more:<br /><a target="_blank" href="https://www.youtube.com/watch?v=elRBOak3vek">Watch the Demo</a></p>
<p>Or take a look at the product page here:<br /><a target="_blank" href="https://mattwatson.blog/dynamic-template-parts">Dynamic Template Parts Product Page</a></p>
<p>Or skip all that and just download it directly to see what all the fuss is about:<br /><a target="_blank" href="https://en-gb.wordpress.org/plugins/dynamic-template-parts/">Dynamic Template Parts on WordPress.org</a></p>
<hr />
<p>Dynamic Template Parts is here to save you time, simplify your workflow, and unlock endless customisation options. Whether you’re a developer or a site owner, this plugin is the design tool you didn’t know you needed.</p>
]]></content:encoded></item><item><title><![CDATA[Creating Sidebars in WordPress: Block, Post, Plugin, and Full Site Editor]]></title><description><![CDATA[In this guide, we’ll explore sidebars within WordPress, including block sidebar controls (for new and existing blocks), post sidebar controls. We’ll also delve into creating a fully custom plugin sidebar, and for those feeling adventurous, we will wr...]]></description><link>https://mattwatson.blog/creating-sidebars-in-wordpress-block-post-plugin-and-full-site-editor</link><guid isPermaLink="true">https://mattwatson.blog/creating-sidebars-in-wordpress-block-post-plugin-and-full-site-editor</guid><category><![CDATA[WordPress]]></category><category><![CDATA[Gutenberg]]></category><category><![CDATA[Block Editor]]></category><category><![CDATA[Gutenberg block editor]]></category><category><![CDATA[WordPress Full Site Editing]]></category><category><![CDATA[sidebar]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Wed, 30 Oct 2024 23:00:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730329132316/74d30770-8208-4199-b371-e3f63ac69973.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this guide, we’ll explore sidebars within WordPress, including block sidebar controls (for new and existing blocks), post sidebar controls. We’ll also delve into creating a fully custom plugin sidebar, and for those feeling adventurous, we will wrap up with a Full Site Editor (FSE) sidebar!</p>
<p>Sidebars serve as quick-access control panels within the block editor, perfect for custom settings and content controls.</p>
<p>You’ll see how to set up each one with <code>@wordpress/create-block</code> or in some cases <code>@wordpress/scripts</code>, and we will add unique settings and functionality to each sidebar.</p>
<p>If you are completely new to WordPress development environments and block creation, please <a target="_blank" href="https://mattwatson.blog/getting-started-with-wordpress-block-development-using-studio-by-wordpresscom">read the getting started with WordPress development guide</a> first.</p>
<h2 id="heading-what-are-wordpress-sidebars">What are WordPress Sidebars?</h2>
<p>In the WordPress block editor, sidebars are panels on the right-hand side of the screen that offer quick access to settings and configuration options. These sidebars can be customised to provide controls specific to your content and enhance the editing experience.</p>
<p>There are several types of sidebars you can create:</p>
<ol>
<li><p><strong>Block Sidebar (Inspector Controls)</strong>: Ideal for configuring individual block settings, such as adding specific styling options, or other customisation for specific blocks in a post or page.</p>
</li>
<li><p><strong>Block Filters for All Blocks</strong>: By using block filters, you can add controls to all blocks (even core blocks). This allows for consistent settings across different block types without modifying each block individually.</p>
</li>
<li><p><strong>Post Sidebar</strong>: Provides options applicable to the entire post, such as a control for post meta.</p>
</li>
<li><p><strong>Custom Plugin Sidebar</strong>: Acts as a dedicated settings panel, usually for broader configurations that need to be accessed across different content types or pages.</p>
</li>
<li><p><strong>Full Site Editor (FSE) Sidebar</strong>: Ideal for site-wide settings and controls when working within the FSE, allowing global options that apply to all pages and posts.</p>
</li>
</ol>
<p>In this article we will take a look at how to implement each type of sidebar and controls.</p>
<h2 id="heading-1-block-level-sidebar-toggle-block-visibility">1. Block-Level Sidebar: Toggle Block Visibility</h2>
<p>Blocks can render pretty much any type of content. When you select a block the block sidebar usually becomes visible. It is here we can add controls so that we can customise our blocks layout, style or behaviour.</p>
<p>To showcase our block sidebar, we’ll create a custom block with a visibility toggle. When disabled, this toggle will prevent the block from rendering on the front end.</p>
<h3 id="heading-11-setting-up-the-block-attributes">1.1: Setting Up the Block Attributes</h3>
<p>First, we’ll use <code>@wordpress/create-block</code> to scaffold a new block plugin. Open your terminal and run the following command:</p>
<pre><code class="lang-bash">npx @wordpress/create-block block-visibility-toggle
</code></pre>
<p>This command will generate all the necessary files for a basic block plugin called <code>block-visibility-toggle</code>.</p>
<p>Next, open the generated <code>src/block.json</code> file in your code editor. We’ll need to add an attribute to control the visibility of the block. To do this add the following code inside the generated "attributes" object.</p>
<pre><code class="lang-json"><span class="hljs-string">"attributes"</span>: {
    <span class="hljs-attr">"isVisible"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"boolean"</span>,
        <span class="hljs-attr">"default"</span>: <span class="hljs-literal">true</span>
    }
}
</code></pre>
<p>Your <code>block.json</code> file should now look like the following screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244300141/8f2b176d-3348-46d5-a767-530e41db3412.png" alt="The block.json file with added attributes" class="image--center mx-auto" /></p>
<h3 id="heading-12-building-the-sidebar-toggle">1.2: Building the Sidebar Toggle</h3>
<p>Next, we’ll create a toggle control in the block’s sidebar to allow users to show or hide the block. Open the <code>src/edit.js</code> file and modify it as follows:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { InspectorControls, useBlockProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/block-editor'</span>;
<span class="hljs-keyword">import</span> { ToggleControl, PanelBody } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/components'</span>;
<span class="hljs-keyword">import</span> { __ } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/i18n'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Edit</span>(<span class="hljs-params"> { attributes, setAttributes } </span>) </span>{
    <span class="hljs-keyword">const</span> { isVisible } = attributes;

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">InspectorControls</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">PanelBody</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Block</span> <span class="hljs-attr">Settings</span>', '<span class="hljs-attr">block-visibility-toggle</span>' ) }&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">ToggleControl</span>
                        <span class="hljs-attr">label</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Display</span> <span class="hljs-attr">Block</span>', '<span class="hljs-attr">block-visibility-toggle</span>' ) }
                        <span class="hljs-attr">checked</span>=<span class="hljs-string">{</span> <span class="hljs-attr">isVisible</span> }
                        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{</span> ( <span class="hljs-attr">value</span> ) =&gt;</span> setAttributes( { isVisible: value } ) }
                    /&gt;
                <span class="hljs-tag">&lt;/<span class="hljs-name">PanelBody</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">InspectorControls</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> { <span class="hljs-attr">...useBlockProps</span>() }&gt;</span>
                { isVisible ?
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{ __( 'Your content here.', 'block-visibility-toggle' ) }<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                :
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{ __( 'Your block will be hidden.', 'block-visibility-toggle' ) }<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                }
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/&gt;</span></span>
    );
}
</code></pre>
<h4 id="heading-121-display-controls-to-the-advanced-section">1.2.1: Display Controls to the Advanced Section</h4>
<p>If you prefer, you can place the visibility toggle under the “Advanced” section of the block sidebar.</p>
<p>To do this, replace <code>InspectorControls</code> with <code>InspectorAdvancedControls</code> in your <code>edit.js</code> file. You can also remove the PanelBody too, as the Advanced section acts as its own Panel.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { InspectorAdvancedControls, useBlockProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/block-editor'</span>;
<span class="hljs-keyword">import</span> { ToggleControl } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/components'</span>;
<span class="hljs-keyword">import</span> { __ } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/i18n'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Edit</span>(<span class="hljs-params"> { attributes, setAttributes } </span>) </span>{
    <span class="hljs-keyword">const</span> { isVisible } = attributes;

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">InspectorAdvancedControls</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">ToggleControl</span>
                    <span class="hljs-attr">label</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Display</span> <span class="hljs-attr">Block</span>', '<span class="hljs-attr">block-visibility-toggle</span>' ) }
                    <span class="hljs-attr">checked</span>=<span class="hljs-string">{</span> <span class="hljs-attr">isVisible</span> }
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{</span> ( <span class="hljs-attr">value</span> ) =&gt;</span> setAttributes( { isVisible: value } ) }
                /&gt;
            <span class="hljs-tag">&lt;/<span class="hljs-name">InspectorAdvancedControls</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> { <span class="hljs-attr">...useBlockProps</span>() }&gt;</span>
                { isVisible ?
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{ __( 'Your content here.', 'block-visibility-toggle' ) }<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                :
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{ __( 'Your block will be hidden.', 'block-visibility-toggle' ) }<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                }
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/&gt;</span></span>
    );
}
</code></pre>
<h3 id="heading-13-altering-the-save-function-to-hide-the-block">1.3: Altering the Save Function to Hide the Block</h3>
<p>Modify the save function in <code>src/save.js</code> to check the <code>isVisible</code> attribute. Open <code>src/save.js</code> and update it as follows:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useBlockProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/block-editor'</span>;
<span class="hljs-keyword">import</span> { __ } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/i18n'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">save</span>(<span class="hljs-params"> { attributes } </span>) </span>{
    <span class="hljs-keyword">const</span> { isVisible } = attributes;

    <span class="hljs-keyword">if</span> ( ! isVisible ) {
        <span class="hljs-comment">// If the block is set to not visible, return null to render nothing.</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> { <span class="hljs-attr">...useBlockProps.save</span>() }&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{ __( 'Your content here.', 'block-visibility-toggle' ) }<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>By returning <code>null</code> when <code>isVisible</code> is false, the block will not output any HTML on the front end, effectively hiding it.</p>
<h3 id="heading-14-using-the-sidebar">1.4: Using the Sidebar</h3>
<p>Remember to run <code>npm run build</code> in your block directory and to activate the block.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244403632/797e442a-8dbc-46f3-b3c8-6367a689d961.png" alt="Activate the Block Visibility Toggle Plugin" class="image--center mx-auto" /></p>
<p>Once you have done this, you can insert the custom block you created by typing <code>/</code> and searching for <code>Block Visibility Toggle</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244409757/386bfde0-bad7-42f1-b095-1d2bcceabed2.png" alt="Inserting the Block Visibility Toggle Plugin" class="image--center mx-auto" /></p>
<p>After you have inserted the block, click on it to reveal the “Block” section of the “Post” sidebar. Here you will see the new “Display Block” toggle.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244421272/0eed4b2e-134c-4d0a-896a-6c1c26e1f29a.png" alt="The inserted block and the Display Block Toggle" class="image--center mx-auto" /></p>
<p>Do not toggle it just yet, first check that the block displays as expected on the front end.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244434016/f94dbe07-0f41-45b2-a275-bd3facc1d8ca.png" alt="The block is showing on the front end." class="image--center mx-auto" /></p>
<p>Now toggle the “Display Block” toggle in the backend, and save your post.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244448695/b3bb03a2-a29b-42ba-b46e-f3f60b847635.png" alt="Toggle the &quot;Display Block&quot; toggle" class="image--center mx-auto" /></p>
<p>If all worked as expected, you should no longer be able to see the block on the front end of the site.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244460747/c1ac0d30-ebeb-4eb3-8fd7-fd63becea9eb.png" alt="The block is no longer displayed on the front end of the site" class="image--center mx-auto" /></p>
<p>As mentioned in section 1.2.1 you can display controls in the Advanced section of the sidebar with <code>InspectorAdvancedControls</code>. That should look a little like the following screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730326209899/9ffbee8b-2f50-4ee0-9ea1-1faa8b077b9e.png" alt="Display block toggle showing in the Advanced controls section" class="image--center mx-auto" /></p>
<h2 id="heading-2-using-block-filters-adding-block-visibility-toggle-to-all-blocks">2. Using Block Filters: Adding Block Visibility Toggle to All Blocks</h2>
<p>Sometimes you cannot alter the markup of a block. Perhaps it is a WordPress Core block that you wish to add additional controls to, or one provided by a plugin, or perhaps you just want to register one control that can be used on several blocks (gotta keep it <abbr>DRY</abbr>).</p>
<p>We will showcase this by using block filters to inject the toggle control into the sidebar of every block (realistically you would probably restrict which blocks use it).</p>
<h3 id="heading-21-setting-up-the-plugin">2.1: Setting Up the Plugin</h3>
<p>Since we need to create a plugin that modifies existing blocks, <code>@wordpress/create-block</code> might not be the most suitable tool. Instead, we’ll manually create a plugin and use <code>@wordpress/scripts</code> (also known as <code>wp-scripts</code>) to handle the build process.</p>
<p>Create a new folder in your <code>wp-content/plugins</code> directory called <code>global-block-visibility-toggle</code>. Inside this folder, create a PHP file named <code>global-block-visibility-toggle.php</code> and add the following plugin header:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/**
 * Plugin Name: Global Block Visibility Toggle
 * Description: Adds a visibility toggle to all blocks using a block filter.
 * Version: 1.0.0
 * Author: Your Name
 * Text Domain: global-block-visibility-toggle
 */</span>
</code></pre>
<h3 id="heading-22-setting-up-the-development-environment-with-wp-scripts">2.2: Setting Up the Development Environment with <code>wp-scripts</code></h3>
<p>We’ll use <code>@wordpress/scripts</code> to simplify our build process. This package provides a pre-configured build setup for WordPress development, allowing us to write modern JavaScript without manually configuring tools like webpack and Babel.</p>
<p><code>wp-scripts</code> gives you a build process very similar to the <code>create-block</code> script. To do this we will need to run a few commands, if you need to re-familiarise yourself with some of the concepts, give the <a target="_blank" href="https://mattwatson.blog/getting-started-with-wordpress-block-development-using-studio-by-wordpresscom">WordPress development guide</a> a read.</p>
<h4 id="heading-221-initialise-packagejson">2.2.1: Initialise <code>package.json</code></h4>
<p>Make sure you are in the <code>plugins</code> directory. Open your terminal and navigate to the plugin directory:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> global-block-visibility-toggle
</code></pre>
<p>Initialize a new <code>package.json</code> file by running:</p>
<pre><code class="lang-bash">npm init -y
</code></pre>
<p>This will create a basic <code>package.json</code> file in your plugin directory.</p>
<h4 id="heading-222-install-wordpressscripts">2.2.2: Install <code>@wordpress/scripts</code></h4>
<p>Install <code>@wordpress/scripts</code> as a development dependency:</p>
<pre><code class="lang-bash">npm install @wordpress/scripts --save-dev
</code></pre>
<h4 id="heading-223-update-packagejson-scripts">2.2.3: Update <code>package.json</code> Scripts</h4>
<p>In your package.json file, add the following scripts:</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"wp-scripts build"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"wp-scripts start"</span>,
    <span class="hljs-attr">"format"</span>: <span class="hljs-string">"wp-scripts format"</span>,
    <span class="hljs-attr">"lint:css"</span>: <span class="hljs-string">"wp-scripts lint-style"</span>,
    <span class="hljs-attr">"lint:js"</span>: <span class="hljs-string">"wp-scripts lint-js"</span>,
    <span class="hljs-attr">"lint:md:docs"</span>: <span class="hljs-string">"wp-scripts lint-md-docs"</span>
},
</code></pre>
<p>To add in the description and to save some time, you can copy the below to your `package.json file.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"global-block-visibility-toggle"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Adds a visibility toggle to all blocks using a block filter."</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"wp-scripts build"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"wp-scripts start"</span>,
    <span class="hljs-attr">"format"</span>: <span class="hljs-string">"wp-scripts format"</span>,
    <span class="hljs-attr">"lint:css"</span>: <span class="hljs-string">"wp-scripts lint-style"</span>,
    <span class="hljs-attr">"lint:js"</span>: <span class="hljs-string">"wp-scripts lint-js"</span>,
    <span class="hljs-attr">"lint:md:docs"</span>: <span class="hljs-string">"wp-scripts lint-md-docs"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Your Name"</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"GPL-2.0-or-later"</span>,
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@wordpress/scripts"</span>: <span class="hljs-string">"^24.0.0"</span>
  }
}
</code></pre>
<h4 id="heading-224-create-the-src-directory-and-indexjs-file">2.2.4: Create the <code>src</code> Directory and <code>index.js</code> File</h4>
<p>Create a src directory inside your plugin folder:</p>
<pre><code class="lang-bash">mkdir src
</code></pre>
<p>Inside the <code>src</code> directory, create a file named <code>index.js</code>. This is where we’ll write our JavaScript code to add the visibility toggle to all blocks.</p>
<h3 id="heading-23-registering-the-block-filter">2.3: Registering the Block Filter</h3>
<p>In <code>src/index.js</code>, add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { addFilter } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/hooks'</span>;
<span class="hljs-keyword">import</span> { InspectorControls } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/block-editor'</span>;
<span class="hljs-keyword">import</span> { PanelBody, ToggleControl } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/components'</span>;
<span class="hljs-keyword">import</span> { __ } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/i18n'</span>;

<span class="hljs-comment">/**
 * Add the 'isVisible' attribute to save the visibility state.
 *
 * @param {Object} settings Original block settings.
 * @returns {Object} Modified block settings with new attribute.
 */</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addVisibilityAttribute</span>(<span class="hljs-params"> settings </span>) </span>{
    settings.attributes = {
        ...settings.attributes,
        <span class="hljs-attr">isVisible</span>: {
            <span class="hljs-attr">type</span>: <span class="hljs-string">'boolean'</span>,
            <span class="hljs-attr">default</span>: <span class="hljs-literal">true</span>,
        },
    };

    <span class="hljs-keyword">return</span> settings;
}

addFilter(
    <span class="hljs-string">'blocks.registerBlockType'</span>,
    <span class="hljs-string">'gbvt/add-visibility-attribute'</span>,
    addVisibilityAttribute
);

<span class="hljs-comment">/**
 * Wraps the block's edit component to include the visibility toggle control in advanced settings.
 *
 * @param {Function} BlockEdit The original block edit component.
 * @returns {Function} Wrapped component with visibility toggle control.
 */</span>
<span class="hljs-keyword">const</span> addVisibilityToggleControl = <span class="hljs-function">(<span class="hljs-params"> BlockEdit </span>) =&gt;</span> <span class="hljs-function">(<span class="hljs-params"> props </span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { attributes, setAttributes } = props;
    <span class="hljs-keyword">const</span> { isVisible } = attributes;

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">BlockEdit</span> { <span class="hljs-attr">...props</span> } /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">InspectorControls</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">PanelBody</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Block</span> <span class="hljs-attr">Settings</span>', '<span class="hljs-attr">block-visibility-toggle</span>' ) }&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">ToggleControl</span>
                        <span class="hljs-attr">label</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Display</span> <span class="hljs-attr">Block</span>', '<span class="hljs-attr">global-block-visibility-toggle</span>' ) }
                        <span class="hljs-attr">checked</span>=<span class="hljs-string">{</span> <span class="hljs-attr">isVisible</span> }
                        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{</span> ( <span class="hljs-attr">value</span> ) =&gt;</span> setAttributes( { isVisible: value } ) }
                        help={ __( 'Toggle block visibility on the front end.', 'global-block-visibility-toggle' ) }
                    /&gt;
                <span class="hljs-tag">&lt;/<span class="hljs-name">PanelBody</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">InspectorControls</span>&gt;</span>
        <span class="hljs-tag">&lt;/&gt;</span></span>
    );
};

addFilter(
    <span class="hljs-string">'editor.BlockEdit'</span>,
    <span class="hljs-string">'gbvt/add-visibility-toggle-control'</span>,
    addVisibilityToggleControl
);
</code></pre>
<h3 id="heading-24-applying-the-visibility-on-the-front-end">2.4: Applying the Visibility on the Front End</h3>
<p>Now, we need to ensure that blocks with <code>isVisible</code> set to false do not render on the front end. We’ll use a PHP filter to modify the rendered content.</p>
<p>In your <code>global-block-visibility-toggle.php</code> file, add the following code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/**
 * Plugin Name: Global Block Visibility Toggle
 * Description: Adds a visibility toggle to all blocks using a block filter.
 * Version: 1.0.0
 * Author: Your Name
 * Text Domain: global-block-visibility-toggle
 */</span>

<span class="hljs-comment">/**
 * Enqueue block editor assets.
 */</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">gbvt_enqueue_block_editor_assets</span>(<span class="hljs-params"></span>) </span>{
    wp_enqueue_script(
        <span class="hljs-string">'gbvt-editor-script'</span>,
        plugins_url( <span class="hljs-string">'build/index.js'</span>, <span class="hljs-keyword">__FILE__</span> ),
        [ <span class="hljs-string">'wp-blocks'</span>, <span class="hljs-string">'wp-dom-ready'</span>, <span class="hljs-string">'wp-edit-post'</span>, <span class="hljs-string">'wp-hooks'</span> ],
        filemtime( plugin_dir_path( <span class="hljs-keyword">__FILE__</span> ) . <span class="hljs-string">'build/index.js'</span> )
    );
}
add_action( <span class="hljs-string">'enqueue_block_editor_assets'</span>, <span class="hljs-string">'gbvt_enqueue_block_editor_assets'</span> );

<span class="hljs-comment">/**
 * Filter block content to control visibility on the front end.
 *
 * <span class="hljs-doctag">@param</span> string $block_content The block content.
 * <span class="hljs-doctag">@param</span> array  $block         The block settings.
 * <span class="hljs-doctag">@return</span> string Modified block content.
 */</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">gbvt_filter_block_visibility</span>(<span class="hljs-params"> $block_content, $block </span>) </span>{
    <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">isset</span>( $block[<span class="hljs-string">'attrs'</span>][<span class="hljs-string">'isVisible'</span>] ) &amp;&amp; ! $block[<span class="hljs-string">'attrs'</span>][<span class="hljs-string">'isVisible'</span>] ) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>; <span class="hljs-comment">// Do not render the block</span>
    }
    <span class="hljs-keyword">return</span> $block_content;
}
add_filter( <span class="hljs-string">'render_block'</span>, <span class="hljs-string">'gbvt_filter_block_visibility'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span> );
</code></pre>
<h3 id="heading-25-building-and-activating-the-plugin">2.5: Building and Activating the Plugin</h3>
<p>We need to build our JavaScript code into a <code>build/index.js</code> file that can be enqueued.</p>
<p>With <code>@wordpress/scripts</code>, we can use the following command to build our JavaScript files:</p>
<pre><code class="lang-bash">npm run build
</code></pre>
<p>This will compile <code>src/index.js</code> and output the result to <code>build/index.js</code>.</p>
<p>Alternatively, if you want to watch for changes and rebuild automatically during development, you can use:</p>
<pre><code class="lang-bash">npm run start
</code></pre>
<p>This command runs in watch mode, rebuilding the code whenever you save changes.</p>
<h3 id="heading-26-using-the-sidebar">2.6: Using the Sidebar</h3>
<p>Remember after you have ran <code>npm run build</code> that you need to activate the plugin.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244619121/71099cb7-0714-4553-be44-63bc7570cdaa.png" alt="Activate the Global Block Visibility Toggle" class="image--center mx-auto" /></p>
<p>Select any block (in this case I’m selecting the paragraph block) and note that the Display Block toggle is now visible.</p>
<p>Toggle this so that it no longer displays, and save the post.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244631695/c31adc02-c627-4619-8296-67ff40fb0953.png" alt="The paragraph block now has the display block toggle" class="image--center mx-auto" /></p>
<p>When you view the post on the front end, that block (the paragraph block) is no longer visible.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244656198/1ba68ece-b096-4fd8-b196-bc8d81d98d9c.png" alt="The paragraph block is no longer visible" class="image--center mx-auto" /></p>
<h2 id="heading-3-post-level-sidebar-toggle-post-content-visibility">3. Post-Level Sidebar: Toggle Post Content Visibility</h2>
<p>If you would like to add a control that impacts the entire post, you can add controls to the post sidebar. In this type of sidebar it might make sense for the controls to alter post meta data.</p>
<p>To showcase a post-level sidebar that allows you to toggle the visibility of the post content. If disabled, the content will not display on the front end, and a custom message will be shown instead.</p>
<h3 id="heading-31-setting-up-the-plugin">3.1: Setting Up the Plugin</h3>
<p>Create a new plugin folder called <code>post-content-visibility</code> in your WordPress <code>wp-content/plugins</code> directory.</p>
<p>Inside this folder, create a <code>src</code> folder for your JavaScript code and a <code>post-content-visibility.php</code> file for the plugin’s PHP code.</p>
<p>As per the last example we are going to use <code>wp-scripts</code>. We will take a bit of a shortcut this time, by setting up the <code>package.json</code> file and then running our setup command.</p>
<p>In the <code>post-content-visibility</code> folder, initialise your package with <code>wp-scripts</code> creating the following <code>package.json</code> file in the root of the plugin.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"post-content-visibility"</span>,
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Toggle the visibility of post content from the editor sidebar."</span>,
    <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
    <span class="hljs-attr">"scripts"</span>: {
        <span class="hljs-attr">"build"</span>: <span class="hljs-string">"wp-scripts build"</span>,
        <span class="hljs-attr">"start"</span>: <span class="hljs-string">"wp-scripts start"</span>
    },
    <span class="hljs-attr">"keywords"</span>: [],
    <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Your Name"</span>,
    <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"@wordpress/scripts"</span>: <span class="hljs-string">"^25.0.0"</span>
    }
}
</code></pre>
<p>Now run <code>npm install</code> from inside the <code>post-content-visibility</code> folder, and wait for the package to initialise. You can now build and run your JavaScript using <code>npm run build</code> or <code>npm start</code>.</p>
<h3 id="heading-32-registering-post-meta">3.2: Registering Post Meta</h3>
<p>We need to register a post meta field to store the visibility setting. Open the <code>post-content-visibility.php</code> file in the plugin directory and add the following code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/**
 * Plugin Name:       Post Content Visibility
 * Description:       Adds a toggle to control the visibility of post content on the front end.
 * Version:           1.0.0
 * Author:            Your Name
 * Text Domain:       post-content-visibility
 */</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pcv_register_post_meta</span>(<span class="hljs-params"></span>) </span>{
    register_post_meta( <span class="hljs-string">''</span>, <span class="hljs-string">'pcv_display_post_content'</span>, [
        <span class="hljs-string">'show_in_rest'</span> =&gt; <span class="hljs-literal">true</span>,
        <span class="hljs-string">'single'</span>       =&gt; <span class="hljs-literal">true</span>,
        <span class="hljs-string">'type'</span>         =&gt; <span class="hljs-string">'boolean'</span>,
        <span class="hljs-string">'default'</span>      =&gt; <span class="hljs-literal">true</span>,
    ] );
}
add_action( <span class="hljs-string">'init'</span>, <span class="hljs-string">'pcv_register_post_meta'</span> );
</code></pre>
<p>By registering the meta for all post types (denoted by the empty string <code>''</code>), we can use this setting across different content types.</p>
<h3 id="heading-33-enqueuing-the-sidebar-toggle-script">3.3: Enqueuing the Sidebar Toggle Script</h3>
<p>Add the following code to your <code>post-content-visibility.php</code> file to enqueue the sidebar toggle script in the block editor:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pcv_enqueue_editor_assets</span>(<span class="hljs-params"></span>) </span>{
    wp_enqueue_script(
        <span class="hljs-string">'post-content-visibility'</span>,
        plugins_url( <span class="hljs-string">'build/index.js'</span>, <span class="hljs-keyword">__FILE__</span> ),
        <span class="hljs-keyword">array</span>( <span class="hljs-string">'wp-plugins'</span>, <span class="hljs-string">'wp-edit-post'</span>, <span class="hljs-string">'wp-element'</span>, <span class="hljs-string">'wp-components'</span>, <span class="hljs-string">'wp-data'</span>, <span class="hljs-string">'wp-i18n'</span> ),
        <span class="hljs-string">'1.0.0'</span>,
        <span class="hljs-literal">true</span>
    );
}
add_action( <span class="hljs-string">'enqueue_block_editor_assets'</span>, <span class="hljs-string">'pcv_enqueue_editor_assets'</span> );
</code></pre>
<p>This function ensures that the script is loaded in all editors that support the block editor.</p>
<h3 id="heading-34-building-the-toggle-in-the-post-sidebar">3.4: Building the Toggle in the Post Sidebar</h3>
<p>We’ll use the <code>PluginDocumentSettingPanel</code> component to add a custom panel in the post sidebar. Open <code>src/index.js</code> and replace its content with the following code.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { PluginDocumentSettingPanel } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/editor'</span>;
<span class="hljs-keyword">import</span> { ToggleControl } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/components'</span>;
<span class="hljs-keyword">import</span> { useSelect, useDispatch } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/data'</span>;
<span class="hljs-keyword">import</span> { registerPlugin } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/plugins'</span>;
<span class="hljs-keyword">import</span> { __ } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/i18n'</span>;

<span class="hljs-keyword">const</span> PostContentVisibility = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> { editPost } = useDispatch( <span class="hljs-string">'core/editor'</span> );
    <span class="hljs-keyword">const</span> displayContent = useSelect(
        <span class="hljs-function">(<span class="hljs-params"> select </span>) =&gt;</span> select( <span class="hljs-string">'core/editor'</span> )?.getEditedPostAttribute( <span class="hljs-string">'meta'</span> )?.[<span class="hljs-string">'pcv_display_post_content'</span>],
        []
    );

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">PluginDocumentSettingPanel</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"post-content-visibility"</span>
            <span class="hljs-attr">title</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Post</span> <span class="hljs-attr">Content</span> <span class="hljs-attr">Visibility</span>', '<span class="hljs-attr">post-content-visibility</span>' ) }
            <span class="hljs-attr">className</span>=<span class="hljs-string">"post-content-visibility-panel"</span>
        &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ToggleControl</span>
                <span class="hljs-attr">label</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Display</span> <span class="hljs-attr">Post</span> <span class="hljs-attr">Content</span>', '<span class="hljs-attr">post-content-visibility</span>' ) }
                <span class="hljs-attr">checked</span>=<span class="hljs-string">{</span> <span class="hljs-attr">displayContent</span> }
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{</span> ( <span class="hljs-attr">value</span> ) =&gt;</span> editPost( { meta: { pcv_display_post_content: value } } ) }
                help={ __( 'Toggle the visibility of the post content on the front end.', 'post-content-visibility' ) }
            /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">PluginDocumentSettingPanel</span>&gt;</span></span>
    );
};

<span class="hljs-comment">// Check if we're in the Post Editor before registering the plugin.</span>
<span class="hljs-keyword">if</span> ( <span class="hljs-built_in">window</span>.pagenow !== <span class="hljs-string">'site-editor'</span> ) {
    registerPlugin( <span class="hljs-string">'post-content-visibility'</span>, {
        <span class="hljs-attr">render</span>: PostContentVisibility,
        <span class="hljs-attr">icon</span>: <span class="hljs-string">'visibility'</span>,
    } );
}
</code></pre>
<p>This component fetches the <code>pcv_display_post_content</code> meta field and updates it when the toggle is changed.</p>
<h3 id="heading-35-modifying-the-front-end-display">3.5: Modifying the Front-End Display</h3>
<p>We need to check the meta value when rendering the post content and conditionally display it. In <code>post-content-visibility.php</code>, add the following code:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pcv_filter_the_content</span>(<span class="hljs-params"> $content </span>) </span>{
    <span class="hljs-keyword">if</span> ( is_singular() ) {
        $display_content = get_post_meta( get_the_ID(), <span class="hljs-string">'pcv_display_post_content'</span>, <span class="hljs-literal">true</span> );
        <span class="hljs-keyword">if</span> ( ! $display_content ) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">'&lt;p&gt;'</span> . __( <span class="hljs-string">'This post is hidden from view.'</span>, <span class="hljs-string">'post-content-visibility'</span> ) . <span class="hljs-string">'&lt;/p&gt;'</span>;
        }
    }
    <span class="hljs-keyword">return</span> $content;
}
add_filter( <span class="hljs-string">'the_content'</span>, <span class="hljs-string">'pcv_filter_the_content'</span> );
</code></pre>
<p>This function checks if the <code>pcv_display_post_content</code> meta is <code>false</code> and replaces the content with a custom message if so.</p>
<h3 id="heading-36-building-the-plugin">3.6: Building the Plugin</h3>
<p>After updating <code>src/index.js</code>, build your JavaScript file using <code>wp-scripts</code>:</p>
<pre><code class="lang-bash">npm run build
</code></pre>
<p>This will generate a <code>build/index.js</code> file. Your plugin is now ready to use!</p>
<h3 id="heading-step-7-using-the-sidebar">Step 7: Using the Sidebar</h3>
<p>Remember to activate the plugin.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244722333/fbc8e72b-6cd1-4cf5-9c53-717985e4b7f6.png" alt="Activate the Post Content Visibility Sidebar" class="image--center mx-auto" /></p>
<p>Note that in the post sidebar we now have the Display Post Content toggle.</p>
<p>Switch the toggle and save the post.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244730696/58b72d05-42a2-49ec-993c-126a25e8b752.png" alt="The Display Post Content toggle is available in the post sidebar" class="image--center mx-auto" /></p>
<p>Note that we have now replaced the content of the entire post.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244756112/7cffac99-3de0-4b2b-9c6b-ef9fd5b30d28.png" alt="The post content has now been replaced" class="image--center mx-auto" /></p>
<h2 id="heading-4-custom-plugin-sidebar-toggle-entire-post-visibility">4. Custom Plugin Sidebar: Toggle Entire Post Visibility</h2>
<p>Sometimes you might have a plugin that adds many post controls, when this happens it might make sense to put all of these in a custom sidebar so that they are all together, easily identifiable and reduce post sidebar clutter.</p>
<p>In this section, we’ll create a custom plugin sidebar that allows you to toggle the entire post’s visibility. When disabled, the post will not be accessible on the front end, and visitors will see a 404 error.</p>
<h3 id="heading-41-setting-up-the-plugin">4.1: Setting Up the Plugin</h3>
<p>Create a new plugin folder called <code>post-visibility-toggle</code> in your WordPress <code>wp-content/plugins</code> directory.</p>
<p>Inside this folder, create a <code>src</code> folder for your JavaScript code and a <code>post-visibility-toggle.php</code> file for the plugin’s PHP code.</p>
<p>As per the last couple of examples, we are going to use <code>wp-scripts</code>. In the <code>post-visibility-toggle</code> folder, initialise your package with <code>wp-scripts</code> by creating the following <code>package.json</code> file in the root of the plugin:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"post-visibility-toggle"</span>,
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Toggle the visibility of the entire post from the editor sidebar."</span>,
    <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
    <span class="hljs-attr">"scripts"</span>: {
        <span class="hljs-attr">"build"</span>: <span class="hljs-string">"wp-scripts build"</span>,
        <span class="hljs-attr">"start"</span>: <span class="hljs-string">"wp-scripts start"</span>
    },
    <span class="hljs-attr">"keywords"</span>: [],
    <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Your Name"</span>,
    <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"@wordpress/scripts"</span>: <span class="hljs-string">"^25.0.0"</span>
    }
}
</code></pre>
<p>Now run <code>npm install</code> from inside the <code>post-visibility-toggle</code> folder, and wait for the package to initialise. You can now build and run your JavaScript using <code>npm run build</code> or <code>npm start</code>.</p>
<h3 id="heading-42-registering-post-meta">4.2: Registering Post Meta</h3>
<p>Open the <code>post-visibility-toggle.php</code> file, make sure the enqueue for the editor and register a post meta field to store the visibility setting:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/**
 * Plugin Name:       Post Visibility Toggle
 * Description:       Adds a toggle to control the visibility of the entire post on the front end.
 * Version:           1.0.0
 * Author:            Your Name
 * Text Domain:       post-visibility-toggle
 */</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pvt_enqueue_editor_assets</span>(<span class="hljs-params"></span>) </span>{
    wp_enqueue_script(
        <span class="hljs-string">'post-visibility-toggle'</span>,
        plugins_url( <span class="hljs-string">'build/index.js'</span>, <span class="hljs-keyword">__FILE__</span> ),
        [ <span class="hljs-string">'wp-plugins'</span>, <span class="hljs-string">'wp-edit-post'</span>, <span class="hljs-string">'wp-element'</span>, <span class="hljs-string">'wp-components'</span>, <span class="hljs-string">'wp-data'</span>, <span class="hljs-string">'wp-i18n'</span> ],
        <span class="hljs-string">'1.0.0'</span>,
        <span class="hljs-literal">true</span>
    );
}
add_action( <span class="hljs-string">'enqueue_block_editor_assets'</span>, <span class="hljs-string">'pvt_enqueue_editor_assets'</span> );

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pvt_register_post_meta</span>(<span class="hljs-params"></span>) </span>{
    register_post_meta( <span class="hljs-string">''</span>, <span class="hljs-string">'pvt_display_post'</span>, [
        <span class="hljs-string">'show_in_rest'</span> =&gt; <span class="hljs-literal">true</span>,
        <span class="hljs-string">'single'</span>       =&gt; <span class="hljs-literal">true</span>,
        <span class="hljs-string">'type'</span>         =&gt; <span class="hljs-string">'boolean'</span>,
        <span class="hljs-string">'default'</span>      =&gt; <span class="hljs-literal">true</span>,
    ] );
}
add_action( <span class="hljs-string">'init'</span>, <span class="hljs-string">'pvt_register_post_meta'</span> );
</code></pre>
<h3 id="heading-43-creating-the-plugin-sidebar">4.3: Creating the Plugin Sidebar</h3>
<p>In <code>src/index.js</code>, create the plugin sidebar with a toggle control:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { PluginSidebar } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/editor'</span>;
<span class="hljs-keyword">import</span> { ToggleControl, PanelBody } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/components'</span>;
<span class="hljs-keyword">import</span> { useSelect, useDispatch } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/data'</span>;
<span class="hljs-keyword">import</span> { registerPlugin } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/plugins'</span>;
<span class="hljs-keyword">import</span> { __ } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/i18n'</span>;

<span class="hljs-keyword">const</span> PostVisibilityToggle = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> { editPost } = useDispatch(<span class="hljs-string">'core/editor'</span>);
    <span class="hljs-keyword">const</span> displayPost = useSelect(
        <span class="hljs-function">(<span class="hljs-params"> select </span>) =&gt;</span> select( <span class="hljs-string">'core/editor'</span> )?.getEditedPostAttribute( <span class="hljs-string">'meta'</span> )?.[<span class="hljs-string">'pvt_display_post'</span>],
        []
    );

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">PluginSidebar</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"post-visibility-toggle"</span>
            <span class="hljs-attr">title</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Post</span> <span class="hljs-attr">Visibility</span>', '<span class="hljs-attr">post-visibility-toggle</span>' ) }
            <span class="hljs-attr">icon</span>=<span class="hljs-string">"visibility"</span>
        &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">PanelBody</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>('<span class="hljs-attr">Post</span> <span class="hljs-attr">Visibility</span> <span class="hljs-attr">Settings</span>', '<span class="hljs-attr">post-visibility-toggle</span>' ) }&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">ToggleControl</span>
                    <span class="hljs-attr">label</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Display</span> <span class="hljs-attr">Post</span>', '<span class="hljs-attr">post-visibility-toggle</span>' ) }
                    <span class="hljs-attr">checked</span>=<span class="hljs-string">{</span> <span class="hljs-attr">displayPost</span> }
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{</span> ( <span class="hljs-attr">value</span> ) =&gt;</span> editPost( { meta: { pvt_display_post: value } } ) }
                    help={ __( 'Toggle the visibility of the entire post on the front end.', 'post-visibility-toggle' ) }
                /&gt;
            <span class="hljs-tag">&lt;/<span class="hljs-name">PanelBody</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">PluginSidebar</span>&gt;</span></span>
    );
};

<span class="hljs-comment">// Check if we're in the Post Editor before registering the plugin.</span>
<span class="hljs-keyword">if</span> ( <span class="hljs-built_in">window</span>.pagenow !== <span class="hljs-string">'site-editor'</span> ) {
    registerPlugin( <span class="hljs-string">'post-visibility-toggle'</span>, {
        <span class="hljs-attr">render</span>: PostVisibilityToggle,
        <span class="hljs-attr">icon</span>: <span class="hljs-string">'visibility'</span>,
    } );
}
</code></pre>
<p>This code creates a sidebar that appears in the editor where you can toggle the entire post’s visibility.</p>
<h3 id="heading-44-redirecting-hidden-posts-to-404">4.4: Redirecting Hidden Posts to 404</h3>
<p>In the plugin’s PHP file, we’ll add an action to redirect to a 404 page if the post is hidden:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pvt_redirect_hidden_posts</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">if</span> ( is_singular() &amp;&amp; !is_admin() ) {
        $display_post = get_post_meta( get_the_ID(), <span class="hljs-string">'pvt_display_post'</span>, <span class="hljs-literal">true</span> );
        <span class="hljs-keyword">if</span> ( ! $display_post ) {
            <span class="hljs-keyword">global</span> $wp_query;
            $wp_query-&gt;set_404();
            status_header( <span class="hljs-number">404</span> );
            nocache_headers();
            <span class="hljs-keyword">include</span>( get_query_template( <span class="hljs-string">'404'</span> ) );
            <span class="hljs-keyword">exit</span>;
        }
    }
}
add_action( <span class="hljs-string">'template_redirect'</span>, <span class="hljs-string">'pvt_redirect_hidden_posts'</span> );
</code></pre>
<p>This function checks if the post should be displayed and, if not, sets a 404 status and loads the 404 template.</p>
<h3 id="heading-45-using-the-sidebar">4.5: Using the Sidebar</h3>
<p>Remember to run <code>npm run build</code> in your block directory and to activate the plugin.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244790373/70a3d4e1-f60a-495a-bf0b-61459a7ad826.png" alt="Activate the Post Visibility Toggle" class="image--center mx-auto" /></p>
<p>Now you should be able to see your custom Post Visibility sidebar, identified by the “eye” icon. Click the icon and it should open up the sidebar. Once you have done this you should be able to see the controls that you registered for the sidebar.</p>
<p>Be sure to switch the toggle and save the post.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244799780/2cd14481-6387-4af1-83fa-2839776b1452.png" alt="You can select the Post Visibility Sidebar" class="image--center mx-auto" /></p>
<p>As per the logic we set, this will redirect the post to a 404 page on the front end.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244809627/47143d70-ba69-4a61-87a0-72c1d46c4625.png" alt="Our custom logic redirects the post to a 404" class="image--center mx-auto" /></p>
<h2 id="heading-5-full-site-editor-fse-sidebar-global-settings">5. Full Site Editor (FSE) Sidebar: Global Settings</h2>
<p>We have explored block settings with block sidebars, post settings with post sidebars and custom sidebars. What if we want something that impacts the entire site? Say something that is stored in an option?</p>
<p>To explore this we’ll create a sidebar in the Full Site Editor (FSE) to manage global site-wide settings. This sidebar can control settings that affect the entire website.</p>
<p>In this example, we will prevent the entire site from displaying with a <code>wp_die()</code>. <strong>This is absolutely overkill (and you should never do this)</strong>, but it serves to prove how this kind of sidebar can be used.</p>
<h3 id="heading-51-setting-up-the-plugin">5.1: Setting Up the Plugin</h3>
<p>Create a new plugin folder called <code>fse-global-settings</code> in your WordPress <code>wp-content/plugins</code> directory.</p>
<p>Inside this folder, create a <code>src</code> folder for your JavaScript code and a <code>fse-global-settings.php</code> file for the plugin’s PHP code.</p>
<p>As per the last few examples, we are going to use <code>wp-scripts</code>. In the <code>fse-global-settings</code> folder, initialise your package with <code>wp-scripts</code> by creating the following <code>package.json</code> file in the root of the plugin:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"fse-global-settings"</span>,
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Adds a custom sidebar to the Full Site Editor for global settings."</span>,
    <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
    <span class="hljs-attr">"scripts"</span>: {
        <span class="hljs-attr">"build"</span>: <span class="hljs-string">"wp-scripts build"</span>,
        <span class="hljs-attr">"start"</span>: <span class="hljs-string">"wp-scripts start"</span>
    },
    <span class="hljs-attr">"keywords"</span>: [],
    <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Your Name"</span>,
    <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
    <span class="hljs-attr">"dependencies"</span>: {
        <span class="hljs-attr">"@wordpress/scripts"</span>: <span class="hljs-string">"^25.0.0"</span>
    }
}
</code></pre>
<p>Now run <code>npm install</code> from inside the <code>fse-global-settings</code> folder, and wait for the package to initialise. You can now build and run your JavaScript using <code>npm run build</code> or <code>npm start</code>.</p>
<h3 id="heading-52-enqueueing-scripts-and-styles">5.2: Enqueueing Scripts and Styles</h3>
<p>We need to include our plugin header in the root plugin file.</p>
<p>We’ll also enqueue our JavaScript file that registers the sidebar component. In <code>fse-global-settings.php</code>, add the following code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/**
 * Plugin Name: FSE Global Settings
 * Description: Adds a custom sidebar to the Full Site Editor for global settings.
 * Version: 1.0.0
 * Author: Your Name
 * Text Domain: fse-global-settings
 */</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fse_gs_enqueue_assets</span>(<span class="hljs-params"></span>) </span>{
    wp_enqueue_script(
        <span class="hljs-string">'fse-gs-editor-script'</span>,
        plugins_url( <span class="hljs-string">'build/index.js'</span>, <span class="hljs-keyword">__FILE__</span> ),
        [ <span class="hljs-string">'wp-edit-site'</span>, <span class="hljs-string">'wp-plugins'</span>, <span class="hljs-string">'wp-element'</span>, <span class="hljs-string">'wp-data'</span>, <span class="hljs-string">'wp-components'</span>, <span class="hljs-string">'wp-i18n'</span> ],
        filemtime( plugin_dir_path( <span class="hljs-keyword">__FILE__</span> ) . <span class="hljs-string">'build/index.js'</span> )
    );
}
add_action( <span class="hljs-string">'enqueue_block_editor_assets'</span>, <span class="hljs-string">'fse_gs_enqueue_assets'</span> );
</code></pre>
<h3 id="heading-53-creating-the-sidebar-component">5.3: Creating the Sidebar Component</h3>
<p>Now, we’ll add the code for the sidebar. In <code>src/index.js</code>, add the following:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { registerPlugin } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/plugins'</span>;
<span class="hljs-keyword">import</span> { PluginSidebar } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/edit-site'</span>;
<span class="hljs-keyword">import</span> { PanelBody, ToggleControl } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/components'</span>;
<span class="hljs-keyword">import</span> { useSelect, useDispatch } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/data'</span>;
<span class="hljs-keyword">import</span> { __ } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/i18n'</span>;

<span class="hljs-keyword">const</span> GlobalSettingsSidebar = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> { editEntityRecord } = useDispatch( <span class="hljs-string">'core'</span> );
    <span class="hljs-keyword">const</span> globalVisibility = useSelect( <span class="hljs-function">(<span class="hljs-params"> select </span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> settings = select(<span class="hljs-string">'core'</span>).getEntityRecord( <span class="hljs-string">'root'</span>, <span class="hljs-string">'__experimentalSite'</span> );
        <span class="hljs-keyword">return</span> settings ? settings.global_visibility : <span class="hljs-literal">true</span>;
    }, []);

    <span class="hljs-keyword">const</span> onChange = <span class="hljs-function">(<span class="hljs-params">value</span>) =&gt;</span> {
        editEntityRecord( <span class="hljs-string">'root'</span>, <span class="hljs-string">'__experimentalSite'</span>, <span class="hljs-literal">undefined</span>, { <span class="hljs-attr">global_visibility</span>: value } );
    };

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">PluginSidebar</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"fse-global-settings"</span>
            <span class="hljs-attr">title</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Global</span> <span class="hljs-attr">Settings</span>', '<span class="hljs-attr">fse-global-settings</span>' ) }
            <span class="hljs-attr">icon</span>=<span class="hljs-string">"admin-site"</span>
        &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">PanelBody</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Site-Wide</span> <span class="hljs-attr">Settings</span>', '<span class="hljs-attr">fse-global-settings</span>' ) }&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">ToggleControl</span>
                    <span class="hljs-attr">label</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>('<span class="hljs-attr">Enable</span> <span class="hljs-attr">Global</span> <span class="hljs-attr">Content</span> <span class="hljs-attr">Visibility</span>', '<span class="hljs-attr">fse-global-settings</span>' ) }
                    <span class="hljs-attr">checked</span>=<span class="hljs-string">{</span> <span class="hljs-attr">globalVisibility</span> }
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{</span> <span class="hljs-attr">onChange</span> }
                    <span class="hljs-attr">help</span>=<span class="hljs-string">{</span> <span class="hljs-attr">__</span>( '<span class="hljs-attr">Toggle</span> <span class="hljs-attr">global</span> <span class="hljs-attr">visibility</span> <span class="hljs-attr">settings</span> <span class="hljs-attr">for</span> <span class="hljs-attr">the</span> <span class="hljs-attr">site.</span>', '<span class="hljs-attr">fse-global-settings</span>' ) }
                /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">PanelBody</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">PluginSidebar</span>&gt;</span></span>
    );
};

<span class="hljs-comment">// Check if we're in the Site Editor before registering the plugin.</span>
<span class="hljs-keyword">if</span> ( <span class="hljs-built_in">window</span>.pagenow === <span class="hljs-string">'site-editor'</span> ) {
    registerPlugin( <span class="hljs-string">'fse-global-settings'</span>, {
        <span class="hljs-attr">render</span>: GlobalSettingsSidebar,
    } );
}
</code></pre>
<h3 id="heading-54-registering-global-settings">5.4: Registering Global Settings</h3>
<p>We need to register the <code>global_visibility</code> setting so it can be saved and retrieved. In <code>fse-global-settings.php</code>, add:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fse_gs_register_settings</span>(<span class="hljs-params"></span>) </span>{
    register_setting( <span class="hljs-string">'general'</span>, <span class="hljs-string">'global_visibility'</span>, [
        <span class="hljs-string">'type'</span>         =&gt; <span class="hljs-string">'boolean'</span>,
        <span class="hljs-string">'default'</span>      =&gt; <span class="hljs-literal">true</span>,
        <span class="hljs-string">'show_in_rest'</span> =&gt; <span class="hljs-literal">true</span>,
    ] );
}
add_action( <span class="hljs-string">'init'</span>, <span class="hljs-string">'fse_gs_register_settings'</span> );
</code></pre>
<h3 id="heading-55-applying-global-settings-on-the-front-end">5.5: Applying Global Settings on the Front End</h3>
<p>Finally, we’ll check the <code>global_visibility</code> setting when rendering the site. In your theme’s <code>functions.php</code> or within the plugin, add:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fse_gs_check_global_visibility</span>(<span class="hljs-params"></span>) </span>{
    $global_visibility = get_option( <span class="hljs-string">'global_visibility'</span>, <span class="hljs-literal">true</span> );
    <span class="hljs-keyword">if</span> ( ! $global_visibility ) {
        <span class="hljs-comment">// Redirect to maintenance page or display a message.</span>
        wp_die( __( <span class="hljs-string">'The site is currently not visible.'</span>, <span class="hljs-string">'fse-global-settings'</span> ) );
    }
}
add_action( <span class="hljs-string">'template_redirect'</span>, <span class="hljs-string">'fse_gs_check_global_visibility'</span> );
</code></pre>
<p>This function checks the <code>global_visibility</code> option and, if set to <code>false</code>, prevents the site from displaying.</p>
<h3 id="heading-56-using-the-sidebar">5.6: Using the Sidebar</h3>
<p>Remember to run <code>npm run build</code> in your block directory and to activate the plugin.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244832299/19206c17-26c1-4c65-b794-0bb1872591c6.png" alt="Activate the FSE Global Settings plugin" class="image--center mx-auto" /></p>
<p>Now, when you edit a template in the Full Site Editor, you will see the “globe” icon. Clicking on this icon will open the sidebar we just created.</p>
<p>Again, the control we added to the sidebar is visible. Toggle this and save the sidebar.</p>
<p>You will likely then be prompted by another confirmation sidebar, save the setting here too.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244842808/766ff3bc-8470-4b70-b9ff-5413d7221bf8.png" alt="The Global Settings sidebar is now visible" class="image--center mx-auto" /></p>
<p>As per our logic, we get a side wide message, preventing us from loading the site.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730244849551/1e03f086-9561-4e87-bf8b-7b508800e279.png" alt="The logic relating to our custom control is shown" class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>You’ve learned how to create four distinct types of sidebars in WordPress: block-level controls, post-specific options, a custom plugin sidebar, and a Full Site Editor sidebar. Each of these sidebars provides specialized content control options, enhancing both the editing experience and the front-end behavior of your site.</p>
<p>We also explored how to use block filters to add features to all blocks, and discussed when <code>@wordpress/create-block</code> is suitable for plugin creation and when manual <code>@wordpress/scripts</code> is more appropriate.</p>
<p>If you have any questions, please drop them below in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[Getting Started with WordPress Block Development Using Studio by WordPress.com]]></title><description><![CDATA[Starting a WordPress development environment doesn’t have to feel like trying to pilot the Starship Enterprise with an Ikea manual. Luckily, these days we have the tools to make it almost disappointingly simple. Enter Studio by WordPress.com, a quick...]]></description><link>https://mattwatson.blog/getting-started-with-wordpress-block-development-using-studio-by-wordpresscom</link><guid isPermaLink="true">https://mattwatson.blog/getting-started-with-wordpress-block-development-using-studio-by-wordpresscom</guid><category><![CDATA[create-block]]></category><category><![CDATA[WordPress]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Gutenberg]]></category><category><![CDATA[VS Code]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Sun, 27 Oct 2024 11:18:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1730027827197/8e357bc0-4cbf-4efc-8009-16819b403ede.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Starting a WordPress development environment doesn’t have to feel like trying to pilot the Starship Enterprise with an Ikea manual. Luckily, these days we have the tools to make it almost disappointingly simple. Enter <a target="_blank" href="https://developer.wordpress.com/studio/">Studio by WordPress.com</a>, a quick, no-fuss solution to spin up WordPress instances for development.</p>
<p>This guide will walk you through setting up Studio for block development and making sure everything works just as it should.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730021821330/5c4fc022-608b-4f36-8d43-fd8d894fdee4.png" alt="Studio by WordPress.com Website" class="image--center mx-auto" /></p>
<h2 id="heading-studio-vs-localwp-why-choose-studio">Studio vs. LocalWP: Why Choose Studio?</h2>
<p>If you’re after a comprehensive tool with support for local domains, multisite, and more, <a target="_blank" href="https://localwp.com">LocalWP</a> is a popular choice. But if ease-of-use and a streamlined setup sound good, Studio by WordPress.com is highly recommended here. It’s fast, efficient, and doesn’t bog you down with extra setup steps.</p>
<h2 id="heading-step-1-download-and-install-studio">Step 1: Download and Install Studio</h2>
<p>First things first: head to the <a target="_blank" href="https://developer.wordpress.com/studio/">Studio by WordPress.com</a> site and download the installer that suits your operating system.</p>
<p><strong>On Mac</strong>: Open the downloaded <code>.dmg</code> file, then drag the Studio app into your Applications folder. Easy as pie (mmmm, Apple Pie).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730022102830/102f4550-5177-4aea-97a1-edffbcdc1c61.png" alt="Studio Installation Process" class="image--center mx-auto" /></p>
<h2 id="heading-step-2-set-up-your-first-site">Step 2: Set Up Your First Site</h2>
<p>Once installed, open Studio, and you’ll be greeted with the friendly <em>“Add Your First Site”</em> prompt. Click it to start creating your local WordPress site.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730022164795/f444dd04-a14c-4745-b0b9-64567d66fff3.png" alt="Add your first site prompt" class="image--center mx-auto" /></p>
<p><strong>Customize Your Site Path</strong>: If you’re a stickler for organisation (who isn’t?), hit the “Advanced Settings” dropdown. I like to keep all my sites in a <code>/Sites</code> folder for easy access, so here’s where you can set a custom installation path if you’re the same way.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730022252382/6b98b5dd-1258-4841-9f36-d7869a3a9d71.png" alt="Advanced Settings" class="image--center mx-auto" /></p>
<p>While we are at it, let’s give our site a name. I’ve chosen “Sandbox”.</p>
<h2 id="heading-step-3-finalise-your-site-creation">Step 3: Finalise Your Site Creation</h2>
<p>After you’ve named your site and set any advanced settings, go ahead and click “Add Site.” Studio will now work its magic. You’ll see a progress bar, and in no time, you’ll be met with your site dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730022310722/e8485d43-9c81-43c0-9189-f7e3f3949f8f.png" alt="Site Installation Progress Bar" class="image--center mx-auto" /></p>
<p>The dashboard provides links to key areas of your site, so you’re always one click away from the WordPress dashboard, file access, or terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730022365374/668ac7ba-47b1-42f9-9975-b6a5b9210ba6.png" alt="Studio Site Dashboard" class="image--center mx-auto" /></p>
<p>But first, let’s make sure everything is working.</p>
<h2 id="heading-step-4-open-the-wordpress-dashboard">Step 4: Open the WordPress Dashboard</h2>
<p>Click the “WP Admin” link beneath your site title, and this will bring you right into the WordPress dashboard, where you’ll do most of your WordPress configuration and plugin management. If the WordPress dashboard appears, you’re ready to start developing!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730022492498/deba8f64-b85b-45ef-9d71-1f92c9235c54.png" alt="The WordPress Dashboard" class="image--center mx-auto" /></p>
<h2 id="heading-step-5-access-your-code-editor">Step 5: Access Your Code Editor</h2>
<p>Studio provides a quick “VS Code” button on the dashboard, so if VS Code is your editor of choice, this opens your site project directly in VS Code. Not using VS Code? You might want to give it a try <a target="_blank" href="https://code.visualstudio.com">download it directly from the VS Code website</a>. It’s incredibly versatile, with plugins for just about anything.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730022892979/07f01fe8-df49-430f-bf84-9eeefa3931be.png" alt="VS Code Website" class="image--center mx-auto" /></p>
<p>After you have VS Code installed, clicking “Open in VS Code” pulls up your site files in the editor, ready to start development.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730022968930/937bc4bb-327a-4dbf-b8b5-48f689c97ac8.png" alt="Sandbox Website in VS Code" class="image--center mx-auto" /></p>
<h2 id="heading-step-6-use-the-integrated-terminal">Step 6: Use the Integrated Terminal</h2>
<p>For block development, we’ll need the command line for some operations. VS Code has an integrated terminal that makes this easy to access:</p>
<p><strong>Access the Terminal</strong>: Go to “View” &gt; “Terminal” in VS Code.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730023223920/5c2b7207-fe25-4cfa-800f-c08059585007.png" alt="Open Terminal with View &gt; Terminal" class="image--center mx-auto" /></p>
<p>From here, you can run commands directly within your development environment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730023250001/e5074468-c2e2-4875-b28b-a1e3abea9fe9.png" alt="VS Code with the Integrated Terminal" class="image--center mx-auto" /></p>
<h2 id="heading-step-7-install-nodejs-and-start-with-create-block">Step 7: Install Node.js and Start with <code>create-block</code></h2>
<p>Most block development projects require Node.js, a JavaScript runtime that lets you run development scripts on your computer. Go to the <a target="_blank" href="https://nodejs.org/en">Node.js website</a> and install it. Once installed, you’re ready to start creating custom blocks.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730023589145/aad23276-0a9b-4040-a969-07db79ce2efd.png" alt="Node.js website" class="image--center mx-auto" /></p>
<p>With Node.js installed, navigate to the plugins folder in your terminal:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> wp-content/plugins/
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730023864565/82aba910-a980-4526-8ffc-46516a1cb9df.png" alt="Change Directory to the Plugins folder" class="image--center mx-auto" /></p>
<p><strong>Tip:</strong> <code>cd</code> means “Change Directory”.</p>
<p>Run the <code>create-block</code> command to generate your first block plugin scaffold:</p>
<pre><code class="lang-bash">npx @wordpress/create-block@latest
</code></pre>
<p>This command guides you through setting up a custom block plugin in interactive mode, with prompts to configure the basics like slug, name, and category.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730024178312/34c1f5af-c736-45f0-8aaa-b5638bfc5ea0.png" alt="Create Block Prompt" class="image--center mx-auto" /></p>
<p>For my installation I chose the following options:</p>
<ul>
<li><p>The template variant to use for this block: <code>static</code></p>
</li>
<li><p>The block slug used for identification (also the output folder name): <code>my-first-block</code></p>
</li>
<li><p>The internal namespace for the block name (something unique for your products): <code>my-first-block</code></p>
</li>
<li><p>The display title for your block: <code>My First Block</code></p>
</li>
<li><p>The short description for your block (optional): <code>This is my first block, there are many like it, but this one is mine</code></p>
</li>
<li><p>The dashicon to make it easier to identify your block (optional): <code>smiley</code></p>
</li>
<li><p>The category name to help users browse and discover your block: <code>design</code></p>
</li>
<li><p>Do you want to customize the WordPress plugin? <code>N</code></p>
</li>
</ul>
<p>The script will then take a moment to initiate the plugin.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730024630412/c1c1f254-f86a-4d11-a10c-5f4867db18c4.png" alt="The script indicates it is building the plugin" class="image--center mx-auto" /></p>
<p>Once done, you’ll see your new block plugin appear in the plugins folder.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730024737636/1d340698-3d66-4368-afdb-a6f749059e9c.png" alt="The block has been created" class="image--center mx-auto" /></p>
<p>Follow the instructions provided, <code>cd</code> into the plugin (<code>cd my-first-block</code>) and run <code>rpm run build</code> to build the assets.</p>
<p>You should now be able to access the block in the Block Editor (after a refresh of course).</p>
<hr />
<h2 id="heading-wrapping-up-your-block-development-setup-with-studio">Wrapping Up: Your Block Development Setup with Studio</h2>
<p>With <a target="_blank" href="https://developer.wordpress.com/studio/">Studio by WordPress.com</a> and a few additional tools, you’ve now got a fast and efficient setup for block development. You’re now ready to dive into custom WordPress block development with a streamlined local environment.</p>
<p>Time to start building the next great WordPress plugin! If you hit any snags, drop a question in the comments.</p>
]]></content:encoded></item><item><title><![CDATA[6 Essential WordPress Security Tips to Safeguard Your Site From Hackers]]></title><description><![CDATA[Keeping your WordPress site secure should always be at the top of your priority list. While WordPress itself is relatively secure, it’s the additional steps you take as a developer that ensure your site isn’t vulnerable to some of the most common att...]]></description><link>https://mattwatson.blog/6-essential-wordpress-security-tips-to-safeguard-your-site-from-hackers</link><guid isPermaLink="true">https://mattwatson.blog/6-essential-wordpress-security-tips-to-safeguard-your-site-from-hackers</guid><category><![CDATA[WordPress]]></category><category><![CDATA[Security]]></category><category><![CDATA[tips]]></category><category><![CDATA[listicle]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Sat, 19 Oct 2024 21:21:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729372884484/66902f07-a6b7-4c5b-9f37-7ee0d83d43fb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Keeping your WordPress site secure should always be at the top of your priority list. While WordPress itself is relatively secure, it’s the additional steps you take as a developer that ensure your site isn’t vulnerable to some of the most common attacks. The following tips address security vulnerabilities and bad practices, helping you protect your site and users from harm.</p>
<p><em>(I’ve gathered these trusty code snippets from some of my older blog posts. Some might be a bit old, but they might still come in handy.)</em></p>
<hr />
<h2 id="heading-1-prevent-users-from-sharing-login-credentials">1. Prevent Users from Sharing Login Credentials</h2>
<p>It’s one thing to share a Netflix password, but when users start sharing credentials on your WordPress site, it becomes a security issue. You want to ensure that each account is only used by one person at a time to prevent account sharing, data leaks, or worse. This snippet limits users to a single active login session at a time. If someone logs in while another session is active, the previous one is terminated. This stops users from sharing their login credentials with others.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_login_one_instance</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">global</span> $sessions;
    $sessions = WP_Session_Ttokens::get_instance( get_current_user_id() );
    $sessions-&gt;destroy_others( wp_get_session_token() );
}
add_action(<span class="hljs-string">'setup_theme'</span>, <span class="hljs-string">'matt_watson_login_one_instance'</span>, <span class="hljs-number">0</span>);
</code></pre>
<h2 id="heading-2-prevent-clickjacking-in-wordpress">2. Prevent Clickjacking in WordPress</h2>
<p>Clickjacking is a malicious technique where attackers trick users into clicking something different from what they think they’re clicking. Attackers often use iframes to load your site invisibly on their own malicious website. By adding the <code>X-FRAME-OPTIONS</code> header to your site, you can instruct the browser not to display your content within an iframe, thereby protecting your site from clickjacking attempts.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_prevent_clickjacking</span>(<span class="hljs-params"></span>) </span>{
    header( <span class="hljs-string">'X-FRAME-OPTIONS: SAMEORIGIN'</span> );
}
add_action( <span class="hljs-string">'send_headers'</span>, <span class="hljs-string">'matt_watson_prevent_clickjacking'</span>, <span class="hljs-number">10</span> );
</code></pre>
<h2 id="heading-3-stop-wordpress-from-loading-in-a-frame-older-browsers-too">3. Stop WordPress from Loading in a Frame (Older Browsers Too!)</h2>
<p>While the <code>X-FRAME-OPTIONS</code> header works well for most modern browsers, older versions of browsers (such as Internet Explorer 8 and earlier) don’t support it. To protect users who may still be using older browsers, this JavaScript fallback ensures that your site can’t be loaded in a frame on those browsers either. It continuously checks if the site is loaded in a frame and, if it is, wipes out the content.</p>
<pre><code class="lang-php"><span class="hljs-keyword">try</span> { top.document.domain } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-keyword">var</span> f = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        document.body.innerHTML = <span class="hljs-string">''</span>;
    };

    setInterval( f, <span class="hljs-number">1</span> );

    <span class="hljs-keyword">if</span> ( document.body ) {
        document.body.onload = f;
    }
}
</code></pre>
<h2 id="heading-4-enforce-version-control-in-wordpress">4. Enforce Version Control in WordPress</h2>
<p>WordPress allows users to edit theme files and install plugins directly from the dashboard. While this might seem convenient, it’s not ideal from a security standpoint, especially in environments where multiple users have access to the admin area. By disabling file editing and plugin installations via the WordPress dashboard, you can ensure that all changes go through your version-controlled workflow, avoiding surprise edits and potential vulnerabilities.</p>
<pre><code class="lang-php">define( <span class="hljs-string">'DISALLOW_FILE_EDIT'</span>, <span class="hljs-literal">true</span> );
define( <span class="hljs-string">'DISALLOW_FILE_MODS'</span>, <span class="hljs-literal">true</span> );
</code></pre>
<h2 id="heading-5-limit-login-attempts-to-prevent-brute-force-attacks">5. Limit Login Attempts to Prevent Brute Force Attacks</h2>
<p>Brute force attacks happen when malicious users or bots repeatedly try to guess login credentials. To mitigate this, you can limit the number of login attempts a user can make before they are temporarily locked out. In this snippet, after three failed login attempts, the user is locked out for a defined period, reducing the likelihood of a brute force attack succeeding.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_limit_login_attempts</span>(<span class="hljs-params"></span>) </span>{
    $lockout_duration = <span class="hljs-number">1800</span>; <span class="hljs-comment">// Lockout duration in seconds (30 minutes)</span>
    $data = get_transient( <span class="hljs-string">'mattwatson_attempted_login'</span> ) ?: [ <span class="hljs-string">'tried'</span> =&gt; <span class="hljs-number">0</span> ];

    $data[<span class="hljs-string">'tried'</span>]++;

    <span class="hljs-keyword">if</span> ( $data[<span class="hljs-string">'tried'</span>] &gt;= <span class="hljs-number">3</span> ) {
        <span class="hljs-comment">// Logic for handling lockout after 3 failed attempts</span>
        <span class="hljs-comment">// Example: temporarily block user login for the duration</span>
    }

    set_transient( <span class="hljs-string">'mattwatson_attempted_login'</span>, $data, $lockout_duration );
}

add_action( <span class="hljs-string">'wp_login_failed'</span>, <span class="hljs-string">'matt_watson_limit_login_attempts'</span> );
</code></pre>
<h2 id="heading-6-prevent-xss-by-escaping-values-in-your-wordpress-templates">6. Prevent XSS by Escaping Values in Your WordPress Templates</h2>
<p>Cross-site scripting (XSS) is one of the most common vulnerabilities on the web. It occurs when an attacker injects malicious code into your site, often via user inputs that are displayed without proper sanitation. By using WordPress functions like <code>esc_attr()</code> and <code>esc_html()</code> when displaying user-generated content, you can prevent XSS attacks. These functions escape any special characters that could be interpreted as code, ensuring that only safe data is output.</p>
<p>Here’s how you should sanitize user input in your WordPress templates:</p>
<pre><code class="lang-php">&lt;input type=<span class="hljs-string">"text"</span> value=<span class="hljs-string">"&lt;?php echo esc_attr( <span class="hljs-subst">$my_value</span> ); ?&gt;"</span>&gt;
</code></pre>
<hr />
<p>These six tips provide a solid foundation for securing your WordPress site. From preventing account sharing and brute force attacks to mitigating XSS vulnerabilities, these strategies will help ensure that your WordPress site remains safe from common threats.</p>
]]></content:encoded></item><item><title><![CDATA[9 Must-Know PHP and WordPress Hacks That Will Instantly Boost Your Development Workflow]]></title><description><![CDATA[Efficiently handling data and managing PHP logic is key to smooth WordPress development. Sometimes, it’s the little tricks and techniques that can make all the difference in your workflow. This listicle focuses on tips to streamline your work with PH...]]></description><link>https://mattwatson.blog/9-must-know-php-and-wordpress-hacks-that-will-instantly-boost-your-development-workflow</link><guid isPermaLink="true">https://mattwatson.blog/9-must-know-php-and-wordpress-hacks-that-will-instantly-boost-your-development-workflow</guid><category><![CDATA[PHP]]></category><category><![CDATA[WordPress]]></category><category><![CDATA[tips]]></category><category><![CDATA[listicle]]></category><category><![CDATA[data]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Sat, 19 Oct 2024 20:55:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729371310513/45f11bc8-65f4-4de6-88c4-a5a818100d8b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Efficiently handling data and managing PHP logic is key to smooth WordPress development. Sometimes, it’s the little tricks and techniques that can make all the difference in your workflow. This listicle focuses on tips to streamline your work with PHP arrays, dynamic variables, and content management in WordPress. These trusty snippets, though some might seem a bit old, still pack a punch when you need them.</p>
<p><em>(I’ve gathered these trusty code snippets from some of my older blog posts. Some might be a bit old, but they might still come in handy.)</em></p>
<h2 id="heading-general-php-and-data-management-tips">General PHP and Data Management Tips</h2>
<h3 id="heading-1-array-change-key-case">1. Array Change Key Case</h3>
<p>When handling external data, especially data from APIs or third-party services, you’ll often come across array keys that aren’t in a format you’d prefer—sometimes they’re all uppercase or inconsistent with your internal structure. To solve this, you can use PHP's <code>array_change_key_case()</code> function to quickly convert all array keys to either lowercase or uppercase. This makes your data easier to work with and ensures uniformity.</p>
<p>Here’s an example that converts an array’s keys to lowercase:</p>
<pre><code class="lang-php">$array            = [ <span class="hljs-string">'P'</span> =&gt; <span class="hljs-number">42</span>, <span class="hljs-string">'POST_STATUS'</span> =&gt; <span class="hljs-string">'publish'</span> ];
$lower_case_array = array_change_key_case( $array, CASE_LOWER );

print_r( $lower_case_array );

<span class="hljs-comment">// Output:</span>
<span class="hljs-comment">// Array</span>
<span class="hljs-comment">// (</span>
<span class="hljs-comment">//     [p] =&gt; 42</span>
<span class="hljs-comment">//     [post_status] =&gt; publish</span>
<span class="hljs-comment">// )</span>
</code></pre>
<h3 id="heading-2-dynamic-variable-names-in-php">2. Dynamic Variable Names in PHP</h3>
<p>There are cases when you need to create variables dynamically within loops or repetitive structures, especially when dealing with user-generated data or flexible input formats. Using dynamic variable names allows you to generate new variables on the fly based on conditions or data, which can be handy in certain situations. However, use this technique cautiously, as it can make your code harder to debug.</p>
<p>Here’s an example that dynamically creates variables based on an array’s values:</p>
<pre><code class="lang-php">$custom_variable_array = [<span class="hljs-string">'name_1'</span>, <span class="hljs-string">'name_2'</span>, <span class="hljs-string">'name_3'</span>];

<span class="hljs-keyword">foreach</span> ( $custom_variable_array <span class="hljs-keyword">as</span> $custom_variable ) {
    $$custom_variable = <span class="hljs-string">'Some Value'</span>;
}
<span class="hljs-keyword">echo</span> $name_1;
</code></pre>
<h3 id="heading-3-remove-duplicates-from-arrays">3. Remove Duplicates from Arrays</h3>
<p>Working with arrays often involves cleaning up data and ensuring there are no duplicate values. PHP’s <code>array_unique()</code> function provides a simple way to eliminate duplicate entries from an array, leaving you with clean, unique data. This is particularly useful when you’re aggregating data from multiple sources or handling form input where duplicates might occur.</p>
<p>Here's how you can remove duplicate values from an array:</p>
<pre><code class="lang-php">$array = [ <span class="hljs-string">'apple'</span>, <span class="hljs-string">'banana'</span>, <span class="hljs-string">'apple'</span>, <span class="hljs-string">'orange'</span>, <span class="hljs-string">'banana'</span> ];
$unique_array = array_unique( $array );

print_r( $unique_array );

<span class="hljs-comment">// Output:</span>
<span class="hljs-comment">// Array</span>
<span class="hljs-comment">// (</span>
<span class="hljs-comment">//     [0] =&gt; apple</span>
<span class="hljs-comment">//     [1] =&gt; banana</span>
<span class="hljs-comment">//     [3] =&gt; orange</span>
<span class="hljs-comment">// )</span>
</code></pre>
<h3 id="heading-4-merge-arrays-recursively">4. Merge Arrays Recursively</h3>
<p>Merging arrays in PHP can be tricky when you have nested arrays or need to combine multidimensional arrays. The <code>array_merge_recursive()</code> function comes in handy by merging arrays while preserving their nested structure. This is particularly useful when combining configurations or settings from different sources without losing any nested values.</p>
<p>Here’s an example that merges two multidimensional arrays:</p>
<pre><code class="lang-php">$array1 = [
    <span class="hljs-string">'settings'</span> =&gt; [
        <span class="hljs-string">'color'</span> =&gt; <span class="hljs-string">'blue'</span>,
        <span class="hljs-string">'size'</span>  =&gt; <span class="hljs-string">'medium'</span>,
    ]
];

$array2 = [
    <span class="hljs-string">'settings'</span> =&gt; [
        <span class="hljs-string">'size'</span>  =&gt; <span class="hljs-string">'large'</span>,
        <span class="hljs-string">'shape'</span> =&gt; <span class="hljs-string">'round'</span>,
    ]
];

$merged_array = array_merge_recursive( $array1, $array2 );

print_r( $merged_array );

<span class="hljs-comment">// Output:</span>
<span class="hljs-comment">// Array</span>
<span class="hljs-comment">// (</span>
<span class="hljs-comment">//     [settings] =&gt; Array</span>
<span class="hljs-comment">//         (</span>
<span class="hljs-comment">//             [color] =&gt; blue</span>
<span class="hljs-comment">//             [size] =&gt; Array</span>
<span class="hljs-comment">//                 (</span>
<span class="hljs-comment">//                     [0] =&gt; medium</span>
<span class="hljs-comment">//                     [1] =&gt; large</span>
<span class="hljs-comment">//                 )</span>
<span class="hljs-comment">//             [shape] =&gt; round</span>
<span class="hljs-comment">//         )</span>
<span class="hljs-comment">// )</span>
</code></pre>
<h2 id="heading-text-and-content-management-tips">Text and Content Management Tips</h2>
<h3 id="heading-5-filter-text-strings-in-wordpress-using-gettext">5. Filter Text Strings in WordPress Using <code>gettext</code></h3>
<p>Customizing text strings in themes or plugins can sometimes involve modifying the original code, which is neither practical nor update-friendly. Instead, WordPress provides the <code>gettext</code> filter, allowing you to dynamically change text strings without touching the source files. This is particularly useful for making quick adjustments to text used by third-party themes or plugins.</p>
<p>Here’s how to modify a string using the <code>gettext</code> filter:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_filter_text</span>(<span class="hljs-params"> $translated_text, $untranslated_text, $domain </span>) </span>{
    <span class="hljs-keyword">if</span> ( <span class="hljs-string">'String to translate'</span> === $untranslated_text &amp;&amp; <span class="hljs-string">'mattwatson'</span> === $domain ) {
        <span class="hljs-keyword">return</span> esc_html__( <span class="hljs-string">'Translated Text'</span>, <span class="hljs-string">'mattwatson'</span> );
    }
    <span class="hljs-keyword">return</span> $translated_text;
}
add_filter( <span class="hljs-string">'gettext'</span>, <span class="hljs-string">'matt_watson_filter_text'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">3</span> );
</code></pre>
<h3 id="heading-6-sanitize-titles-with-sanitizetitle">6. Sanitize Titles with <code>sanitize_title()</code></h3>
<p>When generating URLs or slugs from user inputs, it’s important to sanitize the title to ensure it's URL-friendly. WordPress offers the <code>sanitize_title()</code> function, which removes unwanted characters and spaces, turning your input into a clean slug. This is useful for creating SEO-friendly URLs or unique identifiers for content.</p>
<p>Here’s an example of how to sanitize a title in WordPress:</p>
<pre><code class="lang-php">$title = <span class="hljs-string">"This is My Awesome Post!"</span>;
$slug = sanitize_title( $title );

<span class="hljs-keyword">echo</span> $slug; <span class="hljs-comment">// Output: this-is-my-awesome-post</span>
</code></pre>
<h3 id="heading-7-programmatically-create-wordpress-pages">7. Programmatically Create WordPress Pages</h3>
<p>If you’re setting up a WordPress site and need to automatically generate pages upon theme activation or plugin installation, you can programmatically create new pages using <code>wp_insert_post()</code>. This allows you to set up default content or landing pages, saving time and ensuring consistency across installations.</p>
<p>Here’s an example of how to create a page programmatically:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_create_default_page</span>(<span class="hljs-params"></span>) </span>{
    $page_data = [
        <span class="hljs-string">'post_title'</span>    =&gt; <span class="hljs-string">'Contact Us'</span>,
        <span class="hljs-string">'post_content'</span>  =&gt; <span class="hljs-string">'This is the contact page.'</span>,
        <span class="hljs-string">'post_status'</span>   =&gt; <span class="hljs-string">'publish'</span>,
        <span class="hljs-string">'post_type'</span>     =&gt; <span class="hljs-string">'page'</span>,
    ];

    wp_insert_post( $page_data );
}
add_action( <span class="hljs-string">'after_setup_theme'</span>, <span class="hljs-string">'matt_watson_create_default_page'</span> );
</code></pre>
<h3 id="heading-8-automatically-shorten-excerpts-in-wordpress">8. Automatically Shorten Excerpts in WordPress</h3>
<p>By default, WordPress excerpts can be too long or inconsistent for certain themes. You can programmatically set the length of excerpts to ensure they’re a consistent size across your site. This is especially useful for improving the look and feel of archive pages, blog listings, or search results.</p>
<p>Here’s a simple function to control excerpt length:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_set_excerpt_length</span>(<span class="hljs-params"> $length </span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-number">20</span>; <span class="hljs-comment">// Limit excerpt to 20 words</span>
}

add_filter( <span class="hljs-string">'excerpt_length'</span>, <span class="hljs-string">'matt_watson_set_excerpt_length'</span>, <span class="hljs-number">999</span> );
</code></pre>
<h3 id="heading-9-replace-wordpress-shortcodes-programmatically">9. Replace WordPress Shortcodes Programmatically</h3>
<p>If you need to replace shortcodes dynamically in your content, WordPress provides a filter called <code>do_shortcode_tag</code> that allows you to replace shortcodes with custom logic. This is especially useful if you want to swap out certain shortcodes or modify their output without changing the underlying content structure.</p>
<p>Here’s how you can replace a shortcode with a custom output:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_replace_shortcode_output</span>(<span class="hljs-params"> $output, $tag, $attr </span>) </span>{
    <span class="hljs-keyword">if</span> ( <span class="hljs-string">'old_shortcode'</span> === $tag ) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">'This is the new output!'</span>;
    }
    <span class="hljs-keyword">return</span> $output;
}

add_filter( <span class="hljs-string">'do_shortcode_tag'</span>, <span class="hljs-string">'matt_watson_replace_shortcode_output'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">3</span> );
</code></pre>
<hr />
<p>These nine tips and tricks will help streamline your WordPress development process, whether you're working with PHP arrays or fine-tuning your WordPress content. By mastering these techniques, you’ll be able to work faster, more efficiently, and create better-performing websites with less effort.</p>
]]></content:encoded></item><item><title><![CDATA[7 Game-Changing WordPress Performance Hacks]]></title><description><![CDATA[Performance in WordPress is like that elusive Holy Grail — always in demand, often hard to achieve, but oh so rewarding when you finally get it right. The good news? Optimizing your WordPress site doesn’t have to involve complex systems or in-depth r...]]></description><link>https://mattwatson.blog/7-game-changing-wordpress-performance-hacks</link><guid isPermaLink="true">https://mattwatson.blog/7-game-changing-wordpress-performance-hacks</guid><category><![CDATA[WordPress]]></category><category><![CDATA[performance]]></category><category><![CDATA[tips]]></category><category><![CDATA[listicle]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Sat, 19 Oct 2024 20:41:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729370088644/7333cb15-338b-4065-a77c-db64f2a801b2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Performance in WordPress is like that elusive Holy Grail — always in demand, often hard to achieve, but oh so rewarding when you finally get it right. The good news? Optimizing your WordPress site doesn’t have to involve complex systems or in-depth reengineering. Sometimes, the smallest tweaks can make the biggest difference. Whether you're streamlining queries, optimizing database performance, or removing unnecessary features, these code snippets will help you improve your site's speed and responsiveness.</p>
<p><em>(I’ve gathered these trusty code snippets from some of my older blog posts. Some might be a bit old, but they might still come in handy.)</em></p>
<h2 id="heading-1-array-chunking-for-large-data-sets">1. Array Chunking for Large Data Sets</h2>
<p>Handling large datasets in WordPress can quickly become problematic if you try to process everything at once. A massive array of data can overwhelm your server, leading to slow page loads or even server crashes. By chunking arrays into smaller pieces, you can improve the way your server handles large datasets, optimizing performance and memory usage.</p>
<p>The following snippet demonstrates how to split a large array into smaller, more manageable chunks. This helps the server process data more efficiently, making your site faster and more responsive.</p>
<pre><code class="lang-php">$array = [ <span class="hljs-string">'Post 0'</span>, <span class="hljs-string">'Post 1'</span>, <span class="hljs-string">'Post 2'</span>, <span class="hljs-string">'Post 3'</span>, <span class="hljs-string">'Post 4'</span>, <span class="hljs-string">'Post 5'</span> ];
$chunked_array = array_chunk( $array, <span class="hljs-number">2</span> );

print_r( $chunked_array );

<span class="hljs-comment">// Output:</span>
<span class="hljs-comment">// Array</span>
<span class="hljs-comment">// (</span>
<span class="hljs-comment">//     [0] =&gt; Array</span>
<span class="hljs-comment">//         (</span>
<span class="hljs-comment">//             [0] =&gt; Post 0</span>
<span class="hljs-comment">//             [1] =&gt; Post 1</span>
<span class="hljs-comment">//         )</span>
<span class="hljs-comment">//     [1] =&gt; Array</span>
<span class="hljs-comment">//         (</span>
<span class="hljs-comment">//             [0] =&gt; Post 2</span>
<span class="hljs-comment">//             [1] =&gt; Post 3</span>
<span class="hljs-comment">//         )</span>
<span class="hljs-comment">//     [2] =&gt; Array</span>
<span class="hljs-comment">//         (</span>
<span class="hljs-comment">//             [0] =&gt; Post 4</span>
<span class="hljs-comment">//             [1] =&gt; Post 5</span>
<span class="hljs-comment">//         )</span>
<span class="hljs-comment">// )</span>
</code></pre>
<h2 id="heading-2-disable-emoji-scripts">2. Disable Emoji Scripts</h2>
<p>Emojis, while fun, aren’t essential for every website. Yet, WordPress loads emoji scripts by default, even on sites where they aren’t needed. These scripts add unnecessary weight to your pages, slightly slowing down your site’s load time. By disabling these emoji scripts, you can streamline your site and speed up page loads.</p>
<p>Here’s how you can remove those extra emoji scripts from your site’s header and styles, which will result in fewer HTTP requests and quicker page rendering.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_disable_emojis</span>(<span class="hljs-params"></span>) </span>{
    remove_action( <span class="hljs-string">'wp_head'</span>, <span class="hljs-string">'print_emoji_detection_script'</span>, <span class="hljs-number">7</span> );
    remove_action( <span class="hljs-string">'wp_print_styles'</span>, <span class="hljs-string">'print_emoji_styles'</span> );
}

add_action( <span class="hljs-string">'init'</span>, <span class="hljs-string">'matt_watson_disable_emojis'</span> );
</code></pre>
<h2 id="heading-3-query-optimization">3. Query Optimization</h2>
<p>Inefficient database queries can significantly slow down your WordPress site, especially when generated by plugins or themes. By analyzing these queries, you can refine them to make your database interactions faster and more efficient. This is particularly useful for large, content-heavy sites where unnecessary queries can impact performance.</p>
<p>The following snippet optimizes the SQL queries generated by WPML (WordPress Multilingual Plugin) by removing unnecessary post-type lookups, reducing query load, and improving page speed.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_remove_post_type_sql_search</span>(<span class="hljs-params"> $query </span>) : <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">global</span> $wpdb;

    $id_sql        = <span class="hljs-string">"<span class="hljs-subst">{$wpdb-&gt;posts}</span>.ID = wpml_translations.element_id"</span>;
    $post_type_sql = <span class="hljs-string">"AND wpml_translations.element_type = CONCAT('post_', <span class="hljs-subst">{$wpdb-&gt;posts}</span>.post_type)"</span>;

    <span class="hljs-keyword">if</span> ( ! str_contains( $query, $id_sql ) || ! str_contains( $query, $post_type_sql ) ) {
        <span class="hljs-keyword">return</span> $query;
    }

    $query = str_replace( $post_type_sql, <span class="hljs-string">''</span>, $query );

    <span class="hljs-keyword">return</span> $query;
}

add_filter( <span class="hljs-string">'query'</span>, <span class="hljs-string">'matt_watson_remove_post_type_sql_search'</span> );
</code></pre>
<h2 id="heading-4-xdebug-profiling-for-slow-plugins">4. XDebug Profiling for Slow Plugins</h2>
<p>Plugins are a key feature of WordPress, but they can also be a source of performance bottlenecks. Some plugins load unnecessary resources or execute inefficient PHP processes, significantly slowing down your site’s response time. Using tools like XDebug allows you to profile your PHP processes, identifying which plugins or theme components are hogging resources.</p>
<p>Once you know which plugins are the main offenders, you can either disable them, find more efficient alternatives, or work on optimizing them directly. This process can greatly improve your Time to First Byte (TTFB) and overall site speed.</p>
<h2 id="heading-5-leveraging-object-caching">5. Leveraging Object Caching</h2>
<p>WordPress allows you to cache objects, reducing the need for repeated database queries, but excessive autoloaded options can overwhelm your cache. When too many options are autoloaded, performance can degrade, especially if your hosting provider disables object caching altogether. By controlling which options are autoloaded, you can keep your cache lean and efficient, improving your site’s performance.</p>
<p>Here’s a snippet that prevents specific options from being autoloaded, reducing unnecessary load on the cache and improving database efficiency:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_set_autoload_to_no</span>(<span class="hljs-params"> $option </span>) </span>{
    <span class="hljs-keyword">global</span> $wpdb;

    $autoloaded_options = [<span class="hljs-string">'permalink-manager-uris'</span>, <span class="hljs-comment">/* other options */</span>];

    <span class="hljs-keyword">if</span> ( ! in_array( $option, $autoloaded_options ) ) {
        <span class="hljs-keyword">return</span>;
    }

    $wpdb-&gt;update(
        $wpdb-&gt;prefix . <span class="hljs-string">"options"</span>, 
        [<span class="hljs-string">'autoload'</span> =&gt; <span class="hljs-string">'no'</span>], 
        [<span class="hljs-string">'option_name'</span> =&gt; $option]
    );
}

add_action( <span class="hljs-string">'updated_option'</span>, <span class="hljs-string">'matt_watson_set_autoload_to_no'</span> );
add_action( <span class="hljs-string">'added_option'</span>, <span class="hljs-string">'matt_watson_set_autoload_to_no'</span> );
</code></pre>
<h2 id="heading-6-advanced-conditional-plugin-loading-for-elementor">6. Advanced Conditional Plugin Loading for Elementor</h2>
<p>Some plugins, like Elementor, can dramatically increase page load times if loaded on every page, even those that don’t require their functionality. By conditionally loading these plugins only when necessary, you can significantly reduce the load on your server and improve page speed for non-Elementor pages.</p>
<p>Here’s a detailed example of how to conditionally disable Elementor and its dependencies unless needed, such as for specific post types or Elementor edit modes:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_restrict_elementor_loading</span>(<span class="hljs-params"> $plugins </span>) : <span class="hljs-title">array</span> </span>{
    <span class="hljs-keyword">if</span> ( is_admin() || wp_doing_ajax() ) {
        <span class="hljs-keyword">return</span> $plugins;
    }

    <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">isset</span>( $_GET[<span class="hljs-string">'elementor-preview'</span>] ) ) {
        <span class="hljs-keyword">return</span> $plugins;
    }

    $path      = $_SERVER[<span class="hljs-string">'REQUEST_URI'</span>];
    $path_base = basename( $path );

    $rest_prefix         = trailingslashit( rest_get_url_prefix() );
    $is_rest_api_request = strpos( $path, $rest_prefix ) !== <span class="hljs-literal">false</span>;

    <span class="hljs-keyword">if</span> ( $is_rest_api_request ) {
        <span class="hljs-keyword">return</span> $plugins;
    }

    $post = get_page_by_path( $path_base, <span class="hljs-keyword">OBJECT</span>, [ <span class="hljs-string">'page'</span>, <span class="hljs-string">'getting_started_hub'</span> ] );
    $is_elementor_page = is_object( $post ) &amp;&amp; ! <span class="hljs-keyword">empty</span>( get_post_meta( $post-&gt;ID, <span class="hljs-string">'_elementor_edit_mode'</span>, <span class="hljs-literal">true</span> ) );

    <span class="hljs-keyword">if</span> ( $is_elementor_page ) {
        <span class="hljs-keyword">return</span> $plugins;
    }

    $remove_plugins = [
        <span class="hljs-string">'elementor/elementor.php'</span>,
        <span class="hljs-string">'elementor-pro/elementor-pro.php'</span>,
        <span class="hljs-string">'elemental-menu/elemental-menu.php'</span>,
        <span class="hljs-string">'unlimited-elements-for-elementor-premium/unlimited-elements-pro.php'</span>,
    ];

    <span class="hljs-keyword">foreach</span> ( $remove_plugins <span class="hljs-keyword">as</span> $remove_plugin ) {
        $key = array_search( $remove_plugin , $plugins );
        <span class="hljs-keyword">unset</span>( $plugins[ $key ] );
    }

    <span class="hljs-keyword">return</span> $plugins;
}

add_filter( <span class="hljs-string">'option_active_plugins'</span>, <span class="hljs-string">'matt_watson_restrict_elementor_loading'</span> );
</code></pre>
<h2 id="heading-7-remove-wpml-distinct-strings-in-english">7. Remove WPML Distinct Strings in English</h2>
<p>On a multilingual site powered by WPML, WordPress often loads distinct language strings, even when browsing the site in English. Since your default language is likely English, loading these strings is unnecessary and adds extra load to your database. By removing the query for distinct strings when viewing the English version, you can improve database performance and speed up page load times.</p>
<p>This code snippet stops WPML from loading distinct language strings on English pages, reducing the number of queries executed and optimizing site performance for English-speaking users:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_remove_wpml_distinct_strings_in_english</span>(<span class="hljs-params"> $query </span>) </span>{
    <span class="hljs-keyword">global</span> $wpdb;

    <span class="hljs-keyword">if</span> ( is_admin() ) {
        <span class="hljs-keyword">return</span> $query;
    }

    $path = $_SERVER[<span class="hljs-string">'REQUEST_URI'</span>];
    $path = explode( <span class="hljs-string">'/'</span>, $path );
    $path = array_filter( $path );
    $lang = reset( $path );

    <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">isset</span>( $lang ) &amp;&amp; in_array( $lang, [ <span class="hljs-string">'es'</span>, <span class="hljs-string">'fr'</span>, <span class="hljs-string">'it'</span>, <span class="hljs-string">'de'</span>, <span class="hljs-string">'br'</span> ] ) ) {
        <span class="hljs-keyword">return</span> $query;
    }

    $id_sql = <span class="hljs-string">"FROM <span class="hljs-subst">{$wpdb-&gt;prefix}</span>icl_strings"</span>;

    <span class="hljs-keyword">if</span> ( <span class="hljs-literal">false</span> === strpos( $query, $id_sql ) &amp;&amp; <span class="hljs-literal">false</span> === strpos( $query, <span class="hljs-string">'DISTINCT'</span> ) ) {
        <span class="hljs-keyword">return</span> $query;
    }

    <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>;
}

add_filter( <span class="hljs-string">'query'</span>, <span class="hljs-string">'matt_watson_remove_wpml_distinct_strings_in_english'</span> );
</code></pre>
<hr />
<p>These seven tips may seem small individually, but combined, they can lead to significant performance improvements for your WordPress site. From chunking arrays and optimizing database queries to conditionally loading plugins and disabling unnecessary features, each tweak helps ensure your site runs as fast and efficiently as possible.</p>
]]></content:encoded></item><item><title><![CDATA[7 Essential WordPress Tips to Improve SEO and Customise Your Site Easily]]></title><description><![CDATA[WordPress development comes with various challenges, from managing multisite setups to customising the interface and optimising performance. This listicle dives into practical tips for enhancing SEO and customising the WordPress interface.
(I’ve gath...]]></description><link>https://mattwatson.blog/7-essential-wordpress-tips-to-improve-seo-and-customise-your-site-easily</link><guid isPermaLink="true">https://mattwatson.blog/7-essential-wordpress-tips-to-improve-seo-and-customise-your-site-easily</guid><category><![CDATA[Customise]]></category><category><![CDATA[WordPress]]></category><category><![CDATA[tips]]></category><category><![CDATA[listicle]]></category><category><![CDATA[SEO]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Wed, 16 Oct 2024 22:04:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729116279545/d6f8b1a0-ec03-42a6-bab0-8ef55322ac2a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>WordPress development comes with various challenges, from managing multisite setups to customising the interface and optimising performance. This listicle dives into practical tips for enhancing SEO <em>and</em> customising the WordPress interface.</p>
<p><em>(I’ve gathered these trusty code snippets from some of my older blog posts. Some might be a bit old, but they might still come in handy.)</em></p>
<h2 id="heading-seo-and-search-engine-optimisation-tips">SEO and Search Engine Optimisation Tips</h2>
<p>Optimising your site for search engines is crucial. These tips focus on controlling what search engines can access, helping you maintain a clean SEO profile.</p>
<h3 id="heading-1-add-custom-rules-to-robotstxt-in-wordpress">1. Add Custom Rules to robots.txt in WordPress</h3>
<p>When search engines like Google crawl your site, they can index everything by default, including files and sections you might not want to appear in search results. For instance, you might not want certain file types, such as PDFs, to be indexed by Google.</p>
<p>This snippet allows you to add custom rules to the <code>robots.txt</code> file in WordPress, preventing search engines from indexing specific files. By controlling what is indexed, you maintain a cleaner and more relevant SEO profile.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_disallow_pdf_index</span>(<span class="hljs-params">$output, $public</span>) </span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-string">'1'</span> !== $public || ! is_string( $output )) {
        <span class="hljs-keyword">return</span> $output;
    }
    $output .= <span class="hljs-string">"Disallow: *.pdf
"</span>;
    <span class="hljs-keyword">return</span> $output;
}
add_filter(<span class="hljs-string">'robots_txt'</span>, <span class="hljs-string">'matt_watson_disallow_pdf_index'</span>, <span class="hljs-number">0</span>, <span class="hljs-number">2</span>);
</code></pre>
<h3 id="heading-2-add-blog-to-post-slug">2. Add /blog to Post Slug</h3>
<p>For larger websites, organising content effectively can significantly improve SEO and user experience. One way to do this is by adding <code>/blog</code> to your post slugs, which helps structure your URLs more clearly, particularly on content-heavy sites.</p>
<p>This snippet allows you to customise the post slug in WordPress by adding <code>/blog</code>, improving the overall content hierarchy and making it easier for search engines to understand the structure of your site.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_add_blog_to_post_slug</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">if</span> ( post_type_exists( <span class="hljs-string">'post'</span> ) ) {
        <span class="hljs-keyword">return</span>;
    }
    register_post_type(
        <span class="hljs-string">'post'</span>,
        [
            <span class="hljs-string">'rewrite'</span>  =&gt; [ <span class="hljs-string">'slug'</span> =&gt; <span class="hljs-string">'blog'</span> ],
            <span class="hljs-string">'supports'</span> =&gt; [ <span class="hljs-string">'title'</span>, <span class="hljs-string">'editor'</span>, <span class="hljs-string">'author'</span>, <span class="hljs-string">'thumbnail'</span>, <span class="hljs-string">'excerpt'</span>, <span class="hljs-string">'comments'</span> ]
        ]
    );
}
add_action( <span class="hljs-string">'init'</span>, <span class="hljs-string">'matt_watson_add_blog_to_post_slug'</span>, <span class="hljs-number">1</span> );
</code></pre>
<h3 id="heading-3-add-custom-rewrite-tags-and-customise-author-urls-with-a-dynamic-prefix"><strong>3. Add Custom Rewrite Tags and Customise Author URLs with a Dynamic Prefix</strong></h3>
<p>Customising your URLs can significantly improve SEO by making them more descriptive. This snippet adds a custom rewrite tag, %faculty%, allowing you to modify URLs for posts (in this case, journal posts) to include both an author’s name and another dynamic value, like a category or department. This makes URLs more informative, which helps search engines better understand the context of the page.</p>
<p>In the example, %faculty% is replaced by a static value ('my-test-faculty'), but it could be dynamically set from post metadata. The %author% tag is automatically replaced with the author’s nicename, creating a URL structure like:</p>
<pre><code class="lang-plaintext">/my-test-faculty/john/journal/
</code></pre>
<p>This improves readability for users and search engines, benefiting your SEO.</p>
<pre><code class="lang-php"><span class="hljs-comment">// Add a custom rewrite tag for 'faculty'</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_register_rewrite_tags</span>(<span class="hljs-params"></span>) </span>{
    add_rewrite_tag( <span class="hljs-string">'%faculty%'</span>, <span class="hljs-string">'([^&amp;]+)'</span> ); <span class="hljs-comment">// We add the faculty tag for URL rewrites</span>
}
add_action( <span class="hljs-string">'init'</span>, <span class="hljs-string">'matt_watson_register_rewrite_tags'</span> );

<span class="hljs-comment">// Modify the permalink structure for the 'journal' post type</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_post_type_link</span>(<span class="hljs-params"> $post_link, $post, $leavename, $sample </span>) </span>{

    <span class="hljs-comment">// Check that the post is of type 'journal' and return early if not</span>
    <span class="hljs-keyword">if</span> ( <span class="hljs-string">'journal'</span> !== get_post_type( $post ) ) {
        <span class="hljs-keyword">return</span> $post_link;
    }

    <span class="hljs-comment">// Get the author's nicename to insert into the URL</span>
    $authordata = get_userdata( $post-&gt;post_author );
    <span class="hljs-keyword">if</span> ( ! $authordata ) {
        <span class="hljs-keyword">return</span> $post_link;
    }
    $author = $authordata-&gt;user_nicename;

    <span class="hljs-comment">// Replace the %author% tag in the URL with the actual author's nicename</span>
    $post_link = str_replace( <span class="hljs-string">'%author%'</span>, $author, $post_link );

    <span class="hljs-comment">// Replace the %faculty% tag with a static value or a dynamic value (this can be replaced with post meta or other logic)</span>
    <span class="hljs-comment">// For now, using a static test value 'my-test-faculty'</span>
    $post_link = str_replace( <span class="hljs-string">'%faculty%'</span>, <span class="hljs-string">'my-test-faculty'</span>, $post_link );

    <span class="hljs-comment">// Example of the resulting URL structure:</span>
    <span class="hljs-comment">// /my-test-faculty/john/journal/</span>

    <span class="hljs-keyword">return</span> $post_link;
}
add_filter( <span class="hljs-string">'post_type_link'</span>, <span class="hljs-string">'matt_watson_post_type_link'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">4</span> );
</code></pre>
<h2 id="heading-customisation-tips-for-the-wordpress-interface">Customisation Tips for the WordPress Interface</h2>
<p>Customising the WordPress interface can improve usability for site admins and users alike. These tips will help you add custom styles, controls, and settings to WordPress with ease.</p>
<h3 id="heading-4-disable-the-admin-bar-for-non-admins">4. Disable the Admin Bar for Non-Admins</h3>
<p>The WordPress admin bar can be distracting for non-admin users, especially if it’s not necessary for their experience. Disabling the admin bar for these users can simplify the frontend interface and reduce clutter.</p>
<p>This snippet provides a solution for hiding the admin bar for non-admin users, with a guard to ensure administrators and backend users still see it.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_remove_admin_bar</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">if</span> ( current_user_can( <span class="hljs-string">'administrator'</span> ) || is_admin() ) {
        <span class="hljs-keyword">return</span>;
    }
    show_admin_bar( <span class="hljs-literal">false</span> );
}
add_action( <span class="hljs-string">'after_setup_theme'</span>, <span class="hljs-string">'matt_watson_remove_admin_bar'</span> );
</code></pre>
<h3 id="heading-5-custom-conditional-statements-for-wordpress-stylesheets">5. Custom Conditional Statements for WordPress Stylesheets</h3>
<p>Sometimes, you need to load specific stylesheets based on certain conditions, such as the browser version. This is particularly useful for older browsers like Internet Explorer.</p>
<p>This snippet enables conditional loading of stylesheets using the <code>style_loader_tag</code> filter. A guard ensures the custom logic only applies to the desired stylesheet.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_custom_style_loader_tag</span>(<span class="hljs-params"> $tag, $handle </span>) </span>{
    <span class="hljs-keyword">if</span> ( <span class="hljs-string">'ie-8-plus'</span> !== $handle ) {
        <span class="hljs-keyword">return</span> $tag;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-string">"&lt;!--[if gt IE 8]&gt;&lt;!--&gt;<span class="hljs-subst">$tag</span>&lt;![endif]--&gt;"</span>;
}
add_filter( <span class="hljs-string">'style_loader_tag'</span>, <span class="hljs-string">'matt_watson_custom_style_loader_tag'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span> );
</code></pre>
<h3 id="heading-6-extend-the-wordpress-customiser-using-javascript">6. Extend the WordPress Customiser Using JavaScript</h3>
<p>The WordPress Customiser is a powerful tool for making theme adjustments, but you may need to extend its functionality to create custom sections or panels.</p>
<p>This snippet demonstrates how to extend the Customiser using JavaScript, providing more flexibility in tailoring it to fit your theme or plugin’s needs.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { customize } = wp;

customize.bind( <span class="hljs-string">'ready'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">if</span> (!customize.panel) {
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> panelKey = <span class="hljs-string">'mattwatson-customizer-panel'</span>;
    <span class="hljs-keyword">const</span> sectionKey = <span class="hljs-string">'mattwatson-customizer-section'</span>;

    customize.panel.add( <span class="hljs-keyword">new</span> customize.Panel( panelKey, {
        <span class="hljs-attr">title</span>: __( <span class="hljs-string">'Matt Watson Panel'</span>, <span class="hljs-string">'mattwatson'</span> ),
        <span class="hljs-attr">priority</span>: <span class="hljs-number">1000</span>,
     } ) );

    customize.section.add( <span class="hljs-keyword">new</span> customize.Section( sectionKey, {
        <span class="hljs-attr">title</span>: __(<span class="hljs-string">'Matt Watson Section'</span>, <span class="hljs-string">'mattwatson'</span>),
        <span class="hljs-attr">panel</span>: panelKey,
    } ) );
});
</code></pre>
<h3 id="heading-7-add-a-custom-settings-link-to-your-wordpress-plugin">7. Add a Custom Settings Link to Your WordPress Plugin</h3>
<p>Adding a settings link to your plugin’s listing in the WordPress admin interface can make it easier for users to find and adjust the plugin’s settings.</p>
<p>This snippet shows how to add a custom settings link directly in the plugin list, improving the user experience by reducing the number of steps needed to access settings.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_plugin_settings_link</span>(<span class="hljs-params"> $links </span>) </span>{
    <span class="hljs-keyword">if</span> ( ! is_array( $links ) ) {
        <span class="hljs-keyword">return</span> $links;
    }
    $label = esc_html__( <span class="hljs-string">'Settings'</span>, <span class="hljs-string">'mattwatson'</span> );
    $slug  = <span class="hljs-string">'mattwatson_plugin_settings'</span>;

    array_unshift( $links, <span class="hljs-string">"&lt;a href='<span class="hljs-subst">$slug</span>'&gt;<span class="hljs-subst">$label</span>&lt;/a&gt;"</span> );

    <span class="hljs-keyword">return</span> $links;
}
add_action( <span class="hljs-string">'plugin_action_links_'</span> . plugin_basename(<span class="hljs-keyword">__FILE__</span>), <span class="hljs-string">'matt_watson_plugin_settings_link'</span>, <span class="hljs-number">10</span> );
</code></pre>
<hr />
<p>By applying these SEO and interface customisation tips, you can enhance your WordPress site’s performance, user experience, and overall flexibility. Whether you're managing large amounts of content or improving the backend interface, these snippets provide efficient solutions for common WordPress challenges.</p>
]]></content:encoded></item><item><title><![CDATA[8 Essential Code Snippets to Master the WordPress Block Editor]]></title><description><![CDATA[Ah, the Block Editor (formerly Gutenberg) — a source of joy for some and mild frustration for others. Whether you're still getting the hang of it or looking to make it work the way you want, these code snippets will help you navigate some common issu...]]></description><link>https://mattwatson.blog/8-essential-code-snippets-to-master-the-wordpress-block-editor</link><guid isPermaLink="true">https://mattwatson.blog/8-essential-code-snippets-to-master-the-wordpress-block-editor</guid><category><![CDATA[Block Editor]]></category><category><![CDATA[WordPress]]></category><category><![CDATA[Gutenberg]]></category><category><![CDATA[tips]]></category><category><![CDATA[listicle]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Wed, 16 Oct 2024 21:21:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729113683663/1fcfe224-215e-4fec-9403-70b0148ba98f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ah, the Block Editor (formerly Gutenberg) — a source of joy for some and mild frustration for others. Whether you're still getting the hang of it or looking to make it work the way you want, these code snippets will help you navigate some common issues.</p>
<p><em>(I’ve gathered these trusty code snippets from some of my older blog posts. Some might be a bit old, but they might still come in handy.)</em></p>
<h2 id="heading-1-busting-cache-in-getentityrecords">1. Busting Cache in <code>getEntityRecords</code></h2>
<p>When working with dynamic blocks that rely on fetching posts or data, you may encounter issues where outdated data is cached, making your content appear stale. This can be particularly problematic when you’re fetching frequently updated content. The <code>getEntityRecords</code> cache in the Block Editor can sometimes keep old data around longer than you’d like.</p>
<p>This snippet shows you how to bust the cache for <code>getEntityRecords</code>, ensuring that the content on your site is always up-to-date, especially after creating or editing posts.</p>
<pre><code class="lang-javascript">wp.data.dispatch(<span class="hljs-string">'core'</span>).receiveEntityRecords(<span class="hljs-string">'postType'</span>, <span class="hljs-string">'post'</span>, [], {}, <span class="hljs-literal">true</span>);
</code></pre>
<h2 id="heading-2-hooking-into-the-save-action">2. Hooking into the Save Action</h2>
<p>In some situations, you may want to trigger additional actions whenever a post is saved, such as updating external services, sending notifications, or performing some custom processing. Unfortunately, the Block Editor doesn’t provide a simple hook to intercept the save action by default.</p>
<p>This snippet listens for the post-saving process and allows you to hook into that event, enabling you to run custom code every time a post is saved, ensuring you can automate processes as needed without manual intervention.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { select, subscribe } <span class="hljs-keyword">from</span> <span class="hljs-string">'@wordpress/data'</span>;

subscribe(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> isSavingPost = select(<span class="hljs-string">'core/editor'</span>).isSavingPost();
    <span class="hljs-keyword">const</span> isAutosavingPost = select(<span class="hljs-string">'core/editor'</span>).isAutosavingPost();

    <span class="hljs-keyword">if</span> (isAutosavingPost &amp;&amp; !isSavingPost) {
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-comment">// Your custom action here</span>
});
</code></pre>
<h2 id="heading-3-creating-custom-block-categories">3. Creating Custom Block Categories</h2>
<p>With a growing number of blocks in the Block Editor, it can become increasingly difficult to locate specific blocks, especially custom ones you’ve developed. By default, all blocks are placed into general categories, making it harder to organise them.</p>
<p>This snippet demonstrates how to create a custom block category, allowing you to organise your blocks better. It not only improves the workflow for developers but also helps users find and use your blocks more efficiently.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_block_categories</span>(<span class="hljs-params">$categories</span>) </span>{
    <span class="hljs-keyword">return</span> array_merge($categories, [
        [
            <span class="hljs-string">'slug'</span>  =&gt; <span class="hljs-string">'matt-watson-blocks'</span>,
            <span class="hljs-string">'title'</span> =&gt; __(<span class="hljs-string">'Matt Watson Blocks'</span>, <span class="hljs-string">'mattwatson'</span>),
        ],
    ]);
}
add_filter(<span class="hljs-string">'block_categories_all'</span>, <span class="hljs-string">'matt_watson_block_categories'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>);
</code></pre>
<h2 id="heading-4-fetching-posts-with-getentityrecord">4. Fetching Posts with <code>getEntityRecord</code></h2>
<p>Fetching individual posts by their ID in the Block Editor can be tricky for developers accustomed to using the <code>get_post()</code> function in classic WordPress. The Block Editor uses <code>getEntityRecord()</code> instead, which offers a more modern approach for fetching data.</p>
<p>This snippet shows how to use <code>getEntityRecord()</code> to retrieve a specific post by its ID in the Block Editor, making the transition from classic WordPress development smoother.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> post = wp.data.select(<span class="hljs-string">'core'</span>).getEntityRecord(<span class="hljs-string">'postType'</span>, <span class="hljs-string">'post'</span>, <span class="hljs-number">42</span>);
</code></pre>
<h2 id="heading-5-querying-posts-with-getentityrecords">5. Querying Posts with <code>getEntityRecords</code></h2>
<p>If you’ve ever missed the flexibility of <code>WP_Query</code> when working in the Block Editor, <code>getEntityRecords()</code> can provide a similar experience. This function allows you to query posts and custom post types using parameters such as post status or pagination.</p>
<p>This snippet demonstrates how to retrieve posts based on a specific status and limit the number of posts returned, offering an efficient way to handle content querying in the Block Editor.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> posts = wp.data.select(<span class="hljs-string">'core'</span>).getEntityRecords(<span class="hljs-string">'postType'</span>, <span class="hljs-string">'post'</span>, {
    <span class="hljs-attr">status</span>: <span class="hljs-string">'draft'</span>,
    <span class="hljs-attr">per_page</span>: <span class="hljs-number">2</span>,
});
</code></pre>
<h2 id="heading-6-updating-post-meta-with-editpost">6. Updating Post Meta with <code>editPost</code></h2>
<p>In the past, updating post meta in WordPress typically involved using PHP’s <code>update_post_meta()</code> function. With the Block Editor, you can now update post meta directly using the <code>editPost</code> function from JavaScript.</p>
<p>This snippet shows how to update the metadata of a post within the Block Editor interface, keeping your workflow modern and efficient without needing to switch back to server-side PHP.</p>
<pre><code class="lang-javascript">wp.data.dispatch(<span class="hljs-string">'core/editor'</span>).editPost({
    <span class="hljs-attr">meta</span>: {
        <span class="hljs-attr">matt_watson_meta_key_example</span>: <span class="hljs-string">'Example Value'</span>,
    },
});
</code></pre>
<h2 id="heading-7-find-posts-with-specific-blocks-in-wordpress">7. Find Posts with Specific Blocks in WordPress</h2>
<p>If you need to identify posts containing a specific block, this snippet can help. It runs a <code>WP_Query</code> to search for posts that include a block by its namespace and block name. This can be useful when you want to apply changes or track down posts that contain a certain block across your site.</p>
<pre><code class="lang-php">$posts = <span class="hljs-keyword">new</span> WP_Query([
    <span class="hljs-string">'post_type'</span>   =&gt; <span class="hljs-string">'any'</span>,
    <span class="hljs-string">'post_status'</span> =&gt; <span class="hljs-string">'any'</span>,
    <span class="hljs-string">'s'</span>           =&gt; <span class="hljs-string">'wp:some-block-namespace/block-name'</span>,
]);
</code></pre>
<h2 id="heading-8-add-font-family-support-to-the-core-heading-block">8. Add Font Family Support to the Core Heading Block</h2>
<p>Extending core blocks can improve design flexibility without needing to create new custom blocks. By using the <code>block_type_metadata_settings</code> filter, this snippet enables font family support for the core Heading block. It modifies the block settings to allow users to select custom font families, making your Heading blocks more versatile in terms of typography options.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add_font_family_support</span>(<span class="hljs-params"><span class="hljs-keyword">array</span> $settings, <span class="hljs-keyword">array</span> $metadata</span>) </span>{
    <span class="hljs-keyword">if</span> ($metadata[<span class="hljs-string">'name'</span>] !== <span class="hljs-string">'core/heading'</span>) {
        <span class="hljs-keyword">return</span> $settings;
    }

    $settings[<span class="hljs-string">'supports'</span>][<span class="hljs-string">'typography'</span>][<span class="hljs-string">'__experimentalFontFamily'</span>] = <span class="hljs-literal">true</span>;
    $settings[<span class="hljs-string">'supports'</span>][<span class="hljs-string">'typography'</span>][<span class="hljs-string">'__experimentalDefaultControls'</span>][<span class="hljs-string">'fontFamily'</span>] = <span class="hljs-literal">true</span>;

    <span class="hljs-keyword">return</span> $settings;
}

add_filter(<span class="hljs-string">'block_type_metadata_settings'</span>, <span class="hljs-string">'add_font_family_support'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>);
</code></pre>
<hr />
<p>Whether you're fixing outdated data in blocks or customising your Block Editor environment, these snippets will make your work easier and more efficient. A well-optimised Block Editor can significantly improve your workflow, saving you time and frustration in the process.</p>
]]></content:encoded></item><item><title><![CDATA[7 Code Tweaks to Tame WordPress Multisite and Multilingual Mayhem]]></title><description><![CDATA[Ah, WordPress Multisite and Multilingual (in this case WPML) — because managing one site wasn’t quite complex enough! Whether you’re trying to make Bing respect your multilingual site or want to simplify activation steps in Multisite, these seven cod...]]></description><link>https://mattwatson.blog/7-code-tweaks-to-tame-wordpress-multisite-and-multilingual-mayhem</link><guid isPermaLink="true">https://mattwatson.blog/7-code-tweaks-to-tame-wordpress-multisite-and-multilingual-mayhem</guid><category><![CDATA[WPML]]></category><category><![CDATA[WordPress]]></category><category><![CDATA[yoast]]></category><category><![CDATA[Bing]]></category><category><![CDATA[tips]]></category><category><![CDATA[listicle]]></category><category><![CDATA[Multisite]]></category><category><![CDATA[multilingual]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Wed, 16 Oct 2024 21:02:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729112540316/48110d94-0ed0-47ba-b0db-321cec4867c2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ah, WordPress Multisite and Multilingual (in this case WPML) — because managing one site wasn’t quite complex enough! Whether you’re trying to make Bing respect your multilingual site or want to simplify activation steps in Multisite, these seven code tweaks will keep you sane.</p>
<p><em>(I’ve gathered these trusty code snippets from some of my older blog posts. Some might be a bit old, but they might still come in handy.)</em></p>
<h2 id="heading-1-ensure-bing-knows-your-languages">1. Ensure Bing Knows Your Languages</h2>
<p>Bing doesn’t always automatically recognise the different languages used on your multilingual website. This can affect how well your pages perform in search results, particularly in non-English languages.</p>
<p>This code snippet solves this by adding the appropriate language meta tags to your site’s header, allowing Bing to understand the various languages your site uses and serve the right content to users.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_bing_compatible_wpml_header</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">if</span> ( ! function_exists( <span class="hljs-string">'icl_get_languages'</span> ) ) {
        <span class="hljs-keyword">return</span>;
    }

    $langs = icl_get_languages();
    <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">empty</span>( $langs ) ) {
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">foreach</span> ( $langs <span class="hljs-keyword">as</span> $lang ) {
        <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;meta http-equiv="content-language" content="'</span> . esc_attr( $lang[<span class="hljs-string">'code'</span>] ) . <span class="hljs-string">'"/&gt;'</span> . <span class="hljs-string">"
"</span>;
    }
}
add_action( <span class="hljs-string">'wp_head'</span>, <span class="hljs-string">'matt_watson_bing_compatible_wpml_header'</span>, <span class="hljs-number">0</span> );
</code></pre>
<h2 id="heading-2-wpml-bing-a-match-made-in-headers">2. WPML + Bing = A Match Made in Headers</h2>
<p>When your WordPress site supports multiple languages, it’s important to tell search engines the language of each individual page to prevent them from misinterpreting your content. This code snippet helps by adding <code>Content-Language</code> meta tags to your posts.</p>
<p>The snippet checks the current post and outputs the language code only if it matches the current language context. This ensures that Bing correctly identifies the language of each page it crawls.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_wpml_bing_compatible_header</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">if</span> ( ! function_exists( <span class="hljs-string">'icl_get_languages'</span> ) || ! is_singular() ) {
        <span class="hljs-keyword">return</span>;
    }

    $langs = icl_get_languages();
    <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">empty</span>( $langs ) ) {
        <span class="hljs-keyword">return</span>;
    }

    $current_post_id = get_the_ID();

    <span class="hljs-keyword">foreach</span> ( $langs <span class="hljs-keyword">as</span> $lang ) {
        $id = icl_object_id( $current_post_id, get_post_type(), <span class="hljs-literal">false</span>, $lang[<span class="hljs-string">'code'</span>] );
        <span class="hljs-keyword">if</span> ( ! $id || $id !== $current_post_id ) {
            <span class="hljs-keyword">continue</span>;
        }
        <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;meta http-equiv="content-language" content="'</span> . esc_attr( $lang[<span class="hljs-string">'code'</span>] ) . <span class="hljs-string">'"&gt;'</span> . PHP_EOL;
    }
}
add_action( <span class="hljs-string">'wp_head'</span>, <span class="hljs-string">'matt_watson_wpml_bing_compatible_header'</span>, <span class="hljs-number">0</span> );
</code></pre>
<h2 id="heading-3-set-the-right-content-language-for-browsers">3. Set the Right Content Language for Browsers</h2>
<p>Browsers and crawlers rely on server responses to determine the correct language of your content, but WPML doesn’t automatically send this information in the HTTP headers. This can lead to browsers and crawlers incorrectly interpreting the language of your pages, especially when serving non-default languages.</p>
<p>The provided code snippet adds the correct <code>Content-Language</code> header to your server responses, ensuring that both browsers and search engines accurately detect the language of the page.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_wpml_set_content_langauge</span>(<span class="hljs-params"> $headers </span>) </span>{
    <span class="hljs-keyword">if</span> ( is_admin() || ! defined( <span class="hljs-string">'ICL_LANGUAGE_CODE'</span> ) ) {
        <span class="hljs-keyword">return</span> $headers;
    }

    $headers[<span class="hljs-string">'Content-Language'</span>] = ICL_LANGUAGE_CODE;
    <span class="hljs-keyword">return</span> $headers;
}
add_filter( <span class="hljs-string">'wp_headers'</span>, <span class="hljs-string">'matt_watson_wpml_set_content_langauge'</span>, <span class="hljs-number">0</span> );
</code></pre>
<h2 id="heading-4-add-alternate-language-links-to-yoast-sitemaps">4. Add Alternate Language Links to Yoast Sitemaps</h2>
<p>When using Yoast SEO to manage sitemaps on a multilingual site, it’s crucial to inform search engines about the alternative language versions of each page. However, Yoast doesn’t automatically include these alternate links in the sitemap when WPML is involved.</p>
<p>The following code snippet enhances Yoast’s sitemaps by adding alternate language links. This tells search engines that each page has a corresponding version in other languages, helping them to index the right content for each region.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_yoast_wpml_alternate_links_to_sitemap</span>(<span class="hljs-params"> $url, $type, $post </span>) </span>{
    <span class="hljs-keyword">if</span> ( ! function_exists( <span class="hljs-string">'icl_get_languages'</span> ) || <span class="hljs-keyword">empty</span>( $post ) ) {
        <span class="hljs-keyword">return</span> $url;
    }

    $langs = icl_get_languages();
    <span class="hljs-keyword">if</span> ( <span class="hljs-keyword">empty</span>( $langs ) ) {
        <span class="hljs-keyword">return</span> $url;
    }

    $id          = ( <span class="hljs-string">'post'</span> === $type ) ? $post-&gt;ID : $post-&gt;term_id;
    $object_type = ( <span class="hljs-string">'post'</span> === $type ) ? $post-&gt;post_type : $post-&gt;taxonomy;

    <span class="hljs-keyword">foreach</span> ( $langs <span class="hljs-keyword">as</span> $lang ) {
        $translated_id = icl_object_id( $id, $object_type, <span class="hljs-literal">false</span>, $lang[<span class="hljs-string">'code'</span>] );
        <span class="hljs-keyword">if</span> ( ! $translated_id || $translated_id === $id ) {
            <span class="hljs-keyword">continue</span>;
        }
        $link = ( <span class="hljs-string">'post'</span> === $type ) ? get_the_permalink( $translated_id ) : get_term_link( $translated_id );
        $url[<span class="hljs-string">'wpml'</span>][<span class="hljs-string">'xhtml:link'</span>][] = <span class="hljs-string">'&lt;xhtml:link rel="alternate" hreflang="'</span> . esc_attr( $lang[<span class="hljs-string">'code'</span>] ) . <span class="hljs-string">'" href="'</span> . esc_url( $link ) . <span class="hljs-string">'"/&gt;'</span>;
    }

    <span class="hljs-keyword">return</span> $url;
}
add_filter( <span class="hljs-string">'wpseo_sitemap_entry'</span>, <span class="hljs-string">'matt_watson_yoast_wpml_alternate_links_to_sitemap'</span>, <span class="hljs-number">100</span>, <span class="hljs-number">3</span> );
</code></pre>
<h2 id="heading-5-sitemaps-per-translation-with-wpml-and-yoast-seo">5. Sitemaps per Translation with WPML and Yoast SEO</h2>
<p>When managing a multilingual site, generating a single sitemap might not be enough, as each language version of your site should have its own sitemap. This ensures search engines fully crawl all translations and index the correct language versions.</p>
<p>The following code snippet modifies the Yoast SEO sitemap query to generate separate sitemaps for each language, based on the current language context of WPML. This allows search engines to accurately map out each language version of your content.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_yoast_wpml_sitemap_per_translation</span>(<span class="hljs-params"> $join, $type </span>) </span>{
    <span class="hljs-keyword">global</span> $wpdb, $sitepress;

    <span class="hljs-keyword">if</span> ( ! <span class="hljs-keyword">isset</span>( $sitepress ) ) {
        <span class="hljs-keyword">return</span> $join;
    }

    $lang = $sitepress-&gt;get_current_language();
    <span class="hljs-keyword">return</span> $wpdb-&gt;prepare(
        <span class="hljs-string">"JOIN <span class="hljs-subst">{$wpdb-&gt;prefix}</span>icl_translations ON element_id = ID AND element_type = %s AND language_code = %s"</span>,
        <span class="hljs-string">'post_'</span> . $type,
        $lang
    );
}
add_filter( <span class="hljs-string">'wpseo_posts_join'</span>, <span class="hljs-string">'matt_watson_yoast_wpml_sitemap_per_translation'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span> );
</code></pre>
<h2 id="heading-6-create-a-virtual-file-in-wordpress-multisite">6. Create a Virtual File in WordPress Multisite</h2>
<p>In a WordPress Multisite setup, you may need to generate virtual files that change based on the specific site being accessed. This could be useful for things like dynamic sitemaps or XML feeds.</p>
<p>The code snippet below adds a query variable and creates a custom rewrite rule to serve a virtual file dynamically. It ensures that each individual site in the Multisite network can have its own version of the virtual file.</p>
<p>The rewrites:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_sitemap_rewrites</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">global</span> $wp;
    $wp-&gt;add_query_var(<span class="hljs-string">'map'</span>);

    <span class="hljs-keyword">if</span> ( get_current_blog_id() == <span class="hljs-number">1</span> ) {
        add_rewrite_rule( <span class="hljs-string">'sitemap.xml$'</span>, <span class="hljs-string">'index.php?map=sitemap'</span>, <span class="hljs-string">'top'</span> );
    }

    <span class="hljs-keyword">if</span> ( get_current_blog_id() === get_id_from_blogname(<span class="hljs-string">'fr'</span>) ) {
        add_rewrite_rule( <span class="hljs-string">'^sitemap-fr.xml$'</span>, <span class="hljs-string">'index.php?map=sitemap-fr'</span>, <span class="hljs-string">'top'</span> );
    }
}
add_action( <span class="hljs-string">'init'</span>, <span class="hljs-string">'matt_watson_sitemap_rewrites'</span>, <span class="hljs-number">99</span> );
</code></pre>
<p>An example of a virtual file creation:</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_generate_sitemap</span>(<span class="hljs-params"></span>) </span>{
    $map = get_query_var( <span class="hljs-string">'map'</span> );

    <span class="hljs-keyword">if</span> ( ! $map ) {
        <span class="hljs-keyword">return</span>;
    }

    header( <span class="hljs-string">'Content-Type: application/xml; charset=utf-8'</span> );

    <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;?xml version="1.0" encoding="UTF-8"?&gt;'</span>;
    <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"&gt;'</span>;

    $posts = get_posts( [
        <span class="hljs-string">'post_type'</span>      =&gt; <span class="hljs-string">'post'</span>,
        <span class="hljs-string">'posts_per_page'</span> =&gt; <span class="hljs-number">-1</span>,
        <span class="hljs-string">'post_status'</span>    =&gt; <span class="hljs-string">'publish'</span>,
    ] );

    <span class="hljs-keyword">foreach</span> ( $posts <span class="hljs-keyword">as</span> $post ) {
        <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;url&gt;'</span>;
        <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;loc&gt;'</span> . get_permalink( $post ) . <span class="hljs-string">'&lt;/loc&gt;'</span>;
        <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;lastmod&gt;'</span> . get_the_modified_date( <span class="hljs-string">'Y-m-d'</span>, $post ) . <span class="hljs-string">'&lt;/lastmod&gt;'</span>;
        <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;/url&gt;'</span>;
    }

    <span class="hljs-keyword">echo</span> <span class="hljs-string">'&lt;/urlset&gt;'</span>;

    <span class="hljs-keyword">exit</span>;
}
add_action( <span class="hljs-string">'template_redirect'</span>, <span class="hljs-string">'matt_watson_generate_sitemap'</span> );
</code></pre>
<h2 id="heading-7-disable-the-wordpress-multisite-activation-step">7. Disable the WordPress Multisite Activation Step</h2>
<p>In a WordPress Multisite environment, the default process requires users to activate their accounts via an email confirmation link. This can be unnecessary if you're managing internal users or trusted accounts.</p>
<p>The following code disables the activation step, allowing users to be added and activated instantly without requiring an additional email verification.</p>
<pre><code class="lang-php"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">matt_watson_remove_wpmu_activation</span>(<span class="hljs-params"> $user_login, $user_email, $key </span>) </span>{
    wpmu_activate_signup( $key );
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
add_filter( <span class="hljs-string">'wpmu_signup_user_notification'</span>, <span class="hljs-string">'matt_watson_remove_wpmu_activation'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">3</span> );
</code></pre>
<hr />
<p>With these seven code tweaks, your WordPress Multisite and multilingual setup will be much easier to manage. From improving Bing’s understanding of your language setup to simplifying user registration, these snippets will help you streamline your operations and ensure your site runs smoothly across languages and networks.</p>
]]></content:encoded></item><item><title><![CDATA[WordPress Chronicles: My Journey Through Myths, Blocks, and Plugins]]></title><description><![CDATA[Over the years, I’ve been known to blog for various companies — whether it’s because they employed me or just invited me to contribute. So, in my ongoing quest to consolidate my scattered online ramblings into one place, here’s a collection of extern...]]></description><link>https://mattwatson.blog/wordpress-chronicles-my-journey-through-myths-blocks-and-plugins</link><guid isPermaLink="true">https://mattwatson.blog/wordpress-chronicles-my-journey-through-myths-blocks-and-plugins</guid><category><![CDATA[WordPress]]></category><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Mon, 14 Oct 2024 18:16:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728937176542/16baca2f-9bbf-470a-96aa-dab51eeeaab8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Over the years, I’ve been known to blog for various companies — whether it’s because they employed me or just invited me to contribute. So, in my ongoing quest to consolidate my scattered online ramblings into one place, here’s a collection of external blog posts. Some of these gems might be a bit dusty, but hey, they still hold value (even if they still mention jQuery).</p>
<hr />
<h2 id="heading-5-more-wordpress-myths-bustedhttpshumanmadecomblog5-more-wordpress-myths-busted"><a target="_blank" href="https://humanmade.com/blog/5-more-wordpress-myths-busted/">5 More WordPress Myths Busted</a></h2>
<p>I continued debunking more WordPress myths, tackling topics like scalability, e-commerce, multilingual support, and plugin quality. Whether it’s a small blog or a complex enterprise site, WordPress can handle it all.</p>
<hr />
<h2 id="heading-5-wordpress-myths-bustedhttpshumanmadecomblog5-wordpress-myths-busted"><a target="_blank" href="https://humanmade.com/blog/5-wordpress-myths-busted/">5 WordPress Myths Busted</a></h2>
<p>Common myths about WordPress include being insecure, bad for SEO, or only good for blogging. I dive into why these are just misconceptions and how proper hosting and management make WordPress secure, scalable, and highly versatile.</p>
<hr />
<h2 id="heading-wholesome-publishing-plugin-featured-on-wp-tavernhttpswptaverncomadd-per-block-notes-and-create-draft-blocks-with-the-wholesome-publishing-plugin"><a target="_blank" href="https://wptavern.com/add-per-block-notes-and-create-draft-blocks-with-the-wholesome-publishing-plugin">Wholesome Publishing Plugin Featured on WP Tavern</a></h2>
<p>My Wholesome Publishing plugin, designed to streamline editorial workflows in the WordPress block editor, was featured on WP Tavern. It highlights key features like per-block notes and draft blocks.</p>
<hr />
<h2 id="heading-creating-a-custom-block-for-wp-owlshttpswpowlscoarticlescreating-a-custom-block-for-wp-owls"><a target="_blank" href="https://wpowls.co/articles/creating-a-custom-block-for-wp-owls/">Creating a Custom Block for WP Owls</a></h2>
<p>I walk through creating a custom “Owl-Link” block for WordPress using the Block Editor (Gutenberg). This post covers the difference between block patterns and custom blocks, and provides step-by-step instructions on building, styling, and implementing the block.</p>
<hr />
<h2 id="heading-developing-with-wordpress-gutenberg-part-2httpswwwmakedonetblogdeveloping-with-wordpress-gutenberg-part-2"><a target="_blank" href="https://www.makedo.net/blog/developing-with-wordpress-gutenberg-part-2/">Developing with WordPress Gutenberg: Part 2</a></h2>
<p>In this follow-up post, I explore advanced block creation using JavaScript and React. I cover the best practices for structuring and styling blocks while handling attributes and dynamic content in the Gutenberg editor.</p>
<hr />
<h2 id="heading-developing-with-wordpress-gutenberg-part-1httpswwwmakedonetblogdeveloping-with-wordpress-gutenberg-part-1"><a target="_blank" href="https://www.makedo.net/blog/developing-with-wordpress-gutenberg-part-1/">Developing with WordPress Gutenberg: Part 1</a></h2>
<p>In this introductory post, I focus on the basics of WordPress Gutenberg block creation, from setting up the development environment to understanding block structure. I guide developers on creating dynamic blocks using JavaScript and React.</p>
<hr />
<h2 id="heading-build-a-plugin-wordpress-technical-agency-guidehttpswwwmakedonetblogbuild-a-plugin-wordpress-technical-agency-guide"><a target="_blank" href="https://www.makedo.net/blog/build-a-plugin-wordpress-technical-agency-guide/">Build a Plugin: WordPress Technical Agency Guide</a></h2>
<p>This guide takes a deep dive into building WordPress plugins from a technical agency’s perspective. I cover the essentials, including planning, code quality, and long-term maintenance.</p>
<hr />
<h2 id="heading-theme-translation-wordpress-agency-guidehttpswwwmakedonetblogtheme-translation-wordpress-agency"><a target="_blank" href="https://www.makedo.net/blog/theme-translation-wordpress-agency/">Theme Translation: WordPress Agency Guide</a></h2>
<p>I discuss how WordPress agencies can efficiently translate themes to support multilingual websites. This includes using translation-ready functions, Poedit, and ensuring compatibility with multilingual plugins like WPML.</p>
<hr />
<h2 id="heading-creating-a-task-driven-public-sector-websitehttpswwwmakedonetblogcreating-task-driven-public-sector-website"><a target="_blank" href="https://www.makedo.net/blog/creating-task-driven-public-sector-website/">Creating a Task-Driven Public Sector Website</a></h2>
<p>This post explores how to build task-driven public sector websites focused on user-centered design to improve accessibility and efficiency for users.</p>
<hr />
<h3 id="heading-how-gutenberg-changes-the-wordpress-experiencehttpswwwmakedonetbloghow-gutenberg-changes-the-wordpress-experience-and-how-you-can-prepare-for-its-release"><a target="_blank" href="https://www.makedo.net/blog/how-gutenberg-changes-the-wordpress-experience-and-how-you-can-prepare-for-its-release/">How Gutenberg Changes the WordPress Experience</a></h3>
<p>I discuss how the Gutenberg editor revolutionised WordPress by introducing a block-based system, simplifying content creation, and how to prepare for its release.</p>
<hr />
<h2 id="heading-extending-cmb2-controls-in-wordpresshttpswwwmakedonetblogextending-cmb2-controls-in-for-wordpress"><a target="_blank" href="https://www.makedo.net/blog/extending-cmb2-controls-in-for-wordpress/">Extending CMB2 Controls in WordPress</a></h2>
<p>I explore how to extend CMB2 controls within WordPress to create more customisable and flexible meta boxes. I walk through how to add new field types and enhance the user experience for custom fields, offering practical examples and insights into CMB2’s versatility.</p>
]]></content:encoded></item><item><title><![CDATA[Learning to Blog Again]]></title><description><![CDATA[Well, here we are again. After years of scribbling my thoughts across the web — be it on company sites, other people’s sites, or random napkins that probably should’ve been blog posts — I’m finally consolidating it all. Welcome to the new, improved h...]]></description><link>https://mattwatson.blog/learning-to-blog-again</link><guid isPermaLink="true">https://mattwatson.blog/learning-to-blog-again</guid><dc:creator><![CDATA[Matt Watson]]></dc:creator><pubDate>Thu, 10 Oct 2024 14:59:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728937115878/86931919-f095-4ad4-9f9c-133f002c028c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Well, here we are again. After years of scribbling my thoughts across the web — be it on company sites, other people’s sites, or random napkins that probably should’ve been blog posts — I’m finally consolidating it all. Welcome to the new, improved home for my adventures in WordPress, Agile, and Leadership!</p>
<p>Those old posts you know and love me for? Don’t worry, they’re making their way over here, getting a fresh coat of paint with up-to-date examples (I mean, we’ve moved on from jQuery, right? Right?!).</p>
<p>Now, in case you’re wondering, “Is this the Matt Watson I know from the glory days of WordCamp Sheffield and those web engineering escapades?” Yes, indeed. I’ve been working in WordPress for nearly two decades, and with web technology for even longer — remember <em>Webmasters</em>? Back when one person was expected to design, code, optimise, and maybe even bring you a cup of tea while the server rebooted. Ah, simpler times… and by simpler, I mean <em>impossibly stressful</em>.</p>
<p>But this blog isn’t just a nostalgia trip. I’ve got some shiny new WordPress plugins in the works, and trust me, they’re not just going to sit there collecting dust (<em>this time</em>). I’ll be linking to them from here, along with a few side projects that might just make you giggle (or at least groan in amusement). Buckle up, because it’s going to be a quirky, clever, and code-filled ride!</p>
]]></content:encoded></item></channel></rss>