Categories
Programming

Cache Bust WordPress Assets Based On Git Revision

Cache busting is to rewrite a URL for an asset (JS, CSS, images, etc) so that is unique. This way, you can cache them for as long as you want. If the URL changes, it will use the new file.

I am using deployments tool to checkout the latest version of a given branch, therefore I know that there will be a .git folder on my production server.

The idea is to use the current Git commit as a version number using git head reference.

function project_get_current_git_commit( $branch = 'master', $length = false ) {
  if ( ! defined( 'PROJECT_CURRENT_GIT_COMMIT' ) ) {
    $hash = file_get_contents( sprintf( '%srefs/heads/%s', dirname(__FILE__), $branch ) );

    define( 'PROJECT_CURRENT_GIT_COMMIT', ( $hash ? $hash : false ) );
  }

  return ( $length ? substr( PROJECT_CURRENT_GIT_COMMIT, 0, $length ) : PROJECT_CURRENT_GIT_COMMIT );
}

// Enqueue WordPress asset
wp_enqueue_script('application','/assets/javascripts/application.min.js', array('), project_get_current_git_commit( 'master', 8 ), true);

Voilá, now you can cache bust everything based upon your latest Git revision.

Of course, there are other ways to do this as well. Another solution is to store environment variables that contains the latest revision and then read it from PHP, that would save some I/O from the physical file.

Categories
Programming

Installing WordPress core via Composer and finding the proper rewrites for Nginx or Apache

The idea is to separate the WordPress site’s components into their own directories right at the root. This creates a simple modular structure, where parts of a WordPress site are totally on their own. This makes things less complex and less prone to errors.

We will not be touching the WordPress subdirectory, we will only update it when a new version is released. Therefore, we will have to put our configuration and custom development (themes & plugins) outside the WordPress folder.

I’m going to use Composer package manager for PHP to install these packages and this is the directory structure that I want to create.

Directory structure

website/
--config/ 
--vendor/
--lib/
--www/
    wp/ - Will contain the WordPress package
    wp-content/ - Will contain all my themes and plugins
    .htaccess
    index.php
    wp-config.php
composer.json

Composer file

The Composer can be used for both the core and plugins, for plugins we will use wpackagist. After you’ve installed composer, create a composer.json in your root folder that looks something like this:

{
  "name": "parse/demo",
  "description": "Parse: Demo",
  "license": "proprietary",

  "repositories": [
    { "type":"composer", "url":"http://wpackagist.org" },

    { "type": "package",
      "package" : {
        "name": "wordpress",
        "type": "webroot",
        "version": "3.7.1",
        "dist": {
          "type": "zip",
          "url": "https://github.com/WordPress/WordPress/archive/3.7.1.zip"
       },
        "require" : {
          "fancyguy/webroot-installer": "1.1.0"
        }
      }
    }
  ],

  "require": {
    "composer/installers": "1.0.x-dev",
    "wordpress": "3.7.1",
    "wpackagist/mp6": "~2.1"
  },

  "require-dev": {
    "wpackagist/debug-bar": "~0.8",
    "wpackagist/debug-bar-console": "~0.3"
  },

  "minimum-stability": "dev",

  "extra": {
    "wp-content": "www/wp-content",
    "webroot-dir": "www/wp",
    "webroot-package": "wordpress",
    "installer-paths": {
      "www/wp-content/plugins/{$name}/": ["type:wordpress-plugin"]
    }
  }
}

This file defines that we want to install WordPress core in a folder www/wp/ and its plugins in www/wp-content/plugins/

index.php

By moving the WordPress core, the index.php that is in www/ will now have to be changed to:

<?php
// WordPress view bootstrapper
define( 'WP_USE_THEMES', true );
require( './wp/wp-blog-header.php' );

wp-config.php

And since we will not use the wp-content/ folder in www/wp/wp-content/, we need to redefine that in wp-config.php:

<?php
define('WP_CONTENT_DIR', dirname(__FILE__) . '/wp-content');
define('WP_CONTENT_URL', 'http://' . $_SERVER['HTTP_HOST'] . '/wp-content');

Now, we have set up the files, your vhost need to be configured to the www/ folder.

Nginx

After struggling a bit with PHP’s PATH_INFO variable and found a solution, this is the important part:

if (!-e $request_filename) {
  rewrite /wp-admin$ $scheme://$host$uri/ permanent;
  rewrite ^(/[^/]+)?(/wp-.*) /wp$2 last;
  rewrite ^(/[^/]+)?(/.*\.php)$ /wp$2 last;
}

location / {
  root   /home/username/apps/appname/current/www/;
  try_files $uri $uri/ /index.php?$args;
  index index.php;
}

location ~ \.php$ {
  fastcgi_pass 127.0.0.1:9000;
  fastcgi_index index.php;
  fastcgi_split_path_info ^(.+\.php)(/.+)$;
  fastcgi_param SCRIPT_FILENAME /home/username/apps/appname/current/www$fastcgi_script_name;
  fastcgi_param ENVIRONMENT production;
  fastcgi_param PATH_INFO $fastcgi_script_name;
  include /etc/nginx/fastcgi_params;
}

Apache

    ServerName domainname.com
    DocumentRoot "/home/username/apps/appname/current/www/"
    <Directory "/home/username/apps/appname/current/www/">
        Options Indexes Includes FollowSymLinks  
        AllowOverride All
        Order allow,deny
        Allow from all

The .htaccess in this case will look something like this:

Options -Indexes

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) wp/$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ wp/$2 [L]
RewriteRule . index.php [L]

To upgrade plugins or core, you simply need to update your composer.json. We still need to add custom plugins into our repository, but you could create your own repositories for them as well.

Categories
Programming

Frontend med Node och Express, backend med WordPress och JSON API

I somras så fick jag tillsammans med en kollega i uppdrag att förnya en existerande webbplats. Några av sakerna vi hade i åtanke var att den skulle vara responsiv, intuitiv och byggas med något annat än bara WordPress.

Vi började rota runt i Node.js och Express och tänkte att det var något vi ville använda. En av styrkorna med WordPress är ju däremot att det är väldigt enkelt att administrera innehåll i det, så vi valde att använda tillägget JSON API tillsammans med WordPress. På så sätt kunde innehållet hämtas via JSON och på så sätt kan frontenden bara vara ett skal som slussar information.

Backend

Som backend så rullade som sagt WordPRess och JSON API. Vi upptäckte ganska tidigt att tillägget var utdaterat när det gäller stödet för custom post types och taxonomies. Vi byggde enkelt ut vår plattform med de funktioner vi behövde. Anta att denna sida rullar på http://backend.dev och det aktiva temat i sig genererar ingen utdata.

Frontend

Basen på frontenden byggdes med hjälp av Node.js och Express. Ett av kraven i funktionaliteten var att vi inte ville att sidan skulle laddas om. Därför använde vi:

  • Sammy.js för att hantera URL:er
  • Futures för att hantera callback-röran (enligt mig) som kan uppstå och för att se till att saker och ting sker asynkront
  • Node-dirty för att skapa en lokal cache av JSON anropen som görs mot http://backend.dev
  • Restler för att göra REST-anrop mot http://backend.dev
  • Forever för att se till att servern hålls igång.
  • Cluster för att styra applikationen

Exempel på en modell som representerar en klient:

var Model   = require('./model'),
    API     = require('./api'),
    config  = require(__dirname + '/../conf/' + (global.process.env.NODE_ENV || 'development') + '.js');

function Client(args) {
  Model.prototype.constructor.call(this, args);  
}

Client.prototype = new Model();

Model.extend(Client, API);

Client.singleUrl = function(slug){ return config.wordpress.url + '/api/CustomTheme/get_taxonomy_posts/?taxonomy=client&slug=' + slug + '&custom_fields=service,type'; };
Client.collectionUrl = function(){ return config.wordpress.url + '/api/CustomTheme/get_taxonomy_keys/?taxonomy=client'; };

Client.singleKey = 'posts';
Client.collectionKey = 'posts';
Client.model = Client;

Client.prototype.constructor = Client;
Client.prototype.render = function() {
  return {
    id:         this.data.term_id, 
    title:      this.data.name,
    slug:       this.data.slug
  };
};

module.exports = Client;

API är en generell klass som alla modeller använder sig av, och Client.singleUrl och Client.collectionUrl är den URL som API:t ska använda sig av, göra anropet, cacha resultatet och generera en struktur som controllern sedan använder sig av.

Cachen är satt på väldigt hög timeout. Det är en webbplats som inte besökaren har någon möjlighet att påverka innehållet på. Så därför behövde vi komma på en lösning att varje gång en uppdatering skett i WordPress-backenden så rensades vår cache.

Hela miljön deployas med hjälp av Capistrano. Där användes en Rakefile för att hantera situationer som att starta om servern, stoppa servern och rensa cachen.

Summan av kardemumman

En ganska krånglig lösning men oerhört kul och inspirerande att få genomföra. Tyvärr blev det tidsbrist och jag hade inte tid att slutföra projektet innan jag åkte till San Jose. Projektet gav mig absolut mersmak för Node.js och vi får se vad jag hittar på i framtiden.

Förra terminen här i San Jose så läste jag en kurs där ett delmoment var i Node, så där hade jag absolut försprång på de andra i klassen. Jag ber om ursäkt för att jag inte kan visa mer kod än vad jag har gjort.