Category Archives: Coding

How to pass php variables to javascript

While developing an ajax-based plugin, you’ll often find yourself in situations where you have to pass some PHP variables to Javascript. This can be a pain especially when your plugin grows.

Luckily, WordPress provides a way to simplify the process drastically.

Here is an example:

wp_enqueue_script('your-js-handler-name', plugins_url('assets/js/some.js', __FILE__), array('jquery'), false, true);

$data = array(
                'ajaxurl' => self_admin_url('admin-ajax.php'),
                'number' => 1,
                'array' => array('I', 'am', 'a', 'wordpress', 'plugin', 'developer')
            );

wp_localize_script('your-js-handler-name', 'EXAMPLE', $data);

Right now, you can access the $data variables in javascript via EXAMPLE.ajaxurl

PHP: How to Flush the Buffer (HTML) Before Processing

Scenario:
You want to display a message “Please wait, we are processing your request”.

PHP Code:



Please wait, we are processing your request

Problem:
You need to wait for the process_request() finished before the message shows up.

Solution:

Well, if you’ve been coding with PHP for a while, you will know the ob_xxx functions which are designed to manipulate the output buffer.

Most of solutions from Google I’ve found out are




Please wait, we are processing your request

This will work out in this simple example. But, in reality, you may need to include some PHP files. In my case, I need to include(‘wp-config.php’) and this solution somehow doesn’t work for me.

I tried to search the WordPress source code to see how it uses ob_xxx functions.

And, here is what I get




Please wait, we are processing your request

And it works!

Custom Taxonomy in Custom Post Type URL

Let’s just say that you want to register one custom post type Product and a corresponding custom taxonomy Product Category.

You want the URL structures to be like this:
http://www.test.com/product/a-category/ —> display a-category archive page.
http://www.test.com/product/a-category/this-is-a-product-page/ —> display this-is-a-product-page

First, you need to register the custom post type and taxonomy the right way:


register_taxonomy( 'product_category', array( 'product' ), array('public'=>true ,'rewrite'=>array('slug'=>'product')) );

register_post_type( 'product', array('public'=>true) );


Obviously, I skip most of arguments but just use 2 of them which are the key to the desired URL structures.

The public is self explanatory, meaning your product and product category can be visited publicly.

The ‘rewrite’=>array(‘slug’=>’product’) will take care of “http://www.test.com/product/a-category/” URL structure.

Okay, right now, we just need to find out how to achieve the second URL structure.

To use custom URL structure, WordPress provides a way to do this: add_rewrite_rule

add_rewrite_rule("^product/([^/]+)/([^/]+)/?",'index.php?post_type=product&product_category=$matches[1]&product=$matches[2]','top');

When and where do we call this function?

Well, I usually do it in the plugin activation.

add_action( 'admin_init', array( $this, 'install' ) );
function install(){
     if($this->is_new_version()){
         add_rewrite_rule("^product/([^/]+)/([^/]+)/?",'index.php?post_type=product&product_category=$matches[1]&product=$matches[2]','top');
         flush_rewrite_rules();
     }
}

Please note that you gotta flush rewrite rules to let the new rewrite rule take place.

I’ve seen many people, including me, add_rewrite_rule in init hook. But from what I found in the wp-include/rewrite.php, those rewrite rules are simply stored in a variable called “extra_rules”, which won’t be merged into “rules” until rewrite rules are flushed. “rules” is the one will be used for URL parsing in class-wp.php. Feel free to correct me if I’m wrong.

Right now, you can visit your product page like this: http://www.test.com/product/a-category/this-is-a-product-page/

You may think we’ve achieve the desired URL structures but actually there is a problem.

The product category archive pagination page is 404. For example http://www.test.com/product/a-category/page/2/

Okay, the fix is simple, you just need to add another rewrite rule:

add_rewrite_rule(“^product/([^/]+)/page/(\d+)/?”,’index.php?post_type=product&product_category=$matches[1]&paged=$matches[2]’,’top’);

It’s worth noting that the order of how you add rewrite rules could affect the result.

If the order is like this

add_rewrite_rule("^product/([^/]+)/([^/]+)/?",'index.php?post_type=product&product_category=$matches[1]&product=$matches[2]','top');

//URL Structure: http://www.test.com/product/a-category/this-is-a-product-page/ 

add_rewrite_rule("^product/([^/]+)/page/(\d+)/?",'index.php?post_type=product&product_category=$matches[1]&paged=$matches[2]','top');

//URL structure http://www.test.com/product/a-category/page/2/

When you visit http://www.test.com/product/a-category/page/2/, you’ll actually get a 404 error instead. This is because WordPress thinks that you are visiting a product named “page”.

