<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Stapps.io]]></title><description><![CDATA[Stapps.io]]></description><link>https://blog.stapps.io/</link><generator>Ghost 0.11</generator><lastBuildDate>Wed, 31 Dec 2025 17:46:03 GMT</lastBuildDate><atom:link href="https://blog.stapps.io/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[KDE connect from Ubuntu to Pixel phone]]></title><description><![CDATA[<p>For this I'm using Ubuntu 24.04 and a Pixel 8a but any similar ubuntu version and android phone should work the same. </p>

<p>On desktop you can install KDE connect, or in my case as I use gnome I installed <a href="https://extensions.gnome.org/extension/1319/gsconnect/">GSConnect</a> which adds to the ubuntu gnome top bar nicely.</p>]]></description><link>https://blog.stapps.io/kde-connect-from-ubuntu-to-pixel-phone/</link><guid isPermaLink="false">ce81c662-334a-4f2f-9d1e-2bddf4dd1e77</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Tue, 15 Jul 2025 12:19:57 GMT</pubDate><content:encoded><![CDATA[<p>For this I'm using Ubuntu 24.04 and a Pixel 8a but any similar ubuntu version and android phone should work the same. </p>

<p>On desktop you can install KDE connect, or in my case as I use gnome I installed <a href="https://extensions.gnome.org/extension/1319/gsconnect/">GSConnect</a> which adds to the ubuntu gnome top bar nicely. </p>

<p>I had to add a couple firewall rules to allow kde connect protocol through with <code>ufw</code>:  </p>

<pre><code class="language-bash">sudo ufw status  # first checking it's running  
sudo ufw allow 1716:1764/tcp  
sudo ufw allow 1716:1764/udp  
</code></pre>

<hr>

<p>On my pixel phone I downloaded <a href="https://play.google.com/store/apps/details?id=org.kde.kdeconnect_tp">KDE connect</a> </p>

<p>From my desktop > top right dropdown > a new GSConnect option showed and I was able to connect to my pixel. <br>
This then notified my phone and I accepted the connection. </p>

<p>Inside the app on my phone, under some of the default controls are text links to enable additional permissions for things like sms etc. where I enabled the ones I wanted to get SMS access and notifications as that's my primary use, as well as occasional clipboard sharing.</p>

<hr>

<p>Desktop menu: <br>
<img src="https://blog.stapps.io/content/images/2025/07/Screenshot-from-2025-07-15-13-16-33.png" alt=""></p>

<p>Phone app:  </p>

<div style="max-width: 300px; margin: 0 auto;">  
<img src="https://blog.stapps.io/content/images/2025/07/Screenshot_20250715-131906.png" alt="">
</div>]]></content:encoded></item><item><title><![CDATA[Google photos backup with rclone]]></title><description><![CDATA[<p>I've been using Google Photos to store photos from my phone for over 10 years and it works great to have them automatically sync when I set up a new phone. </p>

<p>But, I'm paranoid and I want another backup just in case somehow I loose access to the account through</p>]]></description><link>https://blog.stapps.io/google-photos-backup-with-rclone/</link><guid isPermaLink="false">dc384241-c44b-41b7-9596-2e29e2ba6f44</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Tue, 17 Dec 2024 19:13:35 GMT</pubDate><content:encoded><![CDATA[<p>I've been using Google Photos to store photos from my phone for over 10 years and it works great to have them automatically sync when I set up a new phone. </p>

<p>But, I'm paranoid and I want another backup just in case somehow I loose access to the account through a hack or some other issue. </p>

<p>An easy backup solution for this on linux/ubuntu is with <a href="https://rclone.org/">rclone</a>.</p>

<p>Install with:  </p>

<pre><code class="language-bash">sudo apt install rclone  
</code></pre>

<p>Once installed, we can configure it via:  </p>

<pre><code class="language-bash">rclone config  
</code></pre>

<p>This will ask you questions about the setup. <br>
Choose "n" for a new remote. <br>
Select Google Photos as the cloud service. <br>
Follow the instructions to authenticate with your Google account. <br>
It'll ask about setting a client_id, though possible to use the defaults and leave this blank, it's better to use your own for both a quota view but also potentially for privacy.</p>

<p>rclone has docs on creating a client to follow with <br>
<a href="https://rclone.org/drive/#making-your-own-client-id">https://rclone.org/drive/#making-your-own-client-id</a> <br>
which you'll setup with <a href="https://console.cloud.google.com/apis/api/">https://console.cloud.google.com/apis/api/</a></p>

<p>I've created the following script <code>backup-google-photos.sh</code> which then automates backing up a few key folders and all photos split out by year &amp; month for a little organisation, I've saved this in <code>~/Pictures/GooglePhotosBackup/</code>:</p>

<pre><code class="language-bash">#!/bin/bash

echo "Backing up Google Photos!"

echo "=&gt; removing previous log file"  
rm ~/Pictures/google-photos-backup.log

# backup the smaller album, feature and shared-album folders first
echo "=&gt; backup albums:"  
rclone copy googlephotos:album ~/Pictures/GooglePhotosBackup/album --progress --transfers 8 --bwlimit 50M --log-file ~/Pictures/google-photos-backup.log --log-level INFO  
echo  
echo "=&gt; backup featured:"  
rclone copy googlephotos:feature ~/Pictures/GooglePhotosBackup/feature --progress --transfers 8 --bwlimit 50M --log-file ~/Pictures/google-photos-backup.log --log-level INFO  
echo  
echo "=&gt; backup shared-albums:"  
rclone copy googlephotos:shared-album ~/Pictures/GooglePhotosBackup/shared-album --progress --transfers 8 --bwlimit 50M --log-file ~/Pictures/google-photos-backup.log --log-level INFO  
echo

# then in media, it contains sub folders for all, by-day, by-month and by-year
# photos repeat in these so rather than download 4 copies of each image/video
# i'm currently just backing up the by month ones so we have a little folder structure
echo "=&gt; backup all photos by month:"  
rclone copy googlephotos:media/by-month ~/Pictures/GooglePhotosBackup/media/by-month --progress --transfers 8 --bwlimit 50M --log-file ~/Pictures/google-photos-backup.log --log-level INFO  
echo

echo "Complete! View ~/Pictures/google-photos-backup.log for full log detail."  
</code></pre>

<p>This downloads to a directory <br>
Adjust the paths along with the 8 transfers at once limit and bwlimit. Also it logs to a file so you can review what happened later if needed.</p>

<p>With this, we can add run/execute permissions with:  </p>

<pre><code class="language-bash">chmod +x ./backup-google-photos.sh  
</code></pre>

<p>and then going forward we can either run this once a week or so manually with:  </p>

<pre><code class="language-bash">./backup-google-photos.sh
</code></pre>

<p>Or setup a cron to run it if you know your machine will generally be running around a specific time to do the backup.  </p>

<pre><code class="language-bash">crontab -e  
</code></pre>

<p>add a line to run this, e.g. Fridays at 2pm  </p>

<pre><code class="language-bash">0 14 * * 5 ~/Pictures/GooglePhotosBackup/backup-google-photos.sh  
</code></pre>

<p>You may from time to time need to refresh the access token. <br>
To do this you can run:  </p>

<pre><code class="language-bash">rclone config reconnect googlephotos: --auto-confirm  
</code></pre>]]></content:encoded></item><item><title><![CDATA[Email when someone ssh's into a server]]></title><description><![CDATA[<p>Save the following file to <code>/etc/profile.d/ssh-alerts.sh</code> on your server. <br>
<em>Update your@email.here with your email</em>.</p>

<pre><code class="language-bash"># email on ssh login
if [ -n "$SSH_CLIENT" ]; then  
    TEXT="$(date): ssh login to ${USER}@$(hostname -f)"
    TEXT="$TEXT from $(echo $SSH_CLIENT | awk '{print $1}')"
    echo $TEXT</code></pre>]]></description><link>https://blog.stapps.io/email-when-someone-sshs-into-a-server/</link><guid isPermaLink="false">2265fa56-eb44-4a48-8135-824fb02f0fbc</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Mon, 23 Sep 2024 17:12:19 GMT</pubDate><content:encoded><![CDATA[<p>Save the following file to <code>/etc/profile.d/ssh-alerts.sh</code> on your server. <br>
