Should I build a PHPMailer video training course?

I spend a lot of time working on PHPMailer, and even more answering questions about it on Stack Overflow, yet gain very little from doing so. There is a certain amount of professional pride and reputation involved, but that doesn’t pay the bills. I am lucky enough to have a few GitHub and Patreon sponsors (thank you!), but that doesn’t amount to much. The same questions come up again and again, and repeating the same answers over and over, to people who are convinced that they must be the very first person ever to have had trouble sending email through GoDaddy, gets dull and tiring. I also run Smartmessages.net (a privacy-first email marketing service), so I am continuously exposed to large-scale email issues that small senders never have to worry about, but that are near-impossible to find advice about.

Email is a funny thing; it’s been around forever and is absolutely taken for granted, but it’s horribly complicated (way more so than, say, HTTP), and many developers, especially new ones, have absolutely no idea how it works – hence the repeated mistakes. Email grows more complex by the year, with the addition of things like SPF, DKIM, DMARC, ARC, MTA-STS and more. Some of it is really quite tricky yet the resources available for it either suck or are decades old. I’ve previously pitched book proposals to popular publishers (including O’Reilly and Packt) on “Modern email”, but have always been rejected on the basis that it’s not of interest or “outdated”, even though email usage is forever growing and changing.

So I’ve been thinking about putting together a training video course on using PHPMailer, diagnosing related problems, and using email in general. Such courses are apparently very popular, though I have to admit to not using them much myself, preferring text. I’ve been heartened by the success of projects such as Christoph Rumpel’s Laravel Core Adventures, along with his valuable advice on how he built that. I’ve been impressed by the lovely Jeff Geerling‘s dedication to his ansible book and audience, and have contemplated going the self-publishing route, but a book is a really big commitment, especially on such an enormous subject as email. Video courses are a bit more flexible, though keeping them up to date has its own issues, as Jeffrey Way has said about his amazing output on Laracasts.

The big issue is my audience. I know that it’s going to be primarily junior and novice developers with little experience needing help putting together their first contact form, so it needs to be pitched quite low, at least to start with. I gather that the primary feeder for video courses is social media, but my social media profile (principally Twitter; being a privacy advocate means I really don’t want to go anywhere near Facebook) is mainly more experienced developers, who are probably not the ones who need this kind of help, though you never know – it may just be my impostor syndrome assuming that all these clever people already know about this stuff!

In summary: there is fertile ground for such content, there’s little competition, I’ve got all the experience, equipment, and position needed to do it, but I need some help and encouragement with putting together a platform and reaching my audience. Finally, since I really need to dogfood this sort of thing, please sign up to my PHPMailer mailing list!

Minimal jQuery and extensions in Laravel

I recently needed to add a simple tag editor to a form input. I initially thought that Vue might help with this. That was a big mistake! Vue is seemingly pretty hopeless for this kind of thing – you can’t really use it for “sprinkling” JS elements, and progressive enhancement is a complete non-starter; that’s really more the domain of old-school jQuery, though I hear good things about alpine.js for this kind of enhancement. I learned a lot about Vue that I hadn’t planned on doing, but the overall impression was that it was simply the wrong tool for the job. I am not interested in building an SPA, and I’m certainly not out to rebuild my entire site just for sake of a single widget! My analogy for Laravel and Vue is like oil and vinegar in a salad dressing; they will happily coexist in the same bottle and taste great together, but they really like to be separate.

So, back to jQuery I go. jQuery was disabled by default in Laravel 6, but it’s just commented out in resources/js/bootstrap.js. It’s already included in the npm packages.json config file, so running npm install loads it into node_modules. I was really unclear about how additional JS modules should be added to Laravel using Mix. This was also a bit confusing because there isn’t really much you can point at and say “that’s mix”; it really comes down to what you put in webpack.mix.js in the project root and the npm run devscript that builds assets. However, the main thing that this does is load app.js, but then I found that things like this should be added in bootstrap.js (which is loaded by app.js) rather than adding them to the the mix config.

So the aim of the exercise was to add a tagging UI widget that takes a standard text input full of comma-delimited terms, and turns it into a pretty clickable tag element. Out of many worthy options, I settled on Tagify. After adding it with npm i @yaireo/tagify --save I had trouble figuring out where to actually load it from, but eventually I got it working by adding:

require('@yaireo/tagify/dist/jQuery.tagify.min.js');

to my bootstrap.js file, and evidently this looks in node_modules/to find the file without any additional help. After this I created a script in public/js/editthing.js (because inline scripts are worth avoiding if you’re serious about your CSP) that turned the standard input into a tag widget:

