Blog posts under the custom post-types tag https://webdevstudios.com/tags/custom-post-types/ WordPress Design and Development Agency Mon, 15 Apr 2024 16:06:36 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.2 https://webdevstudios.com/wp-content/uploads/2022/07/cropped-wds-icon.white-on-dark-60x60.png Blog posts under the custom post-types tag https://webdevstudios.com/tags/custom-post-types/ 32 32 58379230 Optimizing the WordPress Block Editor Experience https://webdevstudios.com/2022/09/22/optimizing-wordpress-block-editor/ https://webdevstudios.com/2022/09/22/optimizing-wordpress-block-editor/#respond Thu, 22 Sep 2022 16:00:40 +0000 https://webdevstudios.com/?p=25369 Our website agency is currently working on a Headless WordPress Multisite project using Next.js. Development is almost completed for this WebDevStudios client. However, as we’ve begun entering content, we realized that we can make improvements to the editing experience for the client. In this blog post, we outline how by optimizing the WordPress Block Editor Read More Optimizing the WordPress Block Editor Experience

The post Optimizing the WordPress Block Editor Experience appeared first on WebDevStudios.

]]>
Our website agency is currently working on a Headless WordPress Multisite project using Next.js. Development is almost completed for this WebDevStudios client.

However, as we’ve begun entering content, we realized that we can make improvements to the editing experience for the client. In this blog post, we outline how by optimizing the WordPress Block Editor experience, we make it easier for editors to perform their jobs.

Background

The set of websites contains one main site and many subsites. We created several custom post types to handle a variety of content, some of which exist on both the main site and the subsites.

All custom post types contain meta fields, created with Advanced Custom Fields, which are used to add custom content to the post. Additionally, we use the content from two post types from the main site on the subsites.

To build the blocks for this website, we used Advanced Custom Field (ACF) blocks. For a Headless WordPress website, ACF makes it easy to structure data the way we want on the frontend, with minimal extraneous information.

Because of the complexity of information and the challenges of building a headless site, we wanted to make sure the editing experience was as straightforward as possible. We identified five ways to make improvements, optimizing the WordPress Block Editor. It’s all detailed below.

Instructional Text

When creating the blocks, we made sure to clearly label each field and add instructions for editors. We included information like image dimension requirements, character or word length limits, and location specifications (sidebar, main content, etc.).

With these instructions, editors can easily jump in and edit the website. They don’t need to search for documentation or be trained. This detail is especially important with 20+ subsites, each with different editors in different locations.

This is the text editor for a Slide Block, which allows editors to state title, description, upload an image, insert a Link Label, and add a Link URL.

Tabs in Blocks

Advanced Custom Fields provides several layout fields, allowing you to organize content. We leveraged the tab field to separate sections and let users enter specific content in each tab.

For example, if we build a block that contains event details and a sponsor, we can create two tabs. One is for the event information, such as venue, time, etc. The other tab is to input the event’s sponsor information, such as the sponsor’s name, logo, and link.

This sounds so simple and obvious, yet this makes it more convenient than usual for users to add content. Would you prefer a long form where you scroll up and down in the editor or a tabbed block?

This is a screenshot of a Hero Carousel editor with two tabs: Slide Content and Sponsor Details.

Leveraging the power of tabs keeps the user interface and experience better, which allows for optimizing the Block Editor experience for the editor.

Dropdowns

To decrease the chance of human error, we identified places where a dropdown select menu should be used instead of a text field.

Using Advanced Custom Fields, we were able to create custom dropdowns generated from post types and meta fields. For example, we have a block on the subsites that needs to return the ID for an organization, which is a custom post type on the main site.

Initially, we created a Text field for that input, but realized that it opened the door for user error. We changed the Text field to a Select field and queried the organization post type on the main site to get the organization ID from the meta field.

This allowed us to create the options in the Select field with the organization name as the display and ID as the return value. Using a Select field in this way significantly reduces the likelihood of human error, and allows for a more bespoke user experience on the backend.

Readonly Fields

Another way we have addressed the possibility of human error is through ‘readonly’ fields. This is helpful when you want to keep data intact on fields like identifiers imported from an API that doesn’t require any manual editing. The user can view the field values and confirm that content has been entered, but cannot change them.

Frontend Previews

ACF blocks are great for creating complex blocks quickly, but the editor loses the Gutenberg experience of seeing a styled block as they’re building a page. Additionally, since we’re using ACF blocks in a Headless WordPress website, an editor can’t see a preview of the page and blocks they’re editing in the Gutenberg editor.

