Web Development

Create a dev copy of your WordPress site with WordOps

WordOps is a popular framework to setup and manage WordPress infrastructure, forked from EasyEngine in 2018 when EasyEngine v4 changed their architecture to use Docker containers.

This is a quick guide to setting up a development copy of your live WordPress site which can be useful if you’re working on a redesign or testing new themes.

If you’re not familiar with the WordPress CLI (WP), it does some magic behind the scenes to extract the database connection details from wp-config.php, so as long as we’re in the directory of a WordPress installation it will figure out what to do.

1. First, we’ll create the new site ( as a WordPress site with Let’s Encrypt enabled

sudo wo site create --wp --le

2. Next, we’ll cleanup the default content on the newly created dev site and copy the files from the production site

sudo rm -rf /var/www/*
cd /var/www/
sudo cp -a * /var/www/

3. Now we can use the WP CLI to export the database from the production site and import it to the dev site.

sudo -u www-data -H wp db export ~/ --yes
cd /var/www/
sudo -u www-data -H wp db clean --yes
sudo -u www-data -H wp db import ~/ --yes
rm ~/

4. Finally use WP CLI to update the WordPress URLs on the dev site

sudo -u www-data wp option update home ''
sudo -u www-data wp option update siteurl ''

The dev copy of your site should now be available. Depending on the nature of the site it could be sensible to password protect the site or restrict traffic to the dev site by IP address.

Restricting access by IP address

Open the nginx configuration for the new site:

sudo wo site edit

Then before the closing brace add:

allow <Your IP>;
deny all;

Save the changes to the file and check nginx reloads correctly, access to the site should now be restricted to your IP address.

Pushing changes back to production

You can promote your dev site to production by following the instructions the other way around.

Admin Tips

Configuring Atlassian Cloud Single Sign On for ADFS 3.0

Atlassian don’t officially support AD FS with Confluence Cloud – but it is working well now I’ve sorted out the issues I was having passing user’s email address through as the nameId claim. Hopefully these instructions can save you some trial and error.

Enable SAML on Atlassian Cloud

  1. First off – enable SAML on your Atlassian Cloud instance at https://<subdomain>
  2. The Identity Provider Entity ID can be found in the Federation Service Properties in ADFS – but typically will look like mine –
  3. Identity Provider SSO URL can be found in  AD FS Service > Endpoints – look for the SAML 2.0 type, but it should just be
  4. Open up your token signing certificate in AD FS, then select ‘Copy to file’ from the Details tab. Save with Base64 encoded as a txt file – then copy the contents into the Public x509 certificate field.
  5. Save configuration

Add Relying Party Trust wizard

  1. Add a Relying Party Trust to AD FS. On the welcome page select ‘Enter data about the relying party manually’
  2. Select a display name – i.e. Atlassian Confluence
  3. Use the AD FS profile (supports SAML 2.0)
  4. Leave the token encryption certificate blank
  5. Enable support for the SAML 2.0 WebSSO protocol – and enter the SP Assertion Consumer Service URL from the Atlassian Site Administration > SAML section. Currently this is:
  6. For the relying party trust identifier, enter the SP Entity ID – currently this is

    Please note, do not be tempted to add additional relying party trust identifiers (I had added some others in here which caused it not to work)

  7. Optionally configure multi factor authentication settings

Configure the claim rules

  1. First create a rule to send attributes from Active Directory to Atlassian Cloud. I think the only mandatory claim is the email address.
  2. Next, add a second rule to Transform an incoming claim
    (this is another step I hadn’t figured out the first time I tried to configure SAML – without this step it seems like ADFS doesn’t use the right format for the outgoing name ID).

Test it out

I haven’t got Identity Provider initiated sign on working yet (via the /adfs/ls/idpinitiatedsignon.aspx) – but if you use a RelayState URL – and then put this in your corporate bookmarks etc it should work nicely (replace the <yoursubdomain> part<yoursubdomain>

Aeotec Micro Smart Switch & Home Assistant

Several weeks ago my long awaited Z-Wave module for the Pine64 arrived. I managed to get open-zwave  installed on Debian on the Pi, found it was on /dev/ttyS2, but this week ran into trouble setting up my first Z-Wave device, the Aeotec Micro Switch.

Tailing the OZW_Log.txt I could see it get added to the network OK, but it would immediately show as dead. I found some advice online suggesting the security key needed to be added as per the Home-Assistant Adding secure devices tutorial. This did help, but it still wasn’t working right.

Eventually tried pointing the config_path to the openzwave config path – and suddenly things came to life and has been working well since.

  usb_path: /dev/ttyS2
  config_path: /srv/hass/src/python-openzwave/openzwave/config

One Z-Wave parameter on the Aeotec did surprise me – it doesn’t default to reporting to the controller if the switch state has been changed locally (i.e. someone flicks the switch); so the controller doesn’t find out until it next polls.

I called the set_zwave_parameter service on Home Assistant to send a basic report when the state changed, default is 0, 1 sends a message to say something has changed, 2 sends a report showing what has changed.

Enable to send notifications to associated devices (Group 1) when the state of Micro Switch’s load changed (0=nothing, 1=hail CC, 2=basic CC report).
  "entity_id": "switch.ensuite_towel_rail_switch_5",
  "parameter": "80",
  "length": "1",
  "value": "2"

Flight review: Air NZ Auckland to Ho Chi Minh City NZ269

First up had to check we were on the right flight, SGN sounded suspiciously like Singapore, before I clicked – we’re going to Saigon.

The plane: Boeing 767-300. Two seats on the sides, three across the middle. Interior looks 90s but all clean and tidy.

The seat: About row 30, middle, isle. A colleague had suggested we book the two outer seats of a middle row, leaving the middle seat empty. When we arrived on the plane a man is sitting in the middle seat, but thankfully the plan worked and he was just trying to get a row to himself and moved back to his allocated seat. I was quite comfortable, but my colleague had the passenger in front put his seat right back after takeoff.

How full: At least 3/4 full

The food: Pleasantly surprised. I had the slow cooked beef with rice and there was also a chicken option. Can’t remember dinner but dessert was Kapati chocolate icecream.

The entertainment: Standard Air NZ touch screen entertainment system. Good selection of TV and movies – I caught up on Portlandia, New Girl, Big Bang Theory, and enjoyed Hunt for the Wilderpeople. It was a shame to only have two episodes of The Night Manager.

The facilities: Pillow and blanket provided for the flight. I was hoping for some socks or a hot towel, but it isn’t Singapore Airlines. Restrooms were a little difficult to hold the tap down while trying to wash your hands.

The service: Excellent cabin crew, who we were lucky enough to have again on NZ268 back to Auckland two days later. Flight was delayed leaving by half an hour due to late inbound connecting flights (I was on one). It is quite a long flight at 12.5 hours, but you’re rewarded with a 10.5 hour trip home.

The bottom line: Being able to fly (almost) direct to Ho Chi Minh was the difference between going or not, for our two night stay. The flight times were very convenient, leaving around lunchtime, then arriving ~8pm local time; and the return flight leaving at 10pm (good flight to sleep on). I enjoyed this brief trip to Saigon and I’m sure will be back one day!

Next time I might consider paying extra for the fast track visa on arrival service at SGN, as it wasn’t great waiting around for an hour for a Visa stamp.


Enhancing Qlik Sense tables with Emojis

I’m a newcomer to Qlik, and am working on implementing Qlik Sense for a hospital. One area I was considering today was how to better communicate to users when there might be something wrong or they need to be aware of with the data they are looking at.

Our data model has quality checks as part of the data load scripts, and was reporting these to various exception flags. Maybe in QlikView this might work because the experience can be more tightly controlled; you could set up a Data Quality tab, add in all of the various exception flags from around the data model and then filter each of the exceptions. But I don’t want to rely on having to do that, let alone if other staff are building any of their own apps.

So I searched for a better solution, for about an hour. My first thought was ‘could I setup an Exception table’, which could have different numbers and exception reasons linked. To be effective though one would somehow have to then have an Exception link table and insert a row dynamically linking back to any rows in any tables which had data quality issues. I’d still like to figure this one out, but this is not a job for today.

Next I considered using bitmask status codes.. you might have come across these before if you’ve ever had to change settings in the registry. That way I could at least use one column in each table as an Exception column, and be able to indicate a number of different exceptions all in the same field. But because of the coding you can still separate it out later (i.e. from a data quality page) to find out what exceptions each row had. Also not a job for today, and I don’t think our Qlik partners would appreciate trying to debug that!

Finally I was going to try appending a written exception into a single column, but again I don’t have enough Qlikfu to figure that one out.

So I reverted to prioritising the exceptions in an if statement, and just setting ‘Exception’ to be whatever reason is matched first.

Unfortunately none of the above solve my original question of how do we warn users about this datum. (I mention all these because I’d love to know what solutions people have come up with, maybe someone out there sees this and things ‘oh I can explain how to do that in a forum post!’)

Next part of the challenge – how do indicate this to the user. My current text exception column is taking up precious screen real-estate and ‘Ex Procedure Has Exception = 1’ doesn’t mean much anyway.

So I thought I would try out using Emojis in Qlik. Couldn’t find anything online except someone having issues with Emojis in source data freezing Qlik

Went to looking for a Warning⚠️ symbol (which looks nice and Yellow and sufficiently warning in Firefox) and pasted this into a string in Qlik Sense. To my surprise it pasted into the editor pane and showed up among the Console font text (I thought at least would have to do some special Unicode /x01513/ string or something to make it work). Thankfully, it survived a Load Data and made it through to the data Table I was working on:

Qlik Sense Emoji Warning/Exception Reporting

In this first screenshot I’ve set the Procedure name up to append a warning Emoji if the procedure has exceptions. I’ll come back to the ‘ApplyMap’ bit later, but either of the lines below would achieve the same result.

Load *,
     // Append warning emoji to procedure name if has exceptions
     [Procedure Name] & If([Procedure Exceptions],ApplyMap('Emoji','warning'),'') As [Procedure]

Unfortunately we can’t set the hover text on the emoji to show what the issue is, so we’ll still need a Warnings tab of some sorts.

(Emoji’s are actually just rendered as text, not images, so there isn’t even an image there to attach hover text to).

Next up the stock order table, wanted to show a quick visual way whether the order was closed or not.

Sure we could use conditional formatting, but that relies on the user configuring it for each table they are building (unless there is some Qlikfu to ‘tag’ a column for conditional formatting, hint hint). Using this method if they add the Closed flag they can see it is closed.

In this instance I set it up as a Dual to retain the 0 or 1 of the underlying data for counting purposes. i.e. running a count on the screenshot below would give you 6.

If(closed, Dual('✔️',1), Dual('✖️',0)) AS [Order Closed],

Qlik Sense Emoji Order Status

Annd you can even sort by or filter on our little picture friends:

Filtering by Emoji in Qlik Sense

They also work in KPIs (to show up nice and big), export to Excel and PDF OK (but don’t look as pretty).

Please note they will look bland on Internet Explorer but should show up nicely in Firefox or Chrome, so you need to test from the devices of your users.

I hope this has given you some ideas on how you might be able to improve communication when you’re stuck using a Table for some reason!

Here is a Mapping to get you started, copy and paste into Data Load script, then select which Emoji you want using ApplyMap.

Of course you can just copy and paste from the Unicode or Getemoji website as well, but this way gives some more control if you want to change things up centrally down the track.

Mapping LOAD * Inline [

Would love to know of other ways you’re doing exception reporting or interesting uses of Qlik Sense!


BLF lights not working after switching Asterisk to version 11

Found a strange issue after switching to Asterisk v11, the BLF buttons on our Panasonic SIP phones stopped working.

Eventually thought it was worth trying setting the ‘presence server address’ which previously hadn’t been set (and in Endpoint Manager is set to blank) to the PBX and the lights immediately started working. Much simpler solution than I anticipated.


New UFB ISP option for Greymouth business customers

Today ISP DTS announced availability of their UFB services in Greymouth, providing a compelling option for businesses considering a UFB upgrade. Being a business only ISP you can expect a higher level of service and support than the small business plans offered by other providers (Spark, Snap/2degrees).

Their pricing comes in very competitively at $119/month for 100GB or $149 for unlimited, on a 100/100Mbps connection, and can also provide 200/200Mbps for a little more.

DTS have nearly completed (at the time of writing 1 region to go) a nationwide deployment allowing them to provide UFB services in each area.

I’m not affiliated with DTS in any way, but hope to see a great uptake of UFB in Greymouth and the regions. UFB means New Zealanders (well, those who can get it) have huge opportunities to compete on the global level with digital exports. It means you no longer need your own dedicated phone system or servers, leave that to the experts and focus on more important things.


Factory reset Panasonic KX-UT12x

If you can’t access the web interface to reset a Panasonic SIP phone

  1. Press the Setting softkey
  2. Dial #136
  3. Press Enter, then the down arrow (to select Yes) then Enter/li>

The phone will shortly reboot.

Admin Tips Work

802.1X authentication woes with NPS & EAP

Had a frustrating issue with some UniFi APs where clients were not able to authenticate to the Pro models, but OK to the standard UniFis.

Running a packet capture on the NPS server I could see many Access-Requests arriving at the server with an Access-Challenge immediately being sent back, but the AP would just keep sending the same request and the server was neither Rejecting or Allowing the connection.

If you’re having similar sounding issues, try adding a ‘Framed-MTU’ attribute to the Network Policy settings.

The MS article recommends to use a Framed-MTU of 1344, but ended up settling on 1400. We did had Jumbo frames enabled on the server running NPS role which I think may have been contributing to the problem. Hope this can help someone out!

Framed-MTU Setting



Admin Tips

KB: User’s print jobs showing as coming from another domain user

We’ve just had a strange problem where print jobs for one of our users were printing out and showing up on the printer as coming from a different username.

Normally, probably wouldn’t matter too much, but they use PaperCut account selection – meaning the popups to select the printer account were displaying on the other users’ screen.

After checking the event logs we noticed Explorer and Spoolsv were connecting to the print server as a different user’s account.

I remembered recently coming across the Windows Credential Manager – so opened up Credential Manager and sure enough, there was a saved network credential for this network server.

Deleting the credentials then restarting the computer has thankfully resolved the issue. First time I’ve run into this problem!