$('#tags').tagify();

and this script was loaded from blade using Laravel’s asset helper, which creates appropriate base URLs for public resources:

<script src="{{ asset('js/editthing.js') }}"></script>

I sometimes find Laravel docs and articles infuriating – there is often both too much and too little information at the same time, or they say things like “just add it to mix”. Many articles on simple subjects like this subvert Laravel’s features to shortcut to “working” (but ultimately counterproductive) solutions, and I wanted to approach this “the Laravel way”. For that reason I thought I’d make detailed notes for these very simple steps that I couldn’t find written down together elsewhere!

HTH

Wifi fails with Ubiquiti, again

My in-laws have a large house and wanted to extend wifi coverage. Since its initial teething problems, my Ubiquiti AP AC Lite has mostly been working well (it’s hard to tell, since it works, but UniFi will no longer adopt or connect to it!), so I thought I’d give Ubiquiti a shot for sorting out this location too. They have an old Orange ADSL box made by Thomson dating from about 2008, so I figured just about anything would be an upgrade. My intention was to replace their existing wifi entirely and turn off the built-in wifi on the Orange box, much as I have done with my own setup, and hope that a better access point would provide sufficient coverage. For the access point I specced a UniFi AP AC LR “long range” model. Because I really don’t like the way that the Ubiquiti UniFi control software works when installed locally (and would be beyond my in-laws’ technical abilities to set up), I thought I’d also get a Cloud Key so that they didn’t need to worry about that, and that I could potentially use for remote access to their setup.

The kit is nicely packaged, but didn’t include any ethernet cabling nor a (1A, USB-C) power supply for the Cloud Key, so I popped out to get the necessary bits. First of all I wanted to set up the Cloud Key. The cloud key comes with a small printed setup guide, but the images on it are way too small to read – I found an online PDF version, but it turns out that even blown up the images are unreadable, but I could get a vague idea of what pages I was supposed to see. I inserted its SD card, plugged it into the Orange box via ethernet and its power supply and it started flashing. I then set about connecting to it from an old PC running Windows 7 and latest Chrome. After creating a Ubiquiti cloud account, the docs said I should be prompted to install the Ubiquiti Discovery Tool. This prompt did not happen. After some searching I found a link to it on the Chrome extension store and installed it manually – though it took me a while to realise that it is a standalone application and not a Chrome extension. At this point I was supposed to be shown the results of a network scan which would show me the Cloud Key and a “discover cloud key” switch. No such switch was shown in the scan page, but clicking a button labelled “Ubiquiti family” made the switch appear, but the search results remained resolutely empty. Searching through some forum posts suggested that this is a common problem, so I did a hard reset on the Cloud Key, and it finally appeared in the scan results with a “Pending” status beside it. Docs and forums said this should show an option to “adopt” it, but nothing happened after leaving it for a while. I realised I could connect to the CK directly via its own HTTP interface, but this was via its IP address using https and so inevitably required disabling security checks and ignoring multiple warnings in Chrome before I could get to it, but I successfully logged in using the default credentials.

Next I was shown a warning saying “SD card not installed” – when it was installed. I took it out and reinserted it a couple of times, restarted the Cloud Key again, but it didn’t come back up, so I pulled the power and tried again and it came back, and the card warning had gone away (though it didn’t show any status about its presence).

I know that Ubiquiti issues fairly regular firmware updates, so the first thing I did was check for updates, and sure enough, a Cloud Key update was available. Before doing that it complained that the Cloud Key had no backup – but the interface has no means of performing a backup, only a restore! I proceeded without. As well as the CK firmware, there was also a UniFi update available, so I updated that too. That installed smoothly, and restarted, but I was somewhat surprised to find that another upgrade was then available to both CK and UniFi, from 5.10 -> 5.12.66 -> 5.13.29 (with a reboot between each), and then no more updates were offered. I don’t know why this was not done in a single step. Again, no backup option was visible, but it still complained I didn’t have one.

Back in the Discovery tool I still could not see the CK. Forums reported more success using the UniFi iOS app, so I gave that a try, and found it, and adoption worked! After this had worked, it finally showed up in the discovery tool – so I gather the discovery tool’s discovery tools are no use for actually discovering things… However, if I tried to connect to it from the mobile app, I just received a cryptic error saying “api.env.cloudaccessenabled”, and it refused to go any further. Forums suggested a full factory reset, so I tried that, but it still didn’t work on the phone. The Discovery tool however was now showing the CK and also a demo account (which I deleted), so I could finally click the “Launch” button to start UniFi on the CK – only to be presented with a “Cannot check credentials” error. More forum searching and another hard reset later I was finally able to launch it and get into UniFi running on the CK. At this point I was offered yet another update to UniFi, this time to 5.13.32!