That’s because the website doesn’t use a standard WordPress theme. Plus, the CSS, Javascript, and PHP aren’t pulled into the preview.

To solve this problem, we included a screenshot of the block as a tab, but our lead developer developed functionality that displays an iframe of the block from the frontend when the “Switch to Preview” button is toggled when editing the site. This lets editors preview the block with the content they’ve entered before saving the page.

Conclusion

Working on this complex site reminded our team that not only is frontend usability important, but the editor user experience is also equally important. If an editor can’t enter content correctly, it doesn’t matter what a website looks like or how it functions.

This brings us to the end of learning the ins and outs of optimizing the WordPress Block Editor experience. We’d love to know the steps you take to improve the editing experience for your WordPress websites. Please tell us in the comments below.

The post Optimizing the WordPress Block Editor Experience appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2022/09/22/optimizing-wordpress-block-editor/feed/ 0 25369
Quick Tip: Add Custom Meta Fields to GraphQL https://webdevstudios.com/2021/04/15/add-custom-meta-fields-to-graphql/ https://webdevstudios.com/2021/04/15/add-custom-meta-fields-to-graphql/#respond Thu, 15 Apr 2021 16:00:38 +0000 https://webdevstudios.com/?p=23462 One of WordPress’ greatest strengths, at least in my opinion, is its ability to function as a general-purpose content management system. This is usually done through custom post types and custom meta fields. Putting them onto the website is as easy as adding a few pages to your theme. But what about making a headless Read More Quick Tip: Add Custom Meta Fields to GraphQL

The post Quick Tip: Add Custom Meta Fields to GraphQL appeared first on WebDevStudios.

]]>
One of WordPress’ greatest strengths, at least in my opinion, is its ability to function as a general-purpose content management system. This is usually done through custom post types and custom meta fields. Putting them onto the website is as easy as adding a few pages to your theme.

But what about making a headless WordPress site with GraphQL? How do you add your custom post types and custom meta fields to the GraphQL API?

It’s worth noting that our plugin for creating custom post types, Custom Post Type UI, now does this automatically! But if you’re not using a plugin to create your custom post types, read on to see the code behind the scenes.

I ran into this issue working on a personal website and using the WP GraphQL plugin. This website would be for my music, so I need a custom post type called “Album.”

View the code on Gist.

There are three extra arguments being passed to the register_post_type function: show_in_graphql, graphql_single_name, and graphql_plural_name. The first enables the custom post type to be added to the GraphQL API, and the last two give the name for the object:

GraphiQL IDE showing the album and albums objects

That takes care of our custom post type! But what about the custom meta field?

View the code on Gist.

This code is a little more complicated, so we’ll step through it:

  1. We hook into the graphql_register_types action. This will make sure that our next code is called at the right time.
  2. We call the register_graphql_field field with:
    1. The name of the object we are creating the field on (“Album”)
    2. The name of the field we are creating (externalLinks)
    3. Extra arguments for the function
  3. The extra arguments include a callback function that returns the value of the field.

For our callback function, we’re cheating a little bit. The meta field, find_links, can have multiple values. The purist approach would be to create a new GraphQL object and allow the links to be individually queryable. The pragmatic approach is to instead make our GraphQL field a String and encode the full meta field into a JSON object.

The GraphiQL IDE showing the results of the new custom field

The end result here is that we get the data out of WordPress and into our Next.js frontend where we can use it.

Displaying the contents of the custom meta field on the final website

You can see the full Next.js source code for my website on GitHub.

With a few tricks, it’s not hard to get the full power of WordPress into your Headless WordPress site. Using a decoupled frontend like Next.js means that the final website can be as complicated or as simple as it needs to be. And when it comes time to update or make new content, it’s as easy as… well, as easy as WordPress.

Want to see what we can do for you? Get in touch!

The post Quick Tip: Add Custom Meta Fields to GraphQL appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2021/04/15/add-custom-meta-fields-to-graphql/feed/ 0 23462
WP API: Adding Custom Endpoints https://webdevstudios.com/2016/05/24/wp-api-adding-custom-endpoints/ https://webdevstudios.com/2016/05/24/wp-api-adding-custom-endpoints/#comments Tue, 24 May 2016 16:21:37 +0000 https://webdevstudios.com/?p=13093 Here at WDS, we’re expanding our usage of the WP API. We have had a number of API posts in the past, and now we want to cover custom API endpoints more thoroughly. When working with custom content in WordPress, it is likely that you will come to a point where you want to retrieve that Read More WP API: Adding Custom Endpoints

