Category: Code

  • Custom Block Building Workshop

    Custom Block Building Workshop

    This is a written version of a workshop given most recently at WordCamp Rochester 2024. The workshop is more hands-on, but my hope is that the code and explanations here are detailed enough to help you in creating your first custom block.

    The code for the plugin is available on my GitHub profile, and you can walk through the commits to see the order in which the plugin is built.

    This workshop assumes a basic understanding of PHP and JavaScript, as well as a code editor and somewhere to test the code, such as a test WordPress site, or a local development environment. It does not require the use of React or another JavaScript framework, nor does it require build tools.

    I won’t be describing every single line-by-line change, but if you follow along and view the commits, you should be able to see everything that changes for each step.

    Setting Up the Plugin

    Start by creating a folder in your wp-content/plugins folder in WordPress. You can name the folder whatever you want, though I’m going with testimonial-block-workshop.

    Create plugin files

    Add new files to plugin folder

    In that folder, create three files: testimonial-block.php, testimonial-block.js, and testimonial-block.css. These files are going to hold the code to register the plugin, build the block, and style the block, respectively.

    Add plugin header code

    Add plugin header code

    The following code, placed into testimonial-block.php, will let the plugin show up in the site dashboard. This starts the PHP file and uses PHP comments as a way to register the plugin. We’re providing a name, description, version, and author. All of these show up in the dashboard, and the version is important for when updates come in the future.

    <?php
    /**
     * Plugin Name: Testimonial Block Workshop
     * Description: Basic Custom Testimonial Block with no build tools needed - Workshop Demo
     * Version: 1.0.0
     * Author: david wolfpaw
     */

    After you save this code, you should be able to go to the plugin dashboard and see your plugin! Activate it and make it look all highlighted and happy.

    A screenshot of the WordPress plugin dashboard, showing the Testimonial Block Workshop plugin activated.

    Add exit code and define version constant

    Add exit code and define version constant

    Now we’re gonna add code to the PHP file that will ensure that the plugin file doesn’t get accessed directly, and defines a constant for the version of the plugin. We’ll be using this later.

    if ( ! defined( 'ABSPATH' ) ) {
    	exit; // Exit if accessed directly.
    }
    // Define plugin version
    define( 'TESTIMONIAL_BLOCK_VERSION', '1.0.0' );

    Registering the Block

    Now that we’re setup, it’s time to register the block, so that it’s visible in the WordPress editor.

    Register PHP for the block

    register PHP for the block

    This code registers the JavaScript file for the plugin, which is where we’ll be doing the most work. Note that we first register the file with wp_register_script, and then we use register_block_type and tell it which script file to use.

    // Function to register the block and enqueue scripts/styles
    function register_testimonial_block() {
    	// Register the block's JavaScript file
    	wp_register_script(
    		'testimonial-block-script', // Unique script handle
    		plugins_url( 'testimonial-block.js', __FILE__ ), // URL of where the block file exists
    		array( 'wp-blocks', 'wp-block-editor' ), // Scripts that should load before this script
    		TESTIMONIAL_BLOCK_VERSION, // Version of the plugin script (optional)
    		true, // Is script in HTML Footer (optional)
    	);
    	// Register the block type with the scripts and styles
    	register_block_type(
    		// Block namespace and name
    		'workshop/testimonial-block',
    		// Array of arguments for the block
    		array(
    			'editor_script' => 'testimonial-block-script',
    		)
    	);
    }
    add_action( 'init', 'register_testimonial_block' );

    Register JS for the block

    register JS for the block

    In the JavaScript file, we likewise register the block. We use the same namespace and name for the block that we used in the PHP file (workshop/testimonial-block).

    The main things that we’re doing are adding a title and icon, as well as choosing the category of blocks that it will go into for when we select it. The edit and save functions right now are just returning a paragraph tag. The goal at this point is to test to ensure that we can see the block in the editor, and that when we save it, something will display on the frontend of the site.

    (function (blocks, element) {
        const { registerBlockType } = blocks;
        const { createElement } = element;
        // Step 1: Register the block
        registerBlockType('workshop/testimonial-block', {
            title: 'Workshop Testimonial Block', // Block title
            icon: 'admin-comments', // Icon from WordPress dashicons
            category: 'common', // Category in the block inserter
            edit: function () {
                return createElement('p', null, 'Workshop Testimonial Block'); // Placeholder text in editor
            },
            save: function () {
                return createElement('p', null, 'Workshop Testimonial Block'); // Placeholder text on frontend
            }
        });
    })(window.wp.blocks, window.wp.element);

    With both of these files saved, you should be able to go to edit a post or page and find your block.

    A screenshot of the WordPress editor showing the Workshop Testimonial Block that we're making

    Creating Fields to Edit Block

    Alright, now that we’ve gotten really set up, it’s time to create some fields for our block. We’re going to create some text fields, an image upload button, and color selectors.

    Add editable text fields

    Add editable text fields

    The following code adds some of the other constants that we need to make text controls, and adds two text fields: testimonialText and authorName to unsurprisingly store the testimonial text, and the author name.

    (function (blocks, element, blockEditor, components) {
        const { registerBlockType } = blocks;
        const { TextControl } = components; // Import the TextControl component
        const { createElement, Fragment } = element;
    
        // Step 1: Register the block
        registerBlockType('workshop/testimonial-block', {
            title: 'Workshop Testimonial Block', // Block title
            icon: 'admin-comments', // Icon from WordPress dashicons
            category: 'common', // Category in the block inserter
            // Step 2: Add Editable Text Fields
            attributes: {
                testimonialText: { type: 'string', default: '' }, // Testimonial text
                authorName: { type: 'string', default: '' }, // Author name
            },
            edit: function ({ attributes, setAttributes }) {
                const { testimonialText, authorName } = attributes;
                return createElement(
                    Fragment,
                    null,
                    // Add TextControl for Testimonial Text
                    createElement(TextControl, {
                        label: 'Testimonial Text',
                        value: testimonialText,
                        onChange: (value) => setAttributes({ testimonialText: value }),
                        placeholder: 'Enter the testimonial text…'
                    }),
                    // Add TextControl for Author Name
                    createElement(TextControl, {
                        label: 'Author Name',
                        value: authorName,
                        onChange: (value) => setAttributes({ authorName: value }),
                        placeholder: 'Enter the author’s name…'
                    })
                );
            },
            save: function ({ attributes }) {
                return createElement('p', null, 'Custom Testimonial Block'); // Placeholder text on frontend
            }
        });
    })(window.wp.blocks, window.wp.element, window.wp.blockEditor, window.wp.components);

    We are creating the elements that you’ll be able to see on the backend of the site, including labels and placeholders for those fields.

    a screenshot of the testimonial block backend, showing the editable text fields

    Display text on frontend

    Display text on frontend

    The save function will affect what saves tot he database to display on the frontend of the site. We’ve replaced the placeholder paragraph with a div that holds a blockquote and bolded paragraph tag for the testimonial text and the author name.

    save: function ({ attributes }) {
        // Step 3: Create Elements to Display Text on Frontend
        const { testimonialText, authorName } = attributes;
        return createElement('div', null,
            createElement('blockquote', null, testimonialText),
            createElement('p', { style: { fontWeight: 'bold' } }, authorName)
        );
    }
    a screenshot of the testimonial block frontend, showing the text fields

    Add image upload controls

    Add image upload controls

    Same as before, we add the constants needed for the media uploader. We’re going to be saving the URL of the image to use on the frontend. We’re also creating a button that says “Upload Author Image” if no image is set, and changes to “Change Author Image” if an image is already present.

    (function (blocks, element, blockEditor, components) {
        const { registerBlockType } = blocks;
        // Step 4: Add Button and Media Upload Components
        const { TextControl, Button } = components; // Import the Button Component
        const { MediaUpload, MediaUploadCheck } = blockEditor; // Import the Media Upload Components
        const { createElement, Fragment } = element;
    
        registerBlockType('workshop/testimonial-block', {
            title: 'Workshop Testimonial Block', // Block title
            icon: 'admin-comments', // Icon from WordPress dashicons
            category: 'common', // Category in the block inserter
    
            attributes: {
                testimonialText: { type: 'string', default: '' }, // Testimonial text
                authorName: { type: 'string', default: '' }, // Author name
                // Step 4: Add Image URL Attribute
                authorImage: { type: 'string', default: '' }, // Author image URL
            },
            edit: function ({ attributes, setAttributes }) {
                // Step 4: Add Image URL Attribute
                const { testimonialText, authorName, authorImage } = attributes;
    
                return createElement(
                    Fragment,
                    null,
                    // Step 4: Add Media Upload for Author Image
                    // Media Upload for Author Image
                    createElement(MediaUploadCheck, {},
                        createElement(MediaUpload, {
                            onSelect: (media) => setAttributes({ authorImage: media.url }),
                            allowedTypes: ['image'],
                            value: authorImage,
                            render: ({ open }) => createElement(Button, { onClick: open, className: 'is-primary' }, authorImage ? 'Change Author Image' : 'Upload Author Image')
                        })
                    ),
                    // Display Uploaded Image in Editor
                    authorImage && createElement('img', { src: authorImage, alt: 'Author Image' }),
                    // Add TextControl for Testimonial Text
                    createElement(TextControl, {
                        label: 'Testimonial Text',
                );
            },
    a screenshot of the testimonial block backend, showing the image upload

    Display image on frontend

    Display image on frontend

    Same as with the text, we’re now using the same code to create an image element on the frontend to display our author image.

    save: function ({ attributes }) {
        const { testimonialText, authorName, authorImage } = attributes;
        return createElement('div', null,
            // Step 5: Render Image on Frontend
            authorImage && createElement('img', { src: authorImage, alt: 'Author Image' }),
            createElement('blockquote', null, testimonialText),
            createElement('p', { style: { fontWeight: 'bold' } }, authorName)
        );
    }
    a screenshot of the testimonial block frontend, showing the uploaded image and text

    Add color control settings

    Add color control settings

    With the following four changes, we’re adding the ability to create a color panel like the color selectors that appear in other WordPress blocks. We’re saving the settings for a background color and text color for the testimonial block.

    // Step 6: Import the Panel Color Settings
    const { MediaUpload, MediaUploadCheck, PanelColorSettings } = blockEditor;
    
    
    // Step 6: Add Attributes for Background and Text Colors
    backgroundColor: { type: 'string', default: '#ffffff' }, // Background color
    textColor: { type: 'string', default: '#000000' }, // Text color
                
    
    edit: function ({ attributes, setAttributes }) {
        // Step 6: Add Attributes for Background and Text Colors
        const { testimonialText, authorName, authorImage, backgroundColor, textColor } = attributes;
    
    
    // Step 6: Panel for Color Settings
    createElement(PanelColorSettings, {
        title: 'Color Settings',
        initialOpen: true,
        colorSettings: [
            {
                value: backgroundColor,
                onChange: (value) => setAttributes({ backgroundColor: value }),
                label: 'Background Color',
            },
            {
                value: textColor,
                onChange: (value) => setAttributes({ textColor: value }),
                label: 'Text Color',
            }
        ],
    a screenshot of the testimonial block backend, showing the color settings fields

    Move image and color controls to Inspector Panel

    Move image and color controls to Inspector Panel

    Now that we’ve added more controls to our block, it’s starting to look a bit cluttered in the editor. At this point, I think that it’s reasonable to move these settings from the main block editor to the Inspector Panel that shows up on right sidebar when you have a specific block clicked.

    We’re going to move the media upload button and color selectors to the sidebar, while keeping the text fields in the main editor for now. Spoiler: we’re going to move those later as well to tidy up our block view even more.

    edit: function ({ attributes, setAttributes }) {
        const { testimonialText, authorName, authorImage, backgroundColor, textColor } = attributes;
    
        // Step 7: Move Settings into Inspector Controls
        return (
            createElement(Fragment,
                null,
                // InspectorControls Adds Settings in the Sidebar
                createElement(InspectorControls,
                    null,
                    // PanelBody for Testimonial Settings
                    createElement(PanelBody,
                        { title: 'Testimonial Settings' },
                        // Media Upload for Author Image
                        createElement(MediaUploadCheck, {},
                            createElement(MediaUpload, {
                                onSelect: (media) => setAttributes({ authorImage: media.url }),
                                allowedTypes: ['image'],
                                value: authorImage,
                                render: ({ open }) => createElement(Button, { onClick: open, isPrimary: true }, authorImage ? 'Change Author Image' : 'Upload Author Image')
                            })
                        )
                    ),
                    // Panel for Color Settings
                    createElement(PanelColorSettings, {
                        title: 'Color Settings',
                        initialOpen: true,
                        colorSettings: [
                            {
                                value: backgroundColor,
                                onChange: (value) => setAttributes({ backgroundColor: value }),
                                label: 'Background Color',
                            },
                            {
                                value: textColor,
                                onChange: (value) => setAttributes({ textColor: value }),
                                label: 'Text Color',
                            }
                        ],
                    })
                ),
    
                // Step 8: Make div to Display Testimonial in Editor
                createElement('div',
                    null,
                    // Display Uploaded Image in Editor
                    authorImage && createElement('img', { src: authorImage, alt: 'Author Image' }),
                    // Add TextControl for Testimonial Text
                    createElement(TextControl, {
                        label: 'Testimonial Text',
                        value: testimonialText,
                        onChange: (value) => setAttributes({ testimonialText: value }),
                        placeholder: 'Enter the testimonial text…'
                    }),
                    // Add TextControl for Author Name
                    createElement(TextControl, {
                        label: 'Author Name',
                        value: authorName,
                        onChange: (value) => setAttributes({ authorName: value }),
                        placeholder: 'Enter the author’s name…'
                    })
                )
            )
        );
    },
    a screenshot of the testimonial block backend, showing the media upload button and color settings fields moved into the inspector panel sidebar

    Display color changes in editor

    Display color changes in editor

    Now that we’ve moved the color and media settings to the sidebar, we can add some attributes to the div in the editor. Instead of an attribute set of null as it was before, we’re adding a JSON array that includes styles, and a class name.

    This will make inline style changes that will update when a new color is selected for either the text or background.

    // Step 9: Display Color Changes in Editor
    {
        style: {
            color: textColor,
            backgroundColor: backgroundColor
        },
        className: 'custom-testimonial-block'
    },
    a screenshot of the testimonial block backend, showing custom colors set

    Display color changes on frontend

    Display color changes on frontend

    This should look basically the same as the previous step. We’re making the save function return a div that has some style attributes and a class name. The class name we’ll be using when we do more styling at the end.

    // Step 10: Display Color Changes on Frontend
    const { testimonialText, authorName, authorImage, backgroundColor, textColor } = attributes;
    return createElement('div',
        {
            style: {
                color: textColor,
                backgroundColor: backgroundColor
            },
            className: 'custom-testimonial-block'
        },
    a screenshot of the testimonial block frontend, showing custom colors set

    Move text controls to Inspector Panel

    Move text controls to Inspector Panel

    I had an idea that I’d be moving the text controls to the Inspector Panel in the sidebar as well. While it’s not editable right where it’d be on the page, I think that it looks and acts cleaner this way, with less clutter in the display and all of the things that you’ll actually edit next to one another.

    This step involves moving the controls into the InspectorControls element, as well as creating new elements in the div in the main page editor to display the updated blockquote and paragraph tags.

    // Step 11: Move Text Controls into Inspector Panel
    // Add TextControl for Testimonial Text
    createElement(TextControl, {
        label: 'Testimonial Text',
        value: testimonialText,
        onChange: (value) => setAttributes({ testimonialText: value }),
        placeholder: 'Enter the testimonial text…'
    }),
    // Add TextControl for Author Name
    createElement(TextControl, {
        label: 'Author Name',
        value: authorName,
        onChange: (value) => setAttributes({ authorName: value }),
        placeholder: 'Enter the author’s name…'
    }),
    
    
    // Step 11: Move Text Controls into Inspector Panel
    createElement('blockquote', null, testimonialText),
    createElement('p', { style: { fontWeight: 'bold' } }, authorName)
    a screenshot of the testimonial block backend, showing all editable fields moved to the inspector panel sidebar

    Block Alignment and Styles

    We’re almost at the end! Now that we’ve made it this far, it’s time to add a bit of style to our testimonials. We’re going to do this in two ways: add support to allow you to change the alignment of the testimonial block, and add some custom styles for it.

    Add block alignment support

    Add block alignment support

    We’re adding a new parameter to our block: supports. This will let us use an existing feature in the block editor to control alignment. I’ve set all five core options, to allow us to float the testimonial block, or to make it wide or full width on the page. We set an attribute with whatever is saved to allow it to display properly.

    Unlike with the custom attributes that we created, we don’t need to add this to our edit or save functions.

    // Step 12: Add alignment support
    supports: {
        align: ['left', 'center', 'right', 'wide', 'full'], // Supports alignment options
    },
    attributes: {
        testimonialText: { type: 'string', default: '' }, // Testimonial text
        authorName: { type: 'string', default: '' }, // Author name
        authorImage: { type: 'string', default: '' }, // Author image URL
        backgroundColor: { type: 'string', default: '#ffffff' }, // Background color
        textColor: { type: 'string', default: '#000000' }, // Text color
        blockAlignment: { type: 'string', default: 'none' }, // Block alignment
    },
    a screenshot of the testimonial block backend, showing the block alignment options

    Register block stylesheet

    Register block stylesheet

    Finally touching that PHP file again! The main things of note here are that we are registering the stylesheet that we already created, testimonial-block.css, and telling WordPress where it can be found. We are then adding style_handles to the register_block_type function. This is an array, since there could be more than one stylesheet added.

    You do have the ability to specify stylesheets for the frontend and backend of the site separately, but in our case we’re going to have the same one apply, since it isn’t too hard on this simple block to style to look the same to visitors as it does while editing.

    // Register the block's styles
    wp_register_style(
    	'testimonial-block-style', // Unique stylesheet handle
    	plugins_url( 'testimonial-block.css', __FILE__ ), // URL of where the stylesheet file exists
    	array( 'wp-edit-blocks' ), // Stylesheets that should load before this script
    	TESTIMONIAL_BLOCK_VERSION // Version of the plugin script (optional)
    );
    // Register the block type with the scripts and styles
    register_block_type(
    	// Block namespace and name
    	'workshop/testimonial-block',
    	// Array of arguments for the block
    	array(
    		'editor_script' => 'testimonial-block-script',
    		'style_handles' => array( 'testimonial-block-style' ),
    	)
    );

    Add block styles

    Add block styles

    And now we’re finally using our CSS file!

    Nothing very special of note here. This code:

    • makes a box and adds a border to it (same color as the text color)
    • centers the image and text
    • rounds the image to a circle and adds a border to it as well (same color as the text color)
    • makes the block quote look a bit fancy with a large quotation mark
    • bolds the author name
    .custom-testimonial-block {
        text-align: center;
        padding: 20px;
        border-radius: 20px;
        border: 3px solid;
        font-size: 1em;
    }
    .custom-testimonial-block img {
        border-radius: 50%;
        border: 3px solid;
        background-color: #fff;
    }
    .custom-testimonial-block blockquote {
        font-size: 1.5em;
        padding: 50px;
        font-style: italic;
        position: relative;
    }
    .custom-testimonial-block blockquote::before {
        content: '“';
        font-family: sans-serif;
        font-size: 4em;
        line-height: 1;
        position: absolute;
        left: 10px;
        top: -10px;
    }
    .custom-testimonial-block p {
        font-size: 1em;
        font-weight: bold;
    }

    You probably noticed that the author name was already bolded before. That’s because we originally used this:

    createElement('p', { style: { fontWeight: 'bold' } }, authorName)

    Now we removed the style attributes, to get this:

    createElement('p', null, authorName)

    The main reason that I made this change is just to keep styling consistently in one area, so that we can more easily make changes to it in the future if we want.

    Final Thoughts

    I realize that was a lot to go through at once. I don’t blame you if it takes a few tries (it took more than a few for me!) or requires a few sessions and examples to get it down.

    This is just one way of building a custom block, and not how most that you will see will look. That’s because instead of using the fancy and very helpful @wordpress/create-block tooling, I opted for something that would not require you to learn any build tools, how to use the command line, or having a separation of source and build files.

    From here I want you to go out and create custom blocks that are useful for your own sites, and share them with the world! I’ll be happy to showcase some if you reply with a link to what you end up building.

  • New Kid on the Block: Crafting Custom Blocks – WordCamp Minneapolis 2024

    New Kid on the Block: Crafting Custom Blocks – WordCamp Minneapolis 2024

    david holding the phone and smiling with a room of people at WordCamp Minneapolis 2024

    Attached are my slides and resources for my presentation at WordCamp Minneapolis on 16 August 2024.


    Methods to Create Custom Blocks

    Block Patterns

    Pros: No coding required, No extra plugins needed, Can be repeated or customized
    Cons: Block Editor limitations exist

    Custom HTML Block

    Pros: No extra plugins needed, Custom code for your needs, Can include JavaScript and CSS
    Cons: Code is not contained/scoped

    Advanced Custom Fields

    Pros: Custom settings/options/data, Saves block development time
    Cons: Requires custom coding, Requires Pro Plugin

    Block Variations

    Pros: Good for tweaking existing blocks
    Cons: Limited functionality changes

    Backend of the editor showing a Block Variation of a Media & Text block

    Custom Blocks

    Pros: Custom everything!, Made to fit your exact needs
    Cons: Getting started coding is hard, Build tools and guides change


    Resources Linked

  • What is WP_DEBUG for and how do I use it?

    What is WP_DEBUG for and how do I use it?

    PHP normally only displays fatal errors in the browser, or doesn’t load any page content and gives you a “White Screen of Death (WSOD)” if it has a fatal error before the page can load. A fatal error is one in which something is so wrong in the PHP code that it cannot make sense of it to gracefully fail in the background. If I forget to add chocolate chips when making cookies, I still have a perfectly tasty dough. If I forget to add baking soda though, I end up with a flat, gooey mess.

    WordPress has a few features built in to make it easier to see PHP errors while you are testing. You’d want to activate these while developing a new site, theme, or plugin to ensure that you are seeing any PHP errors that come up.

    As mentioned in the last post on PHP Illegal Strings, there are a few failure types in PHP including warnings, notices, and errors. Turning on WP_DEBUG will allow you to see those failure types so that you can fix them in your code.

    Activating WP_DEBUG

    If you have access to all of the files of your WordPress install, you’ll want to edit the wp-config.php file, which is located in the root directory, meaning the same folder that has the wp-admin, wp-content, and wp-includes folders. You’re going to go into that file and add the following line of code near the bottom of the file, but before the stop editing notice:

    define( 'WP_DEBUG', true );
    
    /* That's all, stop editing! Happy blogging. */

    If that line already exists but says false, change that to true. There can be other lines of code above or below this, but as long as it’s above the comment to stop editing, it’s in the right spot.

    What did we do?

    We’ve now told WordPress that rather than hide PHP errors, we want them to display while viewing the site. WP_DEBUG is a PHP constant, which by convention are written in all caps. We’ve used the PHP define() function to set the value of that constant to a boolean true. Note that we didn’t surround the word true in quotes, otherwise PHP would read it as a string.

    Using WP_DEBUG also allows us to see any deprecated functions that are running on our site. Deprecated functions exist in WordPress but are no longer the standard way to perform a particular tas. As an example, long ago in WordPress history you would get the ID of a category in WordPress with the function the_category_ID(), but now the function to do the same in a better way is get_the_category().

    Using WP_DEBUG_LOG and WP_DEBUG_DISPLAY

    There are some companions to WP_DEBUG that can be used to make it even more helpful. You may not always be able to easily see errors if they are loading behind content, or you may want to keep track of them over time to review later. Two other constants that are built into WordPress that can help are WP_DEBUG_LOG and WP_DEBUG_DISPLAY.

    Using WP_DEBUG_LOG

    Setting up WP_DEBUG_LOG allows you to save all of the debug errors that are getting displayed to a file in your WordPress install. That file gets saved to wp-content/debug.log by default. Whenever an error occurs that WP_DEBUG would display, it will also get saved to that file with a timestamp of when the error occured.

    To turn on WP_DEBUG_LOG you’ll want to define the following constant:

    define('WP_DEBUG_LOG', true);

    You don’t have to worry about creating the debug.log file if it doesn’t already exist. WordPress will do this for you automatically as soon as it has an error to log. So hopefully not right away!

    Changing WP_DEBUG_DISPLAY

    By default, setting WP_DEBUG to true will display all errors on the screen in your browser, on both the visitor-facing frontend, and the admin-facing backend of your site. This is ok while you’re editing a site that isn’t live with other people using it, but you don’t really want those errors displaying to other site visitors. It will make the site look more broken than it is, and can even be a security concern.

    If you want to use WP_DEBUG but don’t want to display errors to the screen, set the following constant:

    define('WP_DEBUG_DISPLAY', false);

    Again, if you don’t set that as false, it will default to true when debug is turned on. If you are setting it to false, you’re probably also setting WP_DEBUG_LOG to true, since otherwise you won’t see the errors on the screen or in a debug log.

    If you want to passively log errors for review later on a live site, just in case any come up, you can combine the three definitions above to turn on debug mode, log errors, and stop them from displaying on the site. I recommend doing this if you don’t have anything else handling these error logs for you, which you’d probably know if you did.

    // Enable WP_DEBUG mode
    define('WP_DEBUG', true);
     
    // Enable Debug logging to the /wp-content/debug.log file
    define('WP_DEBUG_LOG', true);
     
    // Disable display of errors and warnings 
    define('WP_DEBUG_DISPLAY', false);

    Continuing to Debug Your Site

    The settings above display PHP errors, but they don’t actually do anything to fix them. You’ll need to handle that yourself. Still, they provide an invaluable source of information to determine why something is broken on your site. This won’t show all types of errors that could occur, since not all broken page or feature problems are PHP related.

    What it does do is give you a good footing to begin the fun part of debugging: digging into code and squashing bugs as you find them. In this case the old proverb is true: Knowledge is Power.

    Fediverse Reactions
  • PHP v7.1 and v7.2 Illegal Strings

    PHP v7.1 and v7.2 Illegal Strings

    An update to Yoast SEO v11.1 came out yesterday, causing a few site errors relating to illegal strings in PHP. It made me dust off this post to provide some detail on what that PHP warning is, why it is happening, and how it can be fixed.


    Last year I updated all existing client sites that I could to PHP version 7.2, to replace versions 5.6 and 7.0, both of which reached their end of security update lifespans in December of 2018. The current plan for WordPress v5.2 is to drop support for versions of PHP below 5.6, which is targeted to be released on 7 May. If this goes well, the minimum PHP version will be bumped to 7.0 later this year.

    We’re only a few weeks away from this change, and a lot of hosts have been informing users that their version will change, or that they should opt-in to update when they can. One issue is that there are some things that work in earlier versions of PHP that will now throw warnings, notices, and errors in newer versions. One of the ones that I had to contend with on a few sites recently was the “Illegal string offset” warning.

    Warning: Illegal String Offset

    Here’s a few warnings that appeared on a development version of a site that I was upgrading (file path removed for readability):

    __Warning:__ Illegal string offset 'menu' in [file location] on line 13
    __Warning:__ Illegal string offset 'post_types' in [file location] on line 25
    __Warning:__ Illegal string offset 'post_formats' in [file location] on line 36
    

    I’m noting that it’s a development environment, because I had WP_DEBUG set to true in my wp-config.php file, which I don’t do on live, production environments. Basically, I ensure that on a live version of a site I don’t have PHP errors/warnings/notices displayed, even if there are some that would otherwise display.

    I took a look at that file, which was a configuration file for the theme being used. The relevant lines from that file are below:

    /*
     * Theme menu
     */
    $theme['menu'] = array(
        THEMENAME,
        'Slideshow',
        'Sidebars',
        'Style',
        'Upload your fonts',
        //'Help'
    );
    
    /*
     * Post types
     */
    $theme['post_types'] = array(
        'Posts',
        'Pages',
        'Works',
        'Testimonials',
    );
    
    /*
     * Post formats
     * aside, gallery, link, image, quote, status, video, audio, chat
     */
    $theme['post_formats'] = array( 'gallery' );
    

    Can you spot the issue? I didn’t immediately see it myself, as I saw things like $theme['menu'] and thought “how can that be read as a string? The braces clearly indicate that we’re setting an array key.

    Explicitly Declaring an Array in PHP

    What I didn’t realize was that we were missing something that’s necessary as of PHP v7.1.0: an explicit declaration of the variable $theme as an array. If you take a look at the PHP.net manual entry on PHP Array Syntax Modifying, you’ll see the following note:

    Note: As of PHP 7.1.0, applying the empty index operator on a string throws a fatal error. Formerly, the string was silently converted to an array.

    So there’s our issue, and with it, a lead on a solution: the theme never explicitly declared the variable $theme to be an array, and so it was assumed to be a string. Since it is no longer being silently converted, we have a warning being thrown.

    Fixing the Illegal String Offset Warning

    The solution in this case is to add a line to the start of that block of code where we explicitly declare our variable as an array. What that looks like is this:

    $theme = array();
    
    /*
     * Theme menu
     */
    $theme['menu'] = array(
        THEMENAME,
        'Slideshow',
        'Sidebars',
        'Style',
        'Upload your fonts',
        //'Help'
    );
    

    By adding $theme = array();, we’re telling PHP, “yes, this is an array, please treat it as such.” It doesn’t have to try to guess what we mean, which newer versions of PHP no longer do anyway.

    PHP v7.0+ also introduces strict typing, which is great for being even more explicit in your PHP coding. This makes the code more secure (people can’t put different data types in than you intend), and less liable to break (you will always know what type of data to expect). If you want to learn a bit more, Eric Mann wrote a short introductory post on the topic early last year.

    PHP is getting better and better all the time, but this progress sometimes causes old code to break. While this is frustrating, it can also give you the opportunity to review old code with fresh eyes. It’s not always convenient to do this, but it can overall improve your site!

    Fediverse Reactions
  • How to Remove the Genesis SEO Settings

    How to Remove the Genesis SEO Settings

    If you’re like me, you don’t do much for SEO on your site. If you’re a better marketer than me and also have tools that you use for SEO, you probably don’t use the built-in SEO tools with the Genesis Theme Framework.

    The tools do their job and are already there if you want to use them, but they can be limiting, as well as duplicate work being done with any other SEO plugin that you may be using. You can choose not to use them, but they still take up some valuable space in your dashboard and while editing pages, and they can be a bit confusing if you’re handing the site off to someone else to manage.

    WordPress Gutenberg post editor with Genesis SEO enabled
    Look at all of that space devoted to an unused settings section!

    Removing the Genesis SEO Settings

    One of the many great things about Genesis is that it allows you to easily modify or remove various portions of it without having to directly edit the core files of the theme. This allows you to modify your child theme only, so that if you ever switch child themes or Genesis updates, your changes won’t break.

    Place the following code in your functions.php file, or another file that loads on the dashboard.

    // Remove Genesis SEO settings from post/page editor
    remove_action( 'admin_menu', 'genesis_add_inpost_seo_box' );
    
    // Remove Genesis SEO settings option page
    remove_theme_support( 'genesis-seo-settings-menu' );
    
    // Remove Genesis SEO settings from taxonomy editor
    remove_action( 'admin_init', 'genesis_add_taxonomy_seo_options' );

    The first line of code removes the SEO metabox in posts/pages/custom post types. The post editor is already looking cleaner!

    WordPress block post editor without Genesis SEO settings section
    Now there’s less distraction while writing a post!

    The second line of code removes the SEO settings menu from the left sidebar in the dashboard. If we’re not using it at all, no reason to have the settings page!

    Finally, the last line of code removes the SEO settings from taxonomies. That means that you won’t be able to access them on categories, tags, or any other custom taxonomies on the site.

    And with that, we’re done! Three lines of code (plus a bit of spacing and comments to make it easier to read and remember what we did that for later), and we’ve removed access to the Genesis SEO settings. Again, this isn’t a knock on Genesis, but simply a way to clean up your site a bit if you’ve already invested in another SEO tool for WordPress.

  • When to use isset(), empty(), and is_null() in PHP

    I’ll be honest: most of the posts that I write are either because I’ve solved a problem for a client, or because I solved a problem that Past-david created. This is one of those PD problems, where I wrote some code that stopped functioning. When I looked into it, it turns out that I was using the wrong function to test for a variable in PHP.

    There are a variety of functions made to test the state and value of variables, including ones that can tell you if there is anything available to use at all. Three of these functions that are easy to mix up are isset(), empty(), and is_null().

    Built-in Variable Testing Tools

    All three of these functions are built into PHP, so they should always be available for your use when writing code. empty() and isset() are language constructs, while is_null() is a standard function. We’ll go over why that’s important later in the article.

    Before I discuss the difference and show a few examples, here are the descriptions for empty(), isset(), and is_null() from the php.net manual.

    empty()

    empty ( mixed$var ) : bool

    Determine whether a variable is considered to be empty. A variable is considered empty if it does not exist or if its value equals FALSEempty() does not generate a warning if the variable does not exist.

    isset()

    isset ( mixed$var [, mixed$... ] ) : bool

    Determine if a variable is set and is not NULL.

    If a variable has been unset with unset(), it will no longer be set. isset() will return FALSE if testing a variable that has been set to NULL. Also note that a null character (“\0”) is not equivalent to the PHP NULL constant.

    If multiple parameters are supplied then isset() will return TRUE only if all of the parameters are set. Evaluation goes from left to right and stops as soon as an unset variable is encountered.

    is_null()

    is_null ( mixed$var ) : bool

    Finds whether the given variable is NULL.

    What’s the difference between these variable testing functions?

    You can see from the above definitions that these three functions do similar, but not the same things. You’ve gotta determine if you’re trying to test for whether a variable is null, true or false, and whether the variable has been declared.

    When to use empty()

    If you are using empty() you can test if a variable is false, but also if the variable does not exist. This function is best used when you want to ensure both that the variable exists, and has a value that does not equal false. Note that PHP will treat empty strings, integers of 0, floats of 0.0, empty arrays, and the boolean value of false as false. So basically, only use empty() when you want to ensure that there is some actual value to the variable.

    Since you don’t have to declare variables before using them in PHP, you can get in a position where you are trying to perform actions or run other tests on a variable that hasn’t yet been declared. While it’s best practice to declare your variables before use for this and other reasons, this gotcha is one of the reasons that empty() is used differently from isset().

    When to use isset()

    If you are using isset(), you can test specifically if the variable has been declared already, and that the value is not null. So as long as you have a declared variable that has a value set and is not of the value NULL, you’ll return true when you test it with isset(). This would be a good condition to check before doing other checks to perform actions on a variable:

    // Declaring our variable
    $variable = 'Some String';
    
    // Testing that our variable exists, then testing the value
    if ( isset( $variable ) && $variable !== 'Some Other String' ) {
        echo 'This code evaluates since both of the above are true';
    }

    In the above example, we’ve declared our variable as a string, then tested if the variable is set (it is), and if it is not equal to a different string (it is not). Since both of those tests are true, we would then echo out the sentence in that conditional statement.

    Should you use is_null()?

    Finally, is_null() works in a similar manner to isset() as its opposite, with one key difference: the variable must be declared to return true, provided that it is declared without any value, or is declared specifically as NULL.

    I said above that isset() tests whether a variable has been set or not, which is true, but it can handle no variable being set and providing an output of false. That is helpful if somewhere else in the code the unset() construct has been used to remove a variable from scope entirely.

    In contrast, is_null() would not only not properly evaluate, it would also return a notice due to its inability to evaluate. Usually that’ll look something like this:

    Notice:  Undefined variable: variable in /directory/to/code.php on line X

    Since isset() is both a language construction, and can handle variables that aren’t declared, I’d generally recommend it over using is_null() in any situation. If you need to use is_null(), I might suggest finding a way to rewrite your code instead.

    Language Construct vs. Built-In Function

    I mentioned before that isset() and empty() are both language constructs in PHP, where is_null() is a built in function. Language constructs are reserved keywords that can evaluate whatever follows them in a specific manner. That means that it already knows what to do without having to find the definition of the construct like it would a function.

    The main things to keep in mind between the two when evaluating your code is that language constructs in PHP are slightly faster (but honestly not enough to worry about for speed optimization), they can’t be used in variable functions, and they don’t throw any errors when evaluating variables that don’t exist.

    Many times I see warnings and notices because a variable hasn’t been declared, and no one has confirmed that the variable already exists before trying to do some other conditional check with it. Using isset() and empty() can go a long way to avoiding those errors.

    Examples of output of these three functions

    The following table has been taken directly from a demo created by Virendra Chandak on his personal site. You can view the demo here.

    Value of variable ($var)isset($var)empty($var)is_null($var)
    “” (an empty string)bool(true)bool(true)bool(false)
    ” ” (space)bool(true)bool(false)bool(false)
    FALSEbool(true)bool(true)bool(false)
    TRUEbool(true)bool(false)bool(false)
    array() (an empty array)bool(true)bool(true)bool(false)
    NULLbool(false)bool(true)bool(true)
    “0” (0 as a string)bool(true)bool(true)bool(false)
    0 (0 as an integer)bool(true)bool(true)bool(false)
    0.0 (0 as a float)bool(true)bool(true)bool(false)
    var $var; (a variable declared, but without a value)bool(false)bool(true)bool(true)
    NULL byte (“\ 0”)bool(true)bool(false)bool(false)
    Fediverse Reactions
  • Fix Missing Leading Zeroes in WordPress Zip Codes

    Recently, I helped a client import a large set of addresses into a location plugin for WordPress. The import mainly went smoothly, but we noticed some issues when searching in areas with zip codes leading with one or two zeroes. The addresses weren’t coming up as they should.

    After examining some of the imported addresses, we realized that all of the leading zeroes were being stripped, and we could no longer search by those zip codes. I’m going to give a brief overview of why this happened, and how I solved it. Hopefully it helps if you need to make this kind of update to a WordPress database too!

    Why is this happening?

    Some programs “helpfully” strip leading zeroes from numbered cells, including Excel, Numbers, and Google Sheets. This means that 04102 in Portland, Maine becomes 4102, which isn’t a zip code in the US.

    The same could happen upon import into the database, depending on how the import is done. In either case, I’m working with an import that’s already complete, as opposed to having caught this issue before the addresses were added to the site. I don’t want to remove all other relevant content just to import again and fix these zip codes, so I’m going to go directly to the database to solve the problem.

    How to fix the missing zeroes

    There are several ways to add the zeroes back, but most places that you search will suggest changing the datatype of the zip code column, which doesn’t help when it’s in WordPress where we can’t modify that when there is other info stored in the same place. Plus some zip codes have the full nine digit route number depending on where the data was taken from, and some are postal codes from Canada and other countries that don’t follow the same pattern.

    In this particular case, we know what we’re looking for (postmeta with a key of wpsl_zip, and we know where it’s at (the wp_postmeta table). If you connect to the MySQL database through PHPMyAdmin or an external application you can run the following query to see how many zip codes stored have fewer than five digits:

    Important: Always make a backup of your database before doing any of the changes below!

    SELECT
        *
    FROM
        `wp_postmeta`
    WHERE
        `meta_key` = 'wpsl_zip' AND LENGTH(`meta_value`) < 5

    What we’ve told the database to do, is to “select all rows from the wp_postmeta table that have a meta_key of ‘wpsl_zip’, and that have a meta_value of less than five characters in length”.

    It’s important to ignore rows that already have a value of five or more characters, as LPAD will trim them to fit five characters otherwise. We don’t want that, just the ones that are too short.

    The above will return all of the rows that match the query, so that we can review them and confirm that they are indeed the addresses that we want to update.

    Now that we’ve identified how many there are (89 in this case), the following MySQL command will update those zipcodes using LPAD to add a left padding of 0’s until the meta_value is five characters. Values that are already five characters or larger are ignored.

    UPDATE
        `wp_postmeta`
    SET
        `meta_value` = LPAD(`meta_value`, 5, 0)
    WHERE
        `meta_key` = 'wpsl_zip' AND LENGTH(`meta_value`) < 5

    You’ll see that the WHERE clause is the same, since we already confirmed that we had the right records to change before. What we’ve done differently with this query is to say that we want to make updates to the wp_postmeta table by setting the meta_value of the rows that we selected to have exactly five characters, and that if they have fewer than five characters, to left pad them with 0’s.

    Summary

    To review, the MySQL function LPAD works like this:

    LPAD(
        "cell that we want to change",
        "final cell string length",
        "what to use to left pad the cell if needed"
    )

    I hope that helps save you spending the same time that it took me to find the problem that I had and to come up with a solution!

  • Setting up a Custom Palette in Gutenberg

    Setting up a Custom Palette in Gutenberg

    While there’s been a lot written about the new editing experience that came out with WordPress v5.0 last month, I want to give a reminder of some of the neat features for end users. One of the best things about the new editor is that a theme or plugin can add or remove features from the editor with simple hooks, allowing you to craft an experience that fits your needs.

    As an example, I have taken a few client sites that have embraced the new editor, and used their style guides to add their branding colors, fonts, and variants into the page editor. Now, when they want to add a block of content with a colored background or change the color of a button on a page, they have their palette of brand-approved colors already set to use. No need to remember hex codes or anything confusing!

    Sounds great! How do I set up a custom color palette?

    Default WordPress Editor Color Palette
    Notice that the editor will warn you if your background and text colors aren’t high contrast. This makes it a bit easier to keep your content accessible!

    By default the editor will have a palette of 11 colors, plus a color picker to get a different color. You can swap to a palette of your own by adding some code to your theme. Place the following in your functions.php file or where appropriate based on your structure. Next, we’ll modify it to fit our needs.

    This code came directly from the Gutenberg Theme Support Handbook, a good resource for all WordPress developers.

    function mytheme_setup_theme_supported_features() {
        add_theme_support( 'editor-color-palette', array(
            array(
                'name' => __( 'strong magenta', 'themeLangDomain' ),
                'slug' => 'strong-magenta',
                'color' => '#a156b4',
            ),
            array(
                'name' => __( 'light grayish magenta', 'themeLangDomain' ),
                'slug' => 'light-grayish-magenta',
                'color' => '#d0a5db',
            ),
            array(
                'name' => __( 'very light gray', 'themeLangDomain' ),
                'slug' => 'very-light-gray',
                'color' => '#eee',
            ),
            array(
                'name' => __( 'very dark gray', 'themeLangDomain' ),
                'slug' => 'very-dark-gray',
                'color' => '#444',
            ),
        ) );
    }
    
    add_action( 'after_setup_theme', 'mytheme_setup_theme_supported_features' );
    

    There’s a lot of code there, but not a lot to break down. First, remember that after_setup_theme is a hook, on which you add the function mytheme_setup_theme_supported_features that you’re creating. In that function we’re using add_theme_support, a built in WordPress function, where we’re using editor-color-palette to set our palette up.

    We’re adding an array of colors, and each element of that array is itself an array. Within those nested arrays we have the name of the color, which we’re making translatable with the __() function, and setting the textdomain of our theme. Change themeLangDomain to whatever matches your theme. This name is a descriptor for when you hover over it in the palette.

    The slug is a string of how you’ll refer to the color elsewhere in your code. The color is the hexadecimal value of the color that you want in your palette. With the above code, you’ve got a new editor palette with four colors that you’ve set, along with the color picker.

    Our custom WordPress editor color palette
    Our four custom colors now appear, along with the color picker

    Adding to Our Palette

    There are a few more features of the editor color palette that I’d like to show off, including targeting blocks in CSS, Customizer set colors, and removing the color picker.

    Using our Color Palette Selections in CSS

    If you’re editing text with the color palette you shouldn’t have to make any other changes. But what if you want to use the color selection in something a bit more customized, or in your own block type?

    The slug that we added to our colors in the example above lets us target for both background and text colors. We don’t even need to use the color set in the editor, but something custom to our needs. For example, you may want a specific background or text color when you use the strong magenta color. In that case, here’s the CSS that can target the classes added when we use that color:

    .has-strong-magenta-background-color {
        background-color: #313131;
    }
    
    .has-strong-magenta-color {
        color: #f78da7;
    }

    Setting a Color Palette with the Customizer

    The twentynineteen theme that comes with WordPress has a custom palette that includes colors that can be set in the Customizer. This means that you can set your own primary and secondary color from the WordPress dashboard, without changing code!

    array(
    	'name'  => __( 'Primary', 'twentynineteen' ),
    	'slug'  => 'primary',
    	'color' => twentynineteen_hsl_hex( 'default' === get_theme_mod( 'primary_color' ) ? 199 : get_theme_mod( 'primary_color_hue', 199 ), 100, 33 ),
    ),
    array(
    	'name'  => __( 'Secondary', 'twentynineteen' ),
    	'slug'  => 'secondary',
    	'color' => twentynineteen_hsl_hex( 'default' === get_theme_mod( 'primary_color' ) ? 199 : get_theme_mod( 'primary_color_hue', 199 ), 100, 23 ),
    ),

    The new color is now set as the output of a function that will get a theme mod, if you’ve modified the color. If not, it’ll return the default, ensuring that there’s always a color set.

    The WordPress customizer with a primary color selection

    Removing the Color Picker

    You can also do things like disable the color picker, to ensure that users can only use the colors that you have preset for them. Doing so requires just one line of code in your functions file:

    add_theme_support( 'disable-custom-colors' );

    With that single line we’ve made it so the beautiful design that we’ve worked so hard to craft and the branding style guide that we have had to constantly review will always be set the way that we want.

    Wrapping Up

    As you can see, there’s a lot that you can do to change how users edit content in the Gutenberg editor, without having to add a tremendous amount of code.

    This is only the beginning, and even more developer and user friendly features like this already exist or are coming to the editor and the rest of WordPress. I’m excited for the new opportunities this gives to all stakeholders of a site, from designers and developers, to admins and editors, all the way to customers and visitors. Let’s keep making WordPress better for everyone!

    Fediverse Reactions
  • Fixing Style Issues While Editing Beaver Builder

    Fixing Style Issues While Editing Beaver Builder

    I’ve started using Beaver Builder with a few clients after having played with it a bit and hearing lots of great reviews. I’ve looked into multiple WordPress Page Builders, and have had experience with quite a few of them through my work offering WordPress maintenance service.

    I’ve found that Beaver Builder is able to handle a lot of the customizations that my clients may want to make, but there are still a few things that I have to setup externally to get a feature that they want. As an example, a client wanted to use the callout module to make an entire box clickable, not just a button after text and images.

    Doing the above was fairly straightforward for this use-case: I set the entire callout link to be relatively positioned in CSS, so that I could absolutely position the anchor tag within the link to be the full height and width of that box. Finally, I added a hover and focus state to the button so that when hovering with the mouse or focusing with the keyboard there would be a visual indication that it was clickable, besides the cursor icon that was already set.

    .callout-link {
    	position: relative;
    }
    
    .callout-link a {
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
    }
    
    .fl-callout-button a:hover,
    .fl-callout-button a:focus {
    	box-shadow: 1px 1px 3px 0 #315E7D;
    }

    So what’s the issue?

    That looked like a simple solution to the problem that we had, but like many bits of code, I inadvertently created a new issue.

    Beaver Builder is a front-end content editor, which means that it uses the same HTML and CSS structure to display the content while editing. While this is normally a good thing, it means that you need to pay attention to custom code that you’ve added to modify Beaver Builder.

    Since I changed the layout of links in the callout module, I changed the layout of links for the editor of that module. Additionally, I’d styled unordered list bullets with pseudo-elements, which also caused a display issue. This is what the editor looked like when I tried to modify those links:

    Broken Beaver Builder editor CSS
    This is what happens when you let me touch code!

    After I determined that I was the cause of the issue, I set about to fix it. Thankfully, Beaver Builder adds several body classes while the page editor is open, including the class fl-builder-edit which I used to fix this particular issue. I hid the li::before pseudo-elements, and restored the link anchor to relative positioning.

    /* Beaver Builder Editor Fixes */
    .fl-builder-edit .entry-content ul li::before,
    .fl-builder-edit .fl-builder-content ul li::before {
    	display: none;
    }
    
    .fl-builder-edit .callout-link a {
        position: relative;
    }

    With that code in place, the editor layout looked as it should before I mangled it.

    Fixed Beaver Builder editor settings
    That’s a lot better and actually usable!

    Check for unintended consequences of your code.

    This broken CSS wasn’t a major problem, and was thankfully easy to fix. But it did bring up a good reminder: when you make one change to your code, you may change something else that you didn’t mean to. It’s always good to review every time that you make a change. Having some version control in place that you use regularly doesn’t hurt either!

    Fediverse Reactions
  • Display a Notice for New WordPress Posts

    Display a Notice for New WordPress Posts

    If you’re like me, it might not always be easy to get new posts out to your blog. I’m trying to keep a new tech-tip going every regular weekday for a while to see how I keep up with that.

    Since my content might not always be the newest, I may want to highlight when something was recently published.

    Calculate posts published in the last two weeks

    In the following example, I’m going to check to see if a post was published within the past two weeks. If so, I’m going to attach a notice to the title of the post. I’m assuming that the following code is going to go into a loop of posts, or somewhere that we’re already using the correct post ID.

    $post_title = get_the_title();
    if ( get_the_date( 'U' ) >= date( 'U', strtotime( '-2 weeks' ) ) ) {
        $post_title .= ' — New Post!';
    }
    echo $post_title;

    First, on line one, we’re creating a variable in PHP called $post_title. This will hold the title of the post, which we get with the built-in WordPress function get_the_title(). Again, I’m assuming that we’re already in a loop for a specific post, but if not you can pass the ID of the post as an argument in that function.

    Next, line two is going to get the date that the post was published in Unix Timestamp format. I’ve put it into that format to make it easy to compare. I am grabbing the date instead of the exact time since it doesn’t really matter to me if it was exactly within two weeks down to the second, just generally two weeks by day count.

    The post publish date is compared to the current time minus two weeks, also in Unix Timestamp format. The PHP function strtotime() allows you to use human readable formats for time conversions, which we’re using to say “give me the time in Unix seconds for two weeks ago”.

    If that comparison is true and the post was published less than two weeks ago, we’re going to append the text ” — New Post!” to the post title. By using a period followed by the equals sign, we’re saying that we want to concatenate, or add the new value to the existing variable.

    Finally, on line five we’re echoing out the value of $post_title, meaning we’re printing it to the screen. So if I were to use the above code to display titles for this site and this post was published less than two weeks ago, the title would display as Display a Notice for New WordPress Posts — New Post!

    How else could this be used?

    One way that I use this code is for a custom post type that displays properties for sale for a client. They wanted to highlight some recent listings, and using this code along with some CSS let me put a fancy ribbon on the corner of property listings, as well as list the number of days that the home has been on the market.

    Property Listing with new listing notice and number of days on the market

    If you have the need to calculate WordPress post publish date compared to the current date, I hope the above snippet has been a good place to start!

  • How to Keep Gravity Forms Displayed After Submission

    How to Keep Gravity Forms Displayed After Submission

    This post has been updated on 9 March 2022 to reflect updates to the code

    Sometimes you’ll have a Gravity Form that you want to keep visible after it is submitted. Maybe you want people to be able to fill out the same form multiple times, or maybe your design looks better with the form still showing.

    Gravity forms has a filter hook built in called gform_pre_submission_filter, which can be used to make changes to the form, among other things, after the form has validated (ensured that required fields are filled, nothing is blocked, etc), but before the form submits and notifications are sent. You can learn a bit more about that filter on the Gravity Forms documentation.

    We’re going to use this filter and create our own PHP function that will check the form before it is submit, and create a div that holds any confirmation messages that we have set.

    The code is embedded here, or can be viewed as a gist on GitHub.

    Inserting the Form before the Confirmation Message

    First, on line 3 we add our function, dw_show_confirmation_and_form, to the filter. Notice that we use the parameter $form in our function, which gives us access to details about this specific form.

    On line 7 I’m getting the shortcode that inserts the form into the page. In this case I want it to get the proper ID of the form, and I want to display the title of the form but not its description.

    Below that, from line 15 to line 19, we’re checking to see if there are any confirmations for this form. If so, we’re going to loop through each confirmation and append it to the form shortcode (so the form will display again), then put the confirmation text inside of a div that we’ve given the class .confirmation-message. That class can then be used to style the display of the confirmations.

    Finally, on line 21, we return the form. Since we’ve prepended the shortcode with the ID of the form, when the form submits it will display the form again, followed by our confirmation message.

    Gravity Form displaying with confirmation text below it.
    Our post-submission form, with the confirmation text displaying below

    The code above will make this change to all forms. If you need to target just one form, use the ID of the form and change the filter to include the form number after an underscore at the end. For instance, if we’re making this change to form three, we’ll change our filter call to 'gform_pre_submission_filter_3'.

    Clearing Inputs

    So that covers keeping the form displayed, but now we need to clear all of the inputs in the form.

    This is where the function dw_gf_footer_scripts() comes in. Without relying on jQuery it looks for Gravity Forms inputs and textareas to clear them out on reload. Crucially, hidden fields are left alone, so that the values assigned to them are still available for submission.

    Final Notes

    Edit: A few people have pointed out that this can cause issues with using ajax="true" for your form. This can be due to when the JavaScript is loaded, and some necessary jQuery not loading before this. The following post gives an easy way to make Gravity Forms files load in the footer, but it could potentially cause problems with other extensions or plugins.

    https://hereswhatidid.com/2013/01/move-gravity-forms-jquery-calls-to-footer/

    You may also want to do other things, like control whether any fields stay filled or not, update without refreshing the page, or scrolling down to the confirmation when complete, but those are lessons for another day!

    Fediverse Reactions
  • How to Display Links in Print Mode on Your Website

    How to Display Links in Print Mode on Your Website

    I have clients that want to ensure that their pages print well so that people can save things offline for later, like recipes or instructions. I don’t print webpages myself, but I can see plenty of useful reasons to do so.

    One of the issues when printing a webpage is that you lose context and interactivity. This is usually fine, as the site is probably intended to be read online anyway. But sometimes you want to make it easier for people to use that printed site, like still being able to find a linked page from a printed article.

    The Solution: Print Styles for Links

    Let’s say that you want to link to FixUpFox, my WordPress maintenance service. You can put https://fixupfox.com as the text on your page so that it prints properly and people can visit the site from their computer later.

    The above works for print, whether you link it or not, but usually you’ll want to say something like “For site support I use FixUpFox, because they provide great service at an affordable price for unlimited tasks”. In that case, you’ll want to have some way to display that URL next to the linked text when printing.

    Thankfully, there is a media query in CSS that is for print styles. Your browser will pull up that style when viewing the print version of a site. We’re going to use that to create our links.

    Making the Print Styles

    First, we’re going to make a media query in our stylesheet. This can go at the end of your existing stylesheet, or you can place it elsewhere as long as it loads on the page that you want print styles for.

    @media print {
    }

    Next, we’ll add an underline style for links, so they’re easier to see among text in a printed document that might be in black and white. We’ll add that style to visited links as well to override any potential visited link styling already on the page for underlines.

    @media print {
    	a,
    	a:visited {
    		text-decoration: underline;
    	}
    }

    Finally, we’ll use a CSS pseudo-class of :after on links that have an href attribute selector (so it’s not just an empty <a> tag), which is the target URL. We’ll add that target URL attribute of the link (the page that we’re linking to) to the content, after any links on our site. We’ll add a space before the link, and wrap it in parentheses to set it apart from the text. That code looks like this:

    @media print {
    	a,
    	a:visited {
    		text-decoration: underline;
    	}
    	a[href]:after {
    		content: ' (' attr(href) ')';
    	}
    }

    This works with any anchor tag that has a target link, even if it’s a text link instead of the URL. So when I type Ongoing WordPress Support and Maintenance, the printed version of that link will be the text of the link underlined, a space, and the URL in parentheses.

    The following screenshot shows what the link looks like when we view the page in print mode:

    screenshot example of a CSS print style for links
    This is a saved PDF of part of this article. Note the URL in parentheses after the link text.

    Finishing Up

    You’ll likely find that you have lots of things displaying links that you probably don’t need, like the menu to your site, or sidebar content.

    One solution would be to use the print styles to hide those portions of the site entirely. After all, if I’m printing a recipe out for later, I don’t need the navigation, header, footer, or anything else to print besides the content of that recipe. Another solution would be to target links in your content specifically, such as using the .entry-content class to get links that are only in your page and post content if you’re using a theme like the Genesis Framework.

    Whatever method you choose is up to you, but I hope that this helps you consider the ways that you can use print stylesheets, CSS pseudo-classes, as well as CSS attribute selectors to add more context to your site, whether printed or on the web.

    Thanks for following along and putting up with the shameless plugs for my maintenance business!

    Fediverse Reactions
🌙 ☀️