To debug such problem, you can always output global variable $wp where you’ll see some useful information about URL parsing.

Okay. there is still one problem we need to cope.

How to change the post links? The post links in the post table of the backend is still http://www.test.com/product/a-product-page

What you are gonna do is to hook to post_type_link and post_link to change the link.

add_filter( 'post_type_link', array($this,'post_type_link') , 10, 2 );
add_filter( 'post_link', array($this,'post_type_link') , 10, 2 );

function post_type_link($post_link, $id = 0 ){

       $post = get_post($id);
       if(!$post instanceof WP_Post)
	   return $post_link;
       $typenow = $post->post_type;
	if ( $post->post_type !== 'product' )
		return $post_link;


        $terms = get_the_terms($post->ID, 'product_category');

	if( is_wp_error($terms) || !$terms ) {
	    $category = 'uncategorized';
	}
	else {
	   $category_obj = array_pop($terms);
	   $category = $category_obj->slug;
	}

	return home_url(user_trailingslashit( "product/$category/$post->post_name" ));
}

As you can see, I use “uncategorized” for product who doesn’t have a category while pick up a category for post who have more than 1 category.

WordPress Tinymce: How to Create A Dialog-based Button

I need to make a tinymce button which would bring a dialog box for users to fill some options and then generate a shortcode.

I’ve done some tinymce buttons before, such as tinymce dropdown button and of course normal buttons. But I don’t know how to make a dialog.

The first thought is, “Oh, the built-in link button has dialog. Maybe I can decode it. ”

WordPress tinymce butilt-in plugins are stored in wp-includes/js/tinymce/plugins and the link plugin files are located at wplink directory. From what I see, the editor_plugin_src.js is the main file.

ed.addCommand('WP_Link', function() {
     				if ( disabled )
          					return;
				     ed.windowManager.open({
          					id : 'wp-link',
          					width : 480,
         					 height : 'auto',
         					 wpDialog : true,
          title : ed.getLang('advlink.link_desc')
     				}, {
        					plugin_url : url // Plugin absolute URL
      				});
});

The interesting part is “wpDialog : true”. I guess this is the key.

Okay, I figure out how to register a dialog-based tinymce button.

The following question is: where to store the dialog content? After some digging, I find out it is simply stored in the page but wrapped in a hidden div. When the button is clicked, the dialog will popup.

the dialog code is like this:

<div id="wp-link-wrap" style="display: none;">
<form id="wp-link" tabindex="-1">
<div class="form-field form-required">
<label for="header">Header</label>
<input type="text">
</div>
</form>
</div>

Okay then, I can hide the form by hooking “in_admin_footer“. But I don’t want to do this everywhere in the backend.

function my_in_admin_footer(){
 global $post_type;
 if(!in_array($post_type,array('post','page')))
	return;

 include $this->plugin_path('dialog.php');
}

 

Here is the dialog.php

 <div>
<form id="wp-link">
<div><label for="header">Header</label> <input type="text" /></div>
<p><input id="wp-link-submit" type="submit" value="Done" /></p>
</form>
</div>

Please note that the form id “wp-link” must be the same as you register the button id at editor_plugin_src.js.

The next task is simple: I need to hook the submit event and do the form validation before insert content to the editor based on user input.

It’s a hassle to do the form validation. WordPress provides an easy way if you just want to make sure all required fields are filled in.

Here is the js code

$('#wp-link-submit').click(function(e){
		var form = $(this).parents('form');
		if ( ! validateForm( form ) )
			return false;
		var content = '';
		//collect all required data and store in content varialble
		tinyMCE.activeEditor.selection.setContent( content );
		tinyMCEPopup.close();
		return false;
	});

The validateForm will check if “form-required” input text field is empty. It’ll add red background if empty.

Okay, this is basically how I make a tinymce button which would popup a dialog.

How to Get This Week’s Posts

WordPress introduces date_query since 3.7 which makes it much easier for developers who have to retrieves posts in a certain range.

function get_this_week_posts(){

    $date_query = array(
                        array(
                            'after'=>'1 week ago'
                            )
                        );
 
    $args = array(
                    'post_type' => 'post',
                    'post_status'=>'publish',
                    'date_query' => $date_query,
                    'no_found_rows' => true,
                    'suppress_filters' => true,
                    'orderby'=>'ID',
                    'posts_per_page'=>-1
                );

    $query = new WP_Query( $args );

    return $query->posts;
}

As you can see, it’s very amazing that you can simply use “1 week ago” as parameter. If you look into the source code, you’ll find out it’s actually taking advantage of strtotime which will “parse about any English textual datetime description into a Unix timestamp”.

You can also combine tax_query to specific a category.

If you are curious about the other parameters of WP_Query, you may want to take a look at this article.