<em>Update your@email.here with your email</em>.</p>

<pre><code class="language-bash"># email on ssh login
if [ -n "$SSH_CLIENT" ]; then  
    TEXT="$(date): ssh login to ${USER}@$(hostname -f)"
    TEXT="$TEXT from $(echo $SSH_CLIENT | awk '{print $1}')"
    echo $TEXT | mail -s "ssh login" your@email.here
fi  
</code></pre>

<p>Now on yours, and any other user ssh, you'll get an email (assuming you have email setup already with mailutils etc)</p>

<p>The email will look like this, showing the user, which server and their IP <em>(where I've changed the IP to 1.2.3.4 and hostname of the server to my-server-name for the purposes of this screenshot)</em>:</p>

<p><img src="https://blog.stapps.io/content/images/2024/09/Screenshot-from-2024-09-23-18-14-52.png" alt=""></p>]]></content:encoded></item><item><title><![CDATA[Postman desktop app ubuntu 24.04 quick install]]></title><description><![CDATA[<p>Download from <a href="https://www.postman.com/downloads/">https://www.postman.com/downloads/</a> for linux x64.</p>

<p>Extract in your downloads folder and open up the terminal and <code>cd</code> over to the newly extracted folder. </p>

<pre><code class="language-bash">cd ~/Downloads/postman-linux-x64  
</code></pre>

<p>Move it to be available system-wide:  </p>

<pre><code class="language-bash">sudo mv Postman /opt/postman  
</code></pre>

<p>Make the <code>postman</code> command available:  </p>

<pre><code class="language-bash">sudo ln -s</code></pre>]]></description><link>https://blog.stapps.io/postman-ubuntu-24-04-install/</link><guid isPermaLink="false">ae492511-5da4-47f3-90d5-3527ea4939c4</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Mon, 23 Sep 2024 00:18:54 GMT</pubDate><content:encoded><![CDATA[<p>Download from <a href="https://www.postman.com/downloads/">https://www.postman.com/downloads/</a> for linux x64.</p>

<p>Extract in your downloads folder and open up the terminal and <code>cd</code> over to the newly extracted folder. </p>

<pre><code class="language-bash">cd ~/Downloads/postman-linux-x64  
</code></pre>

<p>Move it to be available system-wide:  </p>

<pre><code class="language-bash">sudo mv Postman /opt/postman  
</code></pre>

<p>Make the <code>postman</code> command available:  </p>

<pre><code class="language-bash">sudo ln -s /opt/postman/Postman /usr/bin/postman  
</code></pre>

<p>Add to applications menu:  </p>

<pre><code class="language-bash">sudo vim /usr/share/applications/postman.desktop  
</code></pre>

<p>Add the following:  </p>

<pre><code>[Desktop Entry]
Name=Postman  
Exec=/usr/bin/postman  
Icon=/opt/postman/app/resources/app/assets/icon.png  
Type=Application  
Categories=Development;  
Terminal=false  
</code></pre>

<p>Test &amp; you're done.</p>]]></content:encoded></item><item><title><![CDATA[Quick local mysql access without password entry]]></title><description><![CDATA[<p>Using <code>mysql_config_editor</code> which stores a <code>~/.mylogin.cnf</code> file we can have easy access to mysql as a privileged user from the terminal without needing to enter your password nor needing to use sudo. </p>

<h3 id="step1createtheuser">Step 1. Create the user</h3>

<pre><code class="language-sql">CREATE USER 'USERNAMEHERE'@'localhost' IDENTIFIED BY 'PASSWORDHERE';  
</code></pre>

<h3 id="step2privilegesegthiswouldgiveyoueverything">Step 2. Privileges,</h3>]]></description><link>https://blog.stapps.io/quick-local-mysql-access/</link><guid isPermaLink="false">4eafc9d6-e6a2-4806-9de2-442a91799662</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Sun, 22 Sep 2024 19:23:54 GMT</pubDate><content:encoded><![CDATA[<p>Using <code>mysql_config_editor</code> which stores a <code>~/.mylogin.cnf</code> file we can have easy access to mysql as a privileged user from the terminal without needing to enter your password nor needing to use sudo. </p>

<h3 id="step1createtheuser">Step 1. Create the user</h3>

<pre><code class="language-sql">CREATE USER 'USERNAMEHERE'@'localhost' IDENTIFIED BY 'PASSWORDHERE';  
</code></pre>

<h3 id="step2privilegesegthiswouldgiveyoueverything">Step 2. Privileges, e.g. this would give you everything:</h3>

<pre><code class="language-sql">GRANT ALL PRIVILEGES ON *.* TO 'USERNAMEHERE'@'localhost' WITH GRANT OPTION;  
FLUSH PRIVILEGES  
</code></pre>

<h3 id="step3usetheconfigeditortogenerateyourfile">Step 3. Use the config editor to generate your file.</h3>

<pre><code class="language-bash">mysql_config_editor set --user=USERNAMEHERE --password  
# it'll then ask for your password once
</code></pre>

<h3 id="step4profit">Step 4. Profit.</h3>

<pre><code class="language-bash">mysql  
</code></pre>

<p>^-- you should now be able to access your local mysql with a single command and no password entry needed.</p>

<p>You can also run <code>mysql_config_editor</code> with a <code>--login-path=NAME</code> flag to create multiple of these, then when starting mysql you can add that flag again to login to them. </p>

<p>More info about this command here: <br>
<a href="https://www.prisma.io/dataguide/mysql/tools/mysql-config-editor">https://www.prisma.io/dataguide/mysql/tools/mysql-config-editor</a></p>]]></content:encoded></item><item><title><![CDATA[Apache config errors when configtest shows no errors]]></title><description><![CDATA[<p>Recently I ran into an issue where a configtest with <code>apachectrl -t</code> reported <code>Syntax Ok</code>, reloaded apache and everything went down. <br>
It turned out to be an issue where the SSL cert for one of the sites was invalid. <br>
For context, I'd made a fix to the config around the</p>]]></description><link>https://blog.stapps.io/apache-config-errors-when-configtest-shows-no-errors/</link><guid isPermaLink="false">7ee0a70c-22ee-43bb-bc8e-119fcc24a401</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Tue, 26 Mar 2024 23:15:07 GMT</pubDate><content:encoded><![CDATA[<p>Recently I ran into an issue where a configtest with <code>apachectrl -t</code> reported <code>Syntax Ok</code>, reloaded apache and everything went down. <br>
It turned out to be an issue where the SSL cert for one of the sites was invalid. <br>
For context, I'd made a fix to the config around the use of a bundle and a crt with key (which meant a duplicate crt in the chain as the bundle would have it already, still works but best to fix) vs just using the bundle an key, but in doing so exposed an issue with an old ssl crt file. <br>
The reload command initially didn't show an issue but I spotted the sites were down, along with a flood of alerts. </p>

