Creating Inspector Controls for Shortcode attributes – Part 1

This is next part in the series of Converting Shortcodes to Gutenberg Blocks.

Shortcodes have been part of WordPress for a long time. As the new block editor is gaining users and popularity, converting these shortcodes to blocks will be helpful. Gutenberg blocks are not only useful for creating custom page layouts, but also make the website building experience better.

When Gutenberg blocks can be used in place of shotcodes, it improves the overall user experience. As we are making this transition to blocks, we need to pay attention and make sure that the inspector controls are user friendly.

When converting a shortcode to a block, one of the first things to do is to figure out how you want to display the options in the sidebar. There is no one size fits all option here. The type of user interface we want to show ideally depends on the type of attribute.

I will use the business directory block as an example as it has many different types of attributes. Each attribute is shown in the Inspector Controls to give the user finer control over displaying their business directory listings.

Controls for simple list attributes

Some of the attributes like format, text, orderby, etc were easy to build in the block sidebar. These have a few static options to choose from and could be put into a select dropdown.

Options in the Shortcode:

'format' => 'list',  // options: list, grid2, grid3, grid4, responsive
'text' => 'excerpt', // options: excerpt, description, none
'orderby' => 'title', // options: date, modified, menu_order, rand, membership_level

Format options control in the block – all the available options are listed in the SelectControl.

const formatOptions = [
    { label: 'List', value: 'list' },
    { label: '2 Columns', value: 'grid2' },
    { label: '3 Columns', value: 'grid3' },
    { label: '4 Columns', value: 'grid4' },
    { label: 'Responsive', value: 'responsive' },
 ];
<SelectControl
    label="Directory Layout"
    value={ format }
    options= { formatOptions }
    onChange = {setDirectoryLayout}
/>
const setDirectoryLayout = format => {
    props.setAttributes( { format } );
};

Options with boolean values in the shortcode

Many of the shortcodes we use have attributes that take in boolean options – true or false. It might also be a ‘yes’ or ‘no’ to make it easier for users to understand and use in the shortcodes. The best way to display them in the block sidebar, is to use a toggle button. You can also use a simple dropdown which is easier, but I feel a toggle button is more intuitive.

The business directory shortcode has the option to display the alphabet list at the top so that businesses can be sorted by their first letter.

Image showing the businesses list with the option to sort by first letter

The shortcode has it as a Yes or No option. In the block, the option can be simply switched on or off using a toggle button.

'alpha'	=> 'no',	//options: yes, no
'show_category_filter' => 'no', //options: yes, no

Toggle options in the block inspector controls.

The toggle button did present a couple of challenges. The toggle button gives the output as true or false, but our shortcode needs the values ‘yes’ or ‘no’. So, the true or false values need to be converted to string values before being passed into our shortcode function.

Another issue was that the toggle attribute needs to be the type ‘boolean’ and the shortcode attribute was the type ‘string’.

One of the important things to remember when converting shortcode to blocks is that we need to keep the name and the type of the attribute the same. As the original attribute cannot be modified in the block, I added another attribute ‘alphaToggle which is a boolean.

alphaToggle: {
  type: 'boolean',
  default: false,
},

The toggle button is displayed using the alphaToggle. When the option is toggled, the alpha option is set which gets passed onto the shortcode function.

<PanelRow>
    <ToggleControl
	label={ __( 'Enable Alpha Search' ) }
	checked={ alphaToggle }
        onChange = {setAlpha}
    />
</PanelRow>

const setAlpha = alphaToggle =>{
        props.setAttributes({alphaToggle})
        !! alphaToggle ? 
        __( props.setAttributes({alpha: 'yes'}) ) : __( 
        props.setAttributes({alpha: 'no'}) );
        
    };

This just needed a little bit of tweaking to make it more user friendly and intuitive. I also feel that the toggle buttons are easier to use than a dropdown sometimes as we can visually see which ones are “on” and which ones are “off”.

In the next part, I will cover the dynamic dropdowns for custom taxonomies and displaying toggle buttons for each option in a single shortcode attribute.

MailChimp lists using Ajax

I recently built a Mailchimp addon plugin for the Chamber Dashboard plugins. The plugin uses the Mailchimp api key to connect to Mailchimp and display the lists in the plugin options page. The admin can select a Mailchimp list to which new member emails can be automatically added.

Without Ajax, the user would have to save the api key first to display the Mailchimp lists dropdown. After they select the list, they would have to save the options again so that their list gets saved.

Using Ajax, it is easy to give them an option to save the api key temporarily and display the Mailchimp list. Once the user selects the list, they can save the options all at once.

