• Shortcuts : 'n' next unread feed - 'p' previous unread feed • Styles : 1 2

» Publishers, Monetize your RSS feeds with FeedShow:  More infos  (Show/Hide Ads)


Date: Wednesday, 23 Jan 2013 23:14

There is a lot of debate going on right now in the WordPress world about the WordPress Foundation barring all ThemeForest/CodeCanyon (Envato really) authors from speaking at WordCamp events.

I don’t want to rehash the argument here; the best place to get it is in the original post, and the comments on it.

Instead, I want to make a different point: Regardless of whether he is right or not, it’s good for WordPress that Matt makes these bold choices.

Open source projects benefit hugely from having a spearhead. In their early days most of all, it takes one or two people with drive to get the project off the ground. Then as it grows, dozens of people may contribute to each iteration of the project, but it takes an even stronger leader now to martial that effort together and drive it in the right direction.

And that problem that grows with size. If your project has 4 or 5 contributors, it’s easy to explain to each of them what you believe is best for the project, and to help them see why you believe that. You have the time for that, and the odds are that the small group who liked your style enough to join your fledgling project probably have similar opinions to you anyway.

When your project is WordPress and thousands of people contribute to it, that’s no longer possible. You can’t have 1-to-1s with everyone, and the community is so diverse that you’ll never convince everyone anyway.

Instead, you have to draw your line in the stand and stand by it. Matt has drawn his. If he wants the WordPress community to have the GPL at its heart, then he needs to be the staunchest supporter of it. Sometimes that means going further than what might be considered reasonable. That’s just part of the deal though. If he compromised more, then the emphasis on promoting the GPL would be lessened.

That takes me back to the title of this post. Matt takes a very hard line on the GPL. A lot of the time, he’ll be in the right, but not every time. The project still needs someone to consistently take that stance though.

There are plenty of examples of this. David Heinemeier Hansson created the massively popular Ruby on Rails framework, and his strong opinions annoy people all the time. Linus Torvalds is hardly one to mince words either.

Without strong leaders, who knows what will happen though? Decision by committee? One of my favorite open source projects used to be phpBB, until they took the decision that the next version should be rewritten from scratch… four years ago. A good leader would never have let that happen (Unless of course he wanted it to, in which case he’d have seen it through).

So to sum up; what I want to say is that regardless of your stance in this particular argument, it’s worth remembering it’s good that people get this fired up and they’re willing to stand their ground.

If enough of the community truly believes that the leader makes too many “wrong” decisions, then all it needs is a new opinionated leader to drive a fork. That’s what Matt did after all (Update 26/1/13: As pointed out in the comments, Matt forked it not because he didn’t like the current leader, but because he had vanished and the project needed a new leader. Sorry if this read misleadingly!). And just because the original project is already huge is no reason that a fork might not surpass it one day.

PS – For what it’s worth though, my opinion on this one is with the developers caught in the middle, which is best summed up by Pippin’s comment.

Most of all though, I think it’s mind boggling that one employee took it on themselves to pick out one individual developer, and hit them alone with this, right out of the blue. That’s one place where Matt really should get to the bottom of what went wrong.

Author: "Michael Martin" Tags: "Other, Open-Source, Thoughts, WordPress"
Comments Send by mail Print  Save  Delicious 
Date: Monday, 21 Jan 2013 17:00

Twitter API OAuth

On the 5th of March, Twitter is going to retire version 1 of its API. The replacement, version 1.1 is very similar, but with one major difference; every single call must be authenticated.

This means that come March, your existing API calls will break; including simple things like displaying tweets on your site. To fix this, you need to move to the new v1.1 API, and authenticate with Twitter.

Let’s look at how to do this in WordPress, by embedding a list of tweets on your site (Exactly how it is done in the footer here).

Download Finished Script

The Flow

  1. Authenticate with TwitterOAuth.
  2. Fetch the tweets (If our cache has expired).
  3. Did it fail? Use the backup.
  4. Did it succeed? Parse them into an array, then save to the database.
  5. Display.

This post will skip past most of the display and parsing logic. If you want to read up on those, check out the earlier posts: How to Use the Twitter API in WordPress and Add a Backup to Embedded Tweets in WordPress.

1 – Authenticate with TwitterOAuth

TwitterOAuth is a great free library from Abraham Williams. It makes the authentication a snap.

To get going, first download the twitteroauth folder from Github. Place this in your theme folder.

Now, create a new file called tweet-list.php, and paste the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$numTweets      = 3;                // Number of tweets to display.
$name           = 'problogdesign';  // Username to display tweets from.
$excludeReplies = true;             // Leave out @replies
$transName      = 'list-tweets';    // Name of value in database.
$cacheTime      = 5;                // Time in minutes between updates.
 
$backupName = $transName . '-backup';
 
// Do we already have saved tweet data? If not, lets get it.
if(false === ($tweets = get_transient($transName) ) ) :	
 
  // Get the tweets from Twitter.
  include 'twitteroauth/twitteroauth.php';
 
  $connection = new TwitterOAuth(
    'xxxxxxxxxxxxxxxxxxxxxx',   // Consumer key
    'xxxxxxxxxxxxxxxxxxxxxx',   // Consumer secret
    'xxxxxxxxxxxxxxxxxxxxxx',   // Access token
    'xxxxxxxxxxxxxxxxxxxxxx'    // Access token secret
  );

This code holds all of our configuration. The top options are fairly self-explanatory, and control the display on your site.

Let’s skip to the consumer and access keys section. These values are given to you by Twitter. The combination of these ensures to Twitter that you are the user you claim to be, but without giving your password.

This means that a Twitter app should never ask for your password. It also means that if you change your password, you won’t need to update any of your apps.

To find these details, go to https://dev.twitter.com/ and sign in.

Once in, hover on your name in the top right, and click “My Applications,” then “Create a New Application.”

Create a New Application

Enter a unique name, description, and your site’s URL. You can leave the Callback URL empty (It would be used if your app was authenticating each of its users, but our’s will only ever use your account, so we’re going to put the details directly into the script).

Once created, you will be taken to a new screen where you can copy and paste the Consumer Key and Consumer Secret into the script.

Next, click the “Create my Access Token” button. This is a shortcut to authenticate your own account with your application.

Create Twitter Access Token

Finally, copy the new Access Token and Access Token Secret into the script.

Fetch the Tweets

To make a Twitter API call through TwitterOAuth, you use a HTTP verb (GET, POST, or DELETE), specify the API, and then pass in the arguments as an array. You can read the full documentation here.

The API we want to use is GET statuses/user_timeline. Therefore, our code looks as follows:

1
2
3
4
5
6
7
8
9
10
11
12
// If excluding replies, we need to fetch more than requested as the
// total is fetched first, and then replies removed.
$totalToFetch = ($excludeReplies) ? max(50, $numTweets * 3) : $numTweets;
 
$fetchedTweets = $connection->get(
  'statuses/user_timeline',
  array(
    'screen_name'     => $name,
    'count'           => $totalToFetch,
    'exclude_replies' => $excludeReplies
  )
);

If you exclude replies, the total returned from the API will be less than the total your requested. The $totalToFetch logic ensures that we request enough tweets that even after removing the replies, we will have the number we wanted.

Did The Request Fail?

If the request worked, it would have returned with a HTTP status code of 200. We can simply check this, and if it is anything else, use the backed up tweets in our database (We will set this in a minute).

1
2
3
// Did the fetch fail?
if($connection->http_code != 200) :
  $tweets = get_option($backupName); // False if there has never been data saved.

Did the Request Succeed?

If it all went well, we need to pull out the number of tweets we asked for, and parse the data we need from it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
else :
  // Fetch succeeded.
  // Now update the array to store just what we need.
  // (Done here instead of PHP doing this for every page load)
  $limitToDisplay = min($numTweets, count($fetchedTweets));
 
  for($i = 0; $i user->name;
    $permalink = 'http://twitter.com/'. $name .'/status/'. $tweet->id_str;
 
    /* Alternative image sizes method: http://dev.twitter.com/doc/get/users/profile_image/:screen_name */
    $image = $tweet->user->profile_image_url;
 
    // Message. Convert links to real links.
    $pattern = '/http:(\S)+/';
    $replace = '<a href="${0}" target="_blank" rel="nofollow">${0}</a>';
    $text = preg_replace($pattern, $replace, $tweet->text);
 
    // Need to get time in Unix format.
    $time = $tweet->created_at;
    $time = date_parse($time);
    $uTime = mktime($time['hour'], $time['minute'], $time['second'], $time['month'], $time['day'], $time['year']);
 
    // Now make the new array.
    $tweets[] = array(
            'text' => $text,
            'name' => $name,
            'permalink' => $permalink,
            'image' => $image,
            'time' => $uTime
            );
  endfor;

Store the Result

With the data parsed, the last thing is to store it. We will make two copies:

  1. Cached version, which will expire and trigger an update (The transient value).
  2. Permanent version, which will be used if a subsequent request to Twitter fails.
1
2
3
4
5
    // Save our new transient, and update the backup.
    set_transient($transName, $tweets, 60 * $cacheTime);
    update_option($backupName, $tweets);
  endif;
endif;

Display

Now you have a $tweets array with the data successfully parsed into it. All that remains is to display it. We’ll make use of WordPress’ human_time_diff function to give relative times, like 5 minutes ago.

1
2
3
4
5
6
7
8
9
10
11
12
// Now display the tweets.
?>
<ul id="tweets">  
 
    <li>
      <p>
 
        <span class="tweet-time"> ago</span>
      </p>
    </li>
 
</ul>

And that’s it. As before, you can download the complete script here:

Download Finished Script

One final tip is that to test your code, you will want to invalidate your cache (So that the fetch code is run). The easiest way to do this is to add this line right under the config data:

1
delete_transient($transName);

Feel free to comment if you have any issues! Or let me know if there are any other API examples you’d like to see.

Author: "Michael Martin" Tags: "WordPress, API, Featured, OAuth, PHP, Tr..."
Comments Send by mail Print  Save  Delicious 
Date: Monday, 14 Jan 2013 17:00

Cubic Bezier Curves in CSS3

In the last post, we looked at the basics of CSS transitions, including how to use easing to control how your animation flows. In this post, I want to look deeper at a very powerful easing feature; cubic bezier curves.

In short, they let you define exactly what path your animation will take, and thanks to some awesome community tools, they are extremely easy to use (No maths required at all). This gives you total flexibility with your transitions.

One of the best tools is the free Cubic-Bezier.com by Lea Verou. It lets you make new curves and see them in action just by dragging the handles. We’ll use it for all of the demos in this post, and it’s well worth a bookmark!

The syntax is simple too. Let’s look at an example. This simple code can be used to animate an element left or right:

1
2
3
4
5
6
7
8
9
10
11
12
a.slide {
  position: relative;
  transition: left 1.5s linear;
}
 
a.slide-left {
  left: 0;
}
 
a.slide-right {
  left: 580px;
}

The linear part is actually a shortcut though. What it really says is that you want the animation to follow a particular path on a graph, which we’ll look at more in a second.

For now, you could rewrite that line to use the cubic-bezier function and get the exact same animation:

1
transition: left 1.5s cubic-bezier(0, 0, 1, 1);

That one line can now be used in any transition, and to tweak the animation, all you need to do is change those 4 bracketed values.

What Is This Graph?

Let’s take a look at the linear graph, and see what it means.

Linear Animation

The x-axis represents the time of your animation. The y-axis represents how far it is between its starting state, and its ending state.

The x-axis is easier to understand; the far left is the very start of your animation. As you go right, time progresses towards the ending time.

To grasp the y-axis, think of the slide-to-the-right animation. At the very bottom of the y-axis, our button hasn’t moved, it’s still on the left of the screen. Half way up the y-axis though, it’s now half way between the left and right side of the screen, and at the top of the axis, it is now at the far right.

The Ease Graph

Ease is the default easing style used, and it has the following graph:

Ease Animation

I’ve drawn a red line representing a linear animation on the graph, to help highlight the difference. Reading from left-to-right (start to finish) on the graph, you can see that:

  • The ease animation dips under the linear line slightly at the beginning, so it starts off a little slower.
  • It then rises more steeply to be much well above the linear line, so the middle of the animation is quite fast.
  • Finally, the line flattens out again at the top, so the animation slows down gracefully towards the end.

The values we pass to cubic-bezier are the co-ordinates of the pink and green circles on each graph (In the first, the green circle is hidden under the white corner at the top right of the graph).

The specific points used for ease are:

Ease Coordinates

Therefore, the code to draw this graph is:

1
transition: left 1.5s cubic-bezier(.25, .1, .25, 1);

Custom Animations

Now that you understand what these graphs tell you, let’s make some custom animations.

Say you liked the ease animation, but you wanted to make it a little slower again at the start. You would take the first (pink) point, and drag it to the right. You are saying that rather than getting to this stage after 25% of the animation, I now want it to take 40% of the animation’s total time.

Slower Ease

1
transition: left 1.5s cubic-bezier(.4, .1, .25, 1);

Now let’s go more extreme. Let’s say you wanted the middle of the animation to be really, really fast, but the start and end to be more gradual.

Sharp Middle

1
transition: left 1.5s cubic-bezier(1, 0, 0, 1);

Or of course, you could make the start go faster and the end go slower.

Fast Start Animation

1
transition: left 1.5s cubic-bezier(0.1, 0.25, 0.25, 1);

How about a really slow start with a very fast end?

Fast Ending Animation

1
transition: left 1.5s cubic-bezier(1, 0, 1, 1);

That should be plenty of regular examples for now. There are endless combinations you can set up, so let’s move on to another nifty trick; going beyond the ends of the graph.

Going Past The Start and End

All of our animations so far have started at 0 on the y-axis, and ended at 1. You don’t have to stick to these limits though. It is entirely possible to have your animation go past its supposed end point, and then circle back to it.

In this example, our button start off slow, build up speed, slide right past its goal, and then reverse back into place.

Animate Past the End

1
transition: left 1.5s cubic-bezier(0.4, 0.1, 0.55, 1.3);

These sorts of animations can feel much more natural. As an example, think of scrolling webpages on your phone/tablet. When you flick down to the very bottom or the very top, the page goes right past the top and bottom and then glides back into place.

If you really wanted, you can also do the opposite and have your animation go backwards in the beginning.

Animate Backwards

1
transition: left 1.5s cubic-bezier(0.4, -0.45, 1, 0.9);

There are 2 natural limitations to these techniques though:

  • The time can’t be adjusted in this way. It will always be whatever length you specified.
  • For some CSS properties, going above/below the y-axis boundaries will have no effect, e.g. opacity. The element can’t be any less visible than invisible…

The best thing to do is to experiment. The syntax is incredibly simple. Set up your animation using the cubic-bezier for the default ease function. Once it’s working, tweak the values to see what looks best.

Author: "Michael Martin" Tags: "Coding, Animation, CSS, CSS3, Design"
Comments Send by mail Print  Save  Delicious 
Date: Monday, 07 Jan 2013 17:00

CSS3 Transitions

Transitions (basic animations) are one of the most popular additions in CSS3, and one of the easiest to implement for big gains on your site.

A transition is simply an animation from one set of CSS properties to another. So for example; whilst before you may have had links with blue text, which suddenly turned orange when you hovered on them, you would now replace that sudden jump with a more graceful animation.

In this post, we’ll look at the basic syntax, step through some examples, and finally take a brief look at current browser support.

Syntax

The process is very simple. Let’s say you wanted to set up the blue->orange link example:

  1. Setup CSS for the link to be blue.
  2. Setup CSS for the link to be orange.
  3. Add a transition.

The end result would be something like this (Don’t worry about the transition syntax right now. I just want to show you how little code this really is):

1
2
3
4
5
6
7
8
9
10
11
12
a:link, a:visited {
  color: blue;
 
  -webkit-transition:  color 0.5s;
  -moz-transition:     color 0.5s;
  -o-transition:       color 0.5s;
  transition:          color 0.5s;
}
 
a:hover, a:active {
  color: orange;
}

Now, let’s look at the syntax. The first thing to point out is that as in the example above, you will need to use browser prefixes (-o- for Opera, -moz- for Firefox etc.). This is a nuisance, but a minor one as the actual values you give to each one are identical at least.

The transition property takes up to 4 arguments:

  • Property to animate – You can see a list of all supported properties here.
  • Length of transition.
  • Delay before starting.
  • Easing. We’ll look at this more in a minute, but think of it as the “style” of animation.

So now looking back at the first example, you can see that I specified to animate the color property for 0.5 seconds. The browser then defaulted the delay to 0 (i.e. start immediately), and used its default easing.

Now let’s look at a few more examples of this in use. For each one, I’ll put the code here, and you can see the demo on this page.

1 – A Simple Fade

View Demo

1
2
3
4
5
6
7
8
9
10
11
12
a.fade {
  background: #0068b8;
 
  -webkit-transition:  background-color 0.8s;
  -moz-transition:     background-color 0.8s;
  -o-transition:       background-color 0.8s;
  transition:          background-color 0.8s;
}
 
a.fade:hover, a.fade:active {
  background-color: #05406d;
}

2 – Slide Left and Right

View Demo

I use some JavaScript first in this example, to add/remove slide-left and slide-right classes to the link.[1]

1
2
3
4
5
6
7
8
9
10
11
$('.slide').on('click', function() {
  slideLeftRight($(this));
});
 
function slideLeftRight(ele) {
  if(ele.hasClass('slide-left')) {
    ele.removeClass('slide-left').addClass('slide-right');
  } else {
    ele.removeClass('slide-right').addClass('slide-left');
  }
}

The CSS is just as simple as before though:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a.slide {
  position: relative;
  -webkit-transition:  left 1.5s;
  -moz-transition:     left 1.5s;
  -o-transition:       left 1.5s;
  transition:          left 1.5s;
}
 
a.slide-left {
  left: 0;
}
 
a.slide-right {
  left: 580px;
}

3 – Multiple Properties at Once (Zooming)

View Demo

In the previous examples, we have always specified a particular property to animate. You don’t have to do it this way though. Instead, you can specify a comma-separated list of properties, or better yet, type all, and the browser will animate anything it can.

In this example, we use the all keyword to animate the width, height and line-height all at once.

1
2
3
4
5
6
7
8
9
10
11
12
a.zoom {
  -webkit-transition:  all 0.8s;
  -moz-transition:     all 0.8s;
  -o-transition:       all 0.8s;
  transition:          all 0.8s;
}
 
a.zoom:hover, a.zoom:active {
  width: 200px;
  height: 50px;
  line-height: 50px;
}

4 – Stretch Search Box on Focus

View Demo

The last example is to enlarge the search box for typing when a user clicks into it. This effect has become increasingly popular since being included in the TwentyEleven default theme.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.search-field {
  width: 108px;
  border: 1px solid #ccc;
  background: #f5f5f5;
  padding: 5px;
 
  -webkit-transition:  width 0.8s;
  -moz-transition:     width 0.8s;
  -o-transition:       width 0.8s;
  transition:          width 0.8s;
}
 
.search-field:focus {
  width: 200px;
}

Easing

Now, let’s return to the easing property, as it can be very powerful. Earlier, I said it is like the style of the animation, which is true. More specifically though, it controls your rate of animating.

Consider an animation which moves an object from left to right. It could make the entire animation at the same pace (e.g. it moves 100px every 0.2 seconds, whether it’s the first 0.2 or the last), or it could accelerate rapidly at the start and slow towards the end, or start off slow and pick up speed as it goes.

There are a lot of possibilities, but the easiest way to understand them is to see them in action. The last example I’ve put together uses the slide left/right animation again, but with a different easing option on each button.

View Demo

Click the “click here” link under section 5 of the examples, and you will see the 5 buttons animate. All of them use the same animation, for the same length of time, but with the easing function labelled on their button.

You can specify this with your other properties, e.g.

1
transition:          left 1.5s ease-out;

A more flexible approach however can be to use extra CSS classes, e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
a.ease-in {
  -webkit-transition-timing-function:  ease-in;
  -moz-transition-timing-function:     ease-in;
  -o-transition-timing-function:       ease-in;
  transition-timing-function:          ease-in;
}
 
a.ease-out {
  -webkit-transition-timing-function:  ease-out;
  -moz-transition-timing-function:     ease-out;
  -o-transition-timing-function:       ease-out;
  transition-timing-function:          ease-out;
}

Now to change any animated elements easing, I simply give it the ease-in or ease-out CSS classes.

Browser Support

Browser support for CSS transitions these days is great! Chrome has supported it from version 1.0, Firefox from version 4.0, Safari from 3.2, and Opera from 10.5.[2]

As you would expect though, Internet Explorer is lagging behind. They are fully supported in IE10 (And without any vendor prefix, which is a nice touch), but not in any previous version. Given that IE10 currently only runs on Windows 8, this means it will be a long time before the majority of IE users see your transitions.

Still, don’t let that stop you. The other browsers have great support for these, they’re an extremely simple upgrade to make to your site, and it all degrades gracefully for IE users anyway, so there’s no real loss!

[1] – If you were to replace all of your jQuery animations with CSS ones, you may find that you can rewrite the functions here in plain old JavaScript and not load that library at all.

[2] – Stats from Mozilla Developer Network.

Author: "Michael Martin" Tags: "Coding, Animation, CSS, CSS3, Design"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 25 Jul 2012 16:00

Reading Mode Preview

In this post, I will show you a simple way to add a distraction-free “Reading Mode” to your blog. You can see the end result on this Reading Mode demo site (Click the highlighted “Reading Mode” link).

The purpose of adding a feature like this is to enable a visitor to remove all the clutter of your site, and focus solely on the post itself.

In an ideal world; there would never be a need for such a feature. In reality though, sites have numerous other goals to achieve, such as brand building, serving ads, promoting other content etc. In this way, you can compromise between the two. Do what you need when the user first arrives, but get out of the way when they decide what to read.

This script is also available as a free plugin on Github (Direct download link). No configuration needed, just upload and hit activate. Or feel free to fork and improve it.

The Method

The trick is simple. We will use Jack Moore’s awesome Colorbox plugin for jQuery to “pop out” the post and fade out the rest of the page when needed. Click the “Reading Mode” link here to see how this looks.

All we need to do is set some options for the plugin, and insert our link. We will create the link with JavaScript so that it won’t appear to search engines or RSS readers as part of your post’s content.

1 – Get the Files

The first step is to download the latest Colorbox files from here. When you open up the zip folder, you will get a folder structure like this:

The 5 example folders contain different styles that you can use for your Colorbox popup. Load up the index.html file in each folder and click a link to see how it looks.

  • Copy the jquery.colorbox-min.js file from the ‘colorbox‘ folder into your theme’s /js/ folder (Create one if it doesn’t exist).
  • Choose one of the example styles, and copy the colorbox.css file into your theme’s directory.
  • In the same example folder, copy all of the /images/ files into your theme’s images directory.

As a sidenote; you may prefer to create a ‘colorbox’ directory in your theme’s directory instead, and put all of these files there. That’s what I would do personally, but for this tutorial, I’m following how the default TwentyEleven theme organizes itself.

2 – Queue the Files

Now, you have all the files you need for the script. The next step is to load them into your page. To do this, open your theme’s functions.php file (Create a file with that name if none exists) and paste the following before the closing ?> tag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
 * Queue Reading Mode scripts and styles.
 */
function pbd_rm_scripts() {
	if(is_single() ) :
		wp_enqueue_script(
			'colorbox',
			get_bloginfo('template_directory') . '/js/jquery.colorbox-min.js',
			array('jquery'),
			'1.3.19',
			true
		);
 
		wp_enqueue_script(
			'pbd-reading-mode',
			get_bloginfo('template_directory') . '/js/pbd-reading-mode.js',
			array('jquery', 'colorbox'),
			'0.1',
			true
		);
 
		wp_enqueue_style(
			'colorbox',
			get_bloginfo('template_directory') . '/colorbox.css',
			array(),
			'1.3.19'
		);
	endif;
}
add_action('wp_enqueue_scripts', 'pbd_rm_scripts');

This simply tells the theme about each of your files; the two Colorbox files and the reading-mode JavaScript file we’re going to create next. They will all be loaded in the site now, but only on single-post pages.

3 – Wrap Your Post Content in a Div

For Colorbox to know what counts as your post content, we need to have one element that contains that post, and nothing else. Some themes will already have this, but the class names vary from theme to theme. To make things simpler, we’re going to use a WordPress filter to add our own div around the_content.

Carrying on in functions.php, paste the following:

1
2
3
4
5
6
7
8
9
10
11
/**
 * On single posts, it wraps the content in div.
 */
function pbd_rm_content_filter($content) {
	if(is_single() ) {
		$content = '<div class="rm-content">'. $content .'</div>';
	}
 
	return $content;
}
add_filter('the_content', 'pbd_rm_content_filter');

This filter runs every time your theme displays a post’s content. If the user is on a single post page, then our code will wrap a div with class .rm-content around it.

4 – Create the Link

With everything in place, you’re ready to set up your “Reading Mode” link. In your theme’s /js/ directory, create a new file named ‘pbd-reading-mode.js’, and paste the following into it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
jQuery(document).ready(function($) {
	// Set this to the CSS selector of the element that wraps your post content.
	// e.g. .entry or .entry-content
	var selector = '.rm-content';
 
	// The HTML for your "View This in Reading Mode" link.
	var html = '<p class="rm-button">View this post in <a href="#" class="reading-mode" rel="nofollow">Reading Mode</a>.</p>';
 
	$(selector)
		.prepend(html)
		.find('.reading-mode')
		.colorbox({
			innerHeight: "80%",
			innerWidth: 700,
			inline: true,
			href: selector
		});
});

On line 4, you can set the selector for the element that wraps the post (No need if you’ve used the filter from step 3), and on line 7, you can customize the HTML for the “Reading Mode” link.

Line 10 adds that HTML to our content wrapping element (i.e. it creates the link). On line 12, we initiate Colorbox with options for its size, and specify its content as the wrapping element. You can read more about the possible options here on Colorbox’s site.

5 – Style It

At this point, you can save your work and try it out. The “View in Reading Mode” link should appear at the top of your posts, and clicking it should load the post content in a popup.

The last step is to add a little CSS to make that popup as legible as possible. Feel free to experiment with this, but here is what I have used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * Reading Mode
 */
#cboxLoadedContent > div {
	text-align: left;
	padding: 20px;
	font: 16px/24px Georgia, serif;
	width: 90%;
}
 