<p>The main apache error.log file only reported there was a config issue, each site/vhost have their own files in our set up so grep to the rescue to track which was reporting new errors in their error logs. <br>
I spotted one specific site having issues each time I restarted apache to bring it back up after the change: <br>
<code>"AH02565: Certificate and private key site.com:443:0 from /path/to/ssl.bundle and /path/to/ssl.key do not match"</code>
<em>That'd do it.</em></p>

<p>It seems that the old config had hidden this as it would work but was slightly misconfigured to duplicate the crt in the chain. The site was also behind Cloudflare so it's hard to know if that further hid the issue. </p>

<p>Like many sites we use automatic renewals, but it transpired this certificate's dates were several years old from a prior server migration and the cert hadn't been set up for the new server to auto renew. <br>
As they had cloudflare in front, the site was serving fine over HTTPS and apache didn't seem to mind with no errors in the log either. </p>

<p>Backups are always key and I was able to bring sites back initially in minutes by rolling back the config files, giving me time to debug what had happened. </p>

<p>It's a tricky issue to prevent, the config change had been tested on several sites before it was rolled out to all. The backups worked well and the per site/vhost error logging was also useful for debugging here. <br>
We have automated checks that the ssl certs for sites are valid but I plan to rework this to also check the raw files on the server itself and ensure the 2 files do match up. </p>