Here is how to implement the Ajax functionality. The first step is to add the fields for api key and the MailChimp list.

Here is the callback function that renders the api input field.


function cdash_mailchimp_api_key_render($args){
  //specifying the ajax url
  $ajax_url = admin_url( 'admin-ajax.php' );
  $options = get_option('cdash_addon_options');
  if(!isset($options['cdash_mailchimp_api_key'])){
    $options['cdash_mailchimp_api_key'] = '';
  }
  ?>
  <input type='text' name='cdash_addon_options[cdash_mailchimp_api_key]' id="cdash_mailchimp_api_key" value='<?php echo $options['cdash_mailchimp_api_key']; ?>'>
  <br /><span class="description"><?php echo $args[0]; ?></span><br />
  <?php
  //Displaying the save button only if the api key is empty
  if($options['cdash_mailchimp_api_key'] == ''){
    ?>
    <button type="button" name="cdashmc_save_api_key" class="cdashmc_button" id="cdashmc_save_api_key">Save Api Key</button><span class="cdashmc_success_message save_api"></span>
    <?php
  }else{
    //if api key is not empty, we are displaying the Delete button
    ?>
    <button type="button" name="cdashmc_delete_api_key" class="cdashmc_button" id="cdashmc_delete_api_key">Delete Api Key</button><span class="cdashmc_success_message delete_api"></span>
    <?php
  }
}

The api key render form has a couple of additional buttons. The ‘Save Api Key’ is displayed only if the api key is not saved in the database. If there is a valid Api Key, the ‘Delete Api Key’ is displayed.

The ‘Save Api Key’ button also triggers the JavaScript. When the Save button is clicked, the script checks if there is an api key. If there is a valid api key, it generates a drop down of lists from Mailchimp associated with that api.

The api key from the field is used to connect to MailChimp to get the lists.

Here is the JavaScript and Ajax functions that do the trick.

jQuery(document).ready(function($){
  // When the api key is saved, get the list drop down
  $('#cdashmc_save_api_key').click(function (event) {
    event.preventDefault();
    var api_key = $('#cdash_mailchimp_api_key').val();
    if(api_key == ''){
      $(".cdashmc_success_message.save_api").text(" Please enter an api key.");
    }else{
      $.ajax({
        url: ajaxurl,
        type:'post',
        data: {
          'action': 'cdashmc_list',
          'cdash_mailchimp_api_key': api_key,
        },
        beforeSend: function(){
          // Show loading container
          $("#cdashmc_ajax_loader").css("display", "block");
          $(".cdashmc_api_key_msg").css("display", "none");
        },
        success: function(response){
          $("#cdashmc_list").replaceWith(response);
          $(".cdashmc_api_key_msg").css("display", "none");
          $(".cdashmc_success_message.save_api").text(" API key successfully saved.");
        },
        complete:function(data){
          // Hide image container
          $("#cdashmc_ajax_loader").hide();
          $('#cdashmc_save_api_key').hide();
        }
      });
    }
  });

  $('#cdashmc_delete_api_key').click(function (event) {
    $("#cdash_mailchimp_api_key").val("");
    $("#cdash_mailchimp_list").hide();
    $(".cdashmc_list_desc, .cdashmc_check_api_msg").hide();
    $("#cdashmc_api_delete_notice").css("display", "block");
  });

});

The Ajax function is calling the cdashmc_list function which connects to the server and gets the results back to the Ajax. cdashmc_list calls the function that uses the api to get the Mailchimp lists and send the list as a response back to the Ajax function. Here is the cdashmc_list function.

//Function to display the Mailchimp lists via Ajax
//Used the code from https://rudrastyh.com/mailchimp-api/get-lists.html to display the Mailchimp lists using the api key
function cdashmc_list(){
    $options = get_option('cdash_addon_options');
  if(isset($_POST['cdash_mailchimp_api_key'])){
    $api_key = $_POST['cdash_mailchimp_api_key'];
  }else{
    $api_key = '';
  }
  $response = '';
  if($api_key == ''){
    $response = "No api key found";
  }else{ //if there is an api key
    $data = array(
    	'fields' => 'lists', // total_items, _links
    );
    $url = 'https://' . substr($api_key,strpos($api_key,'-')+1) . '.api.mailchimp.com/3.0/lists/';
    $result = json_decode( cdashmc_mailchimp_curl_connect( $url, 'GET', $api_key, $data) );
    if( !empty($result->lists) ) {
      $response = "<select name = 'cdash_addon_options[cdashmc_list]'>";
      $response .= "<option value=''>Select your Mailchimp List</option>";
      foreach( $result->lists as $list ){
        if ( $options['cdashmc_list'] == $list->id ){
          $selected = 'selected="selected"';
      }else{
          $selected = '';
      }
        $member_count = $list->stats->member_count;
        $response .= "<option value='$list->id' $selected>$list->name ($member_count)</option>";
      }
      $response .= "</select><br />";
      $response .= "<span class='description'>Your members will be subscribed to this list.</span>";
    }elseif ( is_int( $result->status ) ) { // full error glossary is here http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/
    	$response = "<strong> $result->title :</strong> $result->detail";
    }else{
      $response = "<p class='cdashmc_check_api_msg'>Please check your api key.</p>";
    }
    //$response = $api_key;
  }
  die($response);
}
/We only need this as we are doing this only for the logged in users
add_action( 'wp_ajax_cdashmc_list', 'cdashmc_list' );

