Demonstrating Progress Requires Effort

A main focus of Post Notif v1.1, for me, was to both enhance the functionality around the post notification send process (via the addition of schedule post send and “test send”) and improve the feedback in this area. To tackle the latter matter, I set out to add a progress bar to provide the blog author with a visual status of the send process. I was surprised to find that, despite the use, in WordPress core, of several (jQuery) UI goodies (e.g, sliders, draggable objects, sortable elements, themeable menus), the progressbar was not among them.

Fortunately, other people have tackled similar challenges in their own WordPress projects. Merrill Mayer’s post was very helpful in figuring out how to render the progressbar, while Tom McFarlin’s provided guidance on checking for and updating processing status.

Conveniently, WordPress core’s usage of jQuery relies on both of the progressbar’s dependencies (UI Core and Widget Factory, contained in the jquery-ui-core.js and jquery-ui-widget.js scripts, respectively) so they were already enqueued. Making the progressbar widget available, however, necessitated adding its script to the enqueue_scripts() function in Post Notif’s admin class (../admin/class-post-notif-admin.php):

	/**
	 * Register the JavaScript for the admin area.
	 *
	 * @since	1.0.0
	 */
	public function enqueue_scripts() {

		/**
		 * An instance of this class should be passed to the run() function
		 * defined in Post_Notif_Admin_Loader as all of the hooks are defined
		 * in that particular class.
		 *
		 * The Post_Notif_Admin_Loader will then create the relationship
		 * between the defined hooks and the functions defined in this
		 * class.
		 */

		wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/post-notif-admin.min.js', array( 'jquery' ), $this->version, false );
		 
		// Include this to use jQuery UI progressbar
		wp_enqueue_script( 'jquery-ui-progressbar', $this->version, false );
		
	}

The jQuery ThemeRoller provides several nice-looking, ready-to-use themes to choose from (plus the ability to roll your own if you’re so inclined). Though at some point in the future I’d love to give admins the ability to choose the color of their Post Notif progressbar (or, better yet, have the plugin auto-detect the admin color scheme and tailor something accordingly), for now I opted to stick with Cupertino since its blue is relatively close to the blue in the default WordPress dashboard color scheme:

	/**
	 * Register the stylesheets for the admin area.
	 *
	 * @since    1.1.0
	 */
	public function enqueue_styles() {

		/**
		 * An instance of this class should be passed to the run() function
		 * defined in Post_Notif_Admin_Loader as all of the hooks are defined
		 * in that particular class.
		 *
		 * The Post_Notif_Admin_Loader will then create the relationship
		 * between the defined hooks and the functions defined in this
		 * class.
		 */
		 
		wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/jquery-ui-1_12_1_cupertino_progressbar_only.min.css', array(), $this->version, 'all' );
		
	}	

As you may have gathered from the filename I gave the CSS (enqueued above), I had to excerpt the Cupertino theme’s CSS (downloaded from the jQuery CDN) to provide the progressbar with the functionality I was after while still keeping the CSS file size (which I further minified) to a minimum. I found that the following fit the bill:

.ui-progressbar {
	position: relative;
}
#id_spnSendPostNotifProgressBarLabel {
	position: absolute;
	left: 50%;
	top: 4px;
	font-weight: bold;
	text-shadow: 1px 1px 0 #fff;
}

/*! jQuery UI - v1.12.1 - 2016-09-14
* http://jqueryui.com
* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande%2CLucida%20Sans%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=6px&bgColorHeader=deedf7&bgTextureHeader=highlight_soft&bgImgOpacityHeader=100&borderColorHeader=aed0ea&fcHeader=222222&iconColorHeader=72a7cf&bgColorContent=f2f5f7&bgTextureContent=highlight_hard&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=362b36&iconColorContent=72a7cf&bgColorDefault=d7ebf9&bgTextureDefault=glass&bgImgOpacityDefault=80&borderColorDefault=aed0ea&fcDefault=2779aa&iconColorDefault=3d80b3&bgColorHover=e4f1fb&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=74b2e2&fcHover=0070a3&iconColorHover=2694e8&bgColorActive=3baae3&bgTextureActive=glass&bgImgOpacityActive=50&borderColorActive=2694e8&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=ffef8f&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=25&borderColorHighlight=f9dd34&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=cd0a0a&bgTextureError=flat&bgImgOpacityError=15&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffffff&bgColorOverlay=eeeeee&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=90&opacityOverlay=80&bgColorShadow=000000&bgTextureShadow=highlight_hard&bgImgOpacityShadow=70&opacityShadow=30&thicknessShadow=7px&offsetTopShadow=-7px&offsetLeftShadow=-7px&cornerRadiusShadow=8px
* Copyright jQuery Foundation and other contributors; Licensed MIT */