<p>Ultimately I'm writing this up to help my future self and anyone else that may run into a similar issue. <br>
If apache's configtest (-t) reports that the files are ok but then apache fails to restart due to a config issue, a likely answer or at least something to check is the certs. Of course, always have backups before ready to roll the config changes back, check error logs and check the certs using openssl to verify they are correct or force renewing ones that are reporting as erroring in the error log. <br>
Hope this helps someone else as well. </p>]]></content:encoded></item><item><title><![CDATA[Easy PHP version switching on Ubuntu (allows for running multiple versions at the same time)]]></title><description><![CDATA[<p>This article assumes you're using PHP FPM, we'll also show you how to switch PHP versions on the command line at the bottom as well. </p>

<p>You'll need to install the different PHP versions and make sure modules and config matches up. <br>
If you already have php7.4 installed and you're</p>]]></description><link>https://blog.stapps.io/easy-php-version-switching-on-ubuntu/</link><guid isPermaLink="false">a1b72215-48f1-46f6-b391-83406253b011</guid><category><![CDATA[php]]></category><category><![CDATA[tips]]></category><category><![CDATA[server]]></category><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Mon, 18 Mar 2024 23:57:53 GMT</pubDate><content:encoded><![CDATA[<p>This article assumes you're using PHP FPM, we'll also show you how to switch PHP versions on the command line at the bottom as well. </p>

<p>You'll need to install the different PHP versions and make sure modules and config matches up. <br>
If you already have php7.4 installed and you're looking to install 8.3 you could add 8.3 in while leaving 7.4 by installing it by version number with your OS package manager, e.g. with <code>apt</code> on Ubuntu:  </p>

<pre><code class="language-bash">sudo apt install php8.3-{cli,fpm}  
</code></pre>

<p>However, before installing the new version, you'll need to make sure your PHP pools don't reference a central/single sock file as installing the new fpm may auto switch it to the new version. E.g.  </p>

<pre><code class="language-bash">sudo vim /etc/php/7.4/fpm/pool.d/www.conf  
</code></pre>

<p>Check if it references <code>/var/run/php/php-fpm.sock</code> and update it to the version it's on like <code>/var/run/php/php7.4-fpm.sock</code>. <br>
You'll then also need to make this update in your nginx or apache file and reload php-fpm first, then nginx or apache. </p>

<p>Once you've installed your new PHP versions, check both php fpm versions are running with:  </p>

<pre><code class="language-bash">systemctl status php7.4-fpm  
systemctl status php8.3-fpm  
</code></pre>

<p>To help compare config files you could use a tool like <code>meld</code>, <code>vimdiff</code> or <code>diff</code>, e.g.:  </p>

<pre><code class="language-bash">sudo meld /etc/php/7.4/cli/php.ini /etc/php/8.3/cli/php.ini  
</code></pre>

<p>Make sure to compare both the <code>cli</code> and <code>fpm</code> config files.</p>

<p>Also to help compare PHP modules you can use <code>meld</code>, <code>vimdiff</code> or <code>diff</code> again like so:  </p>

<pre><code class="language-bash">meld &lt;(php7.4 -m) &lt;(php8.3 -m)  
</code></pre>

<p>Then install missing ones, for example if you spot <code>gd</code>, <code>gmp</code>, <code>intl</code>, <code>xml</code>, <code>curl</code> and <code>bcmath</code> missing you could install them with:  </p>

<pre><code class="language-bash">sudo apt install php8.3-{gd,gmp,intl,xml,curl,bcmath}  
</code></pre>

<p>Then compare the modules again.</p>

<p>Using PHP FPM you can switch PHP version easily by switching which <code>/var/run/php/php7.4-fpm.sock</code> (this may also be <code>/run/php/php7.4-fpm.sock</code>) you reference in either the Apache or Nginx config (or whichever system you use to host. </p>

<p>E.g. in Nginx it's this line:  </p>

<pre><code class="language-bash">fastcgi_pass unix:/var/run/php7.4-fpm.sock  
</code></pre>

<p>Or Apache:  </p>

<pre><code class="language-bash">SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost"  
</code></pre>

<p>Your line here may be a little different, the above assumes it's the default version number based sock files but there are alternatives. <br>
Here's 3 additional examples you may find:</p>

<ul>
<li><p>A single <code>/var/run/php-fpm.sock</code> instead of specific version.
If that's the case, it will likely be that your php fpm pool for that version points to that single sock file. Your pool will be configured by a file in <code>/etc/php/VERSION/fpm/pool.d/ e.g. /etc/php/7.4/fpm/pool.d/www.conf</code> <br>
You can either, leave the current one pointing here and make sure your new PHP version has the specific version number in the sock file name and update the reference to it in the nginx/apache config. Or, you could swap which php version uses that sock file by editing the current one to have the version in the file name and the new one to this main sock file. Then restart both versions of PHP FPM. </p></li>
<li><p>Multiple pools, one per site.
If you have multiple sites on the server you may have created a php pool for each of them to isolate them. If so, you'll need to copy over these configs to the new version (or at least the ones you want on the new version, maybe test with one first) so if it was <code>/etc/php/7.4/fpm/pool.d/site.com.conf</code> and you're switching to php8.3 then copy to <code>/etc/php/8.3/fpm/pool.d/site.com.conf</code> <br>
<code>SetHandler "proxy:unix:/run/php/php7.4-fpm-site.com.sock|fcgi://localhost"</code> and update the version number in the sock file if it had one, or add one in so it doesn't clash with the other.</p></li>
<li><p>Listing to a port instead of a sock file.
<code>
listen = 127.0.0.1:7401
</code>
In this example you have ports instead of files but they can be updated mostly the same as the sock files. You'll just need to remember or note down which ports relate to which versions &amp; update your nginx/apache configs as needed.</p></li>
</ul>

<p>Remember to reload nginx/apache after editing the configs and if you updated your php-fpm configs or pools, reload php-fpm for the versions you updated.</p>

<p>Then for the CLI you can switch with:  </p>

<pre><code class="language-bash">sudo update-alternatives --set php /usr/bin/php8.3  
</code></pre>

<p>You may also want to switch the php version <code>phar</code> files use as well, you can do this with by running both the following commands:  </p>

<pre><code class="language-bash">sudo update-alternatives --set phar /usr/bin/phar8.3  
sudo update-alternatives --set phar.phar /usr/bin/phar.phar8.3  
</code></pre>

<p>To make this easier, I added a bash function so I can call <code>php-version 7.4</code> or <code>php-version 8.3</code> to switch between them, and call <code>php-version</code> to have it list the php-versions I have available:  </p>

<pre><code class="language-bash"># switch php version for CLI
function php-version {  
    if [[ "$1" == "" ]]; then
        echo "Available versions:"
        currentVersion=$(php -v | grep -oP '(?&lt;=PHP )\d+.\d+')
        availableVersions=$(ls /usr/bin/php* | grep -oP '(?&lt;=/php)(\d+\.\d+)')
        echo $availableVersions | sed "s/$currentVersion/$currentVersion *current/"
        echo "Set default for cli with: php-version $currentVersion"
    else
        echo "Setting default php cli version to $1"
        sudo update-alternatives --set php /usr/bin/php$1
        sudo update-alternatives --set phar /usr/bin/phar$1
        sudo update-alternatives --set phar.phar /usr/bin/phar.phar$1
        echo "php -v:"
        php -v
    fi
}
</code></pre>

<p>Side note, you can also use <code>sudo update-alternatives --config php</code> to have it interactively show you php versions available and switch that way. </p>]]></content:encoded></item><item><title><![CDATA[PHP heredocs could be better]]></title><description><![CDATA[<p><em>... but they can do weird stuff if you really want them to.</em></p>

<p><a href="https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc">PHP heredocs</a> aren't something I think most PHP devs (including myself) use day to day. <br>
But did you know they have a secret power in editors like VSCode? <strong>Sub syntax highlighting.</strong></p>

<p>Say you write some SQL for a</p>]]></description><link>https://blog.stapps.io/php-heredocs-could-be-better/</link><guid isPermaLink="false">ba72e246-029a-4cc3-90d0-18a98b583a00</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Wed, 15 Mar 2023 22:05:56 GMT</pubDate><content:encoded><![CDATA[<p><em>... but they can do weird stuff if you really want them to.</em></p>

<p><a href="https://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc">PHP heredocs</a> aren't something I think most PHP devs (including myself) use day to day. <br>
But did you know they have a secret power in editors like VSCode? <strong>Sub syntax highlighting.</strong></p>

<p>Say you write some SQL for a report, rather than wrapping it in single or double quotes, try with heredocs with the identifier named SQL and you'll get SQL syntax highlighting inside:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-sql-heredoc.png" alt=""></p>

<p>The same is true for HTML:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-html-heredoc.png" alt=""></p>

<p>Variable interpolation works here too:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-unescaped-heredoc.png" alt=""></p>

<p>The above example isn't secure though, it'd be open to an XSS attack. But as we can have variables reference anonymous functions, we can do things like:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-escaped-heredoc-2.png" alt=""></p>

<p><strong><em>Falling further down the rabbit hole.</em></strong></p>

<p>We can't put foreach loops in for example, nor functions directly like array<em>map but like the above example referencing the example escape attr method, we can have a variable which calls both array</em>map and implode like so:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-map-heredoc.png" alt=""></p>

<p>If you have Collections or similar you can do this without the helper:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-collection-map-heredoc.png" alt=""></p>

<p>Heck, if you want you can throw another Heredoc inside it! <br>
<em>Though at least at the point of writing, this breaks the syntax highlighting in VSCode.</em></p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-heredoc-inside-heredoc.png" alt=""></p>

<p><strong><em>The descent into madness begins...</em></strong></p>

<p>At this point, you might ask, what else can we hack together here? </p>

<p>If statements? One way could be to have a function again which takes in a condition, true case and false case. But a simpler option is the ternary operator:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-ternary-heredoc.png" alt=""></p>

<p>Switch (or rather, match statements), just like above:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-match-heredoc.png" alt=""></p>

<p>And if you want something especially weird, there's a <a href="https://tpunt.github.io/jekyll/update/2016/01/25/php-parser-hack.html">parser hack</a> that will let you run essentially any PHP on the right side of this assignment like so:</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-parser-hack-heredoc.png" alt="">
<em><a href="https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation">Though this will trigger a deprecated warning in php-8.2+</a></em></p>

<p>Try any of these examples locally or over on <a href="https://onlinephp.io/">https://onlinephp.io/</a></p>

<h3 id="theycouldbebetter">They could be better</h3>

<p>I'm not suggesting you use these in your next project. Simple heredoc usage is great but these examples can be solved in much better ways such as the use of abstracted template files for rendering. </p>

<p>However, it'd be interesting for simple use cases if you could call functions or php directly without these hacks in heredocs. </p>

<p>There's a couple RFC's that attempted to bring this to PHP: <br>
<a href="https://wiki.php.net/rfc/arbitrary_expression_interpolation">PHP RFC: Arbitrary Expression Interpolation</a> <br>
<a href="https://wiki.php.net/rfc/arbitrary_string_interpolation">PHP RFC: Arbitrary string interpolation</a> <br>
Both appear to be withdrawn and had their drawbacks but maybe there's still chance we could get something similar in the future. </p>

<p>As PHP has <a href="https://wiki.php.net/rfc/deprecate_dollar_brace_string_interpolation">deprecated ${} string interpolation</a> maybe this syntax could be brought back but to allow calling PHP directly. Maybe to limit it's impact it only affects heredocs? <br>
I don't think the presence of this feature would cause developers to no longer use better solutions when needed, but it would be handy for quick things where abstracting to a template is unnecessary.</p>

<p>Here's an example class which tracks logs and needs a simple way to render them as  a list. I've added multiple <code>renderLogs()</code> methods showing a few possible solutions and well as the <code>#{...}</code> style for the possible proposed solution.</p>

<p><img src="https://blog.stapps.io/content/images/2023/03/php-solutions-heredoc-2.png" alt=""></p>

<p>This isn't a perfect example, other examples could be where you want to render a total of 2 values and you don't want to break up the string just to add 2 numbers together. It doesn't have to be HTML of course either, maybe it's outputting to the terminal or sending an SMS, where adding template files perhaps wouldn't make as much sense. </p>

<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">Template Literals</a> for example allow for this in Javascript. <a href="http://ruby-for-beginners.rubymonstas.org/bonus/string_interpolation.html">String interpolation in Ruby</a> also allows for it, and they too use the <code>#{...}</code> style solution and similar solutions exist in other languages. </p>

<p>You can definitely have a view on your preferred method here, for many cases my go to would be either the <code>foreach</code> method or a <code>view()</code> style template function for now. <br>
At the least I hope this helps demonstrate how we could interact with such a solution if it were to be added in the future. </p>

<p>Thank you for reading.</p>]]></content:encoded></item><item><title><![CDATA[Quick and easy local ssl/https with mkcert on ubuntu]]></title><description><![CDATA[<p>One time install of <a href="https://github.com/FiloSottile/mkcert">mkcert</a> with:  </p>

<pre><code class="language-bash">sudo apt install mkcert libnss3-tools  
mkcert -install  
</code></pre>

<p>Then set up your SSL cert for the needed domains, e.g. here I'm setting up <code>localhost</code> &amp; <code>mytestsite.localhost</code> and i'm saving them to <code>/home/andrew/bin/mkcert/</code> but change this to wherever you'd like the</p>]]></description><link>https://blog.stapps.io/quick-and-easy-local-ssl-https-with-mkcert-on-ubuntu/</link><guid isPermaLink="false">d4676e61-e954-4f2b-870e-e0c45338e092</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Thu, 07 Jul 2022 10:26:18 GMT</pubDate><content:encoded><![CDATA[<p>One time install of <a href="https://github.com/FiloSottile/mkcert">mkcert</a> with:  </p>

<pre><code class="language-bash">sudo apt install mkcert libnss3-tools  
mkcert -install  
</code></pre>

<p>Then set up your SSL cert for the needed domains, e.g. here I'm setting up <code>localhost</code> &amp; <code>mytestsite.localhost</code> and i'm saving them to <code>/home/andrew/bin/mkcert/</code> but change this to wherever you'd like the certs to save to:</p>

<pre><code class="language-bash">mkcert -cert-file /home/andrew/bin/mkcert/localhost-cert.pem -key-file /home/andrew/bin/mkcert/localhost-cert-key.pem localhost mytestsite.localhost 127.0.0.1 ::1  
</code></pre>

<p>Simple and done.</p>

<p>For configuring your local apache, nginx or caddy etc. use the <a href="https://ssl-config.mozilla.org/">Mozilla SSL Configuration Generator</a>.</p>]]></content:encoded></item><item><title><![CDATA[Upgrading Ubuntu servers from 16.04 to 20.04 on Digital Ocean (includes handling floating IP)]]></title><description><![CDATA[<p>Recently I upgraded a bunch of our app servers that were on old 16.04 and other similar sub 20.04 Ubuntu versions. <br>
<em>(Why 20.04 vs 21.04? because every 2 years Ubuntu releases an LTS release for long term support, 20.04 is the latest LTS)</em></p>

<p>While upgrading,</p>]]></description><link>https://blog.stapps.io/upgrading-ubuntu-servers-from-16-04-to-20-04-on-digital-ocean-includes-handling-floating-ip/</link><guid isPermaLink="false">b7f744f0-116c-4a41-852f-9b89683ea102</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Sat, 10 Apr 2021 10:49:19 GMT</pubDate><content:encoded><![CDATA[<p>Recently I upgraded a bunch of our app servers that were on old 16.04 and other similar sub 20.04 Ubuntu versions. <br>
<em>(Why 20.04 vs 21.04? because every 2 years Ubuntu releases an LTS release for long term support, 20.04 is the latest LTS)</em></p>