The last function we need is the cdashmc_list_render. The ‘<div id=”cdashmc_list”></div>’ which displays the Ajax response is inside this function. This function is almost the same as the cdashmc_list().


function cdashmc_list_render($args){
  ?>
  <!-- Display the loading button when Ajax is running -->
  <div id="cdashmc_list"></div><div id="cdashmc_ajax_loader">Loading...</div>
  <!--Display this message when the api key is deleted -->
  <div id="cdashmc_api_delete_notice">Please save the changes and enter the api key again to select the list.</div>
  <?php
  $options = get_option('cdash_addon_options');
  if(!isset($options['cdashmc_list'])){
    $options['cdashmc_list'] = '';
  }
  if($options['cdash_mailchimp_api_key'] == ''){
    echo "<p class='cdashmc_api_key_msg'>Please enter your MailChimp API key.</p>";
  }else{
    $api_key = $options['cdash_mailchimp_api_key'];
    // Query String Perameters are here
    // for more reference please vizit http://developer.mailchimp.com/documentation/mailchimp/reference/lists/
    $data = array(
    	'fields' => 'lists', // total_items, _links
    );
    $url = 'https://' . substr($api_key,strpos($api_key,'-')+1) . '.api.mailchimp.com/3.0/lists/';
    $result = json_decode( cdashmc_mailchimp_curl_connect( $url, 'GET', $api_key, $data) );
    //print_r( $result);


    if( !empty($result->lists) ) {
      ?>
    	<select name = "cdash_addon_options[cdashmc_list]"  id="cdash_mailchimp_list"  >
        <option value="">Select your Mailchimp List</option>
        <?php
    	foreach( $result->lists as $list ){
        ?>
        <option value = "<?php echo $list->id; ?>" <?php selected( $options['cdashmc_list'], $list->id ); ?>><?php echo $list->name . ' (' . $list->stats->member_count . ')'; ?></option>
        <?php
    		//echo '<option value="' . $list->id . '">' . $list->name . ' (' . $list->stats->member_count . ')</option>';
    		// you can also use $list->date_created, $list->stats->unsubscribe_count, $list->stats->cleaned_count or vizit MailChimp API Reference for more parameters (link is above)
    	}
      ?>
    	</select>
      <br /><span class="description cdashmc_list_desc"><?php echo $args[0]; ?></span>
      <?php
    } elseif ( is_array($result) && is_int( $result->status ) ) { // full error glossary is here http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/
    	echo '<strong>' . $result->title . ':</strong> ' . $result->detail;
    }else{
      echo "<p class='cdashmc_check_api_msg'>Please check your api key.</p>";
    }
  }
}

Adding a category filter to custom post type shortcode

The Chamber Dashboard plugins make it very easy to create member listings and display the businesses on the site. The shortcode used to display businesses has a few parameters that can be specified like pagination options, business details that need to be displayed like email, phone number, business category, membership level etc.

The business_category option will take in a category slug and filter the businesses accordingly. Although this is a useful feature, it is not the easiest way to filter the business listings by category. Each category listing should go on a different page. This is sometimes useful, but not if the user wants to just sort through the listings based on category. So, we wanted to add a category filter option to the shortcode.

Adding this new feature to the existing shortcode was the best way as it will ensure that the existing styling options can stay as is.

One of the important requirement was that the businesses from the selected category had to show up on the same page without the user getting redirected to a different page. This can be easily done with Ajax, but in this case using Ajax would have been a bit complicated. I just wanted to add the feature with minimal changes to the shortcode.

So, the first thing I added was another parameter to the shortcode – show_category_filter which controls the category filter dropdown. We did not want to show the dropdown by default. So, it is set to ‘no’ and if it is set to ‘yes’, the dropdown will be displayed.

