Making A Bootstrap Modal Plugin For WordPress

written by William Patton on April 4, 2017 in WordPress Custom and WordPress Plugins with no comments

I see a lot of sites using Bootstrap and many of them use additional plugins to make their popups. This post walks you through making a plugin to add Bootstrap 4 popups to your WordPress site. You can grab a version of the code used in this article from GitHub, scroll to the end of the article to grab it.

The idea is to build a plugin that can be used to add bootstrap modal popups to a site. There’s also a module for including the Bootstrap styles and scripts through the plugin if you don’t already have them on the site through the theme or another plugin.

The bootstrap modal plugin example in the article is somewhat of a minimal example but it’s ripe for customization and built with forethought for extendability.

If you’re building a Bootstrap theme you can pair it with the Bootstrap Navwalker for WordPress.

The Bootstrap Modal WordPress Plugin

To get started quickly we’ll use a plugin starter template. I wrote a very long time ago about why you should use a custom functionality plugin and made an extremely basic demo. Since then I switched to using a much more well rounded starter functionality plugin from my friend Dave.

The plugin is setup with a good folder structure, defines some useful constants and has an example of how to include individual features.

The build structure we’ll use is having a main plugin file that contains references to other modules in the /includes/ folder. We’ll separate our code into modules so that it’s easier to maintain and modify. If you planned to have the theme add Bootstrap styles and scripts you’d be putting them inside the /css/ and /js/ directories respectfully.

To get started first rename the functionality-plugin.php file in the root to bootstrap-modal-plugin.php and modify the header to add your details.

bootstrap modal popup plugin directory structure

The Modal Post Type

We’ll be using a custom post type to store our modal content and settings. Being able to easily create additional items, rather than changing a single item, could be useful when extending.

Add a file in the /includes/ folder called bootstrap_modal_cpt.php

<?php
// Register Custom Post Type
function bootstrap_modal_post_type() {

	$labels = array(
		'name'                  => _x( 'Modals', 'Post Type General Name', 'bootstrap_modal' ),
		'singular_name'         => _x( 'Modal', 'Post Type Singular Name', 'bootstrap_modal' ),
		'menu_name'             => __( 'Modals', 'bootstrap_modal' ),
		'name_admin_bar'        => __( 'Modal', 'bootstrap_modal' ),
		'archives'              => __( 'Modal Archive', 'bootstrap_modal' ),
		'attributes'            => __( 'Model Attribute', 'bootstrap_modal' ),
		'parent_item_colon'     => __( 'Modal:', 'bootstrap_modal' ),
		'all_items'             => __( 'All Modals:', 'bootstrap_modal' ),
		'add_new_item'          => __( 'Add New Modal', 'bootstrap_modal' ),
		'add_new'               => __( 'Add New', 'bootstrap_modal' ),
		'new_item'              => __( 'New Modal', 'bootstrap_modal' ),
		'edit_item'             => __( 'Edit Modal', 'bootstrap_modal' ),
		'update_item'           => __( 'Update Modal', 'bootstrap_modal' ),
		'view_item'             => __( 'View Modal', 'bootstrap_modal' ),
		'view_items'            => __( 'View Modals', 'bootstrap_modal' ),
		'search_items'          => __( 'Search Modals', 'bootstrap_modal' ),
		'not_found'             => __( 'Not found', 'bootstrap_modal' ),
		'not_found_in_trash'    => __( 'Not found in Trash', 'bootstrap_modal' ),
		'featured_image'        => __( 'Featured Image', 'bootstrap_modal' ),
		'set_featured_image'    => __( 'Set featured image', 'bootstrap_modal' ),
		'remove_featured_image' => __( 'Remove featured image', 'bootstrap_modal' ),
		'use_featured_image'    => __( 'Use as featured image', 'bootstrap_modal' ),
		'insert_into_item'      => __( 'Insert into item', 'bootstrap_modal' ),
		'uploaded_to_this_item' => __( 'Uploaded to this item', 'bootstrap_modal' ),
		'items_list'            => __( 'Items list', 'bootstrap_modal' ),
		'items_list_navigation' => __( 'Items list navigation', 'bootstrap_modal' ),
		'filter_items_list'     => __( 'Filter items list', 'bootstrap_modal' ),
	);
	$args = array(
		'label'                 => __( 'Modal', 'bootstrap_modal' ),
		'description'           => __( 'Content to be Output in a Modal', 'bootstrap_modal' ),
		'labels'                => $labels,
		'supports'              => array( 'title', 'editor', 'revisions', 'custom-fields', ),
		'hierarchical'          => false,
		'public'                => true,
		'show_ui'               => true,
		'show_in_menu'          => true,
		'menu_position'         => 5,
		'menu_icon'             => 'dashicons-slides',
		'show_in_admin_bar'     => true,
		'show_in_nav_menus'     => true,
		'can_export'            => true,
		'has_archive'           => false,		
		'exclude_from_search'   => true,
		'publicly_queryable'    => true,
		'capability_type'       => 'page',
		'show_in_rest'          => true,
	);
	register_post_type( 'bootstrap_modal', $args );

}
add_action( 'init', 'bootstrap_modal_post_type', 0 );