<p>While upgrading, half of them had no issues at all, while the other half ran into networking issues, bad times but this article will help you resolve those. </p>

<p>Talking to Digital Ocean support about this, its not an uncommon issue with larger upgrades like these. They mentioned their recommendation is that for anyone that can, is to install a new server and transfer over the files instead. If that's an option for you, it's a good one, but in my case that wasn't as simple an option.</p>

<p>In this post I'll run through my tips for doing each server upgrade from 16.04 to 18.04, and then to 20.04, along with a few common issues noticed along the way to help if you run into them too. </p>

<h3 id="firststartwithbackupsnapshot">First, start with backup / snapshot.</h3>

<p>Digital Ocean provide a great way to take live snapshots of the servers, but the preferable way if you're able to is to turn off the server and snapshot it before continuing. For several of the servers this process took less than half an hour, all down to the size of the server, and as I ran into Network issues later, the snapshot proved especially useful as I restored back to it while I debugged the issue further. </p>

<h3 id="updatesupgrades">Updates &amp; upgrades</h3>

<p>You'll want to make sure your packages on the server are working and up to date before you continue. <br>
Pretty standard but run and check the output of both to make sure everything's ok:  </p>

<pre><code class="language-bash">sudo apt update &amp;&amp; sudo apt upgrade  
</code></pre>

<p>If you notice there's any issues like apt repos now 404'ing etc, make a note of them as you can either see if they can be fixed now, but as you're upgrading from <a href="https://wiki.ubuntu.com/Releases">EOL</a> you may find some repos are no longer available on your current version and you'll need to come back to these after the upgrade. </p>

<h3 id="runthereleaseupgrade">Run the release upgrade</h3>

<p>Before starting the next upgrade process, you'll want to open port 1022 ready. This is a port the release upgrade process opens for ssh in case there's an issue. <br>
If you're using <code>ufw</code> you can run:  </p>

<pre><code class="language-bash">sudo ufw allow 1022/tcp  
</code></pre>

