Skip to main content

I'm a self-employed web developer, family man, nerd, scuba diver, and author of many fine books. Manager/maintainer of the Elgg community site, and core Elgg development team member.


Walk To Mordor v2.2.7

1 min read

Just released an update fixing a number of small bugs and laying the groundwork for a few new features:

  • Better unit switching UI
  • use correct distance units in homepage graph
  • round homepage graph values
  • visible long notes for distance display
  • added padding on comment body
  • fixed inability to delete comments
  • fixed incorrect milestone date display
  • added manual sync button to settings
  • Add friends profile page for seeing friend progress

As of today - 20 years of Oilers fandom

3 min read

I came to Canada in 1992, too young and new to the country to know/care about hockey. The oilers failed to make the playoffs from then until 1997.
I was 15, life was shitty (really shitty), but the Oilers made the playoffs and everyone was going crazy about it so I watched.
The oilers were the 7th seed, playing the 2nd place Dallas Stars. This was literally the first time I paid any attention to hockey outside of NHL '94 for the Sega Genesis. I barely knew the rules, but I was a bandwagon fan because of the excitement of the city and my friends. I watched every game, getting more and more excited each time we took the series lead, and getting deflated when the Stars tied it up. With the Oilers unexpectedly up 3 games to 2 in overtime of game 6 the Dallas Stars boogeyman Mike Modano again tied the series forcing it to the epic game 7.
I watched game 7 in the basement of my moms house, with my brothers, on an old crappy tv (CRT right?). Expectations were high. Even though it was only the first round, it was my first playoffs, and I was 6 games invested already. It might as well have been for the cup.
Then Dallas took the lead less than 2 minutes in... and the oilers responded quickly. Then came the Andy Moog show. The Oilers had tons of great chances were denied by some unbelievable saves. The pace was high, full on back and forth hockey, but the Stars were edging out (and mostly out-playing the oilers for a while) and earned a 3-2 lead. In the final minute of the game Todd Marchant gets a 2-on-1, passes to Andre Kovalenko who buries it and ties the game with 19 seconds left. Oilers were about to be eliminated, then a miracle goal, and we're still in it.
Overtime was tense, incredibly fast paced. Dallas was swarming and Curtis Joseph was making all kinds of ridiculous saves to keep the Oilers in it. Then he made the save of his career:
Less than a minute later Todd Marchant gets a partial breakaway and buries the goal that eliminates the Stars, gets Oilers to the second round and cements me as a life-long fan.
Of course it got a little depressing after that for a while, but I was hooked.  This is a good time to be celebrating 20 years.  As of right now we're 2-0 up on Anaheim in the second round and confidence has never been higher.

Error: Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.

3 min read

This error has plagued me for a week now.  Everything was working fine until Android Studio decided to prompt me for updates which I did.  Then suddenly I can't build any android apks with cordova.  The frustrating thing is the wording of this error is so common for misconfigurations that trying to track down a solution has been an exercise of pure frustration.

After much digging around and trial and error what appears to have happened is the latest build of Android Studio has changed the file structure of how it's storing the sdk such that cordova can't find all of the required files even if the $ANDROID_HOME system variable is correct which makes the error message even more difficult as it points to an incorrect root cause of the error.

So for anyone else running into this - I completely nuked Android Studio.  I was only using it for the sdk manager anyway.  As far as I can tell Android Studio in it's current format won't work with cordova cli.

Delete the entire contents of ~/Library/Android/sdk