How To Create A Post With WP JSON REST API Through Ajax

A few months ago, someone started a project called WP JSON REST API and claimed it’ll be core part of WordPress in the future.

Lately, I was playing nodejs and I have some troubles with its xmlrpc module. Then, I thought of this project so I decided to give it a shot.

I spent some time looking at the document and the source code.

After some struggles, I managed to create a post with Ajax. :)

                var content = {
                               'title':article.title,
				'content':article.content,
				'status': 'publish'
			}
						
		var headers = {
						'Authorization':'Basic YWRtaW46MTIzNDU2'
						}
		var options = {
						data:JSON.stringify(content),
						url:'http://www.test.com/wp-json.php/posts/',
						headers:headers,
						type:'POST',
						dataType:'json',
						contentType: 'application/javascript',
						complete:function(xhr,status){
							console.log(xhr);
							console.log(status);

						}
					}
		Zepto.ajax(options);


#1, When creating a post, you need to do 401 Authorization. It’s interesting that zepto.js Ajax supports username and password and the final URL will be http:[email protected]:www.test.com . But from the response, WordPress or the JSON API doesn’t support this kind of Authorization. So I have to do header instead.

#2, I have no idea why it get rids of “post_” prefix and WordPress XMLRPC has “post_”. It would be nice to keep params consistent.

#3, You need to JSON.stringify the content structure and make sure the content type is javascript. It’s using http_raw_post_data to retrieve the data. It takes me a while to figure it out.

#4, I’ve read the source code on insert_post a few times and it looks like it doesn’t supports terms_name even though it says so in the document.

Because of #4, I can’t set category or tags to the created post. So I decided to go back to XMLRPC….. But I’m sure it’ll be fixed when integrate into WordPress core.

Plugin Settings Page: How to Redirect When Settings Are Saved

Plugins usually have their exclusive Settings Page. When I started writing wordpress plugin, I encountered one problem.

How to Redirect When Settings Are Saved

Setup Menu

add_menu_page('Test', 'Test', 'manage_options','test_settings_page',  array($this,'settings_page'));

Handle Settings

function settings_page(){
			$o = $this->get_option();
			if(wp_verify_nonce( $_POST['test_settings_page'], 'test_settings_page' )){
				$_POST = $this->clean_post();
				foreach($_POST as $k => $v)
					$o[$k] = $v;

				$this->update_option($o);
				$this->wp_redirect_to_current_page();
			}
			extract($o);
			@include($this->plugin_path('test_settings_page.php'));
		}

The problem here is, when you call wp_redirect_to_current_page which calls wp_redirect, then you’ll encounter “Cannot modify header information – headers already sent by”.

That’s a classic problem and is self-explanatory. You redirect after some HTML code output. wp_redirect essentially invokes PHP header function which can only take place before HTML output. What’s the “HTML output” in our scenario? yep, when this error occurs, you’ll see WordPress left menu (Posts, Media, Links, etc,.) is already in there.

Solutions

Back then, I have no clue how to deal with the redirect in a perfect way. So, I used meta refresh. This is not a good solution but it works most of the time. Yes, there is a slight chance that it won’t work, especially when you have lots of settings page. And then I tried to use ob_xxx functions to clean buffer before meta refresh. Well, that’s just make this solution more terrible.

Until now, I find a perfect solution to it.

Setup menu

add_menu_page('Test', 'Test', 'manage_options','test_settings_page',  array($this,'settings_page'));
add_action( "admin_action_test_settings_page", array($this, 'settings_page') );

Form Code in Settings Page

Well, I know, you may told me that WordPress has Settings API now. I know and the reason that I still stick to the traditional way is I can keep the html code in individual file and won’t mess with PHP.

Two Things I Hate About Amateur Theme Developers

computer monkey

Don’t Follow Theme Development Guideline

WordPress codex is very thoughtful to provide a guideline on building a standard WordPress theme.

I’ve seen some themes, even premium ones, that don’t have wp_footer() in the footer.php which could cause a disaster for plugins who are enqueuing javascript in the footer. As a result, plugin developers have to load the js file in the head(luckily they somehow don’t forget wp_head()) instead. BTW, obvioulsy, loading js in the head obviously violate the rule of put scripts at the bottom and could slightly slow the page loading.

I know, I know, it’s not a good experience for a designer who have to mangle logical code and views together when building a theme. But, is it an excuse that you don’t follow guideline? Also, there are a bunch of theme boilerplate available such as underscores, bones and roots. You can simply pick up one and create your theme based on.

Deregister jQuery