The post WP API: Adding Custom Endpoints appeared first on WebDevStudios.

]]>
Here at WDS, we’re expanding our usage of the WP API. We have had a number of API posts in the past, and now we want to cover custom API endpoints more thoroughly.

When working with custom content in WordPress, it is likely that you will come to a point where you want to retrieve that custom data via the WordPress REST API. There are a number of methods that you can use to expose your data via the API. This tutorial aims to explain those methods and provide useful examples.


WP API, WordPress, WordPress tutorials, WordPress REST API, WordPress 101, WordPress how-to, learn WordPress

Post Meta

A common way to add custom data to WordPress is to utilize post meta (preferably using CMB2!). If you want to add this custom post meta to the default API output for a post, this is easy to do with register_rest_field(). For a good explanation of how to use register_rest_field(), as well as example usage, it will be helpful to refer to the REST API Documentation.

Adding Custom Post Types

One of the most common way to create custom data in WordPress is to create a Custom Post Type (CPT). When utilizing CPTs, it is very easy to ensure that your data is accessible via the API. When registering your CPT, you can add a few parameters to the register_post_type() function call:

$args = array(
    // other args...
    'show_in_rest'          => true,
    'rest_base'             => 'foo',
    'rest_controller_class' => 'WP_REST_Posts_Controller',
);

register_post_type( 'wds_foo', $args );

Each of these parameters is optional, and you will typically only ever need to use show_in_rest. The show_in_rest parameter allows the REST API to automatically detect your CPT and add it to the registered API Endpoints. Using only the show_in_rest parameter above will give you this for an endpoint: /wp-json/wp/v2/wds_foo.

Suppose you want the API endpoint to be different from the registered post type for some reason. By using the rest_base parameter, you can modify the endpoint to suit your needs. With this parameter, we now have an endpoint that looks like this: /wp-json/wp/v2/foo.

The final parameter, rest_controller_class, is definitely for more advanced usage. This allows you to change the name of the class that the REST API will use to process your CPT. By default, the API will use WP_REST_Posts_Controller for all CPTs. After a bit more explanation below, I’ll provide an example of why you might need this parameter.

Custom Endpoints

There will be times when you find that you need to register your own API endpoints to handle custom functionality. There are two ways of doing this: basic endpoints and the controller pattern. While the REST API Documentation is very good at covering this topic, I would like to add a bit more commentary and some examples.

But first, what exactly is an endpoint? An endpoint has two components: the route (URL), and the method. In this case, method refers to one of the HTTP Methods. Most people are familiar with the GET method, which is what you used to read this page, as well as the POST method, which is what you use to submit data to a website. In the REST API, the same route can do different things depending on what method is used. For example, the posts route /wp-json/wp/v2/posts can list all of the posts on the site using the GET method. But that same route can be used to create a new post when using the PUT or POST method.

When dealing with endpoints, you first specify the route. You can then specify one or more methods that can be used with that route. With that knowledge, let’s take a look at the two ways of setting up your own endpoints.

Basic Endpoints

Basic endpoints are most useful when you have small or uncomplicated pieces of functionality that you need to create. There are endless use cases that you could come up with, but here are a few ideas:

  • Creating a widget that dynamically updates via Ajax and the API
  • Utilizing a custom search engine to return found posts
  • Retrieving data from a custom database table

To create a basic endpoint, you will need to make use of the register_rest_route() function. This function should be called during the rest_api_init action. Here’s a simple example:

add_action( 'rest_api_init', 'myplugin_register_routes' );

/**
 * Register the /wp-json/myplugin/v1/foo route
 */
function myplugin_register_routes() {
    register_rest_route( 'myplugin/v1', 'foo', array(
        'methods'  => WP_REST_Server::READABLE,
        'callback' => 'myplugin_serve_route',
    ) );
}

/**
 * Generate results for the /wp-json/myplugin/v1/foo route.
 *
 * @param WP_REST_Request $request Full details about the request.
 *
 * @return WP_REST_Response|WP_Error The response for the request.
 */
function myplugin_serve_route( WP_REST_Request $request ) {
    // Do something with the $request

    // Return either a WP_REST_Response or WP_Error object
    return $response;
}

For more details, take a look at the documentation on WP-API.org.

Advanced Endpoints

