Categories
WordPress

Hide featured image if it’s in the post

I’ve been running a photoblog at inphotos.org since 2005 on WordPress. (And thanks to writing this I noticed it’s 15 years old today!)

In that time WordPress has changed dramatically. At first I used Flickr to host my images, but after a short time I hosted the images myself. (Good thing too since Flickr limited free user accounts to 1000 images, so I wrote a script to download the Flickr images I used in posts.)

For quite a long time I used the featured image instead of inserting the image into the post content, but then about two years ago I went back to inserting the photo into the post. Unfortunately that meant the photo was shown twice, once as a featured image, and once in the post content.

The last theme I used supported custom post types, one of which was a photo type that displayed the featured image but hid the post content. It was an ok compromise, but not perfect.

Recently I started using Twenty Twenty, but after 15 years I had a mixture of posts with:

  • Featured image with no image in the post.
  • Featured image with the same image in the post.

I knew I needed something more flexible. I wanted to hide the featured image if it also appeared in the post content. I procrastinated and never got around to it until this evening when I discovered it was actually quite easy.

Copy the following code into the function.php of your child theme and you’ll be all set! It relies on you having unique filenames for your images. If you don’t then remove the call to basename(), and that may help.

function maybe_remove_featured_image( $html ) {
        if ( $html == '' ) {
                return '';
        }
        $post = get_post();
        $post_thumbnail_id = get_post_thumbnail_id( $post );
        if ( ! $post_thumbnail_id ) {
                return $html;
        }

        $image_url = wp_get_attachment_image_src( $post_thumbnail_id );
        if ( ! $image_url ) {
                return $html;
        }

        $image_filename = basename( parse_url( $image_url[0], PHP_URL_PATH ) );
        if ( strpos( $post->post_content, $image_filename ) ) {
                return '';
        } else {
                return $html;
        }
}
add_filter( 'post_thumbnail_html', 'maybe_remove_featured_image' );

The post_thumbnail_html filter acts on the html generated to display the featured image. My code above gets the filename of the featured image, checks if it’s in the current post and if it is returns a blank string. Feedback welcome if you have a better way of doing this!

Categories
WordPress

Crowdsignal Polls in your Block Editor

The Crowdsignal team at Automattic have been quietly working on a new poll block for the last few weeks. We finally made it public today on WordPress.org!

We set out with the task of creating a block that would allow the writer to quickly insert a poll in their posts using the block editor. More than that, it had to be simple to use. It also needed to be themed to match the look and feel of the website it would appear on.

We’ve created a block that does that. It also records the votes collected on the Crowdsignal website where you can analyse the results using reports Crowdsignal users have always used.

Search for “Crowdsignal Forms” on your plugins page to install it in the usual way.

A free Crowdsignal account is required to use the block. We made it really easy to connect your site to your Crowdsignal account. If you don’t have one then creating a new account is simple too.

The first 2,500 responses you collect are included in your free account, and further votes are recorded but free users are encouraged to upgrade if they want to do further analysis of all the data they collect.

Categories
Development

Coloured svn diff

The output of svn diff can sometimes be hard to read, especially when there are a lot of changes to read through.

I also realise that you might think I’m a dinosaur for still using svn because git has nicely coloured diffs out of the box but talk to any WordPress plugin developer and they’ll have to use svn at some stage. On the other hand, if you’ve used svn for years you may not even realise you need coloured diffs.

I found a neat solution to that. Pipe the output of svn into colordiff.

This Bash function, svndiff, should be placed in the .bashrc in your home directory (or .zshrc or whatever shell you use, it’ll probably be similar)

function svndiff () {
    svn diff $@ | colordiff | less -R;
}

Log out and log back in or do source ~/.bashrc from the command line to get it working.

Categories
WordPress

WP Super Cache 1.6.9: security update

WP Super Cache is a full page caching plugin for WordPress.

Version 1.6.9 has just been released and is a required upgrade for all users as it resolves a security issue in the debug log. The issue can only be exploited if debugging is enabled in the plugin which will not be the case for almost all users.

The debug log is usually only enabled temporarily if a site owner is debugging a caching problem and isn’t something that should be left on permanently as it will slow down a site.

If there is an existing debug log it will be deleted after updating the plugin.

This release also improves the debug log by hiding sensitive data such as the ABSPATH directory of the WordPress install and login cookies. Unfortunately in the past users have copied the log file data into forum posts. A warning message has been added asking the site owner not to publish the debug log.

Details of the security issue will be added to this post in time to allow sites to update their plugin.

Categories
WordPress

WP Super Cache 1.6.3

WP Super Cache is a full page caching plugin for WordPress. When a page is cached almost all of WordPress is skipped and the page is sent to the browser with the minimum amount of code executed. This makes the page load much faster.