A lot of themes that are deregistering wordpress’ built-in jQuery and load one from google cdn. I’ve talked to one theme developer about this. “It’s for speed’s sake”, he answered. Well, I don’t have doubt about the speed improvement. But what I don’t understand is why they want to load a specific version when you can actually find an always up-to-date jquery http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js ? Or, a good reason is that your code don’t work with latest one?

Another interesting thing is, they load a number of javascript files in the footer at the same time. Why? Do you know the browser, by default, can fetch two same-domain resources simultaneously? If you are very concerned about the speed, you should merge them into one file, right? Or, you deregistering jquery because everybody does it?

BTW, I’ve asked a question at WPSE to ask for suggestions on how to deal with such case but unfortunately, still no good solution found by far.

image credit

WordPress Cron Job Tricks

If you’re familiar with WordPress Cron Job, you may already know the fact that wordpress cron job isn’t real cron job. It’s triggered by visitor.

What if your site is just like mine, nobody paid a visit?

Here is a trick for webmasters:

UptimeRobot is a Free Website Uptime Monitoring service which will monitor your site every 5 minutes. Other than ping, it also supports HTTP as monitor type.

HTTP monitor type, you get my point?

Here is another trick for developer:

It is possible that multiple instances of your cron job are running at the same time. For example, if you have used Autoblog plugin before, you probably experience the duplicate posts issue even though the autoblog claims it does have duplicate posts eraser feature.

The thing deep down is actually if multiple visitors come to your site, there is a chance that your cron job will be executed in parallel. So, how to stop this?

Well, if you are a decent developer, you may think of Lock.

Most of the time, PHP developers will use file lock and there is a PHP function called flock. But flock are not so reliable and may go wrong somehow, therefore, some people will Just create a file on script start and delete it in the end.

Another similar way is database.

if( get_transient( 'my_cron_job_lock' ) == 'locked' )
    return false;

set_transient( 'my_cron_job_lock', 'locked', 60*5 ); //5 mins

//executing cron job here

delete_transient( 'my_cron_job_lock' );

Actually, I don’t like to use WordPress Cron Job. Sometimes, the cron job didn’t fire on time.

So, if clients are very sensitive to the execution time, then I’ll switch to anther way. It’s also how WordPress does, doing the job by comparing current time to last job execution time.

Here is the code from how WordPress handles plugin update check.

add_action( 'admin_init', '_maybe_update_plugins' );
function _maybe_update_plugins() {
    $current = get_site_transient( 'update_plugins' );
    if ( isset( $current->last_checked ) && 12 * HOUR_IN_SECONDS > ( time() - $current->last_checked ) )
        return;
    wp_update_plugins();
}

Don’t get me wrong. WordPress actually add wp_update_plugins to its cron job queue.

if ( !wp_next_scheduled('wp_update_plugins') && !defined('WP_INSTALLING') )
        wp_schedule_event(time(), 'twicedaily', 'wp_update_plugins');

Interesting, right?


Update: 2013-07-31

Lately, I’ve tests different locking methods:

database: simply not working. cron job overlay still happens.

file locking: flock($fp, FLOCK_EX | FLOCK_NB) . someone will take the lock forever and FLOCK_NB is not available at windows.

file presence: almost perfect.

            $lock_file = $this->getLockFile();
            if(!file_exists($lock_file)){
                touch($lock_file);
            }else{
                $interval = absint(get_option(MY_CRON_INTERVAL)) * 59;
                if(time() - filectime($lock_file) > $interval){
                    unlink($lock_file);
                    touch($lock_file);
                }else
                    return ;

            }

Well, I’ve seen some articles telling me writing PID to the file and compare the PID everytime. But I actually don’t like it because it uses exec function. You don’t know if your hosting will be disable such dangerous function or not.

So, it seems it’s very hard to prevent cron job from overlaying. Maybe I should look for real cron job?

Create a special cron job URL along with magic token and put the URL into cron job

WordPress 3.6 – New Built-in Video and Audio Shortcodes

I take a peek at WordPress 3.6 which is still in active development by now and I find out there are a bunch of new functions added in wp-includes/media.php.

The video and audio shortcodes catch my attention.

Here is my test.

Shortcode:
[(whitespace)video src=”http://www.test.com/test.mp4″ widht=”300″ height=”640″ poster=”vide shortcode test”(whitespace)]

The poster is video preview image attribute.

Note: please remove “(whitespace)”

Preview:

video-shortcode-preview

HTML Code the shortcode output:
<video class="wp-video-shortcode" id="video-3325-1" width="604" height="640" poster="https://www.google.com/images/srpr/logo4w.png" controls="controls" preload="none"><source type="video/mp4" src="http://www.test.com/test.mp4"><a href="http://www.test.com/test.mp4">http://www.test.com/test.mp4</a></video>

Apparently, it’s using HTML5.

As for the audio shortcode, it only supports src attribute.