Letting Users Customise CSS Styles in your WordPress Theme using a Settings Page

Creating a WordPress theme can be a simple process. WordPress only needs a couple of files to understand and render your theme without any issues. But as your theme grows larger and more complex, you may need to allow users to customise aspects of it that are difficult to implement using WordPress’ core Customizer API. Font choices, rich colour schemes, shadows and border rounding are all things you may need a bit more control over than you’ll find in the default tooling, which is why many themes choose to add their own Theme Settings page so that admins can bring their own branding to a site. This article will walk through how I manage users customising core styling choices.

The Plan

We’re going to be creating a custom admin page to let users make styling choices. When they submit the form with all those options, we’ll programmatically generate a CSS stylesheet to reflect those choices. That stylesheet is enqueued on every page after our base styles and resets so that it’ll override them.

Caveats & Planning Ahead

On top of our existing theme stylesheet/s, we’re going to be enqueuing a CSS stylesheet that’s unique to each website. Overriding hundreds of rules from the original stylesheet is going to not only increase our file sizes but it’s going to make our code incredibly hard to maintain as we continue development. Luckily, CSS has a perfect solution for this:

Custom Properties, also known as CSS Variables mean we can define some default options for our styles and then override them later. We can have a CSS variable for our heading and body fonts, colour palette, shadow styles and many more. We can even have a single middle value for border radius and then use calc() to add our larger and smaller sizes. Then we can create presets for none, small, medium and large border-radius styles by setting only a single value.

So, in our base file we’ll have some CSS like this:

:root{
    --color-secondary-50: 245 250 255;
    --color-secondary-100: 187 223 255;
    --color-secondary-300: 116 190 255;
    --color-secondary-500: 30 146 248;
    --color-secondary-700: 0 97 183;
    --color-secondary-900: 13 36 70;

    --color-primary-50: 247 249 237;
    --color-primary-100: 231 237 204;
    --color-primary-300: 196 214 115;
    --color-primary-500: 133 167 0;
    --color-primary-700: 92 114 3;
    --color-primary-900: 18 68 0;

    --color-black-100: 255 255 255;
    --color-black-200: 247 252 255;
    --color-black-300: 228 235 239;
    --color-black-400: 204 216 223;
    --color-black-500: 173 186 194;
    --color-black-600: 126 142 151;
    --color-black-700: 88 108 119;
    --color-black-800: 53 73 85;
    --color-black-900: 14 37 51;

    --font-heading: Roboto Slab, 'Roboto Slab';
    --font-body: Muli, 'Muli';

    --border-r: 0.2rem;
    
    --border-r-sm: calc(var(--border-r) * 0.6); /* 0.12rem */
    --border-r-md: calc(var(--border-r) * 1.9); /* 0.38rem */
    --border-r-lg: calc(var(--border-r) * 2.5); /* 0.5rem */

}

And then we can simply override that with our user’s custom styles by declaring the property. You can extend this a lot further to use CSS variables for things like heading styles but in this instance, I’m just writing new selectors.

Reading and writing files in PHP is always something that makes me slightly uncomfortable. There are a lot of things that can go wrong with permissions and managing file paths. An alternative to this might be to inline all our custom styles in the header of every page. However, this will eliminate the browser’s ability to cache our stylesheet which will increase our page load by a tiny amount for returning visitors. It’ll also increase the amount of work our server has to do on every page load. By creating a CSS file, we can make it so the server only does that work whenever the options are updated. I haven’t used the WordPress Filesystem API for this article but I would highly recommend it as it’ll handle many of the permissions and host issues for us.

Creating an Admin Page

I’ve already detailed how I create admin pages in WordPress using wrapper classes to contain all the functionality and remove the boilerplate. Take a look at that article to learn more, but we’re basically just using the built-in add_menu_page function. Setting up menu pages in WordPress is pretty painless but these classes let us do it with less code.

Checking if a WordPress Admin Page has been Submitted

Now that we’ve added our admin page, we can set up a function to check whenever our options are updated. I wasn’t able to find a great hook for this, so I’m simply firing this function on every admin_init call and checking if the options are in the $_POST variable and we’re updating the correct page, like so:

function wrds_on_theme_styling_save()
{
    if (
        !(array_key_exists('option_page', $_REQUEST) &&
            $_REQUEST['option_page'] == "our_custom_styling_page" &&
            !empty($_POST))
    ) {
        return false;
    }

    ob_start();

    // Generate out stylesheet, grabbing the values with get_option.
    // Example:
    // echo ":root{\n";
    // echo "--border-r:" . get_option("wrds_border_r", "0.2rem") . "\n";
    // echo "}\n";


    $custom_css_content = ob_get_clean();
    file_put_contents($my_filepath, $custom_css_content);
}
add_action('admin_init', 'wrds_on_theme_styling_save', 999, 0);