1.6.3 is the latest release and is mostly a bugfix release but it also adds some new features.

  • Added cookie helper functions (#580)
  • Added plugin helper functions (#574)
  • Added actions to modify cookie and plugin lists. (#582)
  • Really disable garbage collection when timeout = 0 (#571)
  • Added warnings about DISABLE_WP_CRON (#575)
  • Don’t clean expired cache files after preload if garbage collection is disabled (#572)
  • On preload, if deleting a post don’t delete the sub directories if it’s the homepage. (#573)
  • Fix generation of semaphores when using WP CLI (#576)
  • Fix deleting from the admin bar (#578)
  • Avoid a strpos() warning. (#579)
  • Improve deleting of cache in edit/delete/publish actions (#577)
  • Fixes to headers code (#496)

This release makes it much easier for plugin developers to interact with WP Super Cache. In the past a file had to be placed in the “WP Super Cache plugins directory” so that it would be loaded correctly but in this release I’ve added new actions that will allow you to load code from other directories too.

Use the wpsc_add_plugin action to add your plugin to a list loaded by WP Super Cache. Use it like this:

do_action( 'wpsc_add_plugin', WP_PLUGIN_DIR . '/wpsc.php' )

You can give it the full path, with or without ABSPATH. Use it after “init”. It only needs to be called once, but duplicates will not be stored.

In a similar fashion, use wpsc_delete_plugin to remove a plugin.

The release also makes it much simpler to modify the cookies used by WP Super Cache to identify “known users”. This is useful to identify particular types of pages such as translated pages that should only be shown to certain users. For example, visitors who have the English cookie will be shown cached pages in English. The German cookie will fetch German cached pages. The action wpsc_add_cookie makes this possible.

do_action( 'wpsc_add_cookie', 'language' );

Execute that in your plugin and WP Super Cache will watch out for the language cookie. The plugin will use the cookie name and value in determining what cached page to display. So “language = irish” will show a different page to “language = french”.

Use wpsc_delete_cookie to remove a cookie. Cache files won’t be deleted. It’s doubtful they’d be served however because of the hashed key used to name the filenames.

do_action( 'wpsc_delete_cookie', 'language' );

If you’re going to use either of the plugin or cookie actions here I recommend using Simple Caching. While the plugin will attempt to update mod_rewrite rules, it is much simpler to have PHP serve the files. Apart from that, any plugins loaded by WP Super Cache will be completely skipped if Expert mode is enabled.

Categories
WordPress

WP Super Cache and Cookie Banners

EDIT: Updated for WP Super Cache 1.6.3 and newer.

More sites use cookie banners now that the GDPR is active but some are finding that their banners are misbehaving once they enable caching.

This is a similar issue to the one that happened to some page counter plugins in the past. The page counter wouldn’t increment.

When a cookie banner is clicked a cookie is set in the browser so the website knows this visitor has agreed to accept cookies. If the cookie is set then the cookie banner html is not sent to the browser.

I suspect the main issue is that the code that sets and checks if the cookie is set is PHP. Unfortunately because the page is cached then no PHP code is executed, and the cookie banner is displayed because it was originally cached that way.

Since WP Super Cache only knows about certain WordPress cookies it assumes everyone who doesn’t have those cookies is a first time “anonymous” visitor. It doesn’t know about your cookie banner cookie.

You have two options:

  1. Rewrite your cookie banner so it’s completely in Javascript. Do the cookie detection in Javascript and also set the cookie in Javascript. If the cookie banner has been clicked then you need to trigger an action, and other Javascript that is hooked on to that trigger will run and load the tracking cookies.
  2. Modify WP Super Cache so it knows about the cookie your cookie banner uses. Caching won’t work quite as well as before as it’ll be split between visitors who have clicked the cookie banner and those that haven’t. One cached file will display the cookie banner, and the other will not but it will have ad tracking Javascript.

Using Javascript completely is a better solution because it runs in the browser on every page load but that might not be possible every time.

Otherwise, use PHP to get WP Super Cache to play nicely with your existing code. You basically need to run do_action( 'wpsc_add_cookie', 'cookie_name' ); after init so WP Super Cache adds the cookie name to it’s cookie list. This only needs to be done once but you can leave it run all the time because it will only ever add the cookie name once. This also ensures the name is added again if the WP Super Cache configuration file is ever reset or deleted.

  1. Create a PHP script in wp-content/mu-plugins/ (or in to your own plugin).
  2. Add the cookie by using the wpsc_add_cookie action in a function that loads on “init”.
  3. Reload any uncached page and the cookie name will be added to the cookie list.
  4. Caching can only be performed by simple caching now, unless you’re willing to edit mod_rewrite rules in your .htaccess file.
/*
 * Tell WP Super Cache to cache requests with the cookie "eucookie" separately
 * from other visitors.
 */
function add_wpsc_cookie_banner() {
    do_action( 'wpsc_add_cookie', 'eucookie' );
}
add_action( 'init', 'add_wpsc_cookie_banner' );

Substitute the name of the cookie (eucookie) for your cookie name.

When your plugin is uninstalled it should remove your cookie with the following:

do_action( 'wpsc_delete_cookie', 'eucookie' );

For future reference, since cookie banners will hopefully not be around forever, here’s what they looked like in the deep, distant past of 2018. 🙂

The LA Times just gave up and don’t show anything to EU visitors.
Categories
General WordPress

Writing WP Super Cache Plugins

WP Super Cache is a full page caching plugin for WordPress. When a page is cached almost all of WordPress is skipped and the page is sent to the browser with the minimum amount of code executed. This makes the page load much faster.

Unfortunately if you want to run code on every page load you’re out of luck as regular WordPress plugins are not loaded or executed. You’ll need to write a WP Super Cache plugin. This short introduction will not teach you how to write plugins but the example plugins that ship with WP Super Cache will get you a long way towards understanding how they work.

Screen Shot 2017-10-25 at 16.49.10

WP Super Cache ships with some example plugins in wp-super-cache/plugins/. Some of them even do useful tasks like help with domain mapping and Jetpack integration. There’s one called “awaitingmoderation.php” which removes the text “Your comment is awaiting moderation.” when someone writes a moderated comment.
There’s also dynamic-cache-test.php which is a complicated beast but it’s heavily commented. It allows you to add template tags to your site that are replaced when the cached page is loaded.

Since WP Super Cache 1.6.3 you can use the “wpsc_add_plugin” action to activate a plugin. You must pass the full path and filename of the plugin to the action like this:

do_action( 'wpsc_add_plugin', ABSPATH . 'wp-content/plugins/myplugin/wpsc-helper.php' );

In a similar fashion you can use the “wpsc_delete_plugin” action to deactivate a plugin. You only need to add a plugin once but if run multiple times it will only be added once.

WP Super Cache plugins can be put anywhere that the web server can load them. If you’re writing a plugin or theme you can activate the plugin in your distribution directory.

Actions

These plugins run before most of WordPress has loaded. That means you can’t rely on some of the nice features of WordPress such as filters and actions. However, WP Super Cache has it’s own action/filter system that is similar to actions and filters in WordPress:

  • add_cacheaction( $action, $func )
  • do_cacheaction( $action, $value = ” )

A cacheaction is also a filter. If you hook on to a cache action that has a parameter, you must return that parameter at the end of the function like you would with a WordPress filter.

Screen Shot 2017-10-25 at 16.52.59

If you need to hook into a WordPress filter use the imaginatively named cache action “add_cacheaction”. That runs on “init” so the function that is executed can use add_action() or add_filter(). You can see this in action in the plugins/dynamic-cache-test.php or plugins/awaitingmoderation.php scripts.

Two very useful filters are the WordPress filter, “wpsupercache_buffer” (in wp-cache-phase2.php) that is used to modify the page before it is cached and the cache action “wpsc_cachedata” (in wp-cache-phase1.php) is used to modify the cached page just before it’s served.

Cookies

WP Super Cache 1.6.3 added the wpsc_add_cookie action. This allows you to change the key used to identify a cache file.

Cookies are one part of the key that tells WP Super Cache what cache file to serve to a visitor. By adding a cookie to the internal cookie list the plugin will know that visitors will a certain cookie should be served a different cache file.

For example, a translation plugin may use the cookie “language” to identify visitors who opted to view a translated website. If that cookie has the value “ie” it will show an Irish translation, “fr” will serve a French translation and so on. If WP Super Cache knows about the “language” cookie it can cache each translated page correctly and then serve it to the appropriate visitor.

The plugin will use the name of the cookie and the value of the cookie to determine the cache file to be used.

Example:

do_action( 'wpsc_add_cookie', 'language' );

You can remove a cookie by using the “wpsc_delete_cookie” action with the same parameter.

Please note that cache files are not refreshed when cookies are added or deleted. You can use the function “wp_cache_clear_cache()” to clear the cache for a given blog. If on a multisite install pass the blog_id of the blog otherwise the contents of the cache directory will be deleted.

Categories
WordPress

WP Super Cache 1.5.0

WP Super Cache is a fast full-page caching plugin for WordPress. Download it from your dashboard or get it here.

Version 1.5.0 has been in development for some time. It has a ton of bug fixes and new features.

REST API

The headline new feature is REST API access to the settings. This will allow developers to create their own interface to the settings of the plugin. Unfortunately it isn’t yet documented but you can see the code in the rest directory. Start with load.php where you’ll find the code that registers all the endpoints. Users who access the API must be logged in as admin users. If you want to test the API, see the end of this post.

Settings Page

We have also simplified the settings page to make it easier to choose which caching method is used.

Instead of maybe confusing the user with technical words like PHP, mod_rewrite and WP-Cache we have split them up into “Simple” and “Expert” delivery methods, and done away with mentioning WP-Cache completely. Simple delivery uses PHP, expert uses mod_rewrite and well, WP-Cache got the boot because it’s always active anyway.

WP-Cache caching is always active, but it can be disabled in different ways.

  • Disable caching for known users.
  • Don’t cache pages with GET parameters
  • Disable caching of feeds

Headers

We expanded the number of headers cached by the plugin. The list of headers was borrowed from Comet Cache. However, anonymous users will still only see the bare minimum like content-length or content-type. If you need to use security headers like “X-Frame-Options” or “Content-Security-Policy” you should enable caching of HTTP headers. This unfortunately disables super caching so only WP-Caching is used but it’s still very fast (and faster in this release than before which I will get to below). You can also use the “wpsc_known_headers” filter to modify the list of recognised headers.

WP-Cache Reorganisation

WP-Cache cache files are split into two files – one holds the page content, the other (meta file) holds information about the page such as cookies, headers and url. In the past these files were stored in two directories which could become a problem if there were many thousands of those files. Even with only a few hundred files, maintenance could be an issue as deleting related files (like page archives, or copies of the front page) needed every meta file to be inspected.
Now the files are stored in the supercache directory structure that mirrors your permalink structure. Deleting related files is is simpler and serving files will be faster as the operating system won’t need to open a directory of thousands of files.
If you currently rely on WP-Cache files, the plugin will still look for them where they are, but new WP-Cache files will be created in cache/supercache/example.com/ (where example.com is your hostname).

Sitemaps

We added support for caching sitemaps, but your sitemap plugin will need to cooperate to get it to work. The sitemap plugin needs to identify the sitemap request as a feed. Jetpack 5.1 now supports this since #7397. You can disable the caching by excluding feeds from caching.

Debugging Improved

The debug log is now protected by a username/password. For convenience, the username and password are the same but they are a long md5 string:

Deleting the log file clears it and resets it ready for more logging. Before, toggling debugging would create a new debug log and the old one would be kept around, but not linked, until deleted by garbage collection, and of course they were text files anyone could access.

This release includes lots of other small bug fixes and changes. Take a look at the number of closed PRs for an exhaustive list of those changes!

Testing the REST API

There are a number of ways to send POST requests to a web server but one I like is using curl in a shell script. You’ll need two bits of information from the website:

  1. The “wordpress_logged_in” cookie from your browser.
  2. The wp_rest nonce which you can get by adding `echo wp_create_nonce( ‘wp_rest’ );` somewhere on your site where you’re logged in. It’s good for 24 hours.

My test script looks something like this:
export NONCE='1234567890'
export COOKIE='wordpress_logged_in_xxxxxxxxxxxxxxxxxxxx=1234567890'
curl -v -X "GET" -H "Content-Type: application/json" -H "X-WP-Nonce: $NONCE" -H "Cache-Control: no-cache" -H "Cookie: wordpress_test_cookie=WP+Cookie+check; $COOKIE" \
-d '{}' "https://example.com/wp-json/wp-super-cache/v1/settings/"

Categories
WordPress

Thank you Translators!

Well, well. WP Super Cache translations are coming along well. 100% of strings in the development version are translated into Canadian English and Romanian! (That might change over the next few days as I have a couple of UI changes in mind, sorry!)

Other languages like Persian, Japanese, Spanish, Russian and Italian are roaring along and sometimes only need a handful of words or sentences translated to hit that magic 100% too. The next version of the plugin will grab translations from WordPress.org if they’re available which is awesome for users around the world. We’re still shipping translation files but I think the release after that may ship without and rely entirely on the up-to-date online translations.

So, thank you so much to the translators. Code is nigh on useless if the user and developer don’t speak the same language and the interface is incomprehensible. WP Super Cache has lots of technical language which must be a pain to translate. Your work is invaluable!

Mulțumesc! متشکرم! ありがとうございました!¡Gracias! Спасибо! Grazie! Hvala ti! 谢谢!Vielen Dank!

Categories
Automattic

12 years at Automattic

Wow.

12 years ago I started out on this adventure with Automattic and Matt. It’s hard to believe it’s been that long.

If you want to discover why I’m still here, take a look at the work with us page. We’re hiring!