Displaying the business categories dropdown

Displaying the dropdown is done using wp_dropdown_categories() and since I wanted this function to return the dropdown, I put it inside a function cdash_bus_cat_dropdown()

The dropdown should be displayed only when the category filter is set to yes.

if($show_category_filter == 'yes'){
	$business_list .= '<p>' . cdash_bus_cat_dropdown() . '</p>';
}

This is how it will show on the front end. This is can be styled to match the theme.

Chamber Dashboard Business Directory category filter dropdown

Displaying the businesses from the selected category

Since there was already an option in the shortcode to take in the category, all I had to do was to pass the selected category into that parameter. Rest of if will be taken care of by the shortcode. To do that, I got the slug of the selected category and appended it to the url using jQuery.

jQuery("#cdash_select_bus_category").change(function() {
        var category_value = $(this).val();
        //var base_url = window.location.href.split('?')[0];
        var base_url = $("#cdash_bus_list_page").text();
        //alert(base_url);
        var category_url = base_url + '/?bus_category=' + category_value;
        window.location.href = category_url;
    });

I got the value of the selected category using the $_GET and passed it to the $category parameter.

if(isset($_GET['bus_category'])){
		$category = $_GET['bus_category'];
}

This ensures that the shortcode will display the businesses from the selected category and all the other shortcode parameters will work as usual.

We wanted to display the selected category to the users and an option to clear the selected category.

if($show_category_filter == 'yes'){
	// remove pagination from url
	$pattern = "/page(\/)*([0-9\/])*/i";
	$current_url = home_url( add_query_arg( array(), $wp->request ) );
	$url = preg_replace($pattern, '', $current_url);
	$business_list .= '<p>' . cdash_bus_cat_dropdown();
	if(isset($_GET['bus_category'])){
		$business_list.= '<a href="'.$url.'">Clear Filter</a>';
	}
	$business_list .= '</p>';
	$business_list .= '<p id="cdash_bus_list_page">'.$url.'</p>';
	if(isset($_GET['bus_category'])){
		$bus_cat_slug = $_GET['bus_category'];
		$bus_cat_name = get_term_by('slug', $bus_cat_slug, 'business_category');
		$business_list .= '<p>Category: ' . $bus_cat_name->name . '</p>';
	}
}

Adding login/logout links to selected WordPress Menu

Choosing the best membership plugin for your WordPress site

WordPress is a powerful platform to build and maintain membership sites. As with anything related to WordPress, there are many different plugins available to build a membership site. There are free and paid options. I am going to compare some of the free plugins to help you decide which one works best for your site. Before I go into the different plugins and their features, I want to talk a bit about membership sites.

Skip to the plugins list

What are Membership sites?

Generally any site with pages, posts or other content protected by a login is considered a membership site. This is the simplest form of membership site. There are many different types of membership sites.

  • Sites with online member directories
  • Sites that give benefits to members based on membership level
  • Chambers of Commerce
  • Visitor guides/Travel Bureau sites
  • Websites offering courses/tutorials

Since there are many different types of membership sites, there will obviously be many different types of membership plugins as well. So, how can we choose the plugin that will work best for the site we want to build.

Requirements of Membership sites

If you want to build a membership site, first you need to come up with the requirements for your site. Each type of membership site has a different requirement. So, until you have a clear idea of all the things you want or need on your site, you cannot decide which plugin you can use.

Here are some of the requirements of membership sites. You can download this list here.

Register new members and event attendees

Most of the membership sites will need to register members. So, you will need a registration form for the users to add themselves as a member on your site. These members can be individuals or businesses.

Display members

Displaying members is not a requirement for all the membership sites. Websites that offer courses or tutorials do not need to display their members.

Update member profiles

If your website has a lot of members, it might be easier to have them update their own profiles. This is faster and more accurate as well. In some cases though, you might not need this feature all the time. So, if this feature is available in the free version, it is beneficial, but otherwise, you might want to think before you shell out the money.

Give instant access to members only content

If you want to have some content protected by a login, you need to be able to give your members instant access when they sign up on your site. So, being able to restrict content based on user role or membership level is a huge bonus. Having this feature as part of the membership plugin makes it very easy to set it up.

Email members and/or send out newsletters

One of the major requirements of membership sites is the ability to email members or send out newsletters. Having all your member emails in your email marketing program is beneficial. It saves you a lot of time and is less prone to errors. This feature is available in many of the membership plugins, but it might not be pro feature.

Add contacts (CRM)