I’m using output buffering here so I can simply echo out all my styling rules rather than appending them to a string, I find that is just easier to manage.

Conclusion

Depending on your use cases you can get a lot fancier than the example I showed above. You could use a colour mixing library to generate shades from a single base colour. You might need to programmatically generate a Google Fonts URL. The benefits of this system are you get to run it once and you can be as complex as you need to be and if you set up your styles with CSS Variables from the start then you get a lot of powerful flexibility and expandability.

Creating an Object Oriented System to Wrap the WordPress Settings API

When you want to allow users to customise your theme or plugin with a setting page the WordPress Settings API allows you to easily and quickly get prototypes up and running. However, you’ll find you end up writing a lot of boilerplate to handle the registering and rendering of each setting that gets in the way. For every setting you need to register the setting, then register the field that displays the setting. We can handle all this in one swoop with a wrapper class.

In this article, we’ll look into creating an object-oriented wrapper classes that handle all the boilerplate for us. This system will be extendable so we can add as many custom input types as we want and it means the file we keep our actual settings data (names, types, etc.) really streamlined.

 

Why not use Advanced Custom Fields?

If you’re looking for something quick for internal use then Advance Custom Fields comes with a user-friendly Options Page system that lets you create new admin pages and sub-pages and handles all the menu and rendering stuff for you. There’s a problem with this though; it’s only available in PRO. Not only do you have to pay for this feature but the ACF license doesn’t allow you to include PRO in free themes/plugins (however if you’re releasing a premium theme/plugin you can include ACF PRO).

ACF’s fields also have a pretty utilitarian look that’s designed the match the existing WordPress ecosystem. Creating an admin page with a customised look gives your plugin a more professional feel to it and allows you to establish a brand.

 

Designing our Base Classes

Before we dive into the code let’s establish what we’re trying to make. We’ll closely match the existing API by creating the following classes: WP_Settings_Page, WP_Settings_Section, and WP_Setting. WordPress divides its admin pages into sections with multiple inputs per section and we’ll match that. When designing out classes I have two main priorities:

Priority 1: Stick closely to the existing WordPress API so it can handle all the complicated stuff. We don’t want to be messing around with WordPress core to add menu links, rendering the page, or writing to/from the database. Not only will this make our code vulnerable to security issues, it means that if anything in core changes our code will break. These are wrapper classes for the API.

Priority 2: Allow the classes to be easily extended so that we can add new page types, input types, etc. If we make our base classes the most barebones versions we can use inheritance to create new classes that extend more bespoke behavior.

 

WP_Settings_Page

This class will handle adding the page to the menu and then rendering a template we provide. We’re also going to give the object a reference to all of its child sections so it can keep track of everything added to it.

For all our classes we’ll take an array of data to configure the class. In this case, all we need is a name, a path to a template, and an icon for the menu.