For a more advanced custom API endpoints, you may find that you need multiple endpoints that work together to create your own functionality. The best way to tie all of these together is by utilizing the Controller Pattern for your endpoints. The Controller Pattern is essentially a template for an entire set of routes that are meant to work together. The various routes and supporting functions are all included in a single class that can handle the details. The WP_REST_Controller class that is provided as part of the REST API is meant to provide basic functionality. By extending this class, you can implement the exact features that you need without having to create your own logic for some of the more mundane details, such as the schema of parameters.

To see an example of an entire implementation of the WP_REST_Controller for a custom set of data, take a look at my API Link Manager code. Additionally, the official documentation is a great resource for examples and best practices.

One other use case of an advanced endpoint is to customize an existing controller to suit your needs. For example, suppose you want to add an API endpoint for your own Custom Post Type, but you don’t want those endpoints to be visible to anyone. One option is to extend the existing WP_REST_Posts_Controller class that is part of the API so that you can tweak it without needing to replace all of its functionality. Below is an example of overriding the register_routes() method in your own class. Specifically note the additional show_in_index keys (lines 42, 49, 61, 68, and 80) of the arrays:

<?php

/**
 * Extend the main WP_REST_Posts_Controller to a private endpoint controller.
 */
class JPry_REST_Private_Posts_Controller extends WP_REST_Posts_Controller {

    /**
     * The namespace.
     *
     * @var string
     */
    protected $namespace;

    /**
     * The post type for the current object.
     *
     * @var string
     */
    protected $post_type;

    /**
     * Rest base for the current object.
     *
     * @var string
     */
    protected $rest_base;

    /**
     * Register the routes for the objects of the controller.
     *
     * Nearly the same as WP_REST_Posts_Controller::register_routes(), but all of these
     * endpoints are hidden from the index.
     */
    public function register_routes() {
        register_rest_route( $this->namespace, '/' . $this->rest_base, array(
            array(
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => array( $this, 'get_items' ),
                'permission_callback' => array( $this, 'get_items_permissions_check' ),
                'args'                => $this->get_collection_params(),
                'show_in_index'       => false,
            ),
            array(
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => array( $this, 'create_item' ),
                'permission_callback' => array( $this, 'create_item_permissions_check' ),
                'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
                'show_in_index'       => false,
            ),
            'schema' => array( $this, 'get_public_item_schema' ),
        ) );
        register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
            array(
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => array( $this, 'get_item' ),
                'permission_callback' => array( $this, 'get_item_permissions_check' ),
                'args'                => array(
                    'context' => $this->get_context_param( array( 'default' => 'view' ) ),
                ),
                'show_in_index'       => false,
            ),
            array(
                'methods'             => WP_REST_Server::EDITABLE,
                'callback'            => array( $this, 'update_item' ),
                'permission_callback' => array( $this, 'update_item_permissions_check' ),
                'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
                'show_in_index'       => false,
            ),
            array(
                'methods'             => WP_REST_Server::DELETABLE,
                'callback'            => array( $this, 'delete_item' ),
                'permission_callback' => array( $this, 'delete_item_permissions_check' ),
                'args'                => array(
                    'force' => array(
                        'default'     => false,
                        'description' => __( 'Whether to bypass trash and force deletion.' ),
                    ),
                ),
                'show_in_index'       => false,
            ),
            'schema' => array( $this, 'get_public_item_schema' ),
        ) );
    }
}

When you’re registering your CPT as I described above, you can now use the name of your custom controller class, and the API will automatically make use of that class to include your endpoints in the API.

These are just a few examples to get you thinking about how much you can do with the REST API, as well as how easy it is to customize the API according to your own needs.

Do you have any questions? Feel free to leave a comment below!

The post WP API: Adding Custom Endpoints appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/05/24/wp-api-adding-custom-endpoints/feed/ 11 13093
How to Search Everything Within Custom Post Types https://webdevstudios.com/2015/09/01/search-everything-within-custom-post-types/ https://webdevstudios.com/2015/09/01/search-everything-within-custom-post-types/#comments Tue, 01 Sep 2015 15:39:06 +0000 http://webdevstudios.com/?p=11630 Editor’s note: The following blog post was written in 2015. We can’t guarantee the accuracy of the code and presented solutions, but we’ve chosen to keep this blog post live for those who still find the information useful. If you’re using Custom Post Types in your WordPress site, you might assume that your site search Read More How to Search Everything Within Custom Post Types