#cboxLoadedContent > div .rm-button {
	display: none;
}
 
#cboxLoadedContent > div p {
	margin-bottom: 24px;
}
 
#cboxLoadedContent > div h2 {
	font-size: 22px;
	margin-bottom: 24px;
}
 
#cboxLoadedContent > div h3 {
	font-size: 18px;
	margin-bottom: 24px;
}
 
#cboxLoadedContent > div h4 {
	font-size: 16px;
	margin-bottom: 24px;
}

Some notes on the CSS:

  • I use #cboxLoadedContent > div to match the content wrapping element. This should make it a little more flexible than specifying a CSS classname.
  • Line 11 is used to stop the “Reading Mode” link from appearing inside the popup.
  • If you want to use your theme’s existing post styles, you can skip the filter and instead specify an element like .entry-content in the JavaScript. The element itself is moved into the popup, so all of its styles will then apply to the popup as well.

And that’s all there is to it. Not a lot of code, for a useful little feature. Let me know what you think. Is this sort of thing a good idea, or better avoided?

Author: "Michael Martin" Tags: "WordPress, Clutter, Design, Featured, Ja..."
Comments Send by mail Print  Save  Delicious 
Date: Monday, 14 May 2012 16:00

As bloggers, we spend a lot of time working on a range of different tasks. This includes things like checking stats, monitoring ad revenue, and interacting on social media.

That leaves us with a lot of sites to log into. But by adding a few WordPress plugins, you can pull some of this data back into the WordPress dashboard, which saves you time by giving a quick snapshot overview of everything.

Analytics 360

Analytics 360 brings Google Analytics and Mailchimp (Newsletter service) data together, and displays them on a subpage under the WordPress dashboard. This helps in accessing analytics tools like visualizing site traffic and managing mailing lists easier even without leaving WordPress.

A Google Analytics account is required, but you do not need to be a Mailchimp user.

Analytics 360 Download.

My Gmail

My Gmail is another simple dashboard plugin which displays all the unread mails from your Gmail account without any need to load Gmail.

However, this plugin works only over HTTPS, so you will need to have purchased an SSL certificate.

My Gmail Download.

Full Comments on Dashboard

The Recent Comments Box in WordPress facilitates managing and replying to comments easily, but it just shows the comment excerpt and the user has to load another page to view the full comment.

This limitation can be overcome with the Full Comments on Dashboard plugin. It extends the default widget to show the full comment, allowing you to reply properly.

Full Comments on Dashboard Download.

Google Adsense Summary

This is another important plugin which lets the user to check Adsense earnings for a stipulated time. After the successful installation of the plugin, the user should enter his username and password in the settings page and the earnings from his/her account are displayed anytime.

Google Adsense Summary Download.

Feedburner Stats

Feed Stats is a simple statistical tool for viewing and managing FeedBurner statistics. Under the Dashboard, you will find a “Feed Stats” link with graphs based on your recent FeedBurner data.

The plugin displays your FeedBurner statistics for things like subscribers on your blog, which the feed readers are being used, and which of the posts are most popular with subscribers.

Feedburner Stats Download.

Twitter Tools

The Twitter Tools plugin makes it easy for the WordPress user to manage his Twitter posts. This plugin helps to Tweet directly from the dashboard itself without opening another browser tab.

Using this plugin, the user can automatically Tweet about new posts, create a collection of his Tweets and publish on the users blogs, Tweet from any page on the blog, etc.

Twitter Tools Download.

Plugin Central

Finally, Plugin Central is a plugin for managing all other plugins for WordPress. This lets you easily install plugins by naming them, or linking to a zip folder, as well as adding multiple plugins at once, and updating all of your existing plugins directly from the dashboard.

Plugin Central Download.

And that concludes our list. Not all of these plugins will be for you, but if you spend a lot of time on some of the services above, then give it a go and see if it saves you some time!

If you need more ideas, check out the dashboard tag on the WP.org Plugin Directory.

If you have any other suggestions for great dashboard plugins, let me know in the comments!

Author: "Claudia Somerfield" Tags: "WordPress, AdSense, Analytics, Comments,..."
Comments Send by mail Print  Save  Delicious 
Date: Thursday, 03 May 2012 16:00

Live.js and WordPress

I want to share a fantastic little tool that I’ve only recently come across; Live.js, written by Martin Kool.

This JavaScript file automatically checks for changes to your CSS and JavaScript files, and refreshes them.

That means you can have your text editor in one half of the screen, and a web browser in the other. When you save changes in the editor, the updates are reflected immediately in your browser. It really helps to speed up your development time.

A lot of text editors offer HTML previews and such, but they only work for plain .html pages. The great thing about Live.js is that it works on any webpage, including a working WordPress theme.

Live.js for WordPress Plugin

I have put together a (very!) simple plugin for WordPress to make things that little bit easier. You can download it here (Rename the folder to “pbd-livejs” once you unzip it), or view it on Github.

It automatically adds Live.js to your theme, but only when you are logged in and working locally. You should never use this script on a live site, and the plugin ensures you won’t.

It also keeps the script out of your theme code, which is good because it’s purely for development. There is no need for it to be present in the files on your server.

One final note is that although Live.js also supports sensing HTML changes, I have disabled this. With it enabled, your webpage essentially “flashes” as it loads every second or two.

Let me know if you try it out, or if you’ve used Live.js before already!

Author: "Michael Martin" Tags: "Tools, CSS, JavaScript, Live.js, Plugin,..."
Comments Send by mail Print  Save  Delicious 
Date: Monday, 23 Apr 2012 16:00

Bypass the Settings API

The following is a guest post by Andy Walpole.

There are many parts of the WordPress API which are fantastic but there are also other parts which, I would argue, are lacking.

The Settings API was introduced in version 2.7 to allow the semi-automation of form creation. All credible Content Management Systems and frameworks have their own set of functions or classes for the same purpose. Drupal has a multitude of hooks which can be leveraged, while CodeIgniter uses a combination of the Form Validation Class and the Form Helper .

When creating a WordPress plugin recently I wanted to create a dynamic form to insert data into a field in the option database table. I decided to create a class for this purpose with the intention of creating reusable code for future projects. For ideas, I closely followed previous tutorials written by Alison Kleinschmidt and Sarah Neuber .

I quickly ran into a brickwall. My main issue that I have with the Settings API is that it’s unintuitive and too basic. It is such an abstraction away from the meat and bones of PHP / HTML form creation and validation that it confuses the professional coder rather than assists them.

As a coding exercise I decided to take a different approach to WordPress form creation using OOP and one that had a more familiar user interface. The code I have written so far can be examined on a Git Gist that I have created. There are some excellent features of the Settings API, such as inserting new form fields into existing core admin pages; and this code doesn’t attempt to replicate that.

This purpose of this article is to provide some feedback on the successes and difficulties of my endeavourer.

There are some key aspects in understanding the option API access class. Firstly, I placed it into three files reflecting an MVC architecture. Admittedly, the script itself doesn’t tightly fit this segregation but it is a commonly used and understood way of organising code. In the view file there are key WordPress hooks such as add_action() and add_options_page(); in the controller class are the purpose built form field creation methods and in the model class are validation, sanitisation, security and database functions.

Another aspect is that I have used namespaces and late static bindings that arrived with PHP 5.3. It is a convention amongst WordPress developers to cater for servers that have versions of PHP lower than 5.3. I don’t follow that convention. If your hosting company is running versions of PHP which are three years behind or more then take you custom elsewhere because they are not providing to you a satisfactory service.

Completed Code

This is a lengthy tutorial, and walks you through exactly how the script works. Instead of manually copying and pasting all of the code chunks to assemble the file though, it will be simpler just to copy the finished code from here.

1 – Instantiate the Class

 
$aForm = array(
    'option_name' => 'a_url_here', // has to be alphanumeric and underscores only
    'page_title' => 'A page title here', // Main page title
    'page_url' => 'a-url-here', // URL
    'dynamic_output' => FALSE); // Should the form be generated on more input
 
new \OptionView\Form_View($aForm);

The above should be reasonably self-explanatory. In the $aform array are placed four different key values: the name of the option database field, the page title, the page URL and a boolean value – I’ll explain this in detail later but for now it’s set to false.

The create_html_cov() method is called by WordPress add_options_hook(). In here is the code that envelopes the form:

 
extract(self::$form);
 
$form = '<div class="wrap">';
$form .= screen_icon();
$form .= "<h2>{$page_title}</h2>";
$form .= '<div id="result">';
echo $form;
 
if (isset($_POST['submit'])) {
 
    $error = array();
 
    // validation here
 
    if (empty($error)) {
 
        $this->update_option($form);
 
    } else {
 
        echo $this->failure_message($error);
 
    } // end if error
 
} // end if isset submit
 
echo '</div>';
echo '</div>';

Anybody who codes PHP will be familiar with the above. After form submission any error messages are added to an array and then displayed. At the top is a div with a class of wrap. This is essential so that the admin CSS is used. Note as well that the page title originally made in the $aForm array is now been called from the static $form attribute. How? The values were used in the method below which in turn was called in the view constructor. The values within config_settings() are used throughout the script.

/**
 * Form_Controller::config_settings()
 * Main array for important values throughout the class
 * @param string $option_name
 * @param string $page_title
 * @param string $page_url
 * @param boolean $dynamic_output
 * @return array
 */
 
protected function config_settings($option_name, $page_title, $page_url, $dynamic_output = FALSE) {
 
    // put together the output array
    $output['option_name'] = $option_name; // name of option database field
    $output['page_title'] = $page_title; // name of page
    $output['page_url'] = $page_url; // url of page
    $output['dynamic_output'] = $dynamic_output;
    return $output;
 
}

2 – Creating the Form Fields