Bootstrap Modal Plugin WordPress Dashboard Admin EntryAlthough there is a lot of labels and options involved in creating a custom post type it’s not as complex as it seems.

Most of the label names are self explanatory so here’s the highlights:

We make a post type called ‘bootstrap_modal’ and set various labels used in the dashboard. The post type supports similar items as posts and pages. It doesn’t appear in archives or searches but is available via the REST API (useful to futureproof).

The final step is to include this file from the root plugin file. Add this to the end of the root plugin file.

// adds the custom post type used to create modals
include( FUNC_PLUGIN_DIR . 'includes/bootstrap_modal_cpt.php' );

The Modal Creation And Render Logic

Next we need the logic used to build the markup, render it and add the triggers that make the modal toggle.

In the file containing the logic needed to output the modal I’ve broken everything into smaller functions called from a main function. The main function looks like this:

// this is the main function. it gets the modal and outputs it, and the
// js triggers, into the page at the end
function bootstrap_modal_add_to_page(){
// add the modal to pages that are not front_page or home
	if( !is_front_page() || !is_home() ){
		// get the data for modal - title, content
		$data = bootstrap_modal_get();
		// generate the markup for the modal
		$markup = bootstrap_modal_generate( $data );
		// for extendability the option is exposed to return or echo modal
		// tell it to echo the modal in render function
		$options['echo'] = true;
		$result = false;
 		// render the markup to the page. returns true if it was rendered
 		$result = bootstrap_modal_render($markup, $options);
 		// if the markup was rendered add trigger script
 		if($result){
 			bootstrap_modal_triggers();
 		}
 	}
} 

That main function – and the functions it references, are going to be inside a file again in the /includes/ folder. Make a file bootstrap_modal.php. That file will contain the main function noted above and all the functions it calls to get, generate and render the modal and triggers. The full file looks like this.

<?php
/**
 * Main function for the modals.
 *
 * this is the main function. it gets the modal data, generates the markup then
 * outputs it, and the triggers, to the page.
 *
 * @since 1.0.0
 *
 * @author William Patton <will@pattonwebz.com>
 *
 */
function bootstrap_modal_add_to_page(){
 	// only add the modal to pages that are not front_page or home
 		if( ! is_front_page() || ! is_home() ){
 		// gets the data for modal - title & content
 		$data = bootstrap_modal_get();
 		// generate markup for the modal
 		$markup = bootstrap_modal_generate( $data );
 		// for extendability the option is there to return or echo modal
 		// we tell it to echo in the render function here
 		$options['echo'] = true;
 		$result = false;
 		// render the markup to the page. returns true if the modal was rendered
 		$result = bootstrap_modal_render($markup, $options);
 		// if the markup was rendered add trigger script immediately after
 		if($result){
 			bootstrap_modal_triggers();
 		}
 	}
}
// hook the main function to wp_footer
add_action( 'wp_footer', 'bootstrap_modal_add_to_page', 100 );

/**
 * Get Modal Data
 *
 * gets the title and content for the the modal and returns it
 *
 * @since 1.0.0
 *
 * @author William Patton <will@pattonwebz.com>
 *
 * @param int $post_id An ID of a post to get the data from
 * @return array $data Array with the title and content
 *
 */
function bootstrap_modal_get( $post_id = 0 ){
 	// if no id was passed then get latest modal data
 	if($post_id === 0 ){
 		$args = array(
 			'numberposts' => 1,
 			'post_type' => 'bootstrap_modal'
 		);
 		$latest_modal = get_posts($args);
		$latest_modal_id = absint( $latest_modal[0]->ID );
	}
 	// grab title and post content and return them
 	$data['title'] = get_the_title( $latest_modal[0]->ID );
 	$data['content'] = $latest_modal[0]->post_content;
 	// return the data array
 	return $data;
};

/**
 * Generate Markup
 *
 * builds the markup needed to create a bootstrap modal popup
 *
 * @since 1.0.0
 *
 * @author William Patton <will@pattonwebz.com>
 *
 * @param array $options Array with modal data and options
 * @return string $modal_markup Markup to render
 *
 */
function bootstrap_modal_generate( $options = array() ){
 	// if we have no options or very small body content return
 	if( empty($options) && strlen( $options['content'] ) > 5 ){
 		return;
 	}
 	// we'll be generating a standard modal with a header, close button and a body
 	// buffer the output
 	ob_start(); ?>
 	<!-- Modal -->
 	<div class="modal fade" id="bsModalPlugin" tabindex="-1" role="dialog" aria-labelledby="bsModalPluginLabel" aria-hidden="true">
 		<div class="modal-dialog" role="document">
 			<div class="modal-content">
 				<div class="modal-header">
 					<?php if( $options['title'] ) { ?>
 						<h5 class="modal-title" id="bsModalPluginLabel"><?php echo esc_html( $options['title'] ); ?></h5>
 					<?php } ?>
 					<button type="button" class="close" data-dismiss="modal" aria-label="Close">
 						<span aria-hidden="true">&times;</span>
 					</button>
 				</div>
 				<div class="modal-body">
 					<?php echo $options['content']; ?>
 				</div>
 				<div class="modal-footer">
 					<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
 				</div>
 			</div>
 		</div>
 	</div>
 	<!-- END Modal -->
 	<?php
 	// get the buffer
 	$modal_markup = ob_get_clean();
 	// return the markup for render
 	return $modal_markup;
}