The post How to Search Everything Within Custom Post Types appeared first on WebDevStudios.

]]>
Editor’s note: The following blog post was written in 2015. We can’t guarantee the accuracy of the code and presented solutions, but we’ve chosen to keep this blog post live for those who still find the information useful.
If you’re using Custom Post Types in your WordPress site, you might assume that your site search will display those posts along with the standard posts and pages in search results. However, WordPress actually has to be told to not only search custom post types, but a little extra work is needed to find in search all of the additional information that you might be storing in custom fields. Here’s a quick and easy way to search EVERYTHING within Custom Post Types.

I encountered this issue last month when I was working through QA tasks; the client was searching a specific keyword phrase and wondering why one of the most prominent pages on the site (that featured that phrase) wasn’t being returned in results. I saw that the page was an archive for the custom post type “Products” and added that to the array of post types available to the search query:

function wds_cpt_search( $query ) {

    // If we're on the search page, working with the main query, and searched something...
    if ( is_search() && $query->is_main_query() && $query->get( 's' ) ){

        // Include our product CPT.
        $query->set( 'post_type', array(
            'post', 
            'page', 
            'product', // Our Product CPT.
        ) );
    }
}
add_filter('pre_get_posts', 'wds_cpt_search');

But I still wasn’t seeing the page show up in results.

Looking at the Product post type, I saw that we were adding nearly all of our content to each Product via fields in a custom meta box. The keyword phrase I was looking for was specifically being entered into a WYSIWYG field via our CMB2 plugin. I checked and made sure the Product custom post type arguments for public and publicly_queryable were both set to true so it was available to the search query.

function wds_register_product_cpt() {

    // Register our product CPT.
    register_post_type( 'product', array(
        'public'             => true,
        'publicly_queryable' => true,
        'label'              => __( 'Products', 'textdomain' ),
    ) );
}
add_action( 'init', 'wds_register_product_cpt' );

Everything looked good there, so I set about building a custom search query that included the meta fields for each post being searched. By default, WordPress only includes post_title and post_content in its search query.

HANDY TIP: I tested two plugins, WP Search Everything and Relevanssi in the course of working on this issue and they both make it very easy to return all data from all post types in your site search if you want to skip the coding.

The Custom Field Parameters available to WP_Query via WP_Meta_Query are extremely handy (once I got over the impulse to just pop a SQL query into a function and call it good).

I just needed to return text from a single custom field, but the Codex page has a terrific breakdown of different ways to translate queries like “Show products where color=orange OR color=red AND size=small” into arguments you can pass to a new WP_Query object.

My query ended up being a little simpler than that but once I added a function to filter through pre_get_posts that used the following query, I got exactly the results I was looking for!

function wds_cpt_search( $query ) {

    // If we're on the search page, working with the main query, and someone searched for something...
    if ( is_search() && $query->is_main_query() && $query->get( 's' ) ) {

        // Add the CPT's to include in search.    
        $query->set( 'post_type', array( 
            'post', 
            'page', 
            'product' // Especially our product CPT.
        ) );

        // Search meta fields.
        $query->set( 'meta_query',  array(
                
            // Search our wysiwyg field for the field they searched for.
            array(
                'key'     => 'wysiwyg',
                'value'   => $query->get( 's' ),
                'compare' => 'LIKE',
            ),
        ) );
    }
}
add_action( 'pre_get_posts', 'wds_cpt_search' );

 

The post How to Search Everything Within Custom Post Types appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2015/09/01/search-everything-within-custom-post-types/feed/ 1 11630
The Custom Post Type UI Million Download Celebration https://webdevstudios.com/2015/07/17/the-custom-post-type-ui-million-download-celebration/ https://webdevstudios.com/2015/07/17/the-custom-post-type-ui-million-download-celebration/#comments Fri, 17 Jul 2015 17:07:47 +0000 http://webdevstudios.com/?p=11457 In the spring of 2013, I was moved into a “Plugin Czar” position within WebDevStudios. This meant that I was now in charge of the support, maintenance, and development of many of our free plugins available on our WordPress.org account. Custom Post Type UI is one of our many plugins, and undoubtedly our most popular Read More The Custom Post Type UI Million Download Celebration

The post The Custom Post Type UI Million Download Celebration appeared first on WebDevStudios.

]]>
In the spring of 2013, I was moved into a “Plugin Czar” position within WebDevStudios. This meant that I was now in charge of the support, maintenance, and development of many of our free plugins available on our WordPress.org account.

Custom Post Type UI is one of our many plugins, and undoubtedly our most popular one. So popular, that it is the first WebDevStudio plugin that has reached one million downloads with an active install count above 200,000. Not only that, but it maintains a 4.6 out of 5 stars rating with an impressive 87 5-stars at the time of this writing. This is quite an impressive feat to achieve since Brad Williams first tagged version 0.1.0 five years ago.