If you have a site like chamber of commerce or a visitor bureau site, your members will most likely be businesses. These businesses can have multiple locations and can have multiple people as the point of contact. In this case, it will be nice to have a CRM feature in your membership plugin that will let you add these contacts for each business. Then when you display these members on your site, you can also display the people with their phone numbers and email addresses.

Process monthly/yearly dues or donations

One of the main reasons for having a membership site is to make recurring revenue. So, payment processing is very important. Many of the plugins provide PayPal options in the free versions and other options are available in the paid versions. If you are not sure what payment option you like, it would be best to try out the free version first.

Create reports

Creating monthly and quarterly reports of your revenue is quite useful. While it is true that you can transfer all of the payment information into QuickBooks or other software, it is definitely beneficial to have to this feature as part of the plugin.

You might want to see the difference in revenue from last year and this year or revenue from a particular category of members etc.. Being able to see this information in the WordPress admin would be nice.

Content dripping

Content dripping is making the content available in a pre-scheduled timeline. If you have a website that has some courses, you might want to give access to the first couple of course videos in the first week and one or two videos every week after that.

Now that we have seen the major requirements of membership sites, let’s look at what plugins are available to build these sites.

Free Plugins

  • WP Members Plugin
  • S2 Member
  • Chamber Dashboard Plugins
  • Simple Membership
  • Ultimate Member
  • Restrict Content
  • Members Plugin

Premium Plugins

  • WP eMember
  • WPMU Dev Membership
  • Member Press
  • Magic Members
  • Member Mouse
  • WC Groups/WC Subscriptions
  • Digital Access Pass (DAP)

Below is a comparison chart for the free plugins and some of the major features they support.

Features/PluginWP Members PluginS2 MemberChamber DashboardSimple MembershipUltimate MemberMembers PluginRestrict Content
Register New MembersYesYesYesYesYesNoYes
Login FormYesYesYesYesYesYesYes
Membership LevelsYesYes (5 levels in the free version)YesYesNoNoNo
Membership ListingsProProYesYes (Some pro options)YesNoNo
Process PaymentsProPayPal StandardPayPal Standard, PayPal SubscriptionsPayPal, StripeNoNoPro
Restrict ContentYes Yes Yes Yes Yes Yes Yes
Create ReportsNoNoYesNoNoNoPro
Update Member ProfilesNoYesProCan Delete ProfilesNoNoNo
Newsletter IntegrationProYes (MailChimp, AWeber)Pro (MailChimp)Yes (MailChimp),
Pro (AWeber, ConvertKit)
Pro (MailChimp)NoPro
Content DrippingYesProNoNoNoNoPro

Looking at the chart, you can determine which features you need for your membership site and choose the plugin accordingly. If a feature is not available in a plugin, it is not necessarily a bad thing. If you do not need that feature on your site, you can still go ahead and use that plugin.

Most of these free plugins provide premium options with more features. I think it is always better to try out the free plugins and see which ones you like. Then, you can go ahead and purchase the premium versions.

List of plugins for different types of membership sites

Restrict access to content

  • Restrict Content
  • Members
  • Ultimate Member

Online Member Directories

  • Simple Membership
  • Ultimate Member
  • Chamber Dashboard

Restrict Content based on Membership Levels

  • Simple Membership
  • Chamber Dashboard
  • S2 Member

Courses/Tutorials

  • WP Members
  • Simple Membership
  • S2 Member

The membership site checklist is a handy guide to help you choose the best membership plugins for your site.

Custom Blocks with Advanced Custom Fields

Advanced Custom Fields is an amazing plugin that I have been using for a while. It makes it so easy to build extra fields for WordPress sites. So, when the custom blocks feature came out, I just had to try.

Here is the first block I built to display a post from a custom post type.

Featured Business block
in the editor
Featured Business Block Settings

Creating the block using acf_register_block

//Custom blocks using ACF
add_action('acf/init', 'my_acf_init');
function my_acf_init() {
    // check function exists
	if( function_exists('acf_register_block') ) {

            acf_register_block(array(
              'name'  => 'featured-business',
              'title' => __('Featured Business ACF'),
              'description' => __('A block that display custom post type posts.'),
              'render_callback' => 'my_acf_block_render_callback',
              'category'  => 'formatting',
              'icon'  => 'admin-comments',
              'keywords'  => array('custom post type', 'custom post'),
          ));
     }
}

‘my_acf_block_render_callback‘ function calls the template to display the block content on the front end. The $slug will be the same as the ‘name’ specified in the acf_register_block. The ‘acf_register_block’ and the callback function can be added to your theme’s functions.php.