<p>(Check if you're running ufw with <code>sudo ufw status</code>)</p>

<p>Now you're ready to begin the upgrade, simply start it with the following command and follow the on screen instructions:  </p>

<pre><code class="language-bash">sudo do-release-upgrade  
</code></pre>

<p>My advice during this is answer Y (yes) to the questions about starting and automated restarts, but answer D (diff) or N (no) to the config changes. <br>
Especially if you know you've made custom changes around files like your /etc/sysctl.conf or your /etc/nginx/nginx.conf file etc. then you'll not want to have to re-do them, but on some files it's worth checking the differences to see what's changed. </p>

<p>The nice thing to remember here though is that if you don't upgrade the config files, it'll save the new default one to the same location but with a <code>.dpkg-dist</code> extension. <br>
e.g. /etc/nginx/nginx.conf.dpkg-dist so you can use this to compare your custom version to the new default they'd have provided later. </p>

<p>The same goes if you went the other way and said yes to changing the files, you'll have <code>.dpkg-old</code> file versions available.</p>

<p>Lastly, if needed you have your backup / snapshot to go back to as well if ever needed. </p>

<p>After finishing up the last stage of this command will be when it requests to restart your machine. Select yes and restart. </p>

<p>In all my upgrades, the 16.04 to 18.04 upgrades came back up fine, the only issues I ran into were during the 18.04 to 20.04 upgrade which was mainly network issues. <br>
However, if you do find the server doesn't come back online after a couple minutes, skip down to the <a href="https://blog.stapps.io/upgrading-ubuntu-servers-from-16-04-to-20-04-on-digital-ocean-includes-handling-floating-ip/#resolvingnetworkissues">Resolving Network Issues</a> section below. </p>

<h3 id="checksrepeat">Checks &amp; repeat</h3>

<p>After the 16.04 to 18.04 upgrade, you'll want to check everything is up and running well on your server before continuing to upgrade. </p>

<p>Check key services are up and running, view your sites on the frontend etc., check api monitoring etc. you might have.</p>

<p>IT's worth checking your apt repos here too, over in your <code>/etc/apt/sources.list</code> file and the files in your <code>/etc/apt/sources.list.d</code> folder, check for commented out repos from the upgrade and check if you want to bring them back and if so do so one at a time. Uncomment, <code>sudo apt update</code> to check it's working and <code>sudo apt upgrade</code> where needed, then repeat.</p>

<p>If everything's good, you can repeat the previous step again and continue on to upgrade from 18.04 to 20.04. </p>

<h3 id="resolvingnetworkissues">Resolving Network Issues</h3>

<p>On a bunch of the servers I upgraded, I ran into networking issues where I couldn't connect over ssh to it, and once inside couldn't ping out. <br>
<em>This happened mostly on the 16.04 servers but during their upgrade from 18.04 to 20.04.</em></p>

<p>Luckily Digital Ocean provide a cloud console you can use to directly access the server even when it's networking is down. (Top right of the droplet info screen)</p>

<p>The cloud console may take a little time to start up and what I ran into was that the networking service was causing startup to take around 2 minutes. <br>
Once in, I quickly found I needed a password as I've always just used ssh keys instead here so if you're like me you'll need to reset the root password which you can do via the servers info page under Access. <br>
That'll email you the new password, it's a long one and the issue I've run into is that you can't paste (at least I couldn't at the time of writing this) into the console so you'll have to type the password manually. <br>
Once you're in, if you did reset the password it'll ask for it again and to change it there, again that means typing out the long password but this'll be the last time. </p>

<p>The main source of the issue is due to Ubuntu switching to use netplan vs the previous networking service used in pre ubuntu 18.04. </p>

<p>View your current config with <code>sudo vim /etc/netplan/50-cloud-init.yaml</code> it should look something like this:  </p>

<pre><code>network:  
    ethernets:
        eth0:
            dhcp4: true
            match:
                macaddress: XX:XX:XX:XX:XX:XX
            set-name: eth0
    version: 2
</code></pre>

<p>Inside <code>eth0</code> you need to add 2 new properties, an array of ip addresses and the gateway to use. <br>
Edit the file and add in your servers IP and it's floating IP if you're using one like so:  </p>

<pre><code>network:  
    version: 2
    ethernets:
        eth0:
            addresses:
              - xxx.xx.xxx.xxx/16
              - xxx.xx.xxx.xxx/16
            gateway4: XXX.XX.XXX.X
            match:
                macaddress: XX:XX:XX:XX:XX:XX
            nameservers:
                addresses:
                - 67.207.67.2
                - 67.207.67.3
                search: []
            set-name: eth0
        eth1:
            addresses:
              - xxx.xx.xxx.xxx/16
            match:
                macaddress: XX:XX:XX:XX:XX:XX
            nameservers:
                addresses:
                - 67.207.67.3
                - 67.207.67.2
                search: []
            set-name: eth1
</code></pre>

<p>Your IP (and floating ip if in use) are available in your droplet info screen and your ip is also shown at the bottom of the cloud console screen too. <em>(Add /16 to the end of these)</em>.</p>

<p>Get the macaddress's for eth0 and eth1 by running <code>ifconfig</code> (next to <code>ether</code> in the output blocks for each). </p>

<p>eth1 is the private network while eth0 is public. </p>

<p>Your gateway / gateway4 ip is also shown both in the droplet info screen and the bottom of the cloud console screen as well. <em>(No /16 needed here though!)</em></p>

<p>Now you'll need to apply the changes with netplan, run:  </p>

<pre><code class="language-bash">sudo netplan apply --debug  
</code></pre>

<p>With this, your main server can now reach and be reached by the outside world again through your droplets default ip but not the floating ip yet. </p>

<p>At this point, I opened an ssh connection to continue as then I can paste commands faster.</p>

<p>In order to fully hook up your floating ip, you need to add your servers <a href="https://docs.digitalocean.com/products/networking/floating-ips/how-to/find-anchor-ips/">"Anchor IP"</a>, to look this up run:  </p>

<pre><code class="language-bash">curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/address  
</code></pre>

<p>With this, go back and edit your <code>/etc/netplan/50-cloud-init.yaml</code> file again and add another address ip line in for this anchor ip (with /16 after it again). </p>

<p>Run netplan apply again and you should now be able to access your server via it's floating ip again.  </p>

<pre><code class="language-bash">sudo netplan apply  
</code></pre>

<p>Last part, after doing the above I found that after rebooting my changes to this file would be lost. So before rebooting, you'll want to copy this file to a couple locations to make sure it's always applied (once for netplan as a backup and another for cloud-init).</p>

<pre><code class="language-bash">sudo cp /etc/netplan/50-cloud-init.yaml /etc/netplan/01-netcfg.yaml  
sudo cp /etc/netplan/50-cloud-init.yaml /etc/cloud/cloud.cfg.d/50-curtin-networking.cfg  
</code></pre>

<p>Next disable the network conf changes in <code>cloud-init</code> by adding "{config: disabled}" into the following file:  </p>

<pre><code class="language-bash">sudo vim /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg  
</code></pre>

<p>Finally you can re-init cloud-init and netplan once more to be safe.  </p>

<pre><code class="language-bash">sudo cloud-init clean -r  
sudo netplan apply  
</code></pre>

<p>You should now be safe to reboot your machine, it can take a couple minutes still to come up due to cloud init waiting for old networking (working on a solution for this too but if you have a simple one please let me know) but it'll come back up fine and all connected to your droplets default ip and floating ip. </p>

<h3 id="phpversionissues">PHP version issues</h3>

<p>You may find after the upgrade that php's upgraded, e.g. if you've been running 7.2 with fpm, you might find that service is not running and instead 7.4 or 8.0 is running instead. <br>
Here what you can do is simply stop the version you don't want yet (if you're not ready to upgrade that yet) and then start the service you want.</p>

<p>E.g.  </p>

<pre><code class="language-bash">sudo systemctl stop php8.0-fpm  
sudo systemctl start php7.2-fpm  
</code></pre>

<p>You can then check it's status and logs with the following commands:  </p>

<pre><code class="language-bash">sudo systemctl status php7.2-fpm  
sudo journalctl -u php7.2-fpm  
less /var/log/php7.2-fpm.log  
</code></pre>

<p>If it's all working, you can disable the previous service all together, or even <a href="https://askubuntu.com/questions/816285/what-is-the-difference-between-systemctl-mask-and-systemctl-disable">mask</a> it to prevent it starting up in the future.  </p>

<pre><code class="language-bash">sudo systemctl disable php8.0-fpm  
sudo systemctl mask php8.0-fpm  
</code></pre>

<p>If you do mask the service you can unmask it at a later date if you want to enable it in the future. </p>

<p>you might also want to change your local CLI version of php, e.g. roll it back to use 7.2 as default.  </p>

<pre><code class="language-bash">sudo update-alternatives --set php /usr/bin/php7.2  
</code></pre>

<h3 id="resolvingmysqlissues">Resolving MySQL issues</h3>

<p>You might also run into some issues if your mysql version upgrades. <br>
On one of the servers I was upgrading that ran mysql it upgraded to v8 from 5.6. <br>
Surprisingly easy upgrade in the end but I did run into some issues start/restarting. </p>

<p>To debug I used 2 terminals, 1 to restart mysql and the other to watch the mysql error log where I'd watch to see error lines.  </p>

<pre><code class="language-bash">tail -f /var/log/mysql/error.log  
</code></pre>

<p>If mysql isn't coming back online you should see the error in the log output. <br>
It'll look something like this:  </p>

<pre><code>2021-04-07T17:52:31.945816Z 0 [ERROR] [MY-000067] [Server] unknown variable 'query-cache-size=0'.  
2021-04-07T17:52:31.946389Z 0 [ERROR] [MY-010119] [Server] Aborting  
</code></pre>

<p>The above was a real example from that server, in this case it turns out mysql v8 [removed a bunch of config params] (<a href="https://dev.mysql.com/doc/refman/8.0/en/added-deprecated-removed.html">https://dev.mysql.com/doc/refman/8.0/en/added-deprecated-removed.html</a>), query-cache-size included so the fix was to edit the mysql config file and comment out that line.  </p>

<pre><code class="language-bash">sudo vim /etc/mysql/my.cnf  
</code></pre>

<p>In this case the query cache has been <a href="https://mysqlserverteam.com/mysql-8-0-retiring-support-for-the-query-cache/">retired from mysql</a> so there's not a straight foward replacement for this config var. But in other cases it's worth exploring what the alternative is if a config variable has maybe been dropped in favour of another etc. Google is your friend here.</p>

<p>Another common connection issue can be from the sql_mode needing to be set to blank or tweaked. <br>
As well as another issue where you need to change the <code>default-authentication-plugin = mysql_native_password</code> to mimic how it used to run before. </p>

<h3 id="nodeversionissues">Node version issues</h3>

<p>Some of our apps on these app servers ran using older node versions and though we plan to upgrade those, while upgrading the server OS you might not want to handle that separate upgrade at the same time. </p>

<p>That's where <a href="https://github.com/nvm-sh/nvm">nvm</a> comes in, the node version manager. </p>

<p>To install nvm, run the following command:  </p>

<pre><code class="language-bash">curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash  
</code></pre>

<p>Then edit where your node service starts up, e.g. for me I use systemd service files for these. </p>

<p>E.g. inside your apps service file "/etc/systemd/system/app-name-here.service"</p>

<p>If previously you had a line like:  </p>

<pre><code>ExecStart=node /srv/app-name/index.js  
</code></pre>

<p>Update it to use the nvm init script and to run with your desired node version: (in this example I'm using an the latest node v6 release)  </p>

<pre><code>ExecStart=bash -c ". /home/andrew/.nvm/nvm.sh &amp;&amp; nvm run 6 /srv/app-name/index.js"  
</code></pre>

<hr>

<p>I hope this guide has helped you with your server upgrades, at the least it serves as a reminder to me of what I've done on mine but if you have any comments or questions let me know in the comments below. </p>

<p>Thanks for reading!</p>]]></content:encoded></item><item><title><![CDATA[How to Send and Receive Heart Internet Emails Through Gmail]]></title><description><![CDATA[<h3 id="howtoconnectyourheartinternetmailboxtogmail">How to Connect Your Heart Internet Mailbox to Gmail</h3>

<p>If you use email hosting with Heart Internet and prefer to manage your emails through Gmail, it’s easy to link the two. This guide will walk you through the steps to set up Gmail so you can send and receive</p>]]></description><link>https://blog.stapps.io/heart-internet-email-with-gmail/</link><guid isPermaLink="false">710cfb60-bda2-4546-8ac5-6c1c71ce6c3d</guid><category><![CDATA[email]]></category><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Tue, 05 May 2020 14:14:27 GMT</pubDate><content:encoded><![CDATA[<h3 id="howtoconnectyourheartinternetmailboxtogmail">How to Connect Your Heart Internet Mailbox to Gmail</h3>

<p>If you use email hosting with Heart Internet and prefer to manage your emails through Gmail, it’s easy to link the two. This guide will walk you through the steps to set up Gmail so you can send and receive emails using your custom domain address.</p>

<p>These steps are specific to Heart Internet, but the process will be similar for many other email providers.</p>

<hr>

<h4 id="step1setupincomingmailreceivingemails">Step 1: Set Up Incoming Mail (Receiving Emails)</h4>

<ol>
<li>Open Gmail and click the gear icon in the top-right corner.  </li>
<li>Select <strong>See all settings</strong>.  </li>
<li>Go to the <strong>Accounts and Import</strong> tab.  </li>
<li>Under <strong>Check mail from other accounts</strong>, click <strong>Add a mail account</strong> (or <strong>edit info</strong> next to an existing account).  </li>
<li>Enter your email address (e.g., yourname@yourdomain.co.uk) and click <strong>Next</strong>.  </li>
<li>Select <strong>Import emails from my other account (POP3)</strong> and click <strong>Next</strong>.  </li>
<li>Fill in the following details: <br>
<ul><li><strong>Username:</strong> Your full email address (e.g., yourname@yourdomain.co.uk)</li>
<li><strong>Password:</strong> Your email account password</li>
<li><strong>POP Server:</strong> mail.yourdomain.co.uk</li>
<li><strong>Port:</strong> 110</li></ul></li>
<li>Optional: Choose any of the additional settings (e.g., label incoming messages).  </li>
<li>Click <strong>Add Account</strong>. Gmail will test the connection and start importing your emails.</li>
</ol>

<hr>

<h4 id="step2setupoutgoingmailsendingemails">Step 2: Set Up Outgoing Mail (Sending Emails)</h4>

<ol>
<li>In the <strong>Accounts and Import</strong> tab, find <strong>Send mail as</strong> and click <strong>Add another email address</strong>.  </li>
<li>Enter your name and your email address (e.g., yourname@yourdomain.co.uk).  </li>
<li>Untick <strong>Treat as an alias</strong> unless you specifically need it.  </li>
<li>Fill in the following SMTP server settings: <br>
<ul><li><strong>SMTP Server:</strong> mailXX.extendcp.co.uk <em>(Check your Heart Internet control panel for the exact server address)</em></li>
<li><strong>Port:</strong> 587</li>
<li><strong>Username:</strong> Your full email address</li>
<li><strong>Password:</strong> Your email account password</li></ul></li>
<li>Choose <strong>Secured connection using TLS</strong>.  </li>
<li>Click <strong>Add Account</strong>. Gmail will send a confirmation email to your mailbox.  </li>
<li>Follow the instructions in the confirmation email to verify your setup.</li>
</ol>

<hr>

<h4 id="thatsit">That’s It!</h4>

<p>Once these steps are complete, you can send and receive emails from your custom domain directly through your Gmail account. This setup gives you the convenience of Gmail with the professional touch of your own domain email address.</p>]]></content:encoded></item><item><title><![CDATA[Helpful tip when upgrading php versions]]></title><description><![CDATA[<p><strong>Todays helpful tip:</strong></p>

<p>Your ini file changes wont be upgraded with you, it's worth checking the ini file changes from the old version to the new one. </p>

<p>You can use <code>diff</code> or <code>vimdiff</code> but my personal favourite is <code>meld</code> which will open a visual gui diff between the files letting</p>]]></description><link>https://blog.stapps.io/helpful-tip-when-upgrading-php-version/</link><guid isPermaLink="false">d13b7782-132f-4db0-b684-b1c47585804a</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Wed, 22 Apr 2020 09:10:13 GMT</pubDate><content:encoded><![CDATA[<p><strong>Todays helpful tip:</strong></p>