It is first necessary to create a form array:

$form = array(
    'method' => 'post',
    'action' => '#result',
    'enctype' => FALSE,
    'description' => 'Add a description here');

The description will go inbetween the legend form tags at the top of the form. The rest are basic attribute form values.

When invoking the create_form() method the form array has to be the first value:

$this->create_form($form);

From here more arrays have to be created for individual form fields. Note how closely the PHP fits the HTML form attributes – something that the user interface of the Settings API is distant from.

 
$textOne = array(
    "input" => "text", // input type
    "name" => "textOne", // name attribute
    "desc" => "This is a text field", // for use in input label
    "maxlength" => "200", // max attribute
    "value" => "YES", // value attribute
    "select" => FALSE // array only for the select input
        );
 
$textTwo = array(
    "input" => "text",
    "name" => "textTwo",
    "desc" => "This is another text field:",
    "maxlength" => "250",
    "value" => "YES",
    "select" => FALSE);

The input can be text, textfield, radio, select or checkbox. The desc key is used in the form field label. Values for text or textfields should be “YES” only and by doing so instructs the class to create a sticky form field. Select is only used for drop-down fields, radio buttons and checkboxes (more of which later). Even if an option is not used there stills needs to be “false” or “null” stated.

Now the two arrays are placed in create_form():

$this->create_form($form, $textOne, $textTwo);

The form has now been created.

3 – Validation and Sanitisation

The following method is used for sanitizing data. If, for instance, it is desirable to remove backslashes or trim white space from the ends of strings, then it is possible to do so like this:

 
$this->sanitize($form, 'trim_post');
 
$this->sanitize($form, 'stripslashes');

That’s straight forward enough. Lets take a look at the code behind this in the model class:

 
/**
 * Form_Model::sanitize()
 * 
 * @param string $handle
 * @param array $form_output
 * @return array
 */
protected function sanitize(&$form_output, $handle) {
 
    switch ($handle) {
 
        case 'sanitize_post':
            array_walk_recursive($form_output, array($this, 'sanitize_post'));
            break;
        case 'trim_post':
            array_walk_recursive($form_output, array($this, 'trim_post'));
            break;
        case 'strip_tags_post':
            array_walk_recursive($form_output, array($this, 'strip_tags_post'));
            break;
        case 'empty_value':
            array_walk_recursive($form_output, array($this, 'empty_value'));
            break;
        case 'stripslashes':
            array_walk_recursive($form_output, array($this, 'stripslashes'));
            break;
        default:
            die("The value you ended into the sanitize() method is not recognised: $handle");
            break;
 
    } // end switch
 
}
 
 
/**
 * Form_Model::trim_post()
 * 
 * @param string $att
 * @param string $single
 * @param array $form_output
 * @return array
 */
 
function trim_post(&$form_output, $att = NULL, $single = NULL) {
 
    if ($single == NULL) {
 
        if (is_array($form_output)) {
            array_walk_recursive($form_output, 'trim');
        } else {
            $form_output = trim($form_output);
        }
 
    } else {
 
        extract(static::$form);
 
        foreach ($form_output[$option_name] as $thisKey => $result) {
 
            if (preg_match("/$att/i", $thisKey)) {
 
                if (is_string($thisKey)) {
                    $form_output[$option_name][$thisKey] = trim($result);
                }
 
            }
 
        }
 
        return $form_output;
    }
 
}

As demonstrated by the trim_post() method it is easy to widen the sanitisation features of the code. Just copy and paste the method, change the name and change trim to another function such as strip_tags() or htmlspecialchars(). Then add a new section to the switch statement in sanitize().

Notice that $this->sanitize($form, ‘trim_post’) trims all input data. It is possible to just sanitise one field by bypassing sanitize() and call its child method directly:

 
$this->strip_tags_post($form, 'feedName', true);
$this->trim_post($form, 'feedName', true);
$this->stripslashes($form, 'feedName', true);

Necessary parameters above are the form array, the name of the attribute and a boolean value of true.

Validation is similar to sanitization. Here are two examples of using validation methods:

 
/**
 * Form_Model::validate_email()
 * 
 * @param string $att
 * @return boolean
 */
 
protected function validate_email($form_output, $att) {
 
    extract(static::$form);
 
    if (is_array($form_output) && is_string($att)) {
 
        foreach ($form_output[$option_name] as $thisKey => $result) {
 
            if (preg_match("/$att/i", $thisKey)) {
 
                if ($result !== "") {
 
                    if (!filter_var($result, FILTER_VALIDATE_EMAIL)) {
                        return false;
                    }
 
                } // end if
 
            } // end if
 
        } // end foreach
 
    } else {
        die("Make sure that the inputs for validate_url() is an array and a string");
    }
 
 
}

To extend the different types of validation it is, like sanitization, a straightforward process of copying the code, renaming it and changing the filter_var().

One other useful validation method is duplicate_entries():

 
if ($this->duplicate_entries($form) == false) {
    $error[] = "Please make sure that all input values are unique";
}

This will ensure that no one inputted value is identical.

4 – Select Drop-Down Lists, Radio Buttons & Checkboxes.

The creating and validating of these form fields is slightly different.

Firstly, when you create radio buttons or checkboxes you must do so by specifying the total number of each in the select value of the field creation array:

 
$aRadioButton = array(
    "input" => "radio",
    "name" => "radioButtonName",
    "desc" => "A radio button",
    "maxlength" => FALSE,
    "value" => "mac",
    "select" => 3);
 
$aCheckBox = array(
    "input" => "radio",
    "name" => "aCheckBoxName",
    "desc" => "First checkbox",
    "maxlength" => FALSE,
    "value" => "mushrooms",
    "select" => 3);

The above would specify that there are three checkboxes in the form and three radio buttons (obviously, it is necessary to create separate arrays for all individual fields).

As is standard in HTML, the radio buttons must have the same name attribute. In this script checkboxes must have individual name attributes. I’m working on a solution to creating checkboxes with the same name attributes! Notice as well that, unlike for text or textareas, the value is no longer “YES” but a value unique to all fields.

Previously, checking for empty submitted values was done so through the empty_value() method

 
// Check whether there are any empty form values:
if ($this->empty_value($form) === FALSE) {
    $error[] = "Please don't leave any input values empty";
}

Radio buttons and checkboxes have their own methods:

 
if ($this->empty_checkboxes($form, 2) === FALSE) {
    $error[] = "Please check at least two checkboxes";
}
 
if ($this->empty_radio_butts($form) === FALSE) {
    $error[] = "Please make sure that you check one of the radio buttons";
}

In empty_checkboxes() the second digit is the minimum number of checkboxes that the user needs to click. If none is specified then the default is one.

When creating a drop-down list it is necessary to add any array of values to the select key:

 
$cities = array(
    'Shanghai',
    'Karachi',
    'Mumbai',
    'Beijing',
    'Moscow',
    'Sao Paulo',
    'Tianjin',
    'Guangzhou',
    'Delhi',
    'Seoul',
    'Shenzhen',
    'Jakarta',
    'Tokyo',
    'Mexico City',
    'Istanbul');
 
$select = array(
    "input" => "select",
    "name" => "selectName",
    "desc" => "Select here select here",
    "maxlength" => FALSE,
    "value" => TRUE,
    "select" => $cities);

Putting together the above examples, the code for a form with multiple different types of input would look like this:

 
function create_html_cov() {
 
    // essential.
    extract(self::$form);
 
    $form = '<div class="wrap">';
    $form .= screen_icon();
    $form .= "<h2>{$page_title}</h2>";
    $form .= '<p>This is the admin section for Affiliate Hoover plugin</p>';
    $form .= '<div id="result">';
    echo $form;
 
    if (isset($_POST['submit'])) {
 
        $error = array();
 
        // ESSENTIAL! Do not leave this out. Needs to come first
        $form = $this->security_check($_POST);
 
        // Sanitization
 
        $this->sanitize($form, 'trim_post');
 
        $this->sanitize($form, 'stripslashes');
 
        // Validation
 
        if ($this->validate_email($form, 'feedName') === FALSE) {
            $error[] = "Please make sure that the email addresses are correct";
        }
 
        if ($this->validate_url($form, 'urlName') === FALSE) {
            $error[] = "URL is not right";
        }
 
        // Check whether there are any empty form values for text or textarea fields:
        if ($this->empty_value($form) === FALSE) {
            $error[] = "Please don't leave any input values empty";
        }
 
        // don't allow empty checkboxes
        if ($this->empty_checkboxes($form, 2) === FALSE) {
            $error[] = "Please check at least two checkboxes";
        }
 
        // don't allow empty radio buttons
        if ($this->empty_radio_butts($form) === FALSE) {
            $error[] = "Please make sure that you check one of the radio buttons";
        }
 
        // Make sure that none of the form values are duplicates
        if ($this->duplicate_entries($form) === FALSE) {
            $error[] = "Please make sure that all input values are unique";
        }
 
        if (empty($error)) {
 
            $this->update_option($form);
 
        } else {
 
            echo $this->failure_message($error);
        } // end if error
 
    } // end if isset submitForm
 
    echo '</div>';
 
    // Create the form here:
 
    $aTextField = array(
        "input" => "text", // input type
        "name" => "textOne", // name attribute
        "desc" => "This is a text field", // for use in input label
        "maxlength" => "200", // max attribute
        "value" => "YES", // value attribute
        "select" => FALSE // array only for the select input
            );
 
    $aTextArea = array(
        "input" => "textarea",
        "name" => "aTextArea",
        "desc" => "This is a textarea:",
        "maxlength" => FALSE,
        "value" => "YES",
        "select" => FALSE);
 
    $aRadioButtonOne = array(
        "input" => "radio",
        "name" => "radioButtonName",
        "desc" => "A radio button one",
        "maxlength" => FALSE,
        "value" => "mac",
        "select" => 3);
 
    $aRadioButtonTwo = array(
        "input" => "radio",
        "name" => "radioButtonName",
        "desc" => "A radio button two",
        "maxlength" => FALSE,
        "value" => "linux",
        "select" => 3);
 
    $aRadioButtonThree = array(
        "input" => "radio",
        "name" => "radioButtonName",
        "desc" => "A radio button three",
        "maxlength" => FALSE,
        "value" => "pc",
        "select" => 3);
 
    $aCheckBoxOne = array(
        "input" => "checkbox",
        "name" => "aCheckBoxOne",
        "desc" => "A radio button one",
        "maxlength" => FALSE,
        "value" => "mushrooms",
        "select" => 3);
 
    $aCheckBoxTwo = array(
        "input" => "checkbox",
        "name" => "aCheckBoxTwo",
        "desc" => "First checkbox",
        "maxlength" => FALSE,
        "value" => "pizza",
        "select" => 3);
 
    $aCheckBoxThree = array(
        "input" => "checkbox",
        "name" => "aCheckBoxThree",
        "desc" => "First checkbox",
        "maxlength" => FALSE,
        "value" => "chicken",
        "select" => 3);
 
    $form = array(
        'method' => 'post',
        'action' => '#result',
        'enctype' => 'multipart/form-data',
        'description' => 'Add a new form underneath');
    $this->create_form($form, $aTextField, $aTextArea, $aRadioButtonOne, $aRadioButtonTwo, $aRadioButtonThree,
        $aCheckBoxOne, $aCheckBoxTwo, $aCheckBoxThree);
 
    echo '</div><!-- end of wrap div -->';
 
}

The above will create a form that looks like this:

screenshot two

5 – Dynamic Forms

Creating a form layout that enables the easy adding, deleting or editing of multiple form blocks with one submit button is almost impossible with PHP alone. The best solution would be AJAX but this is primarily intended as a server-side coding exercise.

To create a dynamic form with my Option API access class then the only value to change is the dynamic output booleanwhen instantiating the class.

It is now possible to create a form with different sections that can be independently deleted or edited.

An example being:

screenshot three

Conclusion

The code I linked to in the Git Gist is still in its development stage. There are a number of issues. Firstly, it is not possible to create checkboxes with the same name attribute – they have to be separate. Secondly, it is only possible to create one set of radio buttons per form section.

Attempting to create a dynamic form like I have done so in this Option API access class create an entire new level of code complexity. Because a server-side script like PHP has only limited use of the DOM, it is necessary to find solutions based on loops of the form arrays and a lot of maths. This is an aspect of the code that I will continue working on.

But, positive parts of the Option API access class are that it takes care of all tables HTML and CSS peculiar to the WordPress admin section; it is relatively easy to create and validate new forms with a multitude of fields, and all security aspects of the WordPress backend like wp_verify_nonce() are incorporated into the form submission process.

Author: "Andy Walpole" Tags: "WordPress, API, Settings"
Comments Send by mail Print  Save  Delicious 
Date: Thursday, 12 Apr 2012 16:00

Varnish and WordPress

The following is a guest post by Austin Gunter of WPEngine.com.

If you’re looking to boost performance for a content-heavy WordPress installation, adding a cache like Varnish is a great way to boost your site’s performance.

NB – This is an advanced topic, and only relevant if you have full control over your server (e.g. you’re on a VPS). It does not apply to regular webhosting.

What is Varnish?

Varnish Cache is a web app accelerator, or a caching HTTP reverse proxy. Install it in front of any server that speaks HTTP and configure it to cache the contents, and the Varnish community claims that it speeds up delivery by a factor of 300 – 1000x. 

In a nutshell, caching a webpage means storing a copy of that website’s content for future visitors to see. Varnish can cache pages of your WordPress site so that your server doesn’t need to call the database each time a visitor visits your site. This reduces server load because the stored copy means the webserver doesn’t have to go find the same images and content for each visitor.

Varnish caches page data in virtual memory, so your site will load much faster, providing a killer SEO boost. Google found that every additional 0.5s load time meant 20% fewer site visitors (Source). Reducing the page load time can dramatically increase your visitors and boost your search rankings at the same time.

Varnish themselves have put together a great video for explaining what it does, as simply as possible:

Installing Varnish

Varnish is free software that you can install this afternoon. It runs on Linux, and primarily FreeBSD, but can work on other platforms as well. Once installed, you can customize how incoming requests will be handled with the Varnish Configuration Language (VCL).

This flexibility means that each Varnish install can and should be designed with a particular site in mind.

You’ll want to begin with a basic configuration of Varnish, and then begin testing small changes as you get the hang of how it works with your particular site, and how the functions work. There are several different subroutines that tell Varnish how to respond to requests going in and out, to errors, and so on.

I’ll start with a basic set up, and then cover basic VCL functions that you’ll be tweaking as you go.

Step by Step

Setting up Varnish is pretty simple. I’m going to assume you’re using Apache on a Debian-based system. It does work on other systems too though.

Start in the command line with this:

apt-get install varnish

First, you want to configure Apache to listen on port 8080 of the localhost interface. Varnish can then listen on port 80 (Where your visitors connect). In /etc/apache2/ports.conf, edit the following settings:

NameVirtualHost 127.0.0.1:8080
Listen 127.0.0.1:8080

To get Varnish to start (it won’t by default), edit the following in /etc/default/varnish

START=yes
DAEMON_OPTS=”-a EXTERNAL_IP_ADDRESS:80 \
	-T localhost:6082 \
	-f /etc/varnish/default.vcl \
	-S /etc/varnish/secret \
	-s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G”

Replace EXTERNAL_IP_ADDRESS with the IP of your external IP address. It can also be an internal address if your server is setup behind a load balancer, or something like NGINX. This setting controls what IP address and port you want Varnish to bind to and listen on.

Now edit /etc/varnish/default.vcl, which should already exist with lots of it commented out. Start by changing the default backend.

backend default {
	.host = “127.0.0.1”;</p>
	.port = “8080”;</p>
}

Now Varnish knows that Apache is listening on port 8080 of the localhost interface, and you can start using the functions. The majority of the work will be with vcl_recv and vcl_fetch, and if you don’t call an action in this subroutine and Varnish reaches the end, it will execute the built-in code from default.vcl.

Note: you never want to cache wp_admin, wp_login, or similar pages.

Here’s how it works – the 4 basic subroutines in your Varnish config which you need to handle requests are:

sub vcl_recv

This is called at the beginning of a request and tells varnish what to do with that particular request: whether or not to serve it, how to serve it, and which backend to use.

Varnish receives a request from your browser, vcl_recv and chooses to do one of 3 things with it: vcl_hash, vcl_pass, and vcl_pipe (More details on those in a minute!). You can change the request if you like, altering cookies or removing the request header.

sub vcl_fetch

vcl_fetch is called after a document has been successfully retrieved from the backend. Use this to alter the response headers, trigger ESI processing, or try alternate backend servers if the request failed.

The request object, req, is still available, and there is also a backend response, beresp, which contains the HTTP headers from the backend.

sub vcl_hash

You can call hash_data on the data you want to add to the hash. This subroutine may terminate with calling return() with one of the keywords, hash or proceed.

sub vcl_deliver

Call this before a cached object is delivered to the client. This may terminate with deliver, error code, or restart. Deliver, delivers the object to the client. Error will return the specified error code to the client and abandon the request. Restart will restart the transaction and increase the restart counter. If the number of restarts exceed the max_restarts, varnish emits a guru meditation error.

Actions

There are a number of actions you can take inside each subroutine as you customize Varnish:

pass

Pass the request and subsequent response to and from the backend server, not cached. pass can be called in both vcl_recv and vcl_fetch.

lookup

Called from vcl_recv to deliver content from cache even if the request indicates that the request should be passed. You can’t call lookup from vcl_fetch.

pipe

From vcl_recv, pipe short-circuits client and backend connections and Varnish will just sits there passing the data back and forth, logging the data, so logs will be incomplete. Beware that with HTTP 1.1 a client can send several requests on the same connection and so you should instruct Varnish to add a “Connection: close” header before actually calling pipe.

deliver

Deliver the cached object to the client. Usually called in vcl_fetch.

esi

ESI-process the fetched document.

The Varnish community has a very detailed tutorial on using VCL, and the functions that you can perform on your site, which I won’t go into here.

Example Configurations

Hopefully this post has given you a good introduction to Varnish, but the best way to truly start playing with it is to see some sample configuration files.