function my_acf_block_render_callback( $block ) {

	// convert name ("acf/featured-business") into path friendly slug 
        ("featured-business")
	$slug = str_replace('acf/', '', $block['name']);

	// include a template part from within the "template-parts/block" 
       folder
	if( file_exists( get_theme_file_path("/template-parts/block/content- 
            {$slug}.php") ) ) {
		include( get_theme_file_path("/template-parts/block/content- 
               {$slug}.php") );
	}
}

Create the fields

I created three fields for this block. The first one is the ‘Title’ field, the second is the drop down to select a post from the business custom post type and the third field to provide an option to display ‘Featured Image’ of the post selected.

Fields in the Featured Business block

The ‘Block Title’ is a text field and and the “Display Featured Image’ is a radio button field. The ‘Select a Business’ field is a ‘Post Object’ field type and I am filtering by the post type ‘business’.

Creating a template to display the block on the front end.

The template can be placed in template-parts/block/content-featured-business.php. The file name should be content-$slug.php. The block title is displayed as a H3 tag, the post title is liked to the actual post. If display featured image is set to ‘yes’, then a featured image is displayed which is also linked to the actual post.

&amp;lt;?php
/**
 * Block Name: Featured Business
 *
 * This is the template that displays the featured business block.
 */

 // create id attribute for specific styling
 $id = 'featured-business-' . $block['id'];

 // create align class ("alignwide") from block setting ("wide")
 $align_class = $block['align'] ? 'align' . $block['align'] : '';

 ?&amp;gt;
 &amp;lt;div id="&amp;lt;?php echo $id; ?&amp;gt;" class="testimonial &amp;lt;?php echo $align_class; ?&amp;gt;"&amp;gt;
   &amp;lt;h3&amp;gt;&amp;lt;?php the_field('block_title'); ?&amp;gt;&amp;lt;/h3&amp;gt;
   &amp;lt;?php $post_object = get_field('select_a_business'); ?&amp;gt;
   &amp;lt;?php $featured_img_url = get_the_post_thumbnail_url($post_object-&amp;gt;ID,'full'); ?&amp;gt;

   &amp;lt;p&amp;gt;&amp;lt;a href="&amp;lt;?php echo get_permalink($post_object-&amp;gt;ID); ?&amp;gt;"&amp;gt;&amp;lt;?php echo $post_object-&amp;gt;post_title; ?&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
   &amp;lt;?php

   if (get_field('display_featured_image') == 'Yes') {
     // code to run if the above is true
     ?&amp;gt;
     &amp;lt;p&amp;gt;&amp;lt;a href="&amp;lt;?php get_permalink( $post_object ); ?&amp;gt;"&amp;gt;&amp;lt;?php echo get_the_post_thumbnail( $post_object, 'thumbnail' ); ?&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
     &amp;lt;?php
   } 
   ?&amp;gt;

 &amp;lt;/div&amp;gt;

The block looks the same in the editor and on the front end which is very nice. One thing I found is that when I initially add the featured business block and select the business, it did not immediately show up on the back end even though the preview showed the block’s content on the front end. I had to save it as a draft and refresh my browser for the contents of the block to show up in the editor.

ACF does make it very easy to create custom blocks which can be used to build custom templates. Can’t wait to build more custom blocks.

This code is on github if you would like to get it from there. The following sites helped me with building this custom block.

https://www.advancedcustomfields.com/blog/acf-5-8-introducing-acf-blocks-for-gutenberg/

https://www.billerickson.net/building-gutenberg-block-acf/

WordPress Parent and child categories using Ajax

Updated: 4/26/2019

wp_dropdown_categories displays the WordPress categories in a nice drop down. You can choose to display the parent and child categories in a hierarchy. Using Ajax, it is simple to display the child categories based on the selected parent category.

I wrote the WP Category Dropdown plugin to make it easy to add this dropdown to a page or the sidebar. The plugin gives you a shortcode that will let you display all the categories in a dropdown. You can select the parent category and the child categories for that parent will be displayed in another dropdown.

This feature will come in handy when you want to give your users an option to navigate easily through the number of categories you have on your site. The shortcode gives you the option to display categories from a custom post type.

The plugin can differentiate between the categories that do and do not have child categories. If the selected parent category has a child, then you will see another drop down with the list of child categories, otherwise, you will be automatically redirected to the selected category page.

The shortcode gives you an option to display only the categories with children and if there only a few categories without children, you can use “exclude” to hide these categories from the drop down.

The plugin also has an option to add a widget to the sidebar to display the dropdown categories.

The plugin can be downloaded from the WordPress plugins page – https://wordpress.org/plugins/wp-category-dropdown/. Plugin documentation is available here.

Here is a quick demo of the how the plugin works.

Adding WordPress Posts using Ajax

Many WordPress themes and plugins come with some demo data (posts and pages) which the users can install when they activate the plugin. These WordPress posts and pages are designed to give the users a head start on setting up the theme or plugin and give them some readily available dummy data to play with.

In the Chamber Dashboard plugins, although we have extensive documentation and a couple of demo sites for users to see, it is still not easy for some users to visualize how their business directory page would look like with their theme. So, we decided to add some demo content that the users will be able to install when they activate the Business Directory plugin for the first time on their site.

Buttons to add demo data

The demo data buttons show up in a modal on the welcome page. The modal is displayed using the jQuery .dialog() function.

$("#demo_content_buttons").dialog({
    modal: true,
    buttons: [
    {
      text: "Ok",
      click: function() {
        $( this ).dialog( "close" );
      }
    }
  ]
  });

Here is the html for adding the demo data buttons. The “loader” div will show when the data is being added and when it is done, the message will show in the success_message div. Initially the loader div is hidden using jQuery.

 
function cdash_install_demo_content_button(){
  ?>
  <div id="demo_content_buttons">
    <p>Welcome to the Business Directory plugin! Install demo content now, then check out the Business Directory page to see a sample Directory, or dismiss this notice to get started with uploading your own listings.</p>
    <p class="demo_content button cdash_admin button-primary">Install Demo Content</p>
    <p class="demo_content_decline button cdash_admin button-primary">No Thanks!</p>
    <p id="loader">Loading...</p>
    <p class="cdash_demo_success_message"></p>
  </div>
  <?php
}
$("#loader").css("display", "none");

Setting a Transient

One of the basic requirements of this feature is that the demo data install buttons should only show up when the users install and activate the plugins for the first time. Users who have been already using it, should not see it when they update their plugin to the new version. So, to take care of these two scenarios, I added a transient. The transient would get set to true the first time the plugin is activated. The demo data buttons would show only if the transient is not set.

//If plugin is activated for the first time, set the transient
function cdash_show_demo_buttons(){
  //$demo_content_install = get_transient('cdash_demo_content_install');
  if(false === get_transient('cdash_demo_content_install')){
    //show the Install Demo Content button
    cdash_install_demo_content_button();
  }
  set_transient('cdash_demo_content_install', 'true');
}

The transient is also set to true if the plugin is updated. This ensures that if the user is just updating the plugin, they will not see the popup asking them if they want to install demo data.

function cdash_set_demo_transient(){
  set_transient('cdash_demo_content_install', 'true');
}
add_action( 'upgrader_process_complete', 'cdash_set_demo_transient');

Ajax call for the demo data

The actual demo data will be added via Ajax call that gets triggered when the ‘Install’ button is clicked.

$(".demo_content.button").click(function(event){
    event.preventDefault();
    $.ajax({
      url: ajaxurl,
      type:'post',
      data: {
        'action': 'cdash_add_demo_data',
      },
      beforeSend: function() {
        $('#loader').show();
        $(".demo_content_decline").hide();
      },
      complete: function(){
         $('#loader').hide();
         $(".dashboard_page_cdash-about .ui-dialog-buttonpane").show();
      },
      success: function(response){
        $(".cdash_demo_success_message").html(response);
      },
    });
  });

The Ajax function calls the cdash_add_demo_data() function. That function adds the business categories, the demo businesses and a couple of demo pages. If the page or post already exists, it will not be created again.

function cdash_add_demo_data(){
  $response = '';

  //Create demo business categories
  cdash_demo_bus_categories();

  //Creating demo businesses
  $demo_post_id = cdash_insert_demo_business();

  //Creating demo pages
  $demo_page_id = cdash_add_demo_pages();

  //Check if there the post or page exists
  if ( ($demo_post_id != 0) || $demo_page_id !=0 )
    {
       //If post or page does not exists
        $response = 'Demo data successfully added.';
    }
    else {
	//Post or page exists
        $response = 'The data already exists.';
    }
    // Return the String
    die($response);
}

// creating Ajax call for WordPress
add_action( 'wp_ajax_cdash_add_demo_data', 'cdash_add_demo_data' );

Let’s look at the add demo content function in a little bit more detail. The categories are added using the wp_insert_term(). The function to add business categories checks to see if the category already exists so as to avoid any duplicate categories.

The next step is to add the businesses. The demo content is in an array. The demo button will install 3 new posts to the custom post type ‘business’. The first step is to create the post object.

// Create post object
  $demo_content = "Create a description for your business here, or install the Member Updater plugin so your members can update their own listings!";

  $demo_bus_data = array(
    array (
      'title' => 'Karleton’s Bakery',
      'content' => $demo_content,
      'post_category' => 'Restaurants',
      'featured_image' => 'images/demo_content/bakery_photogy-karlis-dambrans.jpg',      
    ),
    array (
      'title' => 'Wong’s Coffee & Tea',
      'content' => $demo_content,
      'post_category' => 'Restaurants',
      'featured_image' =>'images/demo_content/coffee_shopphotoby-jason-wong.jpg',
    ),
    array (
      'title' => 'Brendon’s Camera',
      'content' => $demo_content,
      'post_category' => 'Retail Shops',
      'featured_image' =>'images/demo_content/camera_shop_photoby-brendan-church.jpg',
    ),
  );

The next step is to loop through the post object and insert the post.

foreach ($demo_bus_data as $bus_demo) {
    if ( post_exists( $bus_demo['title'] ) ) {
      $demo_post_id = 0;
    }else{
      //post exists
      $add_pages = array(
          'post_title' => $bus_demo['title'],
          'post_content' => $bus_demo['content'],
          'post_status' => 'publish',
          'post_type' => 'business'
      );

      // Insert the post into the database
      $demo_post_id = wp_insert_post( $add_pages );

Then the category for the post needs to be set.

 wp_set_object_terms( $demo_post_id, $bus_demo['post_category'], 'business_category', $append );

Since the demo data was for a business directory, I wanted to add featured images for the demo businesses. I added the images in a folder inside the plugin and generated an image url from the path. The url and the post_id are used to upload the image to the media library and add it to the post. the cdash_insert_attachment_from_url() uses the code from
https://gist.github.com/m1r0/f22d5237ee93bcccb0d9 to add the featured image.

//attach the image files as featured image for each post
      $getImageFile = plugins_url( $bus_demo['featured_image'], dirname(__FILE__) );
      $url = $getImageFile;
  
      $attach_id = cdash_insert_attachment_from_url($url, $demo_post_id);
  
      set_post_thumbnail( $demo_post_id, $attach_id );

The entire code to insert the demo business with content and the featured image.

function cdash_insert_demo_business(){
  //Code to create a post with demo data
  global $wpdb;
  $user_id = get_current_user_id();
  // Create post object
  $demo_content = "Create a description for your business here, or install the Member Updater plugin so your members can update their own listings!";

  $demo_bus_data = array(
    array (
      'title' => 'Karleton’s Bakery',
      'content' => $demo_content,
      'post_category' => 'Restaurants',
      'featured_image' => 'images/demo_content/bakery_photogy-karlis-dambrans.jpg',      
    ),
    array (
      'title' => 'Wong’s Coffee & Tea',
      'content' => $demo_content,
      'post_category' => 'Restaurants',
      'featured_image' =>'images/demo_content/coffee_shopphotoby-jason-wong.jpg',
    ),
    array (
      'title' => 'Brendon’s Camera',
      'content' => $demo_content,
      'post_category' => 'Retail Shops',
      'featured_image' =>'images/demo_content/camera_shop_photoby-brendan-church.jpg',
    ),
  );

  foreach ($demo_bus_data as $bus_demo) {
    if ( post_exists( $bus_demo['title'] ) ) {
      $demo_post_id = 0;
    }else{
      //post exists
      $add_pages = array(
          'post_title' => $bus_demo['title'],
          'post_content' => $bus_demo['content'],
          'post_status' => 'publish',
          'post_type' => 'business'
      );

      // Insert the post into the database
      $demo_post_id = wp_insert_post( $add_pages );

      wp_set_object_terms( $demo_post_id, $bus_demo['post_category'], 'business_category', $append );

       //attach the image files as featured image for each post
      $getImageFile = plugins_url( $bus_demo['featured_image'], dirname(__FILE__) );
      $url = $getImageFile;
  
      $attach_id = cdash_insert_attachment_from_url($url, $demo_post_id);
  
      set_post_thumbnail( $demo_post_id, $attach_id );
  }
  }
  return $demo_post_id;
}

After the posts are added, two new pages are also added. The pages are added similar to the posts except the ‘post_type’ would be ‘page’ instead of ‘business’ which is a custom post type. Since there are no categories or featured images for the pages, it is just creating the post object and looping through it to add the pages. The function also checks to see if the pages already exists so as to avoid duplicate pages.

Verified by MonsterInsights