<p>Your ini file changes wont be upgraded with you, it's worth checking the ini file changes from the old version to the new one. </p>

<p>You can use <code>diff</code> or <code>vimdiff</code> but my personal favourite is <code>meld</code> which will open a visual gui diff between the files letting you go through and quickly copy in changes from the old file to the new one.</p>

<pre><code>sudo meld /etc/php/7.2/cli/php.ini /etc/php/7.3/cli/php.ini  
</code></pre>

<p>Also remember to go through the different php ini files you have, e.g. for cli vs fpm vs apache if you use them.</p>]]></content:encoded></item><item><title><![CDATA[Helping sites publish out during COVID-19]]></title><description><![CDATA[<p>Over a weekend I built a new app to help sites publish simple banners onto their site to say what they are doing in relation to COVID-19. </p>

<p>As many sites / companies have had to temporarily shut down, or had to change delivery schedules or want to reasure customers of how</p>]]></description><link>https://blog.stapps.io/helping-sites-publish-out-during-covid-19/</link><guid isPermaLink="false">5a88ba6a-36af-44fa-92f6-94cea12ef816</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Sun, 29 Mar 2020 18:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Over a weekend I built a new app to help sites publish simple banners onto their site to say what they are doing in relation to COVID-19. </p>

<p>As many sites / companies have had to temporarily shut down, or had to change delivery schedules or want to reasure customers of how they have handled this internally, OurSiteUpdates is a super simple and <strong>entirely free solution</strong>.</p>