The Varnish website has a great collection sample configurations, which can make a perfect starting point for building your own.

More specifically, Mattias Geniar has put up sample fetch and receive configs for WordPress on Github.

Suffice to say that Varnish is very customizable and can do wonders for WordPress. A great example would be a Membership site. You can customize Varnish to cache content based on a user’s permissions, on their membership level, or even if they’re just visiting your site.

Facebook uses Varnish to great avail, and no doubt they have customized it to hell and back, but it goes to show you how efficient and flexible Varnish is.

Plugins

Update (13 Apr 2012): Thanks to Alexander and Pothi for letting us know about an updated Varnish plugin for WP 3.0+ and one on WP.org also for purging your cache (Also, check out both of these guy’s blogs for more Varnish and WP performance tips!)

The varnish plugin will purge the cache when content is added or edited, and it works for multisite. The plugin does include a sample config file for your Varnish backend, however, keep in mind that when you’ve customized it for one particular site, those customizations may or may not play nicely with a different site.

Have you had any luck installing Varnish for your WordPress? What works and doesn’t? Feel free to share some of your ideas in the comments!

Update (24 Jul 2012): Jason Ormand has put together a cool Bash script to make configuring an NGINX server with Varnish and WordPress even faster.

Author: "Austin Gunter" Tags: "WordPress, Cache, Varnish"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 28 Mar 2012 16:00

Extend WordPress Comments

It’s a relatively simple process to add some flair to your blog comments these days. I want to look at 6 of the best free tools and give you a quick overview of the benefits of each.

Let’s start with one you’ve likely heard of before…

Disqus

Disqus

Disqus uses JavaScript to replace your WordPress comments with their own system. You lose the total design flexibility that vanilla WordPress comments offer, but you gain a large feature set, including:

  • Real-time Comments – New comments appear on the page without needing to refresh. Not a major benefit for smaller sites, but very good if your posts tend to get a flurry of comments in the first few hours of publishing.
  • Social Media Integration – Users can comment via Facebook or Twitter, and share to those sites as well if they choose.
  • Global Profiles – A user’s Disqus profile shows their comments across all Disqus sites. The idea is that a Disqus user will be more likely to comment on a Disqus blog because of this, and it makes for a fast way to find out more about a commenter.
  • Plenty of other features, like inline comment replies or “subscribe to updates.”

Another good thing to bear in mind with Disqus is that they are very active. Disqus has been around for several years now, and it still innovates and updates regularly. They even have a mystery Disqus 2012 project to be released soon.

And incase you’re wondering, your comments can all be imported to Disqus, and exported back to WordPress if you choose to leave Disqus. You won’t be hostage to their system.

WordPress Install Instructions for Disqus

Livefyre

Livefyre

Livefyre is very similar to Disqus, and as far as I can tell, they offer almost exactly the same feature set. Real-time comments, social media, user profiles, subscription etc. are all included as standard.

Livefyre is a newer contender in this market though, but personally I think they have an edge over Disqus in 2 ways:

  • Simpler Installation – Disqus isn’t hard to install, but it does have JavaScript issues often enough (For anyone who has ever used it, hands up if your comment counts vanished?). I haven’t used Livefyre on as many sites yet, but it hasn’t given me any hassle.
  • Sleeker Design – This is just a matter of taste, but I like the softer design of Livefyre’s comments more. It’s not a big difference, but every little helps, and even their website has a better design.

WordPress Install Instructions for Livefyre

IntenseDebate

Intense Debate

IntenseDebate is similar again to the 2 above (The last tool in this style, I promise!). The reason I mention it is because it has one very big draw for WordPress users; it’s owned by Automattic, the creators of WordPress.

The feature-set is generally quite similar to the others (Though real-time commenting is noticeably absent), and their design is very similar as well.

Whilst there is nothing that I can say is “wrong” with IntenseDebate, it’s hard to recommend them overly either. Their site and script don’t feel as sleek as Livefyre, and their community no longer seems as vibrant as Disqus’.

Oh, and the bright green “180p” badges look awful… (Look down the sidebar of their blog to see what I mean).

Begin IntenseDebate Install Process

Facebook Comments

Facebook Comments

The Facebook Comments script is exactly what you would expect; it replaces your own comments with a Facebook wall. Users must be signed in to their Facebook accounts, and then what they see is identical to Facebook.com.

There are two big advantages to this approach:

  • Publicity – Users are commenting with their Facebook profiles, so with a little luck, their friends will see the comment and look at your article as well.
  • Anti-spam – Every commenter must use their Facebook account, so spammers have a much harder time bulk-spamming blogs.

The strongest feature is also the strongest drawback. Some people (like me) keep their accounts completely separate to their work or online profiles, and so, you will miss out on those people’s contributions.

There is a plugin for adding these comments to WordPress, but it seems to be broken with WordPress 3.3. For a developer though, the installation is similar to any other Facebook script. You can get the code here.

(If you have trouble with the install or can’t find a reliable guide online, let me know and I’ll put together a walkthrough).

Jetpack

Jetpack

Jetpack is essentially a set of WordPress plugins, which is a topic I’m about to cover in the final section. What sets Jetpack apart is its ease of use. You install one package, and cherry pick the features for you want.

In the case of commenting, that means 3 things:

  • Subscriptions – Users can be emailed when follow-up comments are left after their reply. This is a great way to keep conversations going.
  • Gravatar Hovercards – When you mouse over a commenter’s name, this will pull up a little bubble with their image and a quick bio with a link to their full profile on Gravatar.
  • Sharing – Jetpack makes it easy to add social media sharing links anywhere on your page, including around the comments area.
One tool is then enough to take your existing theme’s comments, and upgrade them with some of the most popular commenting functions.

Install Guide for WordPress Jetpack

Other WordPress Plugins

WordPress Comment Plugins

There are hundreds out there, but I want to focus on just 4 of them:

  • AJAX Edit Comments - This allows users to edit their comments for up to 15 minutes after submitting them. It is a good way to enable users to fix typos, and avoid “double-posting” if they want to add another line. It is particularly popular on coding blogs, where awesome commenters might contribute code snippets of their own but find they get messed up when displayed.
  • CommentLuv – CommentLuv gets the latest blog post from a commenter’s RSS feed, and automatically adds a link to it after their comment. This is a great way to thank visitors for taking the time to post a comment.
  • GD Star Rating – Not the simplest of plugins to configure, but GD Star Rating has a wealth of options that can enable star ratings (Or thumbs) on WordPress comments (And posts).
  • WordPress reCAPTCHA - Spam can be a nightmare on WordPress blogs, but this plugin makes it simple to add Google’s captcha (“Type the letters…”) tool to your blog.

Which Do You Use?

I’ve always used regular WordPress comments with some plugins here on Pro Blog Design, because I like the flexibility of creating your own comments design. The topic of this blog is another reason; it’s good for us to show something unique.

More and more though, I’m working with clients who are choosing the scripts above (Particularly Disqus and Livefyre). These scripts are very impressive, and can even be a good way to speed up the development time of a new site.

I think the Facebook widget is only a great bet if you specifically try to encourage users to interact with you via Facebook (e.g. your site takes signups via Facebook Connect, or you need as much social sharing of your app as possible etc.).

I’d love to hear what approach you’ve taken on your site, and why you went down that road!

 

Author: "Michael Martin" Tags: "WordPress, Comments, Disqus, Jetpack, Li..."
Comments Send by mail Print  Save  Delicious 
Date: Monday, 12 Mar 2012 18:18

Tweets in WordPress with Backups

Update (20 January 2013): There is a newer version of this script available now: Authenticate Your Twitter API Calls Before March.

Around this time last year, I showed you how to embed tweets in WordPress using the Twitter API.

Today, I want to improve on that script by adding a backup to it. Twitter is often down or unresponsive, and when it is, the previous script fails and displays nothing.

With a backup, we store an extra copy of each set of tweets that we find. When Twitter is down, we can then use this backup to display tweets as normal.

WordPress “Transients” vs “Options”

We will use both of these, so it’s best to explain quickly what they are. An “option” is a value that is stored permanently in your database. There is an option for your site title, the slogan etc.

A transient is very similar to an option, but it also has an expiry time. After this time, the transient is deleted. For more information, read using the Transients API.

The transient is perfect for caching tweets. We can store the results from Twitter, and when the transient expires, fetch new ones.

The option however is perfect for the backup, because we never want it to expire (We will just write over it each time we update it).

The Code

The majority of this code is from the previous article. For that reason, I will only explain the new additions. To understand how we process each tweet and display it, check out the original post.

And if you’d rather just skip on, you can grab the fully completed code here.

Find where in your theme you would like the tweets to appear (e.g. sidebar.php), and paste the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
/*
 * JSON list of tweets using:
 *         http://dev.twitter.com/doc/get/statuses/user_timeline
 * Cached using WP transient API.
 *        http://www.problogdesign.com/wordpress/add-a-backup-to-embedded-tweets-in-wordpress/
 */
 
// Configuration.
$numTweets = 3;
$name = 'problogdesign';
$cacheTime = 5; // Time in minutes between updates.
$exclude_replies = false; // Leave out @replies?
 
$transName = 'list-tweets'; // Name of value in database.
$backupName = $transName . '-backup'; // Name of backup value in database.

These are your configuration options. How many tweets do you want to display? From which user? And how often should we fetch new updates? (It’s fine to go to 1 minute even, but it means one of your users will have a slow page load every minute, rather than one every 5 minutes).

The only change from before is that I’ve added the option to exclude @replies from your results, and we now have 2 names for values in the database. Let’s carry on.

The next section of code is the bulk of the script. Essentially, it fetches the data from Twitter’s API, picks out the parts we want, formats them, and saves them as an array called $tweets. The original post explains it all one step at a time.

You can also read about this specific Twitter API call here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Do we already have saved tweet data? If not, lets get it.
if(false === ($tweets = get_transient($transName) ) ) :    
 
    // Get the tweets from Twitter.
    $response = wp_remote_get("http://api.twitter.com/1/statuses/user_timeline.json?screen_name=$name&count=$numTweets&exclude_replies=$exclude_replies");
 
    // If we didn't find tweets, use the previously stored values.
    if( !is_wp_error($response) && $response['response']['code'] == 200) :
        // Get tweets into an array.
        $tweets_json = json_decode($response['body'], true);
 
        // Now update the array to store just what we need.
        // (Done here instead of PHP doing this for every page load)
        foreach ($tweets_json as $tweet) :
            // Core info.
            $name = $tweet['user']['name'];
            $permalink = 'http://twitter.com/#!/'. $name .'/status/'. $tweet['id_str'];
 
            /* Alternative image sizes method: http://dev.twitter.com/doc/get/users/profile_image/:screen_name */
            $image = $tweet['user']['profile_image_url'];
 
            // Message. Convert links to real links.
            $pattern = '/http:(\S)+/';
            $replace = '<a href="${0}" target="_blank" rel="nofollow">${0}</a>';
            $text = preg_replace($pattern, $replace, $tweet['text']);
 
            // Need to get time in Unix format.
            $time = $tweet['created_at'];
            $time = date_parse($time);
            $uTime = mktime($time['hour'], $time['minute'], $time['second'], $time['month'], $time['day'], $time['year']);
 
            // Now make the new array.
            $tweets[] = array(
                            'text' => $text,
                            'name' => $name,
                            'permalink' => $permalink,
                            'image' => $image,
                            'time' => $uTime
                            );
        endforeach;
 
        // Save our new transient, and update the backup.
        set_transient($transName, $tweets, 60 * $cacheTime);
        update_option($backupName, $tweets);

The change to notice comes on line 8. Here, we check if an error occurred or Twitter failed to give us back our tweets. We check the latter by looking at the HTTP status code returned. You can read more about how Twitter uses status codes here, but the short of it is that anything other than 200 means something went wrong.

We only process the page returned if it passes these two checks.

The other change is on the very last line, where we save the transient as normal, but also save the permanent “option”. This updates our backup with the most recent set of results.

Moving on, all we need to do if the fetch fails is populate the $tweets array with the value from our permanent backup.

1
2
3
    else : // i.e. Fetching new tweets failed.
    	$tweets = get_option($backupName); // False if there has never been data saved.
	endif;

Now we can display the tweets as before. To check that we do have tweets to display, we check if $tweets is false (If a backup has never been made, get_option() will have returned false to it).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Now display the tweets, if we can.
if($tweets) : ?>
    <ul id="tweets">
    <?php foreach($tweets as $t) : ?>
        <li>
            <img src="<?php echo $t['image']; ?>" width="48" height="48" alt="" />                
            <div class="tweet-inner">
                <p>
                     <?php echo $t['name'] . ': '. $t['text']; ?>
                     <span class="tweet-time"><?php echo human_time_diff($t['time'], current_time('timestamp')); ?> ago</span>
                </p>
            </div><!-- /tweet-inner -->
        </li>
    <?php endforeach; ?>
	</ul>
	<p><a href="http://twitter.com/#!/<?php echo $name; ?>">[ Follow us on Twitter ]</a></p>
 
<?php else : ?>
	<p>No tweets found.</p>
<?php endif; ?>

At the end, I’ve added a simple error message, rather than displaying nothing. This will only appear if fetching tweets fails, and a backup has never been made.

And that’s it done. If you used the previous script, this can be slotted in over it and all you need to do is update your details in the configuration section at the top. Now your tweet list should be that bit more reliable.

The full code can be found here.

Author: "Michael Martin" Tags: "WordPress, PHP, Transients, Twitter"
Comments Send by mail Print  Save  Delicious 
Date: Sunday, 01 Jan 2012 20:23

Small Business Working

As 2012 begins, I want to look back over the past year to see what can be learnt from it. Learning from your mistakes is less glamorous than a list of grand New Year’s Resolutions (Though I do like those too), but often more effective at ensuring the next year is even better than the last.

With that in mind, what follows are some of the lessons I have picked out from my own 2011. I would love to hear if these resonate with you.

When looking back at your own year, I’d encourage you not to look for when things went “wrong” as such, but simply to think of how things may have gone differently. That’s what I’ve done. If I were to go back, I may well do some of these things the same, but considering the alternatives is a great way to learn.

Plan For the Follow-Through

When working on a large project, launch day is the natural target. All of your work is dedicated to releasing the product.

That is obviously crucial, but equally important is planning in advance to build on the momentum from your big release. That could mean anything from analyzing its success, planning the next release, or simply capitalizing on the good PR.

I learned this lesson from the relaunch of this site in May. We had been working on it on and off for almost a year, so it had been a huge project, but in the push to launch, we built up a backlog of other work.

I worked on this and wrote a number of articles following the launch, but not enough. The backlog took priority because as far as schedules were concerned, the relaunch project was complete. The follow-through should have been a clearly defined of the project.

Short-Term is More Tempting than Long-Term

How many times have you heard a freelancer say they can’t find the time to work on their site or a personal project? Even though they really enjoy it?

All freelancers hit this issue, and I was no different. I think the reason for this is extremely simple; short term benefits have a clearer and more obvious benefit than long term ones. In my case, PliablePress has been hurt by this. Improving the framework brings me incremental benefits over time, but taking on a large client project pays off right away.

The trick here I believe is a balancing act. If you spend all of your time on short-term goals, you’ll never get past where you are right now. But similarly, if you only work on long-term plans, you may not survive that long. Don’t let one overcrowd the other.

Focus Beats Multitasking

This is a common enough productivity tip that you have likely heard before. Only work on one thing at a time. It makes sense, and it will come as no surprise to you that closing down Twitter and Skype etc. will help you work better.

The bigger lesson is to organize yourself each day so you know which projects you will be working on. You don’t need a step-by-step breakdown of the tasks (Though some people swear by that), a simple list of the top x projects that need your attention today will do.

This is something I see in my work again and again. The days in which I have a clear list of projects to work on, and I tackle them one by one, are my most productive days. Sometimes you can even spend a whole day on just one project, which is fantastic. Focus only on the next task, not all of the tasks.

Sidenote – When making your list, try to avoid adding any murky tasks that don’t have clear completion objectives, e.g. research can go on indefinitely so before you start, make sure you’re aware of how you will know when you’re done.

New Does Not Necessarily Equal Worthwhile

In the tech world, we all love the speed at which things change. The impulse is to try as many new products and services as we can, because who knows what the next game changer will be?

And therein lies the problem. It could be anything, and there are new things coming at you every day. You can’t try them all, and the attempt is a waste of your time. I think a better approach is to let the community try them out for you, and join in when a winner seems to be emerging.

Quora is a great Q&A style site.A good example of this is social networks. There are a million different variations out there and new ones all the time. Most won’t catch on, and the time put into them is lost to you. I joined Quora after consistently reading good responses on it for months, but I’m not on Pinterest, Path or any of the other current “hot” networks yet. I’ll check back again in a few months.

The exception is tools that really add value to your business (Webapps you use, development software etc.). Again, I wouldn’t try every single one as it comes out, but every few months, take a look around. It’s fun too, playing with new toys is hard to count as work!

Join The Communities You Value

The web is full of little communities, and you could take part in them from morning to night. On a productivity note, you might try to cut down on this when you have deadlines to meet.

It’s possible to go too far with this though. Once you’ve read a lot on a particular site, and especially after you’ve started enjoying the comment discussions, you can get a lot more out of taking part as well.

Hacker News & Stack OverflowI used to post thousands of comments in forums when I was younger, and then again on blogs as I started Pro Blog Design. That’s been somewhat lessened in 2011, so I’m looking forward to starting again. In particular, I’ve massively enjoyed reading Hacker News and Stack Overflow, so it’s about time I joined up.

Planning is Both Impossible and Essential

A slight paradox, but it’s true. It’s impossible to plan for everything that may happen, even over just a short period of time. But with no plan, things inevitably fall into chaos.

I like to think I’m quite organized, and I always have a good idea of how the next 3-4 weeks will flow. I don’t think any of my “ideas” have ever gone 100% to plan though.

Things change. Something will crash, a client will take longer with their feedback, an awesome new project will come up etc.

