Hands down my favourite WordPress plugin is Elliot Condon’s Advanced Custom Fields, and it’s made even more powerful by the Repeater Field add-on. But a Repeater can get unwieldy when it contains a large number of items, and you might find yourself wanting to paginate the results when you display them to the user. Here’s a technique for doing that.
In this example we will paginate a Repeater image gallery mapped to a custom field named
image_gallery with a sub field named
image which contains an image object. Our Repeater will be displayed on a page at the URL
10 images will be displayed per page, and pagination links will allow the user to navigate between the gallery’s pages at pretty URLS such as
/gallery/page/3 and so on.
On two occasions recently I have run into a problem where WordPress custom fields were disappearing after a post was edited. The first time I noticed this odd behaviour was on a site using the Advanced Custom Fields Repeater add-on for an image gallery. Each gallery item contained an image field along with several meta data fields, and once the gallery grew significantly large new items stopped being created. All of the existing gallery items would save successfully, but newly created fields would disappear when the post was updated.
The second time I encountered disappearing data was on a WooCommerce installation using the Per Product Shipping module. This module requires shipping costs to be entered individually for each country (why, WHY?) and as a consequence each product had a large number of custom fields. Surprisingly it wasn’t the shipping costs that were disappearing, but the product attributes – size, colour and so forth. This had disastrous effects, since when the attributes disappeared the linked product variations were removed too, along with the product prices.
In both cases data would only disappear on the production web server. When I worked on the sites locally custom fields would save as expected, so I was fairly certain that the server configuration was to blame. No errors were being displayed within WordPress or in my server logs so I initially focussed on MySQL, since the bug seemed to stem from a failure to write to the WordPress database. That line of enquiry proved fruitless, but thankfully I came across a thread on the Advanced Custom Fields forum which mentioned PHP’s
max_input_vars setting as a potential culprit.
max_input_vars was introduced in PHP 5.3.9, and limits the number of
$_COOKIE input variables that PHP will accept. Further variables are truncated from the request. The default value for
max_input_vars is 1000, which means that if your form has has more than 1000 inputs, any additional values will be lost. Since each custom field is an input in the WordPress editor, plugins like Advanced Custom Fields Repeater and Per-Product Shipping make it possible to exceed the 1000 input limit.
You can increase
max_input_vars in your php.ini file:
max_input_vars = 3000
or in an .htaccess document:
php_value max_input_vars 3000
After doing that you should find that you can once again save a large number of custom fields.
Git may not have been designed as a deployment tool, but for small projects it can do the job quite nicely. What makes Git deployments attractive is how frictionless the process is: make some changes to your project, merge them into your production branch, push the commit to a remote repository and like magic the changes are live! Git knows which files need to be changed or deleted, so you don’t have to think about it. If you’re already using git to version control your project then you probably won’t even need to modify your existing workflow, once the initial setup is done.
I use Bitbucket for hosting my private repositories, and have recently implemented a deployment process that integrates with Bitbucket’s POST hooks feature. There are basically three things you need to do to make this work:
- Set up SSH keys so your server can talk to Bitbucket
- Clone your Bitbucket repository on your web server
- Setup a hook on Bitbucket and an associated deployment script on your server
Here’s what your deployment workflow will look like once we’re done:
- Develop your website locally
- When you’re ready to deploy, commit your changes and push them to Bitbucket
- When Bitbucket receives the commit it will notify a deployment script on your server
- The deployment script will fetch the changes into a cloned repository on your server, and checkout files to your public web directory
Chris Bowler from the Campaign Monitor team has written an interesting comparison between Typekit and Hoefler and Frere-Jones’ new cloud.typography web font service. If you’ve been sizing up clouds.typography, or didn’t know that H&FJ has (finally) launched their much anticipated web font platform, go read his post.
One thing that Chris didn’t touch on, but I think is relevant to the comparison, is that H&FJ’s annual subscription fees don’t actually give you access to their full range of fonts, just five font families:
Join Cloud.typography and get your first five webfont packages FREE
If you want access to more than five families then any additional fonts need to be purchased separately – currently H&FJ’s popular Gotham family costs $149 for a web font license, or $299 for a web and desktop license.
If you’ve ever wanted to display your latest Twitter tweets on a website, TweetPHP lets you do that using PHP. TweetPHP is a rewrite of my old Twitter RSS feed parser, which stopped working when Twitter phased out their v1 API.
- Works with Twitter API v1.1
- Tweets are cached to avoid exceeding Twitter’s API request rate limits
- A fallback is provided in case the twitter feed fails to load
- Retweets and @replies can be optionally ignored
- A configuration parameter allows you to specify how many tweets are displayed
- Dates can optionally be displayed in “Twitter style”, e.g. “12 minutes ago”
- You can customize the HTML that wraps your tweets, tweet status and meta information
TweetPHP is available on Github
To interact with Twitter’s API you will need to create an API KEY, which you can create at: https://dev.twitter.com/apps
After creating your API Key you will need to take note of following values: “Consumer key”, “Consumer secret”, “Access token”, “Access token secret”
Those values can be passed as options to the class constructor, along with the Twitter screen name you wish to query:
$TweetPHP = new TweetPHP(array(
'consumer_key' => 'xxxxxxxxxxxxxxxxxxxxx',
'consumer_secret' => 'xxxxxxxxxxxxxxxxxxxxx',
'access_token' => 'xxxxxxxxxxxxxxxxxxxxx',
'access_token_secret' => 'xxxxxxxxxxxxxxxxxxxxx',
'twitter_screen_name' => 'yourusername'
Then you can display the results like so:
Options can be overridden by passing an array of key/value pairs to the class constructor. At a minimum you must set the
twitter_screen_name options, as shown above.
Here is a full list of options, and their default values:
'consumer_key' => '',
'consumer_secret' => '',
'access_token' => '',
'access_token_secret' => '',
'twitter_screen_name' => '',
'cache_file' => dirname(__FILE__) . '/cache/twitter.txt', // Where on the server to save the cached formatted tweets
'cache_file_raw' => dirname(__FILE__) . '/cache/twitter-array.txt', // Where on the server to save the cached raw tweets
'cachetime' => 60 * 60, // Seconds to cache feed (1 hour).
'tweets_to_display' => 10, // How many tweets to fetch
'ignore_replies' => true, // Ignore @replies
'ignore_retweets' => true, // Ignore retweets
'twitter_style_dates' => false, // Use twitter style dates e.g. 2 hours ago
'twitter_date_text' => array('seconds', 'minutes', 'about', 'hour', 'ago'),
'date_format' => '%I:%M %p %b %d%O', // The defult date format e.g. 12:08 PM Jun 12th. See: http://php.net/manual/en/function.strftime.php
'date_lang' => null, // Language for date e.g. 'fr_FR'. See: http://php.net/manual/en/function.setlocale.php
'format' => 'html', // Can be 'html' or 'array'
'twitter_wrap_open' => '<h2>Latest tweets</h2><ul id="twitter">',
'twitter_wrap_close' => '</ul>',
'tweet_wrap_open' => '<li><span class="status">',
'meta_wrap_open' => '</span><span class="meta"> ',
'meta_wrap_close' => '</span>',
'tweet_wrap_close' => '</li>',
'error_message' => 'Oops, our twitter feed is unavailable right now.',
'error_link_text' => 'Follow us on Twitter',
'debug' => false
Caching is employed because Twitter rate limits how many times their feeds can be accessed per hour.
When the user timeline is first loaded, the resultant HTML list is saved as a text file on your web server. The default location for this file is:
You can change this file path by setting the
cache_file option. For example, to set a path from your root public directory try:
$_SERVER['DOCUMENT_ROOT'] . '/path/to/my/cache/dir/filename.txt'
If you are experiencing problems using the plugin please set the
debug option to
true. This will set PHP’s error reporting level to
E_ALL, and will also display a debugging report.
Here are a few tips to help you solve common configuration issues:
tmhOAuth response code: 0
If your debugging report shows the error “tmhOAuth response code: 0″, you can find out more about this error message in the tmhOAuth github README. Dario also offers this solution: “My server didn’t like the the two __DIR__ constants in the tmhOAuth.php file, hard-coded them and it all works now.”
If you receive the PHP warning “[function.unserialize]: Error at offset 0 of 49 bytes”, it might be the case that you have magic quotes enabled in your PHP configuration.
- Feed parsing uses Matt Harris’ tmhOAuth
- Hashtag/username parsing uses Mike Cochrane’s twitter-text-php
On Daring Fireball John Gruber proposes that the current enthusiasm for “flat” interface design can be explained by the introduction of retina displays. Gruber argues that high resolution screens are a natural fit for clean, typographic interfaces, whereas crude low resolution screens need skeuomorphism’s visual “parlor tricks” to disguise their inferior pixel density:
The trend away from skeuomorphic special effects in UI design is the beginning of the retina-resolution design era. Our designs no longer need to accommodate for crude pixels. Glossy/glassy surfaces, heavy-handed transparency, glaring drop shadows, embossed text, textured material surfaces [...] work on sub-retina displays because sub-retina displays are so crude. On retina displays, as with high quality print output, these techniques are revealed for what they truly are: an assortment of parlor tricks that fool our eyes into thinking we see something that looks good on a display that is technically incapable of rendering graphic design that truly looks good.
If Gruber’s hypothesis is correct, then how can we explain the fact that highly skeuomorphic interfaces only started to appear en masse around 2008, when the iOS app store launched? UI designers have had to accomodate low resolution displays for decades, yet for the most part the dominant design styles have been “flat” rather than heavily textured or skeuomorphic. Even in recent years that has been the case, at least in the field of web design. The web design gallery siteInspire, launched in 2008, has showcased thousands of interfaces that are (mostly) devoid of decorative embellishments, which is a testament to the enduring popularity of minimalist design.
When working with the Big Cartel ecommerce platform it is common to map a custom domain to your store. For example, you might like to use the domain mysite.com instead of your default Big Cartel subdomain mysite.bigcartel.com.
Big Cartel provide instructions for configuring domain mapping using a CNAME record, so that www.mysite.com becomes an alias for mysite.bigcartel.com. This works great except for one thing: if a customer omits the www prefix from your store’s URL they will get a big fat 404 error. This is because your CNAME record only matches the www subdomain.
Smashing Magazine recently published Dear Web User: Please Upgrade Your Browser, an article by Louis Lazaris encouraging regular Internet users to abandon Internet Explorer and upgrade to a modern web browser. After reading the piece, I tweeted that while I appreciate the sentiment, trying to convince stubborn IE users to upgrade is like whistling in the wind. By which I mean that those users are a) never going to read an article on Smashing Magazine and b) not going to upgrade their browser until they upgrade their computer. Hardly anyone chooses Internet Explorer as their web browser – they use it because it came pre-installed on their computer, and will continue using it for the lifespan of the computer. Any number of articles on Smashing Magazine won’t change that fact.
Yesterday I bought myself a copy of Jeff Atwood’s new ebook Effective Programming: More Than Writing Code, which collects some of the best articles from his popular blog Coding Horror. Sure, I can read all the articles in Effective Programming for free on Coding Horror, but I chose to buy the book anyway, and if you enjoy Jeff’s writing then you should too.
When Rdio rolled out their redesigned application recently, I was intrigued by the app’s scrolling implementation. There are no conventional browser scrollbars in Rdio, and instead scrollbars appear on-demand when the user’s mouse enters a scrollable portion of the interface, or when the user performs a mousewheel or trackpad scroll. Rdio’s implementation mimics the experience of scrolling in OSX Lion using a trackpad pointing device, except that it works for any user, on any platform.
Inspired by Rdio’s scrollbars I have created a jQuery plugin that emulates Lion’s trackpad-style scrolling, regardless of the user’s pointing device, browser, or platform.