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.

Categories
Programming

Att köra WordPress Multi-Site på VPS med multi domain mapping

Bakgrund

Ett problem som jag haft väldigt länge är att jag kört många olika WordPress-installationer för relativt små projekt. Det är inte alls särskilt roligt att underhålla alla dessa installationer, dvs att hålla kärnan och tillägg uppdaterade. I ett tidigare projekt så skapade Tedde Lundgren ett tillägg som tillät oss att koppla flera olika domäner (inte begränsat till subdomäner) till en installation.

Jag är medveten om att det finns liknande tillägg som exempelvis WordPress MU Domain Mapping. Det som skiljde Teddes version från denna var att vi fick tillgång till gruppering av domäner, så vi kan köra separata utvecklingsdomäner. Tillägget är för övrigt en riktig lättviktare och innehåller endast det som behövs, dvs inte bloatat. Tedde har planer på att släppa tillägget öppet senare i sommar.

Screenshot Domain Mapper

 
Så förra veckan gjorde jag slag i sak och beställde en VPS hos Ipeer. Vi har använt Ipeer på många projekt hos Populate så jag visste sedan tidigare att det fungerar bra.

Mjukvara och tjänster som används

  • Nginx – Webbserver
  • Apache– Webbserver
  • PHP + APC – APC används för cachning
  • Memcached – Memcached används för cachning
  • WordPress + W3 Total Cache – Publiceringsverktyg samt W3 Total Cache för att cacha
  • Ruby + Capistrano – Capistrano är en samling Rubyscript som används för att enkelt deploya till drift/utvecklingsserver
  • Git + GitHub – Git är versionshantering av källkod, GitHub är en tjänst för att samarbeta med andra utvecklare

Webbserver

Wikipedia skriver följande angående Nginx:

Nginx quickly delivers static content with efficient use of system resources. It can deploy dynamic HTTP content on a network using FastCGI handlers for scripts, and can serve as a very capable software load balancer.[2]

Med detta som grund så valde jag att använda Nginx som webbserver (@port 80) för att servera allt statiskt material (JavaScript, CSS och bilder), för att sedan proxy:a alla andra anrop vidare till Apache (@port 81).

Cachning och WordPress

Genom att använda Memcached, APC i kombination med W3 Total Cache så har jag fått ner laddningstiden i WordPress oerhört mycket. Ett problem som jag tyvärr upplever för tillfället är att administrationen i WordPress 3.1.3 är väldigt trög, det verkar vara ett känt problem som många inte upplevde i 3.0.x-branchen. Vi får se ifall det fixas i 3.2 som borde lanseras inom en snar framtid.

GitHub och Capistrano

Versionshantera, snälla! Även ifall du är den enda i projektet… På GitHub har jag samlat all kod jag använder i mina projekt. Jag har ett betalkonto som tillåter mig att ha 10 privata repositories och en medarbetare på dessa. All utveckling sker lokalt (på exempelvis http://hassis.dev) och när jag är nöjd så laddar jag upp på GitHub och den jag arbetar med laddar ner det lokalt och tittar över det. När vi sedan är överens om att det går att släppa publikt så kör vi ut allt genom Capistrano.

Capistrano förutsätter att man har tillgång till SSH och använder sig av publika nycklar för att autentisera sig mot servern.

Slutsats

Detta är tänkt som en genomgång vad jag använder för verktyg när jag utvecklar. Vill ni se lite exempel på källkod så får ni gärna säga till.

Categories
Programming

WP Multi-Site: Aktivering av tillägg

Inledning

Stötte för någon vecka på ett nytt problem för min del. Det handlade om att jag skulle skapa ett plugin som används i multi-site. Jag har jobbat relativt lite med Multi-Sites och problemet var att tabellerna inte skapades för alla “siterna” som fanns i nätverket.

Källkod

register_activation_hook(__FILE__, 'hass_vote_activate');

function hass_vote_activate() {
	global $wpdb;
 
	if (function_exists('is_multisite') && is_multisite()) {
		if (isset($_GET['networkwide']) && ($_GET['networkwide'] == 1)) {
			$old_blog = $wpdb->blogid;
		
			$blogids = $wpdb->get_col($wpdb->prepare("SELECT blog_id FROM $wpdb->blogs"));
			foreach ($blogids as $blog_id) {
				switch_to_blog($blog_id);
				_hass_vote_activate();
			}
			switch_to_blog($old_blog);
			
			return;
		}	
	} 
	_hass_vote_activate();		
}
 
function _hass_vote_activate() {
	global $wpdb;

	$table_name = $wpdb->prefix . 'yourtable';

	if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
		$sql = "CREATE TABLE " . $table_name . " (
			id mediumint(9) NOT NULL AUTO_INCREMENT,
     			dateadded bigint(11) DEFAULT '0' NOT NULL,
     			subject VARCHAR(255) NOT NULL,
	  		UNIQUE KEY id (id)
	  	);";

		require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
  		dbDelta($sql);
	}
}

Ovanstående kod vandrar igenom alla dina siter som är uppsatta i nätverket och kör aktiveringsfunktionen för dessa.

Koden ovan raderar inte tabellerna vid inaktivering, det skapar heller inte tabellerna för nya siter som aktiveras i nätverket. Du får i sådana fall använda följande actions:

add_action( 'wpmu_new_blog', 'your_function_activation_on_new_blog'); 		// For new site
register_deactivation_hook( __FILE__, 'your_deactivation_function' );		// For deactivation