Lord of the Rings Meme.

What Exactly Is Custom Post Type UI?

In case you are not familiar with what Custom Post Type UI does, it provides a user interface that someone can use to easily provide settings and labels to be used for registering custom post types and custom taxonomies for their install. The plugin then takes those values and does the actual registration, allowing users to start using them right away. Other features as of the 1.1.0 release include import, export, “get code”, and settings listing functionality. The import and export area helps port the plugin’s settings between installs. The “get code” facilitates moving where the post type and taxonomy registration occurs, while the settings listing acts as a quick view of all your settings with our plugin.

It really takes the pain out of having to type out all the necessary code to register post types or taxonomies and helps non-coders to get a start with extending their sites content. It’s the ease of use and aid to which I attribute its success.

Evolution

Much of the development of the plugin in the early years was the gradual addition of features and fields for associated parameters available to post types and taxonomies. This allowed for more flexibility with the settings for users. By the time I first stepped into maintaining the plugin, we were at the 0.8.0 release, and that was the first release I pushed up to the WordPress.org repository.

Custom Post Type Settings in CPTUI 0.2.0
Custom Post Type Settings in CPTUI 0.2.0
Custom Taxonomy Settings in CPTUI 0.2.0
Custom Taxonomy Settings in CPTUI 0.2.0

During this time, the seeds of the first major shift were planted. I wanted to make it easier to develop the plugin and add new features, but the codebase at the time had slowly become an unintentionally complex. I also felt that the actual UI was not keeping up with the times of the WordPress Dashboard, and also mixed the settings for post types and taxonomies too much.

Because of this, I set out to do the first major update to Custom Post Type UI. This planned update included both a completely redone UI for the users, as well as a much easier to use codebase for myself and others to develop with. In the winter of 2013, I branched off from our GitHub copy and got to work…then development stopped. It wasn’t for any specific reason that things came to a halt, other than just being busy with other work. It wouldn’t be till winter of 2014/2015 when the initial rewrite would be completed and what I thought would be release ready.

Tremulous Times

No matter how much testing you do, or how solid you believe the code to be, massive codebase overhauls are always going to be difficult and rarely go off without a hitch. This is definitely true with Custom Post Type UI in early 2015. Not enough testing went into the changes for Custom Post Type UI 0.9.0, and I will not point blame at any specific aspect of update testing for plugins. It just is what it is. Very shortly after the first attempt at the 0.9.0 release, the forums lit up citing various issues that became evident as users clicked the update button. Live sites were encountering issues and broken links, and it was related to the new release.

I got a very nice tip from the WordPress Plugin Moderation team about doing another release that simply re-installs the last working version. This idea, coupled with the fact that I preserved the original settings and was simply converting the saved settings into a new option row honestly saved myself more headache than I likely realize. I was able to instruct users that they could upgrade to 0.9.5 which would take them back to the previous stable codebase and regain all their original settings while I worked out the missed bugs.

As I started getting bugs taken care of, I started pushing them out, but it was still a long process. One consolation is that I got to get the version as 1.0.x. I feel it is a more fitting version number for such a massive release. It was not until the 1.0.8 release that everything appeared to finally die down and upgrade hickups stopped for users who stuck with it. It is those who stuck with me and worked with me to get their sites back in proper working order that I personally thank the most.

Change Is Hard

Outside of the undetected bugs was the user-facing side of things–the UI for the plugin. The user experience of the new version got mixed reviews from users, especially those who had been using the plugin for a long time already. While some loved it or did not mind the change enough to raise concern, others found it to be harder to use and involved too many clicks to achieve the same results. I did not ignore that feedback, but did put much of it on the backburner for later review. It was my intent to get the functionality back at square one first. While working on the last couple point releases for the 1.0.x line, I was also working on the 1.1.x release. At that point, I started really reviewing the feedback and doing what I could to aid the users there. A lot of focus was put into reducing the amount of clicking needed to get things done, and also getting some new or returning features into the plugin.

Custom Post Type Settings in CPTUI 1.1.x
Custom Post Type Settings in CPTUI 1.1.x

Thankfully the 1.1.x release has been notably less rocky and I do not believe there have been any issues reported related to the changes it provided. Much of this can be attributed to the fact that majority of the changes were not related to the setting values. Instead the release was primarily cosmetic and related to display of the user settings.