<p>Find out more here: <a href="https://oursiteupdates.com/">https://oursiteupdates.com/</a></p>

<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/_T-O-xTplrs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

<p><em>p.s. this video recorded around 3am to get the app out as fast as possible lol</em></p>]]></content:encoded></item><item><title><![CDATA[mitmproxy on Ubuntu 20.04+]]></title><description><![CDATA[<blockquote>
  <p><a href="https://mitmproxy.org/"><strong>mitmproxy</strong></a> is a free and open source interactive HTTPS proxy.</p>
</blockquote>

<p>To quickly get this up and running on Ubuntu 19.04 (<strong>Updated and tested in 20.04 &amp; 21.04</strong>).</p>

<h3 id="installmitmproxy">Install mitmproxy</h3>

<pre><code class="language-bash">sudo apt install mitmproxy  
</code></pre>

<p>This should install the latest version, if not you can get the binary direct</p>]]></description><link>https://blog.stapps.io/mitmproxy-on-ubuntu-19-04/</link><guid isPermaLink="false">4d23e9bf-5250-447e-b8cd-fa200e6f836b</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Wed, 04 Sep 2019 20:38:00 GMT</pubDate><content:encoded><![CDATA[<blockquote>
  <p><a href="https://mitmproxy.org/"><strong>mitmproxy</strong></a> is a free and open source interactive HTTPS proxy.</p>
</blockquote>

<p>To quickly get this up and running on Ubuntu 19.04 (<strong>Updated and tested in 20.04 &amp; 21.04</strong>).</p>

<h3 id="installmitmproxy">Install mitmproxy</h3>

<pre><code class="language-bash">sudo apt install mitmproxy  
</code></pre>

<p>This should install the latest version, if not you can get the binary direct from the website: <a href="https://mitmproxy.org">https://mitmproxy.org</a></p>

<h3 id="starttheproxy">Start the proxy</h3>

<p>For this demo we'll use port 8881 for the proxy but this can be anything not already in use. </p>

<p>For the command line version:  </p>

<pre><code class="language-bash">sudo mitmproxy -p 8881  
</code></pre>

<p>Or the web UI version  </p>

<pre><code class="language-bash">sudo mitmweb -p 8881 --web-port 8882  
</code></pre>

<p>Using 8882 as the web port, this will let you open <a href="http://127.0.0.1:8882/">http://127.0.0.1:8882/</a> to watch the traffic through but again this can be any port you like.</p>

<p>Or a socks5 proxy  </p>

<pre><code class="language-bash">sudo mitmproxy -p 8881 -m socks5  
or  
sudo mitmweb -p 8881 -m socks5 --web-port 8882  
</code></pre>

<h3 id="setupproxy">Setup proxy</h3>

<p>Open your Network settings and click to edit your Network Proxy.</p>

<p><em>Can also be found by going through chrome://settings/?search=proxy and clicking Open proxy settings</em></p>

<p>Set to Manual &amp; set both your HTTP &amp; HTTP proxies to localhost port 8881.</p>

<h3 id="setupssltlscertificateforhttpssupport">Setup SSL/TLS certificate for HTTPS support</h3>

<p>This step is only needed the first time it's run.</p>

<p>Go to <a href="http://mitm.it/pem/cert">http://mitm.it/pem/cert</a> which will download the certificate.</p>

<p>Then to import this into Chrome, head to chrome://settings/certificates and click Authorities &amp; then click Import to select the downloaded certificate.</p>

<p><em>You may find this doesn't kick in with your existing Chrome session so try it our in Incognito/Private/Guest.</em></p>

<h3 id="enjoy">Enjoy</h3>

<p>If using the command line version the requests will be listed as they come in. Use the up and down arrows to navigate around, press enter to view inside a request and then left &amp; right between the tabs. You can also use your mouse to click the requests and tabs if easier. Then press q to go back to the requests list. Ctrl+c as often to exit.</p>

<p>Or if you're using the web UI, simply head over to <a href="http://127.0.0.1:8882/">http://127.0.0.1:8882/</a> and it'll start listing requests as you navigate the web.</p>

<p><strong>Remember when you're done to disable your Network proxy setting when you're done with the proxy.</strong></p>]]></content:encoded></item><item><title><![CDATA[Debugging Android Chrome with Chrome on your desktop - 2019]]></title><description><![CDATA[<h2 id="ingredients">Ingredients</h2>

<p>You'll need your phone, your desktop / laptop &amp; a cable to connect them.</p>

<h2 id="prep">Prep</h2>

<ol>
<li><p><strong>Enable developer options on your phone</strong></p>

<p>Settings > search for About phone > Tap Build number 7 times. <br>
<em>If after the first time it says no need, you already have it enabled. After the 7th time it</em></p></li></ol>]]></description><link>https://blog.stapps.io/debugging-android-chrome-with-chrome-on-your-desktop-2019/</link><guid isPermaLink="false">5f5ad2f1-6a7c-4f58-929a-255c5bdf6c01</guid><dc:creator><![CDATA[Andrew Stilliard]]></dc:creator><pubDate>Thu, 01 Aug 2019 21:16:00 GMT</pubDate><content:encoded><![CDATA[<h2 id="ingredients">Ingredients</h2>

<p>You'll need your phone, your desktop / laptop &amp; a cable to connect them.</p>

<h2 id="prep">Prep</h2>

<ol>
<li><p><strong>Enable developer options on your phone</strong></p>

<p>Settings > search for About phone > Tap Build number 7 times. <br>
<em>If after the first time it says no need, you already have it enabled. After the 7th time it should say it's enabled.</em></p></li>
<li><p><strong>Enable USB debugging</strong></p>

<p>Settings > search for USB Debugging > Enable.</p></li>
</ol>

<h2 id="recipe">Recipe</h2>

<ol>
<li><p>Plug in your phone</p></li>
<li><p>You should have a notification for "Charging this device with USB", tap this to get to the options.</p></li>
<li><p>Under "Use USB For" select File Transfer</p></li>
<li><p>On your desktop Chrome, go to <a href="chrome://inspect/#devices">chrome://inspect/#devices</a></p></li>
<li><p>On your phone Chrome, load any website, it should now be listed on the inspect page on your desktop.</p></li>
<li><p>Optionally enable port forwarding to debug a local site from your desktop on your phone.</p></li>
</ol>

<p>Troubleshooting: <a href="https://developers.google.com/web/tools/chrome-devtools/remote-debugging/#troubleshooting">https://developers.google.com/web/tools/chrome-devtools/remote-debugging/#troubleshooting</a></p>]]></content:encoded></item></channel></rss>