class WP_Settings_Page
{
    function __construct($data)
    {
        $this->title = $data['title'];
        $this->template = $data['template'];
        $this->icon = $data['icon'];
        $this->data = $data;

        $this->sections = [];

        $this->slug = WRD_SETTINGS_PREFIX . 'page_' . sanitize_title($this->title);

        $this->capabilities = 'manage_options';

        add_action('admin_menu', array($this, 'init'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue'));
    }
}

This function is simple, we just store all the data we need. As I mentioned, we’re going to be keeping track of all the sections inside the page so I’ve initialised an empty array here for that.

In this example, we’re trusting all the data implicitly. If this was going to be exposed we’d want to validate an error report on our title, template file, and icon but in this case, we can trust the input.

We’re also going to add a constant to prefix all our class slugs to prevent any namespace issues. In our case, we’re creating slugs based on the title for the page. When you implement this you’ll probably want the slug to be a seperate key in the parameter so we can change the title without losing any data we might store about the page.

Since we don’t want subclasses to be overwriting the constructor we’ll store all the data we’re given in case it’s useful to someone else. This is a great advantage of recieving an array of data rather than set parameters.

Let’s walk through these callbacks. We can send a reference to the object and one of its methods to keep it all class-based. The init method will just call add_menu_page will all our settings and enqueue is left empty but included for any subclasses that may need it. Our render method just includes the template file. Since the include happens inside our class we can reference the page object from inside the file by using $this. This will allow us to grab that array of sections and render them.

function add_section(WP_Settings_Section $section)
    {
        if (!in_array($section, $this->sections)) {
            $this->sections[$section->slug] = $section;
        }
    }

This function is going to be called by WP_Settings_Section’s constructor. We’ll check that we don’t already have it (just in case) and then add it to our internal list.

 

WP_Settings_Section

This class is fairly identical except this time we also require a WP_Settings_Page be sent in our constructor arguments so we can call $data[‘page’]->add_section($this);

This time init calls add_settings_section rather than add_menu_page and our render functions look like this:

function render()
{
    $this->render_children();
}

function render_children()
{
    foreach ($this->settings as $setting) {
        $setting->render();
    }
}

You might wonder why we bother separating these functions. It means that we can allow templates to specifically choose what they want to show in case any inheriting classes make changes to how the full render looks. For example, we might display the section title before all the children.

Typically we want the page template to handle all the layout stuff for sections and include the title only if the template wants it but it’s good to be to future proof, render_children is specific enough that we always know what we’ll get from it.

This class is pretty spare so you might wonder why we even want it. The WordPress Settings API requires that settings be part of a section and we want to match it. It also can be useful to group our settings together for the page template.

 

WP_Setting

This is our main class and the one that we expect will be inherited from most. There’s not much you might want to change about WP_Settings_Page or WP_Settings_Section but this class will vary a lot based on what kind of field you want.

Our constructor works pretty much the same as before so we’ll skip that. Let’s take a look at the whole class and we can go through it after.

class WP_Setting
{
    function __construct($data)
    {
        $this->data = $data;

        $this->name = $data['name'];
        $this->slug = WRD_SETTINGS_PREFIX . sanitize_title($data['name']);
        $this->section = $data['section']->slug;
        $this->page = $data['page']->slug;

        if (array_key_exists('default', $data)) {
            $this->default = $data['default'];
        } else {
            $this->default = null;
        }

        $data['section']->add_setting($this);

        add_action('admin_init', array($this, 'init'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue'));

        $this->setup($data);
    }

    function setup($data)
    {
    }

    function render_callback()
    {
        $name = $this->slug;
        $val = $this->get_value();

        echo ">input name='$name' value='$val'<";
    }

    function enqueue($hook)
    {
    }

    function register_setting()
    {
        register_setting($this->page, $this->slug);
    }

    function get_value()
    {
        return get_option($this->slug, $this->default);
    }

    function init()
    {
        $this->add_field();
        $this->register_setting();
    }

    function render()
    {
        $attrs = $this->get_wrapper_attrs();
        echo ">div $attrs<";
        echo $this->render_callback();
        echo ">/div<";
    }

    function add_field()
    {
        add_settings_field(
            $this->slug,
            $this->name,
            array($this, 'render_callback'),
            $this->page,
            $this->section,
            [
                "slug" => $this->slug,
                "value" => $this->get_value()
            ]
        );
    }

    function get_wrapper_attrs()
    {
        $attrs = [
            "id" => $this->slug,
            "class" => "wp-setting-wrapper "
        ];

        // Conditional fields
        if (array_key_exists('condition', $this->data)) {
            $attrs["data-wp-setting-condition"] = "#" . $this->data['condition']->slug;

            if ($this->data['condition']->get_value() == false) {
                $attrs["class"] .= "wp-setting-disabled ";
            }
        }

        return $this->attrs_to_string($attrs);
    }

    function attrs_to_string($attrs)
    {
        $output = "";

        foreach ($attrs as $attribute => $value) {
            $output .= " $attribute='$value'";
        }

        return $output;
    }
}

The first thing you probably noticed is the number of empty methods. We’re leaving space here for inheriting classes that might need any of these since we don’t want people messing with our constructor. Setup, render_callback, enqueue, get_value, and register_setting are all designed to be overwritten.

Those last two might surprise you since they handle interacting with the database via the API which is one of the things we don’t want to mess with. Consider a use case in which we want an input that handles font styling- picking a family, the size, weight, and any decoration. We could split this into multiple settings but then our page might feel overly long when these could be a single input that’s styled differently. In this case, we need to register multiple settings in the database and format how we return them.

Otherwise, it’s pretty much what you would expect, it’s wrapping all of the Settings API calls for us so we can make our code less verbose. The real advantage of this object-oriented setup comes from…

 

Extending the Base Classes

The first step to creating an extendable library is to set up a clear file structure. Our system will be fairly simple but we’ll want folders to hold any extensions to our base classes. For us, this will be a ‘inputs’ directory and a ‘views’ directory.

Inputs will contain all our different types of input: Text, Number, File Picker. However, this system will also allow us to create custom inputs with complicated behavior. In my case, I’m going to create a Palette Picker that allows the user to select 5 different colours to form a palette. For each input, we have a directory so we can store any styles or script neatly.

Let’s take a look at an example of extending the base WP_Setting class.

class WP_Setting_Palette extends wrd\results\WP_Setting
{
    public $colors = [
        "bg-color" => "Background",
        "p-color" => "Paragraphs",
        "h-color" => "Headings",
        "btn-color" => "Button Background",
        "btn-text" => "Button Text",
    ];

    function render_callback()
    {
        $top_lvl_id = "wrd-palette_" . uniqid();
        $style = $this->data['style'];
        $palette = [];

        foreach ($this->colors as $slug => $label) {
            $name = $this->data['style'] . "_$slug";
            $val = get_option($name, $this->default[$slug]);
            $id = "wrd-setting-palette_$slug-" . uniqid();

            $palette[$slug] = [
                "name" => $name,
                "val" => $val,
                "label" => $label,
                "id" => $id
            ];
        }

?>

        <div id='<?php echo $top_lvl_id; ?>' class='wrd-setting wrd-setting-palette wrd-setting-uses-palette' data-palette='<?php echo $style; ?>'>

            <div class="wrd-setting__title">
                <h3>
                    <?php echo ucwords($style); ?> Colour Palette
                </h3>
                <span class="wrd-setting__toggle" role="button" tabindex="0">
                    <span class="sr-only">Expand</span>
                </span>
            </div>

            <div class="wrd-setting-palette__pickers">
                <?php foreach ($palette as $c_slug => $c_info) : ?>

                    <div class='wrd-setting-palette__picker'>
                        <input type='color' data-palette-color='<?php echo $c_slug; ?>' data-palette='<?php echo $style; ?>' name='<?php echo $c_info['name'] ?>' class='wrd-setting-palette__input' id='<?php echo $c_info['id'] ?>' value='<?php echo $c_info['val'] ?>' />

                        <label class="wrd-setting-palette__label" for="<?php echo $c_info['id'] ?>" title="<?php echo $c_info['label'] ?>">
                            <div class="sr-only"><?php echo $c_info['label']; ?></div>
                            <div class='wrd-setting-palette__label__color' style="background-color: var(--<?php echo $c_slug ?>);"></div>
                        </label>
                    </div>

                <?php endforeach; ?>
            </div>

            <div class='wrd-setting__reveal'>
                <div class='wrd-setting-palette__example'>
                    <h2>
                        Example of Colour Scheme
                    </h2>
                    <p>
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet massa sit amet lorem suscipit auctor. Sed lobortis velit quis nunc lobortis vehicula. Aliquam condimentum ullamcorper velit, id blandit sapien. Donec sit amet nisl <a>diam and a link</a>.
                    </p>
                    <a class='btn'>
                        Register Today
                    </a>
                </div>
            </div>

        </div>

<?php
    }

    function enqueue($hook)
    {
        wp_enqueue_style('palette-style', get_template_directory_uri() . '/admin/inputs/palette/style.css', array(), '1.0');
        wp_enqueue_script('palette-script', get_template_directory_uri() . '/admin/inputs/palette/script.js', array('jquery'), '1.0');
    }

    function register_setting()
    {
        foreach ($this->colors as $slug => $label) {
            register_setting($this->page, $this->data['style'] . "_$slug");
        }
    }
}

Most of this is templating in the render_callback method (render is reserved so we can add some wrappers). Our parent class is doing all the boring boilerplate! Plus, you can see an example of using those empty methods from before. We enqueue our styling and script using the enqueue function (hooking it is handled for us) and you can see how we might want to have multiple settings fields for one setting.

Now it’s super easy to create this setting:

$theme_customisation_colors_palette_light = new WP_Setting_Palette([
    "name" => __('Light Colour Palette', 'results'),
    "section" => $theme_customisation_colors,
    "page" => $theme_customisation_page,

    "style" => "light",

    "default" => [
        "p-color" => "#270B00",
        "h-color" => "#F9F9EA",
        "bg-color" => "#EB7143",
        "btn-color" => "#F9F9EA",
        "btn-text" => "#EB7143",
    ]
]);

It’s clear what’s happening and we can create multiple palettes easily.

 

All Together Now

To finish off I’m going to showcase the complete code for all those classes. You’ll notice a few changes and things you might want to change. We’re using a function to create all the slugs so we can easily find the slug of an option later. I’ve also added a wrapper for getting an option value that uses the slug function.

There are definitely improvements to be made here. Firstly, changing how slugs work so they’re not tied to the title. Adding support for sub-menu pages, customising the capabilities and admin_bar_menu callback without needing an inheriting class. Another good addition would be to add a callback for sanitizing inputs to WP_Settings and error handling using add_settings_error and the sanitize_callback option in register_setting.

WordPress has some fairly powerful APIs built into it for theme & plugin developers to extend off from and creating wrapper classes and libraries can make it easier and cleaner to use. Feel free to use and adapt this code or use it as the inspiration to build a better set of classes.

 

<?php

// Wrapper classes for managing WP Settings

namespace wrd\results;

define('WRD_SETTINGS_PREFIX', 'wrd_results_setting_');

/**
 * Generates a slug for any WP_Setting classes from a title.
 * 
 * @param string $title Required. Title to convert to slug.
 * @param string $class Optional. Class type to create slug for (WP_Setting, WP_Settings_Page, WP_Settings_Page). Defaults to WP_Setting.
 */
function WP_Settings_Slug($title, $class = "WP_Setting")
{
    switch ($class) {
        case "WP_Settings_Page":
            return WRD_SETTINGS_PREFIX . 'page_' . sanitize_title($title);
            break;
        case "WP_Settings_Section":
            return WRD_SETTINGS_PREFIX . 'section_' . sanitize_title($title);
            break;
        case "WP_Setting":
            return WRD_SETTINGS_PREFIX . sanitize_title($title);
            break;
    }
}

/**
 * Gets setting database values for fields managed by WP_Settings.
 * 
 * String values for true and false are converted to booleans.
 * 
 * @param string $setting Required. Name of the setting, automatically converted to the slug.
 * @param mixed $default. Optional. Fallback value if the setting fallback is not found. Defaults to false.
 */
function wp_setting_value($setting, $default = false)
{
    $option = get_option(WP_Settings_Slug($setting), $default);

    if (strtolower($option) === "true") {
        return true;
    } elseif (strtolower($option) === "false") {
        return false;
    }

    return $option;
}

/**
 * Sets up and manages a WordPress admin setting page. Contains a set of WP_Settings_Section objects.
 *
 */
class WP_Settings_Page
{

    /**
     * Create a WP_Settings_Page instance.
     * 
     * @param array $data{
     *      Required. Array of options for the page.
     * 
     *      @type string $title         Required. Acts as the label and slug.
     *      @type string $template      Required. Path to a PHP template that renders all sections/settings fields.
     * }
     */
    function __construct($data)
    {
        $this->title = $data['title'];
        $this->template = $data['template'];
        $this->icon = $data['icon'];
        $this->data = $data;

        $this->sections = [];

        $this->slug = WP_Settings_Slug($this->title, "WP_Settings_Page");

        $this->capabilities = 'manage_options';

        add_action('admin_menu', array($this, 'init'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue'));
        add_action('admin_bar_menu', array($this, 'adminbar'), 80);
    }

    /**
     * Displays the page template.
     * 
     * The page template is in charge of displaying the sections/setting fields. The template can use $this or $page to reference this object.
     * The sections for the page can be accessed using $this->sections.
     * 
     * @return void
     */
    function render()
    {
        $page = $this;
        include $this->template;
    }

    /**
     * Runs on the admin_enqueue_scripts callback to enqueu setting scripts/styles.
     * 
     * Overwritable: Empty in the base class.
     * 
     * @param string $hook The current admin page.
     * 
     * @see admin_enqueue_scripts Hook
     * @link https://developer.wordpress.org/reference/hooks/admin_enqueue_scripts/
     * 
     * @return void
     */
    function enqueue($hook)
    {
    }

    /**
     * Initialises the setting field on the admin_init hook.
     * 
     * @see admin_init
     * @link https://developer.wordpress.org/reference/hooks/admin_init/
     * 
     * @return void
     */
    function init()
    {
        add_menu_page(
            $this->title,
            $this->title,
            $this->capabilities,
            $this->slug,
            array($this, 'render'),
            $this->icon,
            3
        );
    }

    /**
     * Adds the menu to the adminbar on the admin_bar_menu hook.
     * 
     * @see admin_bar_menu
     * @link https://developer.wordpress.org/reference/hooks/admin_bar_menu/
     * 
     * @return void
     */
    function adminbar($admin_bar)
    {
        if (array_key_exists("adminbar", $this->data) && $this->data['adminbar']) {
            $admin_bar->add_menu([
                'id'    => $this->slug,
                'title' => $this->title,
                'href'  => menu_page_url($this->slug, false),
            ]);
        }
    }

    /**
     * Adds a child WP_Settings_Section to the internal list.
     * 
     * @param WP_Settings_Section $setting Required. The settings section to add.
     */
    function add_section(WP_Settings_Section $section)
    {
        if (!in_array($section, $this->sections)) {
            $this->sections[$section->slug] = $section;
        }
    }
}

/**
 * Sets up and manages a section of a WordPress admin page. Contains a set of WP_Setting objects.
 */
class WP_Settings_Section
{
    /**
     * Creates a WP_Settings_Section instance.
     * 
     * @param array $data {
     *      Required. Array of options for the section.
     * 
     *      @type string $title             Required. Title to label the section. Also becomes the slug.
     *      @type WP_Settings_Page $page    Required. The page to display the section on.
     *      @type string $description       Optional. Descriptive text to accompany the section.
     * }
     */
    function __construct($data)
    {
        $this->title = $data['title'];
        $this->description = $data['description'];
        $this->slug = WP_Settings_Slug($this->title, "WP_Settings_Section");
        $this->page = $data['page']->slug;

        $this->settings = [];

        $data['page']->add_section($this);

        add_action('admin_init', array($this, 'init'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue'));
    }

    /**
     * Renders the section and any child settings.
     * 
     * @return void
     */
    function render()
    {
        $this->render_children();
    }

    /**
     * Displays all WP_Setting children. Useful for WP_Settings_Section::render().
     * 
     * Can be used directly inside WP_Settings_Page views for customising how settings render on that page.
     * 
     * @see WP_Settings_Section::render()
     * 
     * @return void
     */
    function render_children()
    {
        foreach ($this->settings as $setting) {
            $setting->render();
        }
    }

    /**
     * Runs on the admin_enqueue_scripts callback to enqueu setting scripts/styles.
     * 
     * Overwritable: Empty in the base class.
     * 
     * @param string $hook The current admin page.
     * 
     * @see admin_enqueue_scripts Hook
     * @link https://developer.wordpress.org/reference/hooks/admin_enqueue_scripts/
     * 
     * @return void
     */
    function enqueue($hook)
    {
    }

    /**
     * Initialises the settings section on the admin_init hook.
     * 
     * @see admin_init
     * @link https://developer.wordpress.org/reference/hooks/admin_init/
     * 
     * @return void
     */
    function init()
    {
        add_settings_section(
            $this->slug,
            $this->title,
            array($this, 'render'),
            $this->page,
        );
    }

    /**
     * Adds a child WP_Setting to the internal list.
     * 
     * @param WP_Setting $setting Required. The setting to add.
     */
    function add_setting(WP_Setting $setting)
    {
        if (!in_array($setting, $this->settings)) {
            $this->settings[$setting->slug] = $setting;
        }
    }
}

/**
 * Setups and manages a WordPress setting including the database value, saving and rendering. This base class provides a text input and can be overwritten for other input types.
 */
class WP_Setting
{
    function __construct($data)
    {
        /**
         * Creates an instance of WP_Setting.
         * 
         * Inheriting classes should not overwrite __construct. WP_Setting::setup() is provided for subclasses to initialise their data.
         * Any additional options sent to __construct are stored under $this->data for reference. This can be useful for an inheriting classes' settings.
         * 
         * @param array $data{
         * Required. Array of options to configure the setting.
         * 
         * 
         *      @type string $name                       Required. Acts as both the label and slug for the field.
         *      @type WP_Settings_Section $section       Required. Group of settings to be included in. Automatically adds to that WP_settings_Section variable.
         *      @type WP_Settings_Page $page             Required. Admin page to be shown on. Should be parent of $section.
         * 
         *      @type mixed $default                     Optional. The default value for the field.
         * }
         * 
         * @return WP_Setting
         */
        $this->data = $data;

        $this->name = $data['name'];
        $this->slug = WP_Settings_Slug($this->name, "WP_Setting");
        $this->section = $data['section']->slug;
        $this->page = $data['page']->slug;

        if (array_key_exists('default', $data)) {
            $this->default = $data['default'];
        } else {
            $this->default = null;
        }

        $data['section']->add_setting($this);

        add_action('admin_init', array($this, 'init'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue'));

        $this->setup($data);
    }

    /**
     * Configures object settings on initialisation.
     * 
     * Overwriteable: Inheriting classes can use this to initialise options.
     * 
     * @param array $data   Required. The constructor options array.
     * 
     * @return void
     */
    function setup($data)
    {
    }

    /**
     * For internal use only, use WP_Setting::render() instead. Displays the setting input HTML. It does not include the wrapping content.
     * 
     * @see WP_Setting::render()
     * 
     * Overwriteable: Inheriting classes should include a HTML input with the name of $this->slug.
     * 
     * @return void
     */
    function render_callback()
    {
        $name = $this->slug;
        $val = $this->get_value();

        echo "<input name='$name' value='$val'>";
    }

    /**
     * Runs on the admin_enqueue_scripts callback to enqueu setting scripts/styles.
     * 
     * Overwritable: Empty in the base class.
     * 
     * @param string $hook The current admin page.
     * 
     * @see admin_enqueue_scripts Hook
     * @link https://developer.wordpress.org/reference/hooks/admin_enqueue_scripts/
     * 
     * @return void
     */
    function enqueue($hook)
    {
    }

    /**
     * Registers the setting with WordPress.
     * 
     * Overwriteable: Inheriting classes may store multiple values per WP_Setting object.
     * 
     * @see register_setting
     * @link https://developer.wordpress.org/reference/hooks/register_setting/
     * 
     * @return void
     */
    function register_setting()
    {
        register_setting($this->page, $this->slug);
    }

    /**
     * Returns the stored value of the setting. Uses the object default as fallback.
     * 
     * Overwriteable: Inheriting classes may parse their data differently or use defaults differently.
     * 
     * @return mixed value
     */
    function get_value()
    {
        return get_option($this->slug, $this->default);
    }

    /**
     * Initialises the setting field on the admin_init hook.
     * 
     * @see admin_init
     * @link https://developer.wordpress.org/reference/hooks/admin_init/
     * 
     * @return void
     */
    function init()
    {
        $this->add_field();
        $this->register_setting();
    }

    /**
     * Displays the setting including it's required wrapping content.
     * 
     * Should not be overwritten by inheriting classes.
     * 
     * @see WP_Setting::render_callback()
     * 
     * @return void;
     */
    function render()
    {
        $attrs = $this->get_wrapper_attrs();

        echo "<div $attrs>";
        echo $this->render_callback();
        echo "</div>";
    }

    /**
     * Registers setting with WordPress.
     * 
     * @see add_setting_field()
     * 
     * @return void
     */
    function add_field()
    {
        add_settings_field(
            $this->slug,
            $this->name,
            array($this, 'render_callback'),
            $this->page,
            $this->section,
            [
                "slug" => $this->slug,
                "value" => $this->get_value()
            ]
        );
    }

    /**
     * Creates HTML class string dependent on the state of the setting. E.g. adds wp-setting-disabled for conditional fields.
     * 
     * @param array $classes Optional. Array of default classes.
     * 
     * @return string
     */
    function get_wrapper_attrs()
    {
        $attrs = [
            "id" => $this->slug,
            "class" => "wp-setting-wrapper "
        ];

        // Conditional fields
        if (array_key_exists('condition', $this->data)) {
            $attrs["data-wp-setting-condition"] = "#" . $this->data['condition']->slug;

            if ($this->data['condition']->get_value() == false) {
                $attrs["class"] .= "wp-setting-disabled ";
            }
        }

        return $this->attrs_to_string($attrs);
    }

    /**
     * Converts array of attributes to HTML string.
     * 
     * @param array $attrs Required. Array of arrays attributes to convert.
     */
    function attrs_to_string($attrs)
    {
        $output = "";

        foreach ($attrs as $attribute => $value) {

            $output .= " $attribute='$value'";
        }

        return $output;
    }
}

WordPress: How to Loop for Custom Post Type Using Bootsrap

A simple little loop that loops through custom posts in WordPress and also shows how Bootstrap works alongside the custom post type loop. The example is used with 4 columns side by side (with col-md-3), but you can change the number to suit whether it is 2 columns, 3 or 4 etc.

Within the loop, you can change the ‘customposttypegoeshere’ to the name of your custom post type. The other parameters are customisable, i.e. define as many posts per page as you wish (here we are returning 24 posts), and you can also edit the orderby and order to suit.

Within the loop, we have chosen to display the post thumbnail, excerpt and title within each column, but these again can be removed or changed to suit your needs. Again, you can wrap the title in a H2 or H3 tag to suit.

<div class="container-fluid">
<?php 
$loop = new WP_Query( 
array(
'post_type' => 'customposttypegoeshere',
'posts_per_page' => 24,
'orderby' => 'date',
'order' => 'DESC'
)
);
?>
<div class="row">
<?php while ( $loop->have_posts() ) : $loop->the_post();?>
<div class="col-md-3 col-lg-3">
<img src="<?php the_post_thumbnail(); ?>" />
<?php the_title(); ?>
<?php the_excerpt();?>
</div>
<?php endwhile; ?>
</div>
</div>

 

If you need any help with Web Maintenance, Web Development Services or Website Security, get in touch with a member of our team and we will be happy to help.

How to Migrate a Website With the Duplicator Plugin

The WordPress Duplicator plugin has been an incredibly useful tool to use when migrating or cloning a website. In a few clicks, you can have a replica of your site ready to upload to a new server.

As well as migration, you can use Duplicator to take backups of a current website and duplicate a website to use on a staging website for testing,

Depending on the size of your website, you can either use the basic Duplicator plugin or Duplicator Pro which supports larger sites as well as 2 step installs. With Duplicator Pro, you can also take scheduled backups, make use of professional support and cloud storage to Dropbox, Google Drive, Microsoft OneDrive, Amazon S3 and FTP/SFTP.

 

Steps to migrate your website using Duplicator

 

This assumes you already have your new hosting set up and ready to go. If you are replacing a current site, make sure you take a backup of it just in case you need to revert back to it.

  • On your website that you are wanting to migrate, download and install & activate the Duplicator plugin.

  • You should now see Duplicator in the left-hand bar in your dashboard. Click Duplicator > Packages
  • Select ‘Create New’ in the top right-hand corner
  • Click Next. This will then start scanning your site

  • Review the scan results before clicking Build. If there is a problem with file sizes and the package doesn’t build, this can usually be resolved by using Duplicator Pro.
  • Once it has finished building you can download both installer.php and the Archive file.
  • Next, create a new database on the new site you are migrating to. Don’t forget to jot down the Database Name, Database Username and Password as you will need these later.
  • If you have any old files on the site you are migrating to, delete them in file manager before uploading both the downloaded installer.php and Archive file as they are.
  • Next, go to your domain/installer.php. i.e www.example.com/installer.php
  • Follow the instructions and put in the database info you have just jotted down. Confirm the URLs are correct. Test the database connection to ensure it works correctly.
  • The last step usually asks you to log into WordPress so put in your username and password of the old site to log in.

It’s as simple as that! Once you are successfully logged in, make sure any duplicator installation files are removed, the site is indexed and the SSL is working correctly.

If you need help migrating a website or would like to find out about our hosting services, then please get in touch to speak to a member of our team.

Essential WordPress Plugins for Online Businesses

The global pandemic and resulting lockdown measures have forced many businesses to shut and, in turn, has seen a dramatic increase in businesses adjusting to online shopping and other digital services. Many businesses, which once relied on footfall, have now established their online presence to ensure they can continuously deliver their goods and services throughout the pandemic.

With an online presence, comes a new website and a higher demand for web development and website maintenance services. There are a large number of website platforms, but perhaps the most popular (dominating over 60% of the market share) is WordPress.

If you have a WordPress website for your business but aren’t sure what essential plugins you need for your business and improve your SEO, then we have selected the top plugins that you will need below.

  1. Contact Form 7/ WP Forms
    If you are hoping to receive enquiries then you will need a contact form on your site. Two of the best free contact form plugins are Contact Form 7 and WP Forms. WP Forms is very easy to use and good for beginners and the lite version is free but you can pay to use PRO features. Contact Form 7 is a popular plugin and easy to set up with Recaptcha and Flamingo plugin, so you can see a saved list of all messages sent to you in your database.
  2. WooCommerce
    It goes without saying a website will require an eCommerce plugin of some sort! If you have an existing WordPress website or are looking for a brand new website with eCommerce functionality, then WooCommerce is one of the best plugins out there and allows for a huge amount of customisation. You can add extra add-on plugins to ensure your shop’s functionality is exactly how you want. For more information, read our helpful guide to adding eCommerce plugins to your site.
  3. Yoast SEO
    An essential WordPress SEO plugin on your site is Yoast. In a nutshell, SEO improves your website’s visibility for relevant searches. Yoast is a great little plugin that allows you to add a Title and Meta Description to your web pages as well as creating a robots.txt file and sitemap. You can also set breadcrumbs on pages, to allow better navigation between web pages. A lot of plugins also integrate with Yoast, which makes it the most popular choice for an SEO plugin.
  4. Security Plugin
    There are many security plugins to choose from but all are important to ensure your website does not get hacked and your business can keep running. Some of the best WordPress security plugins are WordFence, Sucuri and iThemes Security Pro, which are free but some of the better features are paid for. They will alert you if there is any malware, any unwanted attempt at accessing your site and any other issues.
  5. Live Chat
    Adding a live chat feature will allow you to respond to any queries quickly and efficiently. LiveChat, Tawk.To and Zendesk are some good options for chat plugins. Some have a free trial which then changes to pay monthly after the trial has ended.
  6. Insert Headers and Footers
    Insert Headers and Footers is a great little plugin if you don’t have access to the code or want to add a piece of script to your site – i.e. Google Analytics script.
  7. ReCAPTCHA/Honeypot
    With a contact form allows the potential for spam, so make sure you have some anti-spam plugins to keep your inbox clean! Adding a ReCAPTCHA to your contact form is one method – you can sign up with Google and then install Invisible ReCaptcha and pop in your Site and Secret Key. You can also add Honeypot for Contact Form 7, which adds an invisible field to your contact form which bots will automatically fill in and then they are filtered out. You can also use an alternative Honeypot plugin for other contact forms.
  8. Caching Plugin – WP Rocket
    Keep your site speed fast with WP Rocket – your site will need a caching plugin to decrease the load time of your site and therefore improving your SEO. WP Rocket is one of the best caching plugins – although not free, it is probably one of the easiest ways to ensure a blazing fast site and doesn’t require any programming knowledge. An alternative free caching plugin is W3 Total Cache.

 

These are just a few of the plugins you should be considering adding to your website to ensure your online business is a success. If you are looking for website hosting or website security services or need help adding plugins to your site, then get in touch with a member of our team and we would be happy to assist.