Looking Beyond One Million

It is hard for me to say what exactly is in store for Custom Post Type UI in the future. At the time of this writing, there are ten open enhancement issues tagged for 1.2. However, I would not call any of them critical to get in as soon as possible. I would love more to get more people up-to-date on the current version and let it be the stable version for awhile. Looking at our stats page, we still have reported active installs using as far back as version 0.6. While I can sit here scratching my head as to why, I also have to consider that that version is stable enough and still meeting the needs of 0.6% of our users. If it is not breaking for them, and there is no security concerns, then it is not all bad that they are still marching on. There is also the minimum version requirement to keep in mind. There are still WordPress installs active and out in the wild that are not running WordPress 3.8 or higher. Until they are, those users are not going to be notified that there is even an update available.

What One Million Downloads Represents

I believe this milestone represents the fact that making features usable and more user-friendly to the “average Joe” can take you a long ways. Custom Post Type UI made it easier for more people to tap into the power and customization ability that custom post types and taxonomies offer to a WordPress powered website. Because of that ease of use, many have added it to their toolbox for every website they have or work on, and recommend it to their friends. All of those points have lead us to where we are today, and where we will continue to go in years to come.

If you have not given Custom Post Type UI a shot, and are interested, you can install it via your Plugin Install screen, or by downloading from the Custom Post Type UI WordPress.org page. We’re super excited about hitting this major milestone, but there’s still work to be done! We would love to hear from you and receive your feedback.

The post The Custom Post Type UI Million Download Celebration appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2015/07/17/the-custom-post-type-ui-million-download-celebration/feed/ 1 11457
How to Replace WordPress Default Taxonomy Metabox with a Radio Select Metabox https://webdevstudios.com/2013/07/08/replace-wordpress-default-taxonomy-metabox-with-a-radio-select-metabox/ https://webdevstudios.com/2013/07/08/replace-wordpress-default-taxonomy-metabox-with-a-radio-select-metabox/#comments Mon, 08 Jul 2013 17:38:38 +0000 http://webdevstudios.com/?p=7581 There have been many times we have wanted to use taxonomies as a 1-to-1 ratio with a post or custom post types. This is arguably more effective than using metadata because as Alex King pointed out, using a taxonomy query is more efficient than using a meta query, not to mention all the other cool Read More How to Replace WordPress Default Taxonomy Metabox with a Radio Select Metabox

The post How to Replace WordPress Default Taxonomy Metabox with a Radio Select Metabox appeared first on WebDevStudios.

]]>
There have been many times we have wanted to use taxonomies as a 1-to-1 ratio with a post or custom post types. This is arguably more effective than using metadata because as Alex King pointed out, using a taxonomy query is more efficient than using a meta query, not to mention all the other cool things that are done by default with custom taxonomies (i.e. admin columns).

This snippet replaces the default taxonomy checkbox select metabox for hierarchical taxonomies with a radio select metabox, which effectively limits your taxonomy selection to one term. Just include this file from your functions.php or plugin file and update the parameters at the bottom.

Note: A radio select metabox will get out of hand quickly if more than a few terms are needed.

Update:  Helen Housandi was kind enough to look over our snippet and provide some feedback. Thanks to that, there are some pretty awesome improvements to the class.

Update 2: Code is now on Github. Forks/pull requests welcome.

<?php