You need to plan enough to help things run smoothly, but be flexible enough to cope with the inevitable curve balls that come at you. I’m still not sure of the exact formula for this, but it seems that the more experience you have of it, the better your plans and estimates become.

Fight to Work On What You Love

When you run a small business, you’re responsible for everything. I have the pleasure of working with a number of other people throughout the year here, but no matter what, the final responsibility is still mine.

In practice, what this means is that the tasks you can’t easily find someone else to take care of, you do yourself.

That’s fine in the short term, and often even beneficial (Knowing you have to submit your accounts yourself keeps your finances very organized throughout the year!), but it isn’t a long term solution.

You didn’t start a business to work on things you don’t enjoy. There will always be some jobs you just have to do, but when the ones you don’t enjoy are the majority, you need to fix it.

I’m quite happy with where I am in regards to this now; the trick for me was angling towards certain types of projects. Great clients with interesting projects means that more or less everything is fun!

The Truest Test of a Business is When You Leave It

Not that I didn't still enjoy the break!Again, this is something you’ve likely read before, and it’s spot on. In September, I was away for around 3 weeks, and it likely affected every part of the business.

I planned in advance as much as I could, but every change has a knock-on effect. By prioritizing the projects that could most likely be finished before I left, I was leaving others to be done largely after I returned. I was also lining up new projects to be worked on while I was away, but I was still needed at various points to give my thoughts.

In other words, the month before and the month after the trip were crazy. There is no easy fix to this, but removing the dependency on you is definitely a big area to work on if you want your business to grow.

Conclusion

Those were the biggest new “lessons” I would take from my 2011. I’ve deliberately chosen from when things could have gone better, not from when they went great. I’d be happy to share those as well if you like, but learning from mistakes is often the more revealing analysis so they weren’t included here.

In return, I’d love to hear about your year. What have you learnt from it and what will you do differently this year?

Before I finish, I want to apologize for how quiet the past months have been. They’ve been some of the busiest months I’ve ever had, but I’ve missed blogging greatly. January will be a quiet month as well, sorry, but after the 29th, I will be back with a vengeance. I’ve learnt a lot, so there’s a lot I want to share!

If you reached this far in the post, thank you for still being a reader after all this time.

Happy New Year everyone!

Main photo by razvan.caliman.

Author: "Michael Martin" Tags: "Business, Freelance, Pro Blog Design, Pr..."
Comments Send by mail Print  Save  Delicious 
Date: Monday, 08 Aug 2011 16:00

User-Submitted Posts in WordPress

Recently, one of our clients needed to let visitors create WordPress posts (Or custom post types of course) from the website itself.

In this post, I’m going to walk through some of the WordPress functions I used to achieve this. We’ll go over creating the post itself, adding categories/tags, and adding metadata.

(We’ll skip the stage where you build a form, validate what users enter etc. and just focus on the WordPress functions instead)

Create the Post

The method to create the core post is simple. Essentially, you build a $post object, and then insert it with wp_insert_post().

You can use our $post reference to speed this up. Let’s take a look at some examples:

1
2
3
4
5
6
$new_post = array(
	'post_title' => 'Our New Post',
	'post_content' => 'Our filler text for the post.'
);
 
$post_id = wp_insert_post($new_post);

That would create a new post, with the title “Our New Post”, and the filler text we’ve entered.

If wp_insert_post() is successful, it returns the ID of the new post it has created (Which we assign to $post_id here). If it fails, it will return 0.

By default, the status is set to “Draft”, so let’s tweak it to publish the post instantly:

1
2
3
4
5
6
7
$new_post = array(
	'post_title' => 'Our New Post',
	'post_content' => 'Our filler text for the post.',
	'post_status' => 'publish'
);
 
$post_id = wp_insert_post($new_post);

You can set just about any value from the $post variable, e.g.

1
2
3
4
5
6
7
8
9
10
$new_post = array(
	'post_title' => 'Our New Post',
	'post_content' => 'Our filler text for the post.',
	'post_status' => 'publish',
	'post_type' => 'page',
	'post_author' => 2,
	'post_date' => '2011-09-01 15:10:30'
);
 
$post_id = wp_insert_post($new_post);

Our post is now a page, written by user #2, and scheduled to be published on the 1st of September.

Categories, Tags and Custom Taxonomies

Let’s move on to categories and tags. You can either set these as you make the rest of the post, or add them separately later. Let’s start by seeing how it is done in wp_insert_post().

For categories, you pass in an array containing the IDs of the categories you want to add, e.g. for categories 9 and 29:

1
2
3
4
5
6
7
8
$new_post = array(
	'post_title' => 'Our New Post',
	'post_content' => 'Our filler text for the post.',
	'post_status' => 'publish',
	'post_category' => array(9, 29)
);
 
$post_id = wp_insert_post($new_post);

Tags are a little easier. Instead of passing in the IDs, you can use the names of the tags themselves, e.g.

1
2
3
4
5
6
7
8
$new_post = array(
	'post_title' => 'Our New Post',
	'post_content' => 'Our filler text for the post.',
	'post_status' => 'publish',
	'tags_input' => array('tag1', 'Filler Tag')	
);
 
$post_id = wp_insert_post($new_post);

You can even set the terms of a custom taxonomy. This uses the “tax_input” parameter and expect you to give it an array where each key is a custom taxonomy, and each value is an array of tags in that taxonomy.

Update (8 Aug 2011): Rarst has left a comment saying that “tax_input” will only work if initiated by a logged in user with the permission to edit that taxonomy. If this affects you, read on below for a different solution that will work anywhere!

For example, let’s say we have a taxonomy called “custom_tax”, and the two tags we want to add to the post are “Dolor” and “Ipsum”.

1
2
3
4
5
6
7
8
9
10
$new_post = array(
	'post_title' => 'Our New Post',
	'post_content' => 'Our filler text for the post.',
	'post_status' => 'publish',
	'tax_input' => array(
		'custom_tax' => array('Dolor', 'Ipsum')
	)	
);
 
$post_id = wp_insert_post($new_post);

I mentioned that you could also add your tags later on. To do that, use the wp_set_object_terms() function (There are similar functions to this one, specifically for tags etc. but this covers it all for you).

The format is: wp_set_object_terms( post ID , array of tag IDs , taxonomy , append true/false ).

The last value is important. If you set true, then your new tags will be added to any existing ones. If you set false, then any existing tags will be replaced with your new ones.

Here are some examples of adding categories, tags, and a custom taxonomy this way:

1
2
3
wp_set_object_terms( $post_id, array(27, 28, 29), 'category', true);
wp_set_object_terms( $post_id, array('Example', 'Lorem Ipsum'), 'post_tag', true);
wp_set_object_terms( $post_id, array('Lorem', 'Sit'), 'custom_tax', true);

Metadata

If you’ve ever added custom meta data to a post before, you know what to do already.

Let’s say we want to add a custom field with the name “summary” and the value “Quick Summary!”:

1
add_post_meta( $post_id, 'summary', 'Quick summary!', true);

That’s quite self-explanatory. The ‘true’ at the end refers to whether this field is unique or not. If we set this to false, and a summary already exists, then another custom field also titled ‘summary’ would be added to the post.

By setting it to true, this won’t happen. WordPress checks for the ‘summary’ field first, and if it exists, it does nothing (You would then need to use update_post_meta() instead).

One special note to make is that if you want to set a page template, you do this with the meta functions as well. WordPress stores the name of the template file to use in a custom field titled ‘_wp_page_template’

1
update_post_meta( $post_id, '_wp_page_template',  'onecolumn-page.php');

Between those functions, you should be able to set up any post data you need. Feel free to post in the comments if you have any questions, or I’d love to hear how you’ve allowed users to add content to a WordPress site!

Author: "Michael Martin" Tags: "WordPress, Coding, Objects, PHP, Posts"
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 12 Jul 2011 16:00

Floating Share Buttons

This is one of the most common questions I get asked; how do you make a floating box with share links?

Today, we’re going to see just how simple it can be. We will:

  • Set up the current trinity of search (Facebook, Twitter, +1).
  • See how to make sure they share the right URL.
  • Align the box to the bottom left of the user’s browser.
  • Hide the box if the user’s browser is too small (So it never overlaps our content).

You can see the demo site here (Try resizing your browser smaller).

1 – The URL and Text to Share

The first step involves a little WordPress code. In footer.php in your theme, scroll down to the tag, and just before it, paste the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// URL to share
if( is_singular() ) {
	$url = get_permalink();
	$text = the_title('', '', false);
} else if ( is_category() || is_tag() ) {
	if(is_category() ) {
		$cat = get_query_var('cat');
		$url = get_category_link($cat);
	} else {
		$tag = get_query_var('tag_id');
		$url = get_tag_link($tag);
	}
	$text = single_cat_title('', false) . ' on ' . get_bloginfo('name');
} else {
	$url = get_bloginfo('url');
	$text = get_bloginfo('name') . ' - ' . get_bloginfo('description');
}	
?>

The point of this code is to work out the right URL and title text to share, and save them in 2 PHP variables ($url and $text).

The way it works is to run through a “what page are we on?” check:

  • Single Post/Page – Then use the post’s URL and title.
  • Category/Tag – Then use the category/tag’s URL, and “Category on Site Name” as the text.
  • Default – Share the homepage URL, site title and description on any other page.

The reason we go to the trouble of making sure we have the right URL and text here is to ensure that the right details are shared, e.g. we don’t need any utm_source= etc. tracking tags after the address.

Get the Share Codes

The next step is to get the share code for each button, and put it in HTML that we can style.

You can either follow through the steps manually as I show you, or skip to the next block of code after this one, and copy it (But read the paragraph before it too).

Manual way:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="social-float">
	<div class="sf-twitter">
		<!-- Twitter Code Goes Here -->
	</div>
 
	<div class="sf-facebook">
		<!-- Facebook Code Goes Here -->
	</div>
 
	<div class="sf-plusone">
		<!-- Google +1 Code Goes Here -->
	</div>
</div><!-- /social-float -->

You can see HTML comments for where each share code will go. Now, let’s get those codes (And tweak them as needed)

  • Twitter
    • Click here.
    • Choose “Vertical Count”, and enter your Twitter handle below.
    • Copy and paste the code.
    • Find the part that looks like: data-count=”vertical” data-via=”problogdesign”. Take a space after that and paste: data-url=”<?php echo $url; ?>” data-text=”<?php echo $text; ?>”
  • Facebook
    • Click here.
    • Enter http://google.com for “Address to Share”.
    • Deselect the “Send Button” option, choose “box_count” for layout, and set the width to 50.
    • Hit “Get Code” and copy and paste the iframe code (Or XFBML if you already use the FB JavaScript SDK. If you aren’t sure, just take the iframe code).
    • In the iframe code, search for http%3A%2F%2Fgoogle.com, and replace it with: <?php echo urlencode($url); ?>
    • Search for 80 in the code, and replace it with 62 (There are 2 places to change it, both to do with the height).
  • Google +1
    • Click here.
    • Select the “Tall” button, and then copy and paste the code.
    • Find where it says size=”tall” in the code, then take a space and paste: href=”<?php echo $url; ?>”

And that’s you done! Your code should look similar to what’s below.

Automatic Way:

Copy and paste the following, but on line 3, make sure you change the data-via=”problogdesign” part to use your Twitter username instead.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="social-float">
	<div class="sf-twitter">
		<a href="http://twitter.com/share" class="twitter-share-button" data-count="vertical" data-via="problogdesign" data-url="<?php echo $url; ?>" data-text="<?php echo $text; ?>">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
	</div>
 
	<div class="sf-facebook">
		<iframe src="http://www.facebook.com/plugins/like.php?app_id=186708408052490&amp;href=<?php echo urlencode($url); ?>&amp;send=false&amp;layout=box_count&amp;width=50&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font&amp;height=62" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:50px; height:62px;" allowTransparency="true"></iframe>
	</div>
 
	<div class="sf-plusone">
		<!-- Place this tag in your head or just before your close body tag -->
		<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
 
		<!-- Place this tag where you want the +1 button to render -->
		<g:plusone size="tall" href="<?php echo $url; ?>"></g:plusone>
	</div>
</div><!-- /social-float -->

CSS Time

The majority of our effect is done with just a simple piece of CSS. By using fixed positioning, we can anchor our box to the corner of the screen (It won’t work in IE6, but that’s okay).

The following will put the box 10px from the bottom and left edges of their browser (Don’t copy this just yet).

1
2
3
4
5
#social-float {
	position: fixed;
	left: 10px;
	bottom: 10px;
}

If you try that, it works, but of course, we want to style it too. One of the annoyances of these share buttons is that each vendor has decided to vary their button’s size by 1 or 2 pixels.

The following CSS makes a good template to work from. I’ve measured the sizes so each button is 10px from the next, with a 10px padding around the whole thing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
 * Floating Social Buttons
 */
 
#social-float {
	position: fixed;
	left: 10px;
	bottom: 10px;
 
	width: 55px;
	padding: 10px 5px;
	text-align: center;
 
	background-color: #fff;
 
	border: 5px solid rgba(180, 180, 180, .7);
	-webkit-border-radius: 8px;
	-moz-border-radius: 8px;
	border-radius: 8px;
 
	display: none;
}
 
.sf-twitter {
	height: 62px;
	margin-bottom: 10px;
}
 
.sf-facebook {
	height: 60px;
	margin-bottom: 10px;
}
 
.sf-plusone {
	height: 60px;
}

To tweak the style, change the background-color and border parts of #social-float (Right now, I’ve just used some CSS3 to quickly make a rounded, semi-transparent border).

Important: Don’t worry that it’s not showing up for you. The display: none; in there is hiding it. Read on!

Hide on Smaller Browsers

We don’t want to show the box on smaller browsers because it will obstruct the post content. To solve that, let’s use jQuery to check the browser size, and show the box only if it is wide enough.

First up, make sure you’ve loaded jQuery by adding the following to your functions.php file, or to header.php (Before the wp_head(); tag).

1
<?php wp_enqueue_script('jquery'); ?>

Now, go back to footer.php and after our button HTML, paste the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="text/javascript">
	jQuery(document).ready(function($) {
		// Show social voter only if the browser is wide enough.
		if( $(window).width() >= 1030 )
			$('#social-float').show();
 
		// Update when user resizes browser.
		$(window).resize(function() {
			if( $(window).width() < 1030 ) {
				$('#social-float').hide();
			} else {
				$('#social-float').show();
			}
		});
	});
</script>

Lines 4 and 5 run when the page is first loaded. They check the width, and show the box if it is wide enough.

Lines 8 is an event handler that runs when the user resizes their browser. When they do, we run the exact same check as above, and either hide/show the box as appropriate.

I’ve set the width to 1030px, which is 980px (A common page width), plus 150px (The width of our box) times 2 (The page is centered, so 150px on either side). Feel free to change this however you like, but make sure you update it in both locations.

Now save your code and check it out on site. With everything in place now, feel free to tweak the style and you’re good to go.

If you hit any issues, let me know in the comments! Good luck!

Author: "Michael Martin" Tags: "WordPress, CSS, Facebook, Featured, Goog..."
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 22 Jun 2011 16:00

Validate WordPress Forms with jQuery

This article will teach you how to add validation to your WordPress comment forms, using jQuery.

With validation, users can submit their comment and instantly see if they’ve hit an issue. There is no need to submit the page and get WordPress’ default “There is a problem…” page. Instead, errors are displayed inline instantly.

In our example, we will build a plugin which you can drop into any theme and will work instantly.

Just like last week, you can see the finished product in action on the demo site. And you can download the completed files here.

Organize Your Files

Download the bassistance.de form validation plugin for jQuery.

This plugin contains the file jquery.validate.min.js, which will be taking care of all our validation worries for us. All we have to do is tell it how and when to work.

I’ve called the plugin “pbd-validate-comments”, though you’re welcome to name it however you like. You should have a .php file with the same name, and then a “css” and “js” folder. Create a style.css file in the css folder, and place the jquery.validate.min.js file from above in the js folder.

The end result is like this:

Files

With your files in place, open up the pbd-validate-comments.php file and start with the essential plugin info:

1
2
3
4
5
6
7
8
9
10
<?php
/**
 * Plugin Name: PBD Validate Comments
 * Plugin URI: http://www.problogdesign.com/
 * Description: Validate comments instantly with jQuery. Uses <a href="http://bassistance.de/jquery-plugins/jquery-plugin-validation/">jQuery Form Validation</a> plugin by Jörn Zaefferer.
 * Version: 0.1
 * Author: Pro Blog Design
 * Author URI: http://www.problogdesign.com/
 * License: GPLv2
 */

Load Your Scripts

Now, let’s begin. The process today is quite simple, and will have 3 steps.

  1. Load all of our scripts.
  2. Initialize the validation on our form, and tell it the rules to use.
  3. Style the error messages.

Let’s start with loading the scripts. Still in pbd-validate-comments.php, paste the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * Add jQuery Validation script on posts.
 */
function pbd_vc_scripts() {
	if(is_single() ) {
		wp_enqueue_script(
			'jquery-validate',
			plugin_dir_url( __FILE__ ) . '/js/jquery.validate.min.js',
			array('jquery'),
			'1.8.1',
			true
		);
 
		wp_enqueue_style(
			'jquery-validate',
			plugin_dir_url( __FILE__ ) . '/css/style.css',
			array(),
			'1.0'
		);
	}
}
add_action('template_redirect', 'pbd_vc_scripts');

The important thing here is the wp_enqueue_script() section. This is what tells WordPress to load in our validation script. Similarly, wp_enqueue_style() is used to load our stylesheet.

We have then wrapped that up in an is_single() condition, so that our script is only loaded on post pages. And in order for that to work, we have put the whole thing in a hook (Attached to ‘template_redirect’).

The end result is that our script is now loaded on every post page, but not on the homepage/categories etc.

Activate the Script

Congrats, you’ve loaded in the scripts now. The next step is to initialize the validator and give it our rules.

One way to give the rules is to use CSS classes, e.g. class=”required” would mean the field is required.

However, with new themes using the comment_form() function, that gets more complicated. So instead, we will pass all of the rules via JavaScript.