/* Component containers
----------------------------------*/
.ui-widget {
	font-family: Lucida Grande,Lucida Sans,Arial,sans-serif;
	font-size: 1.1em;
}
.ui-widget .ui-widget {
	font-size: 1em;
}
.ui-widget input,
.ui-widget select,
.ui-widget textarea,
.ui-widget button {
	font-family: Lucida Grande,Lucida Sans,Arial,sans-serif;
	font-size: 1em;
}
.ui-widget.ui-widget-content {
	border: 1px solid #aed0ea;
}
.ui-widget-content {
	border: 1px solid #dddddd;
	background: #f2f5f7;
	color: #362b36;
}
.ui-widget-content a {
	color: #362b36;
}
.ui-widget-header {
	border: 1px solid #aed0ea;
	background: #deedf7;
	color: #222222;
	font-weight: bold;
}
.ui-widget-header a {
	color: #222222;
}

.ui-progressbar {
	height: 2em;
	text-align: left;
	overflow: hidden;
}
.ui-progressbar .ui-progressbar-value {
	margin: -1px;
	height: 100%;
}
.ui-progressbar .ui-progressbar-overlay {
	background: url("data:image/gif;base64,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");
	height: 100%;
	filter: alpha(opacity=25); /* support: IE8 */
	opacity: 0.25;
}
.ui-progressbar-indeterminate .ui-progressbar-value {
	background-image: none;
}

/* Misc visuals
----------------------------------*/

/* Corner radius */
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
	border-top-left-radius: 6px;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
	border-top-right-radius: 6px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
	border-bottom-left-radius: 6px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
	border-bottom-right-radius: 6px;
}

I supplemented the existing admin JavaScript (../admin/js/post-notif-admin.js) to handle instantiation of the progressbar, status updates (one per second), and completion of the post notification send process:

(function( $ ) {
	'use strict';
	
	$(function() {
	 	$("#id_btnPostNotifSend").click(function(e) { 
	 		var post_id = document.getElementById("id_hdnPostID").value;

 			// Hide Send button  	
 			jQuery('#id_btnPostNotifSend').hide();
 			
 			// Set Post Notif status message span to contain processing message
	 		jQuery("#id_spnPostNotifStatus").text(post_notif_send_ajax_obj.processing_msg);	 			 		
 			 		
	 		var send_post_notif_now = document.getElementById('id_radSendPostNotifNow');
	 		if (send_post_notif_now.checked) {

	 			// Send NOW	

	 			// Hide radio buttons
	 			jQuery('#id_spnPostNotifSchedRadioButtons').hide();
 		 			
	 			// Call Post Notif send init function
	 			$.post(ajaxurl, {
	 				action: 'init_post_notif_send',
	 				post_id: post_id,
	 			}, function(data) {

	 				if ('-1' === data.status) {

	 					// Process is already running - STOP PROCESSING!
	 					jQuery("#id_spnPostNotifStatus").text(data.message);	 					
	 				} 
	 				else {

	 					// All is well, carry on
	 			
	 					// Create a jQuery progressbar
	 					jQuery("#id_post_notif_progress_bar").progressbar();

	 					// Check status of process every second
	 					var processCheckTimer = setInterval(function() {
	 				
	 						// Get the current status of the send process
	 						$.post(ajaxurl, {
	 							action: 'get_post_notif_send_status',
	 							post_id: post_id,
	 						}, function(response) {

	 							if ('-1' === response) {

	 								// Set the progress bar to display 100% complete
	 								jQuery("#id_divSendPostNotifProgressBar").progressbar({
	 									value: 100
	 								});
	 			
	 								// Kill timer
	 								clearInterval(processCheckTimer);
	 						
	 								// Hardcode progress bar label to 100% 
	 								jQuery("#id_spnSendPostNotifProgressBarLabel").text( "100%" );
	 							} 
	 							else {

	 								// Update the progress bar to display appropriate percent complete
	 								var percentComplete = Math.floor(100 * response);
	 								jQuery("#id_divSendPostNotifProgressBar").progressbar({
	 									value: percentComplete
	 								});
	 						
	 								// Set progress bar label to appropriate percent complete 
	 								jQuery("#id_spnSendPostNotifProgressBarLabel").text( percentComplete + "%" );
	 							}	 					
	 						});

	 					}, 1000);

	 					$.post(post_notif_send_ajax_obj.ajax_url, {
	 						_ajax_nonce: post_notif_send_ajax_obj.nonce,
	 						action: "post_notif_send",
	 						post_id: post_id,
	 					}, function(data) {

	 						// Update Post Notif status message span with total count of notifs sent
	 						jQuery("#id_spnPostNotifStatus").text(data.message);
	 						
	 						// Update and show Post Notif last sent span with last run timestamp
	 						jQuery("#id_spnPostNotifLastSent").text(data.timestamp);
	 						jQuery("#id_spnPostNotifLastSent").show();	 			
	 					});
	 				} 					

	 			});
	 		}
	 		
.
.
.

	 	});
	});

.
.
.

})( jQuery );	

This took more work (and considerably more code!) than I had anticipated but I think the end results justified the effort. Now I know the precise steps required to add a progress indicator when I need one in the future. Further, I hope sharing this with you will prove useful for your projects as well!

10

Leave a Reply

Your email address will not be published. Required fields are marked *