if ( !class_exists( 'WDS_Taxonomy_Radio' ) ) {
   /**
    * Removes and replaces the built-in taxonomy metabox with our radio-select metabox.
    * @link  http://codex.wordpress.org/Function_Reference/add_meta_box#Parameters
    */
   class WDS_Taxonomy_Radio {

      // Post types where metabox should be replaced (defaults to all post_types associated with taxonomy)
      public $post_types = array();
      // Taxonomy slug
      public $slug = '';
      // Taxonomy object
      public $taxonomy = false;
      // New metabox title. Defaults to Taxonomy name
      public $metabox_title = '';
      // Metabox priority. (vertical placement)
      // 'high', 'core', 'default' or 'low'
      public $priority = 'high';
      // Metabox position. (column placement)
      // 'normal', 'advanced', or 'side'
      public $context = 'side';
      // Set to true to hide "None" option & force a term selection
      public $force_selection = false;


      /**
       * Initiates our metabox action
       * @param string $tax_slug      Taxonomy slug
       * @param array  $post_types    post-types to display custom metabox
       */
      public function __construct( $tax_slug, $post_types = array() ) {

         $this->slug = $tax_slug;
         $this->post_types = is_array( $post_types ) ? $post_types : array( $post_types );

         add_action( 'add_meta_boxes', array( $this, 'add_radio_box' ) );
      }

      /**
       * Removes and replaces the built-in taxonomy metabox with our own.
       */
      public function add_radio_box() {
         foreach ( $this->post_types() as $key => $cpt ) {
            // remove default category type metabox
            remove_meta_box( $this->slug .'div', $cpt, 'side' );
            // remove default tag type metabox
            remove_meta_box( 'tagsdiv-'.$this->slug, $cpt, 'side' );
            // add our custom radio box
            add_meta_box( $this->slug .'_radio', $this->metabox_title(), array( $this, 'radio_box' ), $cpt, $this->context, $this->priority );
         }
      }

      /**
       * Displays our taxonomy radio box metabox
       */
      public function radio_box() {

         // uses same noncename as default box so no save_post hook needed
         wp_nonce_field( 'taxonomy_'. $this->slug, 'taxonomy_noncename' );

         // get terms associated with this post
         $names = wp_get_object_terms( get_the_ID(), $this->slug );
         // get all terms in this taxonomy
         $terms = (array) get_terms( $this->slug, 'hide_empty=0' );
         // filter the ids out of the terms
         $existing = ( !is_wp_error( $names ) && !empty( $names ) )
            ? (array) wp_list_pluck( $names, 'term_id' )
            : array();
         // Check if taxonomy is hierarchical
         // Terms are saved differently between types
         $h = $this->taxonomy()->hierarchical;

         // default value
         $default_val = $h ? 0 : '';
         // input name
         $name = $h ? 'tax_input['. $this->slug .'][]' : 'tax_input['. $this->slug .']';

         echo '<div style="margin-bottom: 5px;">
         <ul id="'. $this->slug .'_taxradiolist" data-wp-lists="list:'. $this->slug .'_tax" class="categorychecklist form-no-clear">';

            // If 'category,' force a selection, or force_selection is true
            if ( $this->slug != 'category' && !$this->force_selection ) {
               // our radio for selecting none
               echo '<li id="'. $this->slug .'_tax-0"><label><input value="'. $default_val .'" type="radio" name="'. $name .'" id="in-'. $this->slug .'_tax-0" ';
               checked( empty( $existing ) );
               echo '> '. sprintf( __( 'No %s', 'wds' ), $this->taxonomy()->labels->singular_name ) .'</label></li>';
            }

         // loop our terms and check if they're associated with this post
         foreach ( $terms as $term ) {

            $val = $h ? $term->term_id : $term->slug;

            echo '<li id="'. $this->slug .'_tax-'. $term->term_id .'"><label><input value="'. $val .'" type="radio" name="'. $name .'" id="in-'. $this->slug .'_tax-'. $term->term_id .'" ';
            // if so, they get "checked"
            checked( !empty( $existing ) && in_array( $term->term_id, $existing ) );
            echo '> '. $term->name .'</label></li>';
         }
         echo '</ul></div>';

      }

      /**
       * Gets the taxonomy object from the slug
       * @return object Taxonomy object
       */
      public function taxonomy() {
         $this->taxonomy = $this->taxonomy ? $this->taxonomy : get_taxonomy( $this->slug );
         return $this->taxonomy;
      }

      /**
       * Gets the taxonomy's associated post_types
       * @return array Taxonomy's associated post_types
       */
      public function post_types() {
         $this->post_types = !empty( $this->post_types ) ? $this->post_types : $this->taxonomy()->object_type;
         return $this->post_types;
      }

      /**
       * Gets the metabox title from the taxonomy object's labels (or uses the passed in title)
       * @return string Metabox title
       */
      public function metabox_title() {
         $this->metabox_title = !empty( $this->metabox_title ) ? $this->metabox_title : $this->taxonomy()->labels->name;
         return $this->metabox_title;
      }


   }

   $custom_tax_mb = new WDS_Taxonomy_Radio( 'custom-tax-slug' );

   // Update optional properties

   // $custom_tax_mb->priority = 'low';
   // $custom_tax_mb->context = 'normal';
   // $custom_tax_mb->metabox_title = __( 'Custom Metabox Title', 'yourtheme' );
   // $custom_tax_mb->force_selection = true;

}

View or fork on github.

The post How to Replace WordPress Default Taxonomy Metabox with a Radio Select Metabox appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2013/07/08/replace-wordpress-default-taxonomy-metabox-with-a-radio-select-metabox/feed/ 12 7581