Carry on in the PHP file, and paste the following:

1
2
3
4
5
6
7
8
9
/**
 * Initiate the script.
 * Calls the validation options on the comment form.
 */
function pbd_vc_init() { ?>
	<script type="text/javascript">
		jQuery(document).ready(function($) {
 
			$('#commentform').validate({

Here, we open a new function (Which we will hook into place on the page in a minute), and start writing some JavaScript.

The ‘#commentform‘ needs to be a jQuery selector for your form. In most cases, you can leave this as it is because 99% of WordPress themes will be using that ID on their comment forms.

We are now going to pass in an array of options. The first will be an array of rules. You can see the full list of validation rules available here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
rules: {
	author: {
		required: true,
		minlength: 2
	},
 
	email: {
		required: true,
		email: true
	},
 
	url: {
		url: true
	},
 
	comment: {
		required: true,
		minlength: 20
	}
},

We have simply taken rules from the available list, and applied them to the various fields (author, email etc. are the “name” values for the comment form fields, and will be in every WordPress theme).

These rules translate to:

  • Author Field – Must be filled in, with a minimum of 2 characters.
  • Email Field – Must be filled in, with a valid email address.
  • URL Field – Not required, but if something is entered, it must be a valid URL.
  • Comment Field – Required, and must be at least 20 characters long.

How simple was that?

We could leave it there, but let’s pass in one more option; a custom error message for each field.

1
2
3
4
5
6
messages: {
	author: "Please enter a valid name.",
	email: "Please enter a valid email address.",
	url: "Please use a valid website address.",
	comment: "Message must be at least 20 characters."
}

Again, this is quite self-explanatory. The messages entered will appear when there is an error in the corresponding field.

Now, just close up the JavaScript functions and the script tag, and then hook the PHP function into place.

1
2
3
4
5
6
7
			});
		});
	</script>
<?php }
add_action('wp_footer', 'pbd_vc_init', 999);
 
?>

And that’s the end of our work, so we’ve closed up the PHP file!

Last of All, Some Styles

If you save and activate the plugin now, it all works perfectly well. However, a little CSS for the error messages won’t hurt.

Open the style.css file, and paste the following:

1
2
3
4
5
label.error {
	display: block;
	background: #ffd2d2;
	padding: 0 10px;
}

And that’s it! Save your plugin, activate it in the dashboard and go try posting some phony comments. You should see instant errors pointing out any issues.

The jQuery validation script is hugely powerful and suitable for just about any type of form. Once you know how to use it in WordPress as we have done here, you’ll find you can use it anywhere!

And don’t forget, if you don’t want to put the files together yourself, you can grab the completed download here.

Please let me know if you enjoyed this post! If you’d like to see more jQuery guides on the site, leave a comment!

The idea behind this post was a collaboration between myself, Michael Martin, and Seth Bryant, thanks Seth!

Author: "Michael Martin" Tags: "WordPress, Comments, JQuery, Validation"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 15 Jun 2011 16:00

AJAX Load Posts

Today, we’re going to replace the standard “Older Posts” links on your blog. Our plugin will create a button to instantly load the next page of posts, without reloading the page (Similar to what Twitter used to do at the bottom of profiles)

Click here to load the demo site and see the final project in action.

You can also download the completed files as a plugin here (Just upload and activate it).

Now, let’s get started. Our plugin is going to have several features:

  • Multiple clicks – First clicks will load page 2′s posts, second will load page 3 etc.)
  • Check for posts first – If there are no more posts that can be loaded, we’ll tell the user.
  • Degrade gracefully – If a visitor doesn’t use JavaScript, we won’t change the site at all.

Plugin Structure

There will be 3 files in this plugin (One PHP, one CSS, one JS). For good practice, we’ll keep the CSS and JavaScript in their own folders.

And I’ve called the plugin “pbd-ajax-load-posts”.

File Structure

Now let’s start by putting the necessities at the top of our pbd-ajax-load-posts.php file:

1
2
3
4
5
6
7
8
9
10
<?php
/**
 * Plugin Name: PBD AJAX Load Posts
 * Plugin URI: http://www.problogdesign.com/
 * Description: Load the next page of posts with AJAX.
 * Version: 0.1
 * Author: Pro Blog Design
 * Author URI: http://www.problogdesign.com/
 * License: GPLv2
 */

All good so far? Cool, time to get started for real.

Loading Files and Passing Values