I downloaded the command line tools package from here: (as per the page:  Note that this location is new since the latest release and as of this writing many search results for command line tools installation are out of date.


Unzip the file and copy the resulting tools directory to ~/Library/Android/sdk/tools

Open up the sdk manager with

~/Library/Android/sdk/tools/android sdk

With the sdk manager I installed:

API 25

- SDK Platform

- Android TV Intel x86 Atom System Image

- Google APIs ARM 64 v8a System Image

- Google APIs ARM EABI v7a System Image

- Google APIs Intel x86 Atom_64 System Image

- Sources for Android SDK

API 22

- SDK Platform

- Sources for Android SDK

API 21

- SDK Platform

- Sources for Android SDK


After attempting a new build it looks like it got further but I was greeted with an error of: Error: Please install Android target: "android-23".

So I additionally installed

API 23

- SDK Platform


Next build - new error:


FAILURE: Build failed with an exception.


* What went wrong:

A problem occurred configuring root project 'android'.

> Could not resolve all dependencies for configuration ':_armv7DebugCompile'.


Followed by a list of cordova plugins that had errored out on the build.  I took the armv7 part of that as a hint and installed:

API 23

- Google APIs ARM EABI v7a System Image


When that didn't work, I tried to be smarter and searched for solution first - stackoverflow had at least one resolved hit:

So I installed:



- Android Support Repository

- Google Play Services

- Google Repository


Finally I got a build to work.  I have yet to test the resulting apk, but I'm assuming it's good.


Ionic 2 + Electron

5 min read

On a whim (as almost all my self-enforced learning curves tend to be) I decided to try getting Ionic 2 running inside of Electron.  I’ve never used Electron before, but had previously read the docs and had a vague understanding of it.  That said, I did the easy thing and googled for any ready-made solutions.  Mostly what I got were threads of people trying to get it working and either failing and giving up, or never posting again.  The few examples I found where people did get it working no longer worked at the time of this writing (Ionic 2.2.0).

After some trial and error with different suggestions cobbled together from different sources I finally got the Ionic 2 tutorial project running in Electron!  It complained about the lack of cordova.js in the console log, but that’s not technically required for the tutorial… a minor inconvenience for now.


Ionic tutorial running in electron


Note, I apologize, but I didn’t keep track of each reference/site I found nuggets of info on to get me to this point.  Just be aware that most of this was figured out by people smarter than me and I just managed to aggregate the right things in the right order to get somewhere.  Sorry if you were my source and you’re upset about not being mentioned…


Anyway, getting this far gave me hope, so I copied my considerably larger and more complicated project and got to work.

In the project root create a file "electron.copy.js"

// this is a custom dictionary to make it easy to extend/override
// provide a name for an entry, it can be anything such as 'copyAssets' or 'copyFonts'
// then provide an object with a `src` array of globs and a `dest` string

module.exports = {
  copyAssets: {
    src: ['{{SRC}}/assets/**/*'],
    dest: '{{WWW}}/assets'
  copyIndexContent: {
    src: ['{{SRC}}/index.html', '{{SRC}}/manifest.json'],
    dest: '{{WWW}}'
  copyFonts: {
    src: ['{{ROOT}}/node_modules/ionicons/dist/fonts/**/*', '{{ROOT}}/node_modules/ionic-angular/fonts/**/*'],
    dest: '{{WWW}}/assets/fonts'
  copyPolyfills: {
    src: ['{{ROOT}}/node_modules/ionic-angular/polyfills/polyfills.js'],
    dest: '{{BUILD}}'
  copySwToolbox: {
    src: ['{{ROOT}}/node_modules/sw-toolbox/sw-toolbox.js'],
    dest: '{{BUILD}}'



In the project root create a file "main.js"

const electron = require('electron');
// Module to control application life.
const app =;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const path = require('path');
const url = require('url');

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
    pathname: path.join(__dirname, "www/index.html"),
    protocol: "file:",
    slashes: true

  // Open the DevTools.

  // Emitted when the window is closed.
  mainWindow.on("closed", function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", createWindow);

// Quit when all windows are closed.
app.on("window-all-closed", function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== "darwin") {

app.on("activate", function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.


Edit package.json and add to dev dependencies:   

"electron": "^1.6.2"


  Edit package.json and change the build script

"scripts": {
    "start": "electron .",
    "clean": "ionic-app-scripts clean",
    "build": "ionic-app-scripts build",
    "electron": "ionic-app-scripts build --copy electron.copy.js",
    "ionic:build": "ionic-app-scripts build",
    "ionic:serve": "ionic-app-scripts serve"

Edit package.json and add the main.js

"main": "main.js"

Install Electron and dependencies

$ npm install


Build the project

$ npm run electron

Start the application

$ npm start


and we have a working application!  I clicked around and tested a few things.  It appears to be working, making calls to the external API and getting correct responses.  Cool!



Still some things to figure out, like whether I should get cordova loaded or not, whether it will export as an app nicely, whether there is a good way to do this that can keep the codebase the same and still build for mobile/web, whether I can make this work with a browser production build… those are less interesting details though and can wait for another sleepless learning curve night.  I'll update this post if I find better ways of doing things with this.


[edit 1] - simplified issue with polyfills copying

[edit 2] - created electron-specific build command

[edit 3] - added browser platform and attempted to use browser prod build in ionic - seems to work, but my app needs work with the browser cordova implementation so not sure where issues from that vs electron start/end.

Anyway for that to work I created

ionic build browser --prod
rm -rf www/*
cp -r platforms/browser/www/* www/
npm start

This uses the browser prod build in electron, does so by building for browser prod, then copying the prod files into the local www folder before starting electron.


wget all files from a remote directory

1 min read

Because I'll probably need this again and it's a useful snippet

wget -r -nH -np --cut-dirs=2 -R index.html* http:‌//

This will recursively download all files in the target directory, keeping the existing file structure.

--cut-dirs=2 is the number of directories from the hostname to exclude when building the directory structure.


Therefore this will create:










Fuck insurance, the whole concept can go to hell

3 min read

I cracked my phone last night, the layer under the screen so it's not even usable with a cracked screen.  It's just blank.  "Luckily" my cell phone plan comes with "insurance" so I can get it replaced even in case of an accident.  Great, problem solved right?

Fuck no.

So after bouncing around through the maze of Rogers support I finally get a guy who knows about this who then tells me I have to make a claim through some other website.  He sends me the info - and I start filling out these webforms.

Finally it gets to the shipping and I'm presented with a checkout screen for $134 to cover a "Replacement Service Fee".

What the fuck is a phone "Replacement Service Fee"?  Isn't that just paying for a new phone?  What the hell good is this insurance?

So I get Rogers back on the line to make sure there's no mistake - and there isn't, it's essentially the bullshit insurance "deductible".  Right, fuck you and your deductible.  Nobody has yet to provide me with a compelling explanation of the utility of a deductible anyway.  I'm paying you monthly for the service of covering me if shit goes wrong, then when shit goes wrong I have to pay more anyway?  Fuck off.

Let's look at replacement value - to Amazon!


Oh, cool, so I can get a brand new exact model replacement for $240.  So the deductible is 50%?  But wait, what are the odds that insurance is going to replace with brand new?  I don't think so, the phone I broke wasn't brand new.  Let's look used:

$215 for a "very good" condition replacement phone, which is all I would expect some dickface insurance agent to approve.  So now the "I swear it's a 'deductible' not a slimy cash-grab" is up to 56%.  I sure as hell hope our other insurances are this good.  If my house burns down it'll damn cool to paypal some asshole  $200k for a  "House Replacement fee" to file our god damn paperwork.

Here's how insurance should work:

Matt: Hi Mr. Insurance agent.  I have this thing I'd like to insure.

Insurance asshole: Sure, it's valued at $x so we could cover that for $y/month

Matt: great.



Matt: Hey Mr. Insurance agent - shit went south with thing

Insurance asshole: Sorry to hear that Mr. Beckett, here's a replacement thing

/ End transaction


In summary.  Fuck insurance, the entire concept, and this implementation specifically.  Insurance is perhaps one of the biggest problems of the modern era, possibly worse than "alternative medicine" and anti-vaxxers.


Vagrant performance updates

1 min read

I've been using Vagrant/VirtualBox for a while now but I've found as I add more projects the performance is taking a huge hit. I finally investigated and found that the majority of the issue comes from the shared folder configuration. It's much more efficient to use NFS mounted shared folder instead.

In order to do this we need to set up a private network: "private_network", ip: ""

Then we need to set the shared folder to use NFS

config.vm.synced_folder ".", "/vagrant", nfs: true

(note that the owner/group permissions settings don't work with NFS, hopefully that doesn't become a problem...)

I've also bumped the RAM available to the vm to 4G

config.vm.provider "virtualbox" do |vb|
        vb.memory = 4096

Making these changes has taken a particularly heavy elgg pageload (with no caching) I get the following improvement


TTFB: 14.68 seconds

DOM Ready: 1.8 minutes

Complete: 3.6 minutes



TTFB: 1.08 seconds

DOM Ready: 7.70 seconds

Complete: 15.82 seconds


Still slower than I would like, but I'll take 15 seconds over 3 and a half minutes :)


Day 6, Elfie is still missing - this is unheard of

1 min read

We assumed that Elfie was visiting Santa for an extra day - lobbying since the kids are so bad, but 2 days in a row?  That's unheard of... something is wrong.  The kids are upset and we're concerned.  Going to call the police for a missing elf report soon if this continues.


Day 5, where's Elfie?

1 min read

Woke up today and Elfie is nowhere to be found.  The best hypothesis we can come up with is that the kids were bad yesterday and Elfie is at the north pole discussing with Santa.  This is only a hypothesis though, I sure hope he's ok... the kids are a bit upset.


Updating to MariaDB 10 on CentOS VM

1 min read

One of the projects I work with requires fulltext indexing in innodb, and this is supported in MariaDB 10.  My standard dev VM is running an older version though, so here's how to upgrade it.

Remove the old version

sudo yum remove mariadb-server mariadb-libs

If you don't already have the MariaDB repo configured, you'll need to do that now.

Create the file /etc/yum.repos.d/MariaDB.repo with this content

# MariaDB 10.1 CentOS repository list - created 2015-11-13 19:53 UTC
name = MariaDB
baseurl =

Now install the new version of MariaDB

sudo yum install MariaDB-server MariaDB-client

At this point we have the newest version of MariaDB installed, however the previous uninstallation of the old version also nuked the php mysql drivers.

We can get that back...

sudo yum erase php56w-mysql
sudo yum install php56w-mysqlnd

Reload vagrant and we're back in business!