After about 5 hours of going round in circles, I could finally set up the access point (connected by ethernet to the same switch as the CK). It wanted its own firmware update, so I applied that. This part went relatively smoothly, adopting successfully in UniFi, though as I had found in my own setup, the AP sometimes drops off UniFi and has to be reconnected. I was able to do a network scan (which showed no contention on any channels – houses are far apart here and only 1 other network was visible), set up my SSID and password. I then reconnected to the new wifi network from my MacBook, and all seemed to work. Now to check the range…

The access point and Orange box are in a big room just next a courtyard; the aim was to get wifi coverage on the other side of the courtyard, about 15m away through one stone wall (very old, so no steel in it). The Orange box is under a desk behind another (steel) desk, and I had temporarily hung up the new AP on the wall, so the new AP had the advantage of slightly better placement and fewer obstructions. The Orange wifi dropped out at about 12m and the new AP’s network… did the same. Nowhere could I find a point at which I could remain connected to the new access point and not the old one, despite the “long range” designation, and the age of the old box. I also tried using my iPhone and a recent Android phone and saw similar results on all.

It’s clear that this performance is nowhere near what was expected (or at least hoped for) or needed, and that more access points / mesh antennas would be required to get this to reach the places its wanted, however, at this point I’d entirely lost confidence in the Ubiquiti systems, and we’d spent €200 for wifi that was no better than what we already had, it was getting too expensive, so all this hardware is going back.

The cloud account setup is also confusing – the relationship between the Cloud Key, UniFi, and the Ubiquiti account is messy, and there was no way that my non-technical in-laws would have coped with anything going wrong with it, which seemed likely to happen. So much for their slogan: “At last, simple IT that just works”.

For now we’ve decided we’re just giving up, and we’ll just put up with the limited, but reliable, stock wifi.

Ski equipment I have killed

I ski a lot, very hard, very fast. Not surprisingly, this takes its toll on equipment, and I thought I’d make a list of my kit that has met an untimely end.

  • Graves slalom skis: severely bent about 30cm from the tip.
  • Dynastar Vertical bump skis: tore out both rear bindings; discovered at the very highest point of Val Thorens…
  • Dynastar X9 skis: delaminated base, tore out edge on a rock.
  • Dynafit 3F race boots: Smashed rear shock absorber mechanism, rendering them unusable.
  • Nordica 990 boots: red rear-entry monsters! Smashed the retention flange in an awkward landing; boot cuff could fold flat backwards!
  • Salomon 957 composite bindings: heel piece exploded into little bits while skiing bumps.
  • Volant Chubb Ti skis: broke off tip on one ski, delaminated whole of the other one.
  • K2 Apache Recon skis: Broke tip, pulled out rear binding. These were terrible skis.
  • Diamir Fritschi Freeride bindings: not so much a breakage as exposing a design flaw; never land a jump on a traverse: the toe retention is not good enough and the boot will twist out. A second problem is that the heel can release if a ski is flexed very hard, e.g. crossing a ditch; This flaw was fixed in later models.
  • Diamir Fritschi Freeride bindings: Sheared screw holding rear binding onto the main tube.
  • Rossignol Scratch BC skis: The first skis I got into “extreme carving” on. They couldn’t take it: I rotated the edges out of the ski structure on both skis.
  • Fischer RC4 FIS SL skis: rotated the edges out through the base.
  • Salomon 997 bindings: Barrel of toe piece split, provoking early release at higher DIN settings: cost me a broken thumb.
  • Volkl AC50 Unlimited skis: I loved these skis, but I rotated the edges out through the base.
  • Dynastar Cham 97 skis: rotated the edges out through the base (seeing a pattern here!)
  • Salomon Quest Pro 130 boots: split shell
  • Elan Amphibio 78Ti skis: broke off tip protector, delaminated tail on one ski

Any ski manufacturers that want to know how strong their kit is, do get in touch and I’ll try my best to break it for you.

Ski stuff that is still alive despite my best efforts

  • ICE lightning carbon ski poles: These are awesome. Despite being carbon, they’re not light, but they are very stiff and very strong. Bought in Vail in 1998, still dead straight after 500+ ski days.
  • I never managed to kill my Salomon SX92E ski boots, but I did try very hard.