The first thing we need to do is ensure that our JavaScript and CSS files are loaded on the right pages. Stay in the pbd-ajax-load-posts.php file and paste this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 /**
  * Initialization. Add our script if needed on this page.
  */
 function pbd_alp_init() {
 	global $wp_query;
 
 	// Add code to index pages.
 	if( !is_singular() ) {	
 		// Queue JS and CSS
 		wp_enqueue_script(
 			'pbd-alp-load-posts',
 			plugin_dir_url( __FILE__ ) . 'js/load-posts.js',
 			array('jquery'),
 			'1.0',
 			true
 		);
 
 		wp_enqueue_style(
 			'pbd-alp-style',
 			plugin_dir_url( __FILE__ ) . 'css/style.css',
 			false,
 			'1.0',
 			'all'
 		);

The code above begins by creating a new function, pbd_alp_init() (alp = AJAX Load Posts), which we will then hook into place later on.

You’ll notice that we also call the global $wp_query variable, which we’ll use in the next step.

The important parts begin on line 8. The first statement is a conditional statement. It means that on any page that isn’t an individual post or Page, we are going to run this code.

This is a broad brush to make sure our code runs on the homepage, tag pages, search pages etc. You can adapt it to be more specific, e.g. if you don’t want the code included on your homepage.

We then use wp_enqueue_script() and wp_enqueue_style() to tell WordPress about our two files (As well as the fact that we will be using jQuery).

Now, we need to pass some values to our script, namely:

  • The page number we’re on right now (Going to page 1 99% of the time, but let’s be sure).
  • The total number of pages (So we know when we’ve hit the limit).
  • The link to the next page (e.g. site.com/tag/example/page/2/)

We will use the wp_localize_script() function to calculate each of these values in PHP, and then print them into the webpage so that our script can access them later (Hat tip to Professional WordPress Plugin Development for introducing me to this great function!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// What page are we on? And what is the pages limit?
$max = $wp_query->max_num_pages;
$paged = ( get_query_var('paged') > 1 ) ? get_query_var('paged') : 1;
 
// Add some parameters for the JS.
wp_localize_script(
	'pbd-alp-load-posts',
	'pbd_alp',
	array(
		'startPage' => $paged,
		'maxPages' => $max,
		'nextLink' => next_posts($max, false)
	)
);

We begin by working out the first 2 values. $max is the maximum number of pages the current query can return (e.g. if each page shows 5 posts and there are 12 posts in the current category, then max will be 3).

The $paged variable will store the page we are currently on (The whole point of our plugin is to ensure people never load a second page, but it doesn’t hurt to make sure).

If you skip down to line 12, you’ll see where I’ve worked out the 3rd value (The link to the next page). next_posts() is a built-in WordPress function that will return the URL we need.

The wp_localize_script() function is great because it makes it easy to pass values from PHP to JavaScript. The first value, ‘pbd-alp-load-posts’ must match the first value in the wp_enqueue_script() call.

The second value, ‘pbd_alp’, is the name we will use in our JavaScript later on.

Finally, we send over an array of the data. If you view the HTML source of your webpage later, you’ll see something like this right before your JavaScript file is loaded:

1
2
3
4
5
6
7
8
9
<script type='text/javascript'>
/* <![CDATA[ */
var pbd_alp = {
	startPage: "1",
	maxPages: "6",
	nextLink: "http://www.problogdesign.com/demo/ajax-load-posts/page/2/"
};
/* ]]> */
</script>

Now, we just need to close up our if statement, our pbd_alp_init() function, and then hook it all into place.

1
2
3
 	}
 }
 add_action('template_redirect', 'pbd_alp_init');

We use the template_redirect hook because with the init hook, the $wp_query variable won’t be set yet.

jQuery – The Heart of our Plugin

We have now loaded our scripts and passed the values we need. It’s time to get to the real meat of our tutorial.

Open the load-posts.js file. The first thing we do is access the 3 variables we passed in with our PHP function.

1
2
3
4
5
6
7
8
9
10
jQuery(document).ready(function($) {
 
	// The number of the next page to load (/page/x/).
	var pageNum = parseInt(pbd_alp.startPage) + 1;
 
	// The maximum number of pages the current query can return.
	var max = parseInt(pbd_alp.maxPages);
 
	// The link of the next page of posts.
	var nextLink = pbd_alp.nextLink;

The way to access our values is to use the format: pbd_alp.valueName (pbd_alp was the second value we entered in wp_localize_script(), remember?).

The important thing to remember is that our numbers have been sent over as strings, so we use JavaScript’s parseInt() function to convert them back to numbers.

With pageNum, we add one to the number because it is going to store the number of the next page to load (Not the current page).

Most themes already have navigation to move between pages, in the form of Older Posts/Newer Posts links. We want to replace that with our AJAX button, so our first step will be to remove those navigation links, and insert our button instead.

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * Replace the traditional navigation with our own,
 * but only if there is at least one page of new posts to load.
 */
if(pageNum <= max) {
	// Insert the "More Posts" link.
	$('#content')
		.append('<div class="pbd-alp-placeholder-'+ pageNum +'"></div>')
		.append('<p id="pbd-alp-load-posts"><a href="#">Load More Posts</a></p>');
 
	// Remove the traditional navigation.
	$('.navigation').remove();
}

We start out with a conditional check. Remember that pageNum is the number of the next page, so if it is greater than max, there are no more pages to load. In that case, we don’t want to add the button.

If there is new content to load though, then we look for the #content div, and add two things to the end of it. The first is an empty div, which we will later use to insert our posts into.

The second is the button itself (A regular HTML link), wrapped up in a paragraph.

Finally, we look for the .navigation div and remove it. If your theme uses a different class for the navigation buttons, you will need to change that (.navigation is the default that the 2010 theme uses). The same thing applies to the #content div too!

The result of the code above is that our button is now in place, though it won’t do anything yet.

And because we did it all with JavaScript, we know our plugin degrades gracefully (Because if the JavaScript isn’t loaded, no changes will be made to the page).

Now, let’s take care of what happens when the user actually clicks the button.

1
2
3
4
5
6
7
8
9
10
/**
 * Load new posts when the link is clicked.
 */
$('#pbd-alp-load-posts a').click(function() {
 
	// Are there more posts to load?
	if(pageNum <= max) {
 
		// Show that we're working.
		$(this).text('Loading posts...');

The first line of code is a jQuery event handler, which runs when a user clicks the button.

On line 7, we run the same check as before. This is important because our script would load in a 404 error message if there were no more posts. Definitely not what we want!

Line 10 updates the text on our button to read “Loading posts…” This is good practice because users will get an instant reaction when they click the button.

The next step is to make the AJAX call. Quite a lot happens here, so copy and paste the following into your script and I’ll walk through it all afterwards.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$('.pbd-alp-placeholder-'+ pageNum).load(nextLink + ' .post',
	function() {
		// Update page number and nextLink.
		pageNum++;
		nextLink = nextLink.replace(/\/page\/[0-9]?/, '/page/'+ pageNum);
 
		// Add a new placeholder, for when user clicks again.
		$('#pbd-alp-load-posts')
			.before('<div class="pbd-alp-placeholder-'+ pageNum +'"></div>')
 
		// Update the button message.
		if(pageNum <= max) {
			$('#pbd-alp-load-posts a').text('Load More Posts');
		} else {
			$('#pbd-alp-load-posts a').text('No more posts to load.');
		}
	}
);

The first line is the most important. We use a jQuery selector to select our placeholder div. The reason we have added the pageNum number to the end of the class name is so that if the user clicks the button again, we’ll add the new posts to a new placeholder (Not overwrite the old one).

The .load() function loads the URL we pass (Remember that nextLink is the URL for the next page), and because we have added .post to the end, it will only copy over the .post divs that it finds (Not the whole page!)

On line 2, we start a new function which will run when the AJAX call completes. The first thing it does is update our values for the next time the button is clicked.

pageNum is increased by one (To point to the new next page), and nextLink is updated using a regular expression. This searches the URL for /page/2/ (Or any number), and replaces the number portion with the new next page number.

On line 8, we add a new placeholder div. This will be used the next time the button is clicked.

Finally, on line 12, we update the text on the button again. If there are more posts that can be loaded, we revert back to the original text. If there aren’t, then we’ll update with a message saying so.

Now, we just need to round things off:

1
2
3
4
5
6
7
		} else {
			$('#pbd-alp-load-posts a').append('.');
		}	
 
		return false;
	});
});

This code begins by closing off the first if-statement (Are there more pages to load?). If there aren’t, it adds a ‘.’ to the button’s message. This is simply to give some sort of visual response to the user when the button is clicked (Look at the screenshot below to see what happens).

No More Posts

And last of all, we use return false; to prevent the link from the button itself loading.

Style It

Your button is now fully working! The only thing remaining is to style it. You can do this however you like with CSS. I’ve used CSS3 to round off the corners, and add a gradient and drop shadow.

You can look these up more online (Or post in the comments if you’d like to see some CSS3 tuts here), but a big thanks is due to CSS Tricks for making the cross-browser gradient code easy.

Add this to your css/style.css file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#pbd-alp-load-posts a:link, #pbd-alp-load-posts a:visited {
	display: block;
	text-align: center;
	padding: 4px 0;
	color: #444;
	text-decoration: none;
 
	/** Rounded Corners **/
	-moz-border-radius: 8px;
	border-radius: 8px;
 
	/** Drop shadow **/
	-moz-box-shadow: 1px 1px 1px #999;
	-webkit-box-shadow: 1px 1px 1px #999;
	box-shadow: 1px 1px 1px #999;
 
	/** Gradients : http://css-tricks.com/css3-gradients/ */
	/* fallback */
	background-color: #f1f1f1;
 
	/* Firefox 3.6+ */
	background: -moz-linear-gradient(100% 100% 90deg, #e4e3e3, #f1f1f1);
 
	/* Safari 4-5, Chrome 1-9 */
	/* -webkit-gradient(<type>, <point> [, <radius>]?, <point> [, <radius>]? [, <stop>]*) */
	background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f1f1f1), to(#e4e3e3));
 
	/* Safari 5.1+, Chrome 10+ */
	background: -webkit-linear-gradient(#f1f1f1, #e4e3e3);
 
	/* Opera 11.10+ */ background: -o-linear-gradient(#f1f1f1, #e4e3e3);
}
 
#pbd-alp-load-posts a:hover, #pbd-alp-load-posts a:active {
	/** Drop shadow **/
	-moz-box-shadow: 1px 1px 1px #bbb;
	-webkit-box-shadow: 1px 1px 1px #bbb;
	box-shadow: 1px 1px 1px #bbb;
 
	/** Gradients : http://css-tricks.com/css3-gradients/ */
	/* fallback */
	background-color: #f5f5f5;
 
	/* Firefox 3.6+ */
	background: -moz-linear-gradient(100% 100% 90deg, #eaeaea, #f5f5f5);
 
	/* Safari 4-5, Chrome 1-9 */
	/* -webkit-gradient(<type>, <point> [, <radius>]?, <point> [, <radius>]? [, <stop>]*) */
	background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f5f5f5), to(#eaeaea));
 
	/* Safari 5.1+, Chrome 10+ */
	background: -webkit-linear-gradient(#f1f1f1, #eaeaea);
 
	/* Opera 11.10+ */ background: -o-linear-gradient(#f5f5f5, #eaeaea);
}

And that’s you done! Just save your work and activate the plugin.

If you have any issues, check that your theme uses the #content and .navigation divs in the way that we assumed. Most themes will, but not all. If yours is different, update those values in the JavaScript file.

Let me know what you thought of the effect! And don’t forget you can grab the completed plugin here.

And if you have any feedback on the tutorial, I’d love to hear it! Too long? Or more explanation needed? (The demo site exists now purely because some of you asked for it in previous tutorials, so thank you!)

Author: "Michael Martin" Tags: "WordPress, Ajax, Featured, JQuery, Plugi..."
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 01 Jun 2011 16:00

WordPress short codes are everywhere: in practically every theme and many, many plugins. They are extremely useful and provide huge amounts of extra functionality. With all of their greatness, however, there is one major problem with them: they are not very user friendly, particularly for users who are not familiar with programming, especially when a short code accepts more than one parameter. So, in order to help the non-programmer users, we need a new solution.

The ideal solution is one that provides an interface for the user to input all of their short code options without having to enter any “code”. This is a pretty common technique used by a lot of developers, but until now, there has never been a good tutorial on it, so we are going to fix that.

In this tutorial I’m going to show you how to add short code buttons to the tinyMCE editor that, when clicked, bring up a modal window with all of the options for your short code.

Through the steps in this tutorial, we will be writing a complete WordPress plugin that will provide a small collection of CSS 3 buttons available for use through short codes. All of the options for the buttons, size, color, style, url, text, etc, will be available through the modal window.

If you look at the image at the top of this post, you can see exactly how that modal window will look (Much easier than remembering all of the short code’s option names yourself!)

Prerequisites

In order to follow this tutorial, one of the first things you will need to know is how to create short codes. If you are not yet familiar with the process, please follow my tutorial Working with WordPress Short Codes.

Getting Started

The first thing we need to do is setup our plugin structure. Please use the following file structure as a reference.

To make things easier, I would recommend that you go ahead and create each of the files according to the image. I will reference the filename we’re using in each step.

Beginning the Plugin

Just like every other WordPress plugin, we need to setup our plugin header in order to tell WordPress about our plugin. Place the following code into friendly-shortcode-buttons.php.

1
2
3
4
5
6
7
8
9
10
11
/*
Plugin Name: Friendly Short Code Buttons
Plugin URI: http://pippinsplugins.com/user-friendly-short-codes-plugin-example/
Description: Adds user-friendly short code buttons to your  WordPress site. This plugin is more of an example than anything, but does provide a few nice looking buttons
Version: 1.0
Author: Pippin Williamson
Author URI: http://pippinsplugins.com
*/
 
// plugin root folder
$fscb_base_dir = WP_PLUGIN_URL . '/' . str_replace(basename( __FILE__), "" ,plugin_basename(__FILE__));

Our plugin will now be available to WordPress. Note that I have also placed a variable called $fscb_base_dir. This is a variable that contains the file path to the root folder of our plugin. We will use this later to load CSS and JS scripts.

Setting Up the Short Code

Now that we have our plugin registered with WordPress, we need to setup our short code and its options. This is a very simple function that registers our short code with WordPress and sets up the HTML structure of our buttons. Place this code in friendly-shortcode-buttons.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// setup the shortcode for use
function friendly_button_shortcode( $atts, $content = null )
{
      extract( shortcode_atts( array(
        'color' => 'blue',
        'size' => 'medium',
        'style' => 'round',
        'align' => 'none',
        'text' => '',
        'url' => '',
      ), $atts ) );
 
      return '<div class="friendly_button friendly_button_' . $size . ' friendly_button_' . $color . ' friendly_button_' . $style . ' friendly_button_' . $align . '"><a href="' . $url . '">' . $text . $content . '</a></div>';
 
}
add_shortcode('button', 'friendly_button_shortcode');

The return statement of this function may look a little complex, but all I have done is make it so that a DIV with a class name for each option is displayed. So there is a class for the size, the color, the style, etc. This will allow us to style each kind of button with CSS later on.

The Button CSS

Our button short code is now available in WordPress, so let’s go ahead and add all of the necessary CSS. We will use wp_enqueue_style() to load our CSS into the site header. Again, place this in friendly-shortcode-buttons.php:

1
2
3
4
5
6
7
8
// load button css
function friendly_buttons_css() 
{
        // the path to our root plugin folder
	global $fscb_base_dir;
	wp_enqueue_style('friendly-buttons', $fscb_base_dir . 'includes/css/friendly_buttons.css');
}
add_action('wp_print_styles', 'friendly_buttons_css');

Remember the GLOBAL variable we defined earlier with the path to the root plugin folder? We’re using that here to obtain the correct path to our CSS file, which we will create now. As indicated in the file structure diagram above, the CSS file is called friendly_buttons.css as is located inside of includes/css/.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
.friendly_button a{
	border:1px solid #000; 
	color:#fff; 
	font-family:arial; 
	font-weight:bold; 
	padding:8px 24px; 
	text-decoration:none; 
	text-shadow:1px 1px 0px #000;
	display:block; 
	-moz-border-radius:6px; 
	-webkit-border-radius:6px; 
	border-radius:6px;
	margin: 0 10px 10px 0; 
}
.friendly_button a:active{position:relative; top:1px}
 
/******************************************************
* sizes
******************************************************/
 
.friendly_button_large a{font-size:16px !important;}
.friendly_button_small a{font-size: 11px !important; padding: 6px 16px;}
.friendly_button_medium a{font-size: 12px; padding: 8px 22px; }
 
/******************************************************
* sizes
******************************************************/
 
.friendly_button_left {float: left;}
.friendly_button_left a{margin: 0 10px 10px 0;}
.friendly_button_right {float: right;}
.friendly_button_right a{margin: 0 0 10px 10px;}
.friendly_button_none {float: none; display: inline-block; zoom: 1; *display: inline; }
 
/******************************************************
* styles
******************************************************/
 
.friendly_button_round a {
	-moz-border-radius:15px; -webkit-border-radius:15px; border-radius:15px
}
.friendly_button_less_round a {
	-moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px
}
.friendly_button_square a {
	-moz-border-radius:1px; -webkit-border-radius:1px; border-radius:1px
}
 
/******************************************************
* colors
******************************************************/
 
/*black button*/
.friendly_button_gray a{
	-moz-box-shadow:inset 0px 1px 0px 0px #fff; 
	-webkit-box-shadow:inset 0px 1px 0px 0px #fff; 
	box-shadow:inset 0px 1px 0px 0px #fff; 
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#ededed),color-stop(1,#dfdfdf) ); 
	background:-moz-linear-gradient( center top,#ededed 5%,#dfdfdf 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed',endColorstr='#dfdfdf'); 
	background-color:#ededed; 
	border:1px solid #dcdcdc; 
	color: #777;
	text-shadow:1px 1px 0px #fff;
}
 
.friendly_button_gray a:hover{
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#dfdfdf),color-stop(1,#ededed) ); 
	background:-moz-linear-gradient( center top,#dfdfdf 5%,#ededed 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dfdfdf',endColorstr='#ededed'); 
	background-color:#dfdfdf
}
 
/*black button*/
.friendly_button_blue a{
	-moz-box-shadow:inset 0px 1px 0px 0px #bdddff; 
	-webkit-box-shadow:inset 0px 1px 0px 0px #bdddff; 
	box-shadow:inset 0px 1px 0px 0px #bdddff; 
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#7db8ff),color-stop(1,#2c80e8) ); 
	background:-moz-linear-gradient( center top,#7db8ff 5%,#2c80e8 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#7db8ff',endColorstr='#2c80e8'); 
	background-color:#7db8ff; 
	border:1px solid #047fd1; 
	color:#fff; 
}
 
.friendly_button_blue a:hover{
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#2c80e8),color-stop(1,#7db8ff) ); 
	background:-moz-linear-gradient( center top,#2c80e8 5%,#7db8ff 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#2c80e8',endColorstr='#7db8ff'); 
	background-color:#2c80e8
}
 
/*black button*/
.friendly_button_red a{
	-moz-box-shadow:inset 0px 1px 0px 0px #ffa6a6; 
	-webkit-box-shadow:inset 0px 1px 0px 0px #ffa6a6; 
	box-shadow:inset 0px 1px 0px 0px #ffa6a6; 
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#f55858),color-stop(1,#ba1c1c) ); 
	background:-moz-linear-gradient( center top,#f55858 5%,#ba1c1c 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f55858',endColorstr='#ba1c1c'); 
	background-color:#f55858; 
	border:1px solid #8a0a0a; 
}
 
.friendly_button_red a:hover{
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#ba1c1c),color-stop(1,#f55858) ); 
	background:-moz-linear-gradient( center top,#ba1c1c 5%,#f55858 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ba1c1c',endColorstr='#f55858'); 
	background-color:#ba1c1c
}
 
/*black button*/
.friendly_button_green a{
	-moz-box-shadow:inset 0px 1px 0px 0px #39de10; 
	-webkit-box-shadow:inset 0px 1px 0px 0px #39de10; 
	box-shadow:inset 0px 1px 0px 0px #39de10; 
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#13c20a),color-stop(1,#05ad19) ); 
	background:-moz-linear-gradient( center top,#13c20a 5%,#05ad19 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#13c20a',endColorstr='#05ad19'); 
	background-color:#13c20a; 
	border:1px solid #169124; 
}
 
.friendly_button_green a:hover{
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#05ad19),color-stop(1,#13c20a) ); 
	background:-moz-linear-gradient( center top,#05ad19 5%,#13c20a 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#05ad19',endColorstr='#13c20a'); 
	background-color:#05ad19
}
 
/*black button*/
.friendly_button_black a{
	-moz-box-shadow:inset 0px 1px 0px 0px #8a8a8a; 
	-webkit-box-shadow:inset 0px 1px 0px 0px #8a8a8a; 
	box-shadow:inset 0px 1px 0px 0px #8a8a8a; 
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#525252),color-stop(1,#000) ); 
	background:-moz-linear-gradient( center top,#525252 5%,#000 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#525252',endColorstr='#000'); 
	background-color:#525252; 
}
.friendly_button_black a:hover{
	background:-webkit-gradient( linear,left top,left bottom,color-stop(0.05,#000),color-stop(1,#525252) ); 
	background:-moz-linear-gradient( center top,#000 5%,#525252 100% ); 
	filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#000',endColorstr='#525252'); 
	background-color:#000
}

Note, I am not going to explain which parts of the CSS do what. That is not the purpose of this tutorial. Suffice it to say that there are separate CSS styles for each color, size and type of button, as well as a couple other items, such as alignment. The code is well laid out and commented, so it should not be difficult to follow.

Excellent. Now that we have our button short code created and our CSS loaded, we will now be able to create buttons that look like this:

Setting Up the TinyMCE Button

It is now time to begin the really fun part of this tutorial: adding a button to our tinyMCE editor that allows us to enter the short code into our post content. We are first going to register a “plugin” with tinyMCE (this is our button), and then we will connect that button to a modal popup window. So to begin, we need to place three functions into friendly-shortcode-buttons.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// registers the buttons for use
function register_friendly_buttons($buttons) {
	// inserts a separator between existing buttons and our new one
	// "friendly_button" is the ID of our button
	array_push($buttons, "|", "friendly_button");
	return $buttons;
}
 
// filters the tinyMCE buttons and adds our custom buttons
function friendly_shortcode_buttons() {
	// Don't bother doing this stuff if the current user lacks permissions
	if ( ! current_user_can('edit_posts') && ! current_user_can('edit_pages') )
		return;
 
	// Add only in Rich Editor mode
	if ( get_user_option('rich_editing') == 'true') {
		// filter the tinyMCE buttons and add our own
		add_filter("mce_external_plugins", "add_friendly_tinymce_plugin");
		add_filter('mce_buttons', 'register_friendly_buttons');
	}
}
// init process for button control
add_action('init', 'friendly_shortcode_buttons');
 
// add the button to the tinyMCE bar
function add_friendly_tinymce_plugin($plugin_array) {
	global $fscb_base_dir;
	$plugin_array['friendly_button'] = $fscb_base_dir . 'friendly-shortcode-buttons.js';
	return $plugin_array;
}

The first of these functions registers our button with tinyMCE. It places a separator at the end of the current buttons, then our custom button following that.

The second function uses an add_filter hook to make our custom button actually display. It places the button created in the first function in the tinyMCE buttons.

And the third function connects our button to a javascript file. This is the file that controls the behavior of our button.

Important! In both the first and third function I have used friendly_button as an ID for the button. It’s very important that this never changes. I will use friendly_button a couple more times in this tutorial and it must be the same throughout.

Our TinyMCE Button’s Javascript

It is now time to create the javascript file that will control the behavior of the tinyMCE button that we added above.

Place this code into friendly-shortcode-buttons.js, which, remember the diagram, resides in the root plugin folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(function() {
	tinymce.create('tinymce.plugins.buttonPlugin', {
		init : function(ed, url) {
			// Register commands
			ed.addCommand('mcebutton', function() {
				ed.windowManager.open({
					file : url + '/button_popup.php', // file that contains HTML for our modal window
					width : 220 + parseInt(ed.getLang('button.delta_width', 0)), // size of our window
					height : 240 + parseInt(ed.getLang('button.delta_height', 0)), // size of our window
					inline : 1
				}, {
					plugin_url : url
				});
			});
 
			// Register buttons
			ed.addButton('friendly_button', {title : 'Insert Button', cmd : 'mcebutton', image: url + '/includes/images/icon.gif' });
		},
 
		getInfo : function() {
			return {
				longname : 'Insert Button',
				author : 'Pippin Williamson',
				authorurl : 'http://pippinsplugins.com',
				infourl : 'http://pippinsplugins.com',
				version : tinymce.majorVersion + "." + tinymce.minorVersion
			};
		}
	});
 
	// Register plugin
	// first parameter is the button ID and must match ID elsewhere
	// second parameter must match the first parameter of the tinymce.create() function above
	tinymce.PluginManager.add('friendly_button', tinymce.plugins.buttonPlugin);
 
})();

This code does a couple of things: first, it registers a command that runs when our button is clicked. This command opens a modal window that contains the contents of the button_popup.php file. We have also set the dimensions of our window to be 220×240 px. The second thing this code does is apply an image to our button, for aesthetics. And thirdly, this code sets up the meta data of our tinyMCE plugin button. Not overly important, but nice to have.

Recall above how I mentioned the ID of the button being friendly_button? Line 17 and 34 both use that same ID. It MUST not change, otherwise your button will not work.

Okay, we now have our button and it should look like this (though your image may be different):

If you click the button now, a window will pop up, but it will display an “Object not found!” error. So we need to create the window contents now.

The Modal Popup

The contents of the modal window are nothing more than a PHP (or HTML) file. The file will have all of the necessary components of any other HTML page: HTML, HEAD, TITLE, and BODY tags. So let’s begin our file. The first part is the HEAD section. Place the following code into button_popup.php, which should be located in the root of your plugin folder.

1
2
3
4
5
6
7
8
9
10
11
<?php
// this file contains the contents of the popup window
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Insert Button</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js"></script>
<script language="javascript" type="text/javascript" src="../../wp-includes/js/tinymce/tiny_mce_popup.js"></script>
<style type="text/css" src="../../wp-includes/js/tinymce/themes/advanced/skins/wp_theme/dialog.css"></style>
<link rel="stylesheet" href="includes/css/friendly_buttons_tinymce.css" />

Aside from the normal HTML file stuff, such as declaring a DOCTYPE, I have also placed SCRIPT, STYLE, and LINK tags to load the necessary scripts. We need to load jQuery, the tinyMCE Pop Up file, a CSS file for tinyMCE, and lastly our custom CSS file, which will control the styling for the window contents.

Note that I have not used wp_enqueue_script() and wp_enqueue_style() to load our scripts here. This is because those will not work in this case. With a bit of tweaking, you could probably make them work, but for this tutorial we will not worry about that. Also note that the first three files are all located inside of the WordPress install and are not part of this plugin. We’re just making use of them.

Alright, we have two more parts to our button_popup.php file. One is the javascript that will take care of inserting the short code and its selected attributes into the post editor, and the second is the HTML form code for our short code attributes. Let’s do the javascript first.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<script type="text/javascript">
 
var ButtonDialog = {
	local_ed : 'ed',
	init : function(ed) {
		ButtonDialog.local_ed = ed;
		tinyMCEPopup.resizeToInnerSize();
	},
	insert : function insertButton(ed) {
 
		// Try and remove existing style / blockquote
		tinyMCEPopup.execCommand('mceRemoveNode', false, null);
 
		// set up variables to contain our input values
		var url = jQuery('#button-dialog input#button-url').val();
		var text = jQuery('#button-dialog input#button-text').val();
		var size = jQuery('#button-dialog select#button-size').val();
		var color = jQuery('#button-dialog select#button-color').val();		 
		var style = jQuery('#button-dialog select#button-style').val();		 
		var align = jQuery('#button-dialog select#button-align').val();		 
 
		var output = '';
 
		// setup the output of our shortcode
		output = '[button ';
			output += 'size=' + size + ' ';
			output += 'style=' + style + ' ';
			output += 'color=' + color + ' ';
			output += 'align=' + align;
 
			// only insert if the url field is not blank
			if(url)
				output += ' url=' + url;
		// check to see if the TEXT field is blank
		if(text) {	
			output += ']'+ text + '[/button]';
		}
		// if it is blank, use the selected text, if present
		else {
			output += ']'+ButtonDialog.local_ed.selection.getContent() + '[/button]';
		}
		tinyMCEPopup.execCommand('mceReplaceContent', false, output);
 
		// Return
		tinyMCEPopup.close();
	}
};
tinyMCEPopup.onInit.add(ButtonDialog.init, ButtonDialog);
 
</script>

The important things to know about this code, so that you can modify it for your own uses, are the var declarations and the output string.

Starting at line 15, we have a variable declared for each attribute of our short code: url, text, size, color, style, align. Each of these variables contain the value (using jQuery’s .val() function) of their respective form field, which we will be adding shortly.

Then, starting at line 22, we setup the output string. This variable will contain the final short code to be inserted into the editor. We start by adding [button to it. This is the beginning of our short code. Then we add each attribute so that it ends up like this:

[button size=SIZE style=STYLE color=COLOR align=ALIGN

Next, we perform two IF checks. One to check if the URL field is empty, and if it’s not, we add the url=URL attribute. The second IF statement checks to see if the TEXT field is empty, and if it’s not, we close the short code, place the TEXT value inside of it, and then place the closing short code tag after that, so that the result looks like this:

[button size=SIZE style=STYLE color=COLOR align=ALIGN url=URL]TEXT[/button]

If the TEXT field is empty, then we insert whatever text was selected (if any) when the user clicked the insert button, which would result in and output like this:

[button size=SIZE style=STYLE color=COLOR align=ALIGN url=URL]SELECTED TEXT[/button]

That’s it for the jQuery, now we need to create the HTML form for our short code attributes.

The Short Code Attributes Form

This next piece of code will contain all of the form fields for our short code attributes. One of the important things to note is that the field IDs match those of the variables we declared for each attribute in our jQuery above. Also note that we are included in the closing HEAD, BODY, and HTML tags in this next snippet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
</head>
<body>
	<div id="button-dialog">
		<form action="/" method="get" accept-charset="utf-8">
			<div>
				<label for="button-url">Button URL</label>
				<input type="text" name="button-url" value="" id="button-url" />
			</div>
			<div>
				<label for="button-text">Button Text</label>
				<input type="text" name="button-text" value="" id="button-text" />
			</div>
			<div>
				<label for="button-size">Size</label>
				<select name="button-size" id="button-size" size="1">
					<option value="small">Small</option>
					<option value="medium" selected="selected">Medium</option>
					<option value="large">Large</option>
				</select>
			</div>
			<div>
				<label for="button-style">Style</label>
				<select name="button-style" id="button-style" size="1">
					<option value="less_round">Less Round</option>
					<option value="round" selected="selected">Round</option>
					<option value="square">Square</option>
				</select>
			</div>
			<div>
				<label for="button-color">Color</label>
				<select name="button-color" id="button-color" size="1">
					<option value="gray" selected="selected">Gray</option>
					<option value="blue"=>Blue</option>
					<option value="red">Red</option>
					<option value="green">Green</option>
					<option value="black">Black</option>
				</select>
			</div>
			<div>
				<label for="button-align">Alignment</label>
				<select name="button-align" id="button-align" size="1">
					<option value="gray" selected="selected">None</option>
					<option value="left"=>Left</option>
					<option value="right">Right</option>
				</select>
			</div>
			<div>	
				<a href="javascript:ButtonDialog.insert(ButtonDialog.local_ed)" id="insert" style="display: block; line-height: 24px;">Insert</a>
			</div>
		</form>
	</div>
</body>
</html>

There’s really nothing complex about this code, it’s just a plain HTML form with either an INPUT or SELECT field for each of our short code options. First, notice that I have set default options by defining the VALUE attribute for input fields and SELECTED attribute for select fields. This helps to ensure that blank attributes are not inserted.

The next important thing about this code is at the bottom, where I have placed an anchor link with a bit of javascript for the HREF. This is vital to the modal window’s function. Without this HREF, the short code will not be inserted into the content.

The Modal Window CSS

Recall that a few steps back we loaded a CSS file called friendly_buttons_tinymce.css. This file controls the appearance of the HTML form fields we just created. But without any CSS in that file, it’s not doing very much ;) So here is the CSS:

1
2
3
4
5
6
#button-dialog { }
#button-dialog div{ padding: 5px 0; height: 20px;}
#button-dialog label { display: block; float: left; margin: 0 8px 0 0; width: 80px; }
#button-dialog select, #button-dialog input { display: block; float: right; width: 100px; padding: 3px 5px;}
#button-dialog select { width: 112px; }
#button-dialog #insert { display: block; line-height: 24px; text-align: center; margin: 10px 0 0 0; float: right;}

This file is placed in the includes/css folder, along with the CSS file for the actual buttons.

That’s it! We’re done! Now, when you click the button in the TinyMCE button bar, you should be presented with this:

And, assuming you have done everything correctly, your short code should appear in your post editor as soon as you click the “Insert” button.

Going Further

So what’s next? It is entirely up to you. With the code provided in this tutorial, you have everything you need to add awesome, easy to use short codes to your WordPress theme or plugin. I believe I have done pretty well explaining the code, and making it easy to modify, but if you have any trouble, of course ask in the comments!

Download the Complete Plugin

If doing all the manual coding yourself isn’t quite your thing, you’re in luck! I have put the code from this tutorial into a complete plugin that you can download for free at my website: Pippin’s Plugins.com

Author: "Pippin" Tags: "Coding, WordPress, buttons, Editor, Java..."
Comments Send by mail Print  Save  Delicious 
Date: Monday, 23 May 2011 15:36

Redesign

Here’s something we’ve been working on for a while now; an all new design! If you’re reading this in an RSS reader, please click through and check it out!

I’d love to hear your thoughts! Nicer than the old version?

Goals for the Redesign

We had some very clear goals in mind with this redesign:

  1. Greater emphasis on our design services.
  2. Better integration of PliablePress links.
  3. Improved “sectioning” (i.e. Services area vs. Blog area).

Our design services are the bread and butter of the site, but until now, they’ve always just been one page that gets a link on different corners of the blog. We wanted to show that the two are equal in importance, rather than the services being just a subset of the blog.

The new homepage is intended to be a big part of that, with a lot of real estate given to the Services and PliablePress, not just the blog. And it gave us the chance to give a proper overview of what the site does to first time visitors with our headline:

In terms of the actual design style; we obviously wanted to make the site look nicer, but that’s a little too general to be a real goal. So our goal was to do this in a way that brought more color into the site and gave a very unique and memorable look.

This choice of style is purely because we’re a design team, so our site needs to showcase what we can do in a creative way.

Typography

Improving some of our typography was one of the main reasons for the redesign (To be more specific, getting rid of the fonts I’d used on our post titles was the main reason I was dying to redesign). In the new site, I think we’ve done that well.

Our fonts are now powered by Typekit, so we had a great selection of stunning fonts at our disposal. We’re using the lovely FF Meta Web Pro for a number of titles around the site, and Sorts Mill Goudy for various buttons (And our post titles are in good old-faithful Arial).

Aside from font selection, another thing I’m happy to have improved is our bullet points. We use them a lot in articles, but they vary from only a few words per bullet, to a few lines. That made spacing the bullets out quite awkward.

  • But now, at last
  • we have a nicer design
  • that can handle either.

Yay!

Advertising

The last thing to mention is the 125 by 125 ads in the blog sidebar. A little over a year ago I took all ads off this blog (Largely to make room for PliablePress banners), but now that we’ve built the PliablePress links more effectively into the design, it leaves plenty of room in the sidebar for some tasteful square ads again.

So with that in mind, if you’d like to advertise here, you can do on our BuySellAds page. The current cost is $99 for 30 days (The ads aren’t on the homepage, so it will work out around 120,000 pageviews from 80,000 uniques). And as a little bonus, I’ll tweet/Facebook the first 4 advertisers who join us (Around 10,000 combined followers).

How Did We Do?

Naturally, everything I’ve said above is just what I hoped the redesign will do. The real test is how it actually does. I’d love to hear your thoughts on it. Anything we did well? Or poorly? Better or worse than our previous?

And if you spot any bugs, please do let me know!

PS – We’re still putting on the last layer of polish, so you may notice some area not quite complete yet (e.g. the portfolio listing just one site right now! ;) )

Author: "Michael Martin" Tags: "Design, Pro Blog Design, Redesign, Typog..."
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 10 May 2011 13:52

Appsumo & Sucuri.net Deal

Security is one of those topics that comes up time and time again. You hear the advice, you know you should do it, but somehow, you never get around to it.

The tips in this post are designed to help make your WordPress installation safer and less likely to be hacked or spammed. If you’re short on time, just start at the top and work down. I’ve (roughly) ordered them so that the most important tweaks come first.

We also have a great giveaway at the end of this post, from Sucuri.net and Appsumo. Check it out for the chance to win free site security monitoring for a year!

1 – Set up Backups

This is quite simply a requirement. If you don’t have backups, you’re putting all your eggs in one basket and if something goes wrong, you lose everything. You’d have to be mad to risk this…

There are 3 rules for a good backup:

  1. It can’t be saved on your server (Because then if your server dies, so do your backups).
  2. It needs to be automatic (Because you aren’t going to remember to do this yourself every day.
  3. You need to save a number of them (e.g. if you only save the past 5 backups, and then go on holiday for a week, your backups could all be useless by the time you get home).

Thankfully, they’re easy to do. Here are 3 possible ways:

2 – Always Update WordPress

A fairly common thought used to be that “upgrading WordPress might break my plugins.” And it was true; upgrading WordPress does carry that risk.

Not upgrading is worse; it guarantees that you’re at risk. Almost every WordPress release that comes out has security fixes as part of it. If you don’t update, then by definition, you’re leaving those holes open.

At the very least, you can always do minor updates, e.g. 3.0.1 to 3.0.2. They’re usually solely for security and bugs, so always update right away when one comes out.

As an aside; it’s possible to hide the version of WordPress you’re using by adding this to your functions.php file:

remove_action(‘wp_head’, ‘wp_generator’);

That’s no substitute for not upgrading though (And if they can load your login page, quite often the design is a dead giveaway anyway).

3 – Choose a Better Password

I know you hear this all the time, but crappy passwords are still the #1 reason for sites being hacked. How do you know if your password sucks?

Easy. Does it make any sense whatsoever as a word/phrase/name? If you can pronounce it at all, it’s probably bad. Your password should be a completely random, meaningless set of letters and numbers.

And the meaningless part is important there. Turning “michael” into “m1ch@el” doesn’t make it secure, it just means the hacker will get it on their second guess instead of their first.

4 – Check Your Site Regularly

You’d be surprised how often your site can be compromised without you even knowing. For instance, a lot of unwanted scripts will hide links all over your pages that only search engines can see.

It’s crucial that you catch on fast because once Google penalizes you, getting your rankings back is a slow, painful process.

SecurityOne way to check is to use tools like Google Webmaster Tools to see what links Google is finding on your pages.

Another option is to use a tool like Sucuri.net (Which we’re giving away at the end of this post!), which will automatically scan your site regularly and alert you to any issues.

5 – Prevent Directory Indexing

Directory indexing means that if someone loads a folder on your site (e.g. wp-content/plugins/), they will get a list of all the files in it. This is bad because then a hacker can see everything on your server.

The fix is easy, open up your .htaccess file in a text editor and add the following to it:

#Prevent directory indexing
Options -Indexes

6 – Don’t use FTP

FTP is the most common way to add files to your server, but it isn’t secure. Someone can intercept your transmissions and even your access details. If your host allows you to access your server with SSH, then you can use SFTP instead.

Behind the scenes the two work completely differently, but using them is exactly the same. Odds are you can even continue to use your current FTP program with SFTP instead.

There is no reason not to make this swap, so contact your host and ask them if you can use SFTP (Most should allow it).

7 – Move and Update Your WP-Config.php File

Your wp-config.php file doesn’t have to be in your site’s root folder (Where it is by default). You can move it up a folder (So it is no longer in your public web folders at all).

And it’s as easy as it sounds. Just move the file up one folder, and WordPress will know to find it there.

Also, if you’ve had WordPress for a while now, you may not have all the security keys that you should in your wp-config.php file. These keys help encrypt your passwords and other details. All you need to do is go to https://api.wordpress.org/secret-key/1.1/salt/, copy the code it generates, and then paste it into the relevant section of your wp-config.php file.

8 – Take Care Downloading Plugins/Themes

There are a lot of places to find these items, but not all of them are trustworthy. Many sites get caught out because they installed a script with malicious code hidden in it.

Before downloading a theme or plugin, Google the site to see what people say about it. Lots of good reviews and links? Then go for it. Hard to find any? Then best leave it alone.

For plugins; there is usually little reason for a free plugin not to be hosted on WordPress.org, so always go there first (There are still exceptions though, e.g. cforms). Or alternatively, you can buy premium plugins from sites you trust (Again though, check the site first!).

Themes are trickier. The WordPress.org theme directory is great, but a lot of talented theme developers do choose to host their themes on their own sites (The auto-update feature of the plugin directory isn’t so necessary with themes).

If you do take a theme from a 3rd party website, I’d recommend scanning it right away (Spammy paid links in the footer of a theme are a very common find that Google may penalize you for).

And of course, you always have the option of paying for premium themes (Though do be careful of sites selling themes they did not make. You have no guarantee that they haven’t tampered with the theme first).

Win a Year’s Free Security Scanning

AppsumoAppsumo and Sucuri.net have kindly offered us 2 one-year licenses to their WordPress scanning tools to give away. Sucuri will automatically scan your website and notify you if you if it spots any issues. Better than that, they can help resolve them too.

To enter the competition, just tweet the following:

Update (18th May) – The competition is now over and the winners have been chosen (@ezyblogger and @wpmodder). We’ll more again soon!

I’d like to win a year of Sucuri website monitoring & malware protection on @problogdesign via @appsumo http://bit.ly/j95q8Z

Until midnight tonight only, you can also buy Sucuri’s Business plan for only $85 for the year on Appsumo (Down from $289!). AppSumo gives you a guaranteed refund, no questions asked, so if you buy it and end up winning here, you can get your money back anyway!

Winner will be chosen and emailed this weekend, so good luck!

Author: "Michael Martin" Tags: "WordPress, Backup, Competition, Plugins,..."
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 26 Apr 2011 22:51

Twitter, Facebook, RSS Counts

You will have seen blogs showcase their Twitter, Facebook, and RSS counts with typography. With these numbers in plain text, you can style them into your website far more effectively than if you were to use the old-fashioned “chicklets.”

Twitter and Facebook make this easy, but you’ll need to take an extra step with FeedBurner first. Log in, then click Publicize > Awareness API, and click activate.

All set? Then let’s get started!

Chicklets(And yes, I know we’re still using the chicklets here, but they’ll be gone for good in a few more days so just ignore that for now!)

Planning Our Script

When we get a number from one of these services, we will need to save it to our server.

This is so we can retrieve it from there without having to go out to the service again (Otherwise, the service might cut us off for requesting it too often, and it’s faster this way too).

Regular readers will know where this is heading; the transients API. Transients are WordPress’ way of letting you store information for a limited time (And refreshing it later when you need).

In previous posts where we have used transients though, we have merged the transient code with the code to get our information. This time, we are going to split the two up (So we don’t end up re-writing the transient code each time).

We will have one function that stores/retrieves a transient, and then a function for each service to get the number we need.

1 – Our Transient Function

For a full introduction to transients, you can read our Transients API tutorial. I’ll summarize what the following code is doing afterwards though, so don’t worry if you don’t follow it right away.

Open up your theme’s functions.php file, and paste the following between the opening and closing tags:

/**
 * Retrieve transient value.
 *
 * Returns a transient value, or updates that transient if it has expired.
 *
 * @param $name String. Name of the service, e.g. twitter
 * @return String. New transient value.
 */
function pbd_get_transient($name = 'twitter') {
	$transName = "pbd-transient-$name"; // Name of value in database.
	$cacheTime = 8; // Time in hours between updates.
 
	// Do we already have saved tweet data? If not, lets get it.
	if(false === ($trans = get_transient($transName) ) ) :
 
		// Make the name of the function that gets our transient value.
		$func = "pbd_get_$name";
		if(function_exists($func) ) {
			$trans = $func();
		} else {
			$trans = 'Function not found.';
		}
 
		// Did we get a positive number? If not, use the old value.
		if(!absint($trans) > 0)
			$trans = get_transient($transName . '-old');
 
		// Save our new transient, plus save it in the longer "backup" transient.
		set_transient($transName, $trans, 60 * 60 * $cacheTime);
		set_transient($transName.'-old', $trans, 3 * 60 * 60 * $cacheTime);
	endif;
 
	return $trans;
}

Now let’s break down what this is doing:

  • The pbd_get_transient() expects 1 parameter, the name which makes our transient and “get” function names (In our case, ‘twitter’, ‘facebook’, or ‘rss’, though you could use it for anything else as well).
  • We set a cache time (8 hours), and on line 14, we check if a value is already saved. If it is, we just return it.
  • Line 17 is where it gets interesting. We take the name that was passed to the function, and build a function name from it, e.g. ‘twitter’ becomes ‘pbd_get_twitter’. If the function pbd_get_twitter() exists, we will then call it and save the value it gives (Lines 18 and 19).
  • On line 25, we check the value we have. It should be a positive number. If it isn’t, odds are that the service didn’t respond to us, so we will use the value we saved the last time.
  • On line 29, we save the new transient value, and save it again on line 30 with a longer time limit (So we can use it as a backup if we need to).

And that’s our caching taken care of. We can now re-use this same function for each of our services. Much better, isn’t it?

2 – Get the Numbers

The next step is to go to each service and get the value we want. Thankfully, they’ve all made it very easy with their APIs.

Let’s start with Facebook, add the following to your functions.php file:

/**
 * Get Facebook Page Fan Count.
 */
function pbd_get_facebook() {
	$json = wp_remote_get("http://graph.facebook.com/problogdesign");
 
	if(is_wp_error($json))
		return false;
 
	$fbData = json_decode($json['body'], true);
 
	return intval($fbData['likes']);
}

On line 5, you would replace ‘problogdesign’ with your Facebook Page’s name of course (You can load the URL directly in your browser to make sure you have it right, e.g. click here).

With line 5, WordPress will grab the file. We check that it did this successfully on line 7 (If it didn’t, then our previous function will know to use its backup value).

With line 10, we convert the JSON document we downloaded into a regular PHP array, and last of all, we pick out just the fan count and return it (Making sure it’s an integer).

Twitter is almost identical:

/**
 * Get Twitter follower count.
 *
 * @return Int. Twitter follower number.
 */
function pbd_get_twitter() {
 
	$json = wp_remote_get("http://api.twitter.com/1/users/show.json?screen_name=problogdesign");
 
	if(is_wp_error($json))
		return false;
 
	$twitterData = json_decode($json['body'], true);
 
	return intval($twitterData['followers_count']);
}

As you can see, all we have done is changed some of the names. The method is identical, just replace the problogdesign part with your Twitter username.

Now, let’s do FeedBurner. The method here is similar, but instead of JSON, FeedBurner only offers an XML format:

/**
 * Get RSS subscriber count from FeedBurner.
 *
 * @return Int. RSS follower number.
 */
function pbd_get_rss() {
	$xml = wp_remote_get('http://feedburner.google.com/api/awareness/1.0/GetFeedData?uri=problogdesign');
 
	if(is_wp_error($xml))
		return false;
 
	$sxe = new SimpleXMLElement($xml['body']);
 
	return intval($sxe->feed->entry['circulation']);
}

We start out the same, by grabbing a file from the service (And again, replace the problogdesign part with the name of your feed).

The difference is on line 10, where we use PHP’s SimpleXML extension to get the data we need. And once again, we pick out only the subscriber number to return, and make sure it’s an integer.

3 – Display the Numbers

You’ve now written the 4 functions to get each number and save it. All that’s left to do is print it out on your site.

Browse to wherever you want to list your numbers (e.g. sidebar.php), and insert the following:

<?php echo number_format(pbd_get_transient('rss') ); ?>

We call pbd_get_transient(‘rss’) to return the value, and I’ve wrapped it in number_format() so that commas will be added if the number is in the thousands.

Similarly, for Facebook:

<?php echo number_format(pbd_get_transient('facebook') ); ?>

And Twitter:

<?php echo number_format(pbd_get_transient('twitter') ); ?>

4 – Display a Combined Total

We could have applied number_format() earlier on, before we saved the values. The reason to store the plain integers instead is so you can add the 3 numbers together and display a combined total, if you like.

<?php
$sum = pbd_get_transient('rss') + pbd_get_transient('twitter') + pbd_get_transient('facebook');
echo $sum;
?>

And that’s us all done! Now you (and I) have no more excuses for using the buttons they give us. Instead, just well designed typography.

Update (29th July ’11) – Thanks to Bryan Hadaway for pointing out ‘&gt;’s in the code!

Author: "Michael Martin" Tags: "Coding, WordPress, Facebook, Featured, P..."
Comments Send by mail Print  Save  Delicious 
Next page
» You can also retrieve older items : Read
» © All content and copyrights belong to their respective authors.«
» © FeedShow - Online RSS Feeds Reader