/**
 * Render
 *
 * render the markup to the page or return it
 *
 * @since 1.0.0
 *
 * @author William Patton <will@pattonwebz.com>
 *
 * @param string $modal An ID of a post to get the data from
 * @param array $options Array containing options for output
 * @return booleen If we rendered return true else return string $modal
 *
 */
function bootstrap_modal_render($modal, $options){
 	// test we have markup to output.
 	// done by checking length is greater than an arbritary amount of characters
 	if( strlen( $modal ) > 10 ){
 		// when echo is passed as true do that or we should return the markup
 		if( true === $options['echo'] ){
 			echo $modal;
 			return true;
 		}
 		// since echo was false return $modal again
 		return $modal;
 	}
}

/**
 * Triggers
 *
 * outputs a trigger script to the page to show the modal
 *
 * @since 1.0.0
 *
 * @author William Patton <will@pattonwebz.com>
 *
 */
function bootstrap_modal_triggers(){
 // trigger the modal with javascript after a number of seconds
 ?>
 <script>
 	jQuery( document ).ready( function(){
		// this is the time (in seconds) that the modal will show after
 		var secToShowAfter = 60;
 		var bootstrap_modal_trigger_timer = setTimeout( function(){
 			jQuery('#bsModalPlugin').modal('show');
 		}, (1000 * secToShowAfter) );
 	});
 </script>
 <?php
}

There are comments throughout the code to explain each individual piece. Separating everything into small functions that handle individual parts of the task as a whole means that you should easily be able to find and modify what you need if you wanted to change how things work in your version.

The main function here is hooked to wp_footer(). The function will run in the footer output the modal right at the end of the page.

Include this file from the root plugin file as well by placing this at the end:

// this is the logic to generate and output modals from the cpt
include( FUNC_PLUGIN_DIR . 'includes/bootstrap_modal.php' );

Including Bootstrap through the Plugin – Optional

If your theme, or another plugin, doesn’t already include Bootstrap Styles and Scripts then we can easily let the plugin handle that. First we need to get the styles and scripts and place them in the plugin folder. Grab those and copy them into the plugin directory placing styles in the /css/ folder and scripts in /js/.

I suggest you include both the minified and unminified versions as I feel generally it’s a good practice. You could build the files to only include the styles and scripts you need for the modal. Bootstrap 3 has a customizer you can pick and choose your components from to get compiled versions. Bootstrap 4 doesn’t yet have a customizer available. You can easily roll your own build though using Grunt or another build tool.

Add a file to the /includes/ folder called enqueue_bootstrap.php. The functions to include

<?php
// enqueues bootstrap styles and scripts
function bootstrap_modal_enqueue() {
	// directory of script
	$bs_scripts = FUNC_PLUGIN_DIR . 'js/bootstrap.min.js';
	wp_enqueue_script(
		'bootstrap-scripts',
		$bs_scripts,
		( 'jQuery' ),
		// version number is set to the file mod time
		// you may want to use the actual version number of BS your using instead
		filemtime( $bs_scripts ), 
		true
	);
	// directory of stylesheet
	$bs_styles = FUNC_PLUGIN_DIR . 'css/bootstrap.min.js';
	wp_enqueue_script(
		'bootstrap-styles',
		$bs_styles,
		// version number is set to the file mod time
		// you may want to use the actual version number of BS your using instead
		filemtime( $bs_scripts ),
	);
}

add_action( 'wp_enqueue_scripts', 'bootstrap_modal_enqueue' );

Add a call to this file in the main functionality-plugin.php file.

// enqueues bootstrap styles and scripts
include( FUNC_PLUGIN_DIR . 'includes/enqueue_bootstrap.php' );

Adding Bootstrap Modals to Your Pages in WordPress

Now that the plugin is built upload it to your site and activate it through the plugins menu. When you activate it a new menu item will appear in the sidebar of the dashboard ‘Modal’.

You can use this tab in much the same way as the Posts tab. It has a link to the list of all modals and one to add a new modal.

wordpress editor showing adding content for a bootstrap modal

Add or Edit Modals just like you would with Posts or Pages.

Adding and editing modals is done just like you were editing posts or pages.

Modifying the plugin

There are several easy changes or improvements you could make to the plugin.

  • Allow shortcodes in the post content by processing them in either the generate or the render function.
  • Change the time when the modal shows by adjusting the number in the trigger script function.
  • Trigger the modal with an action other than time by modifying the JavaScript in the trigger function.
  • Output a different type of modal, perhaps wider or with no buttons, by modifying the generate function.

An obvious improvement would be to add an options page where you could input the time or to select which pages the modal does or does not show on.

If you add any features and wanted to contribute them back you could make a PR on the GitHub repo for the Bootstrap Popup WordPress Plugin and I’ll merge in any upgrades.

Leave a comment below if you use this so I can hearing about all the improvements you have made.