Blog

  • Compressing Images and PDFs with bash

    As I’m looking to save us some money with our Google Cloud Storage bill I did some research and built out some scripts to optimize png, jpg, and pdf files.

    You can find the repository here. I cut a 5.5GB folder down to 4.1GB.

    The big disclaimer is that I ran this on my dozen test folders from client sites all weekend just for the images. Starting at 0900 Friday morning it was finally done at some point around noon on Sunday. Then I figured out the pdf script and ran that on my single client folder of 5.5GB. We’re currently running it on the other folders which I expect to take a long time based on the current progress.

    I run a Framework 13 with AMD 7840U so extrapolate your own timeframes.

  • Deleting Gravity Forms Spam Entries

    Over the last week we had a form get noticed by spam bots and get over 100,000 entries without us noticing. While we added Gravity Forms CAPTCHA to stop the spam I still have all those entries in the database to remove. I was only able to do around 1000 via the UI before the server timed out and the process was stopped, but Gravity Forms has a CLI plugin which gives me wp gf as a command I can run against the site.

    I started with small batches (the default page_size is 20) and once --page_size=10000 worked I looked up the number of entries and matched it to --page_size which deleted all the spam entries on the form.

    wp gf entry delete $(wp gf entry list <form_id> --format=ids --page_size=10000) --force
    

    The command takes the output of wp gf entrie list <form_id> and sends it to the wp gf entry delete <entry_id> --force command. Thus listing all the entries based on the --page_size value and deleting them.

    It took about 40 minutes for the command to finish running on around 100,000 entries.

    You can find the Gravity Forms CLI docs for entries here.

  • Redirect unauthenticated users in Laravel 11

    It took me a bit to find this answer so hopefully I’m saving you time. As described in the Laravel Authentication docs go to bootstrap/app.php and add the following to your Middleware.

    $middleware->redirectGuestsTo('/login');
    

    Which should leave app.php looking like this in a brand new install.

    <?php
    
    use Illuminate\Foundation\Application;
    use Illuminate\Foundation\Configuration\Exceptions;
    use Illuminate\Foundation\Configuration\Middleware;
    
    return Application::configure(basePath: dirname(__DIR__))
        ->withRouting(
            web: __DIR__.'/../routes/web.php',
            commands: __DIR__.'/../routes/console.php',
            health: '/up',
        )
        ->withMiddleware(function (Middleware $middleware) {
    	    // redirect users that are NOT logged in to this URL
            $middleware->redirectGuestsTo('/login');
        })
        ->withExceptions(function (Exceptions $exceptions) {
            //
        })->create();
    

    Change /login to whatever URL you want to send guests to.

  • Configure Newsboat with Nix Home Manager

    I assume you have nix installed as a package manager at least if not the whole NixOS thing and a working home manager setup.

    Most of the setup below was obvious, but the password stored externally to your configuration wasn’t quite defined in the section on setting up external sync services. Using feedbin-passwordfile you can set the location of the file you store the password in. Then that file only contains the password on the first line as described in the Newsboat password API docs.

    Unlike some other applications, Newsboat will not work if you use single quotes around the extraConfig options so you have to escape " to stop the parsing error.

      programs.newsboat = {
        enable = true;
        autoReload = true;
        reloadTime = 10;
        extraConfig = "
    urls-source \"feedbin\"
    feedbin-login \"curtis@curtismchale.ca\"
    feedbin-passwordfile \"~/.newsboat/feedbin-password\"
        ";
      };
    
  • SQLite WAL in Laravel

    As I set up a new Laravel project with SQLite I’ve found many references to enabling Write Ahead Logging being a large performance boost. All of the blog posts I’ve seen say to use the after parameter in your database connection like this.

    'sqlite' => [
        'driver' => 'sqlite',
        'url' => env('DATABASE_URL'),
        'database' => env('DB_DATABASE', database_path('database.sqlite')),
        'prefix' => '',
        'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
        'after' => function ($connection) {
            $connection->statement('PRAGMA journal_mode=WAL;');
        },
    ],
    

    But when you look at a modern Laravel database connection you’ll see that there is a defined parameter for journal_mode but no documentation on how to set WAL using this parameter. Thanks to wew for pointing me to the proper Issue showing me how to add WAL using the journal_mode parameter. Now my database connection looks like this.

    'sqlite' => [
    	'driver' => 'sqlite',
    	'url' => env('DB_URL'),
    	'database' => env('DB_DATABASE', database_path('database.sqlite')),
    	'prefix' => '',
    	'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
    	'busy_timeout' => 5000,
    	'journal_mode' => 'WAL',
    	'synchronous' => 'NORMAL',
    ],
    

  • Laravel file_put_contents issue

    I’m new to Laravel and while I had a working project via docker compose on my laptop 2 weeks ago, I’m on a new machine and needed to reinstall the project I had already been working on.

    So far I’ve only built out the templates. I’m not interacting with the database outside whatever the default setup is. I expected to clone the project, setup .env files and run docker compose up to get my project going.

    What actually happened was a big error screen about not being able to access storage/framework/views/<longstring>.php. Searching online I got lots of information saying that I had a file permission issue with Docker that doesn’t exist on macOS or Windows because of how they do virtualisation which sent me down a path of messing with chmod and groups on my Fedora 40 machine.

    The real fix was php artisan migrate which initialised the sqlite database. Then my Laravel project ran on via docker compose without issue.

  • Really HTMX is the Big Deal for WordPress?

    I just read this article on HTMX being a big deal for WordPress and I just don’t see it. To vastly simplify the article, give the code below and using the HTMX JavaScript library:

    <a href="https://mysite.com/books" hx-target="#books">
    	View Books
    </a>
    
    <div id="books"></div>
    

    This tells HTMX to fetch the content at the /books location and populate it in the #books ID. Somehow this is better than making an AJAX call to do the exact same thing.

    I just don’t see it. The author goes on to call WordPress a “monolithic PHP server” as if it’s a bad thing. To me it seems like people like hating on PHP and HTMX is simply a way to “make it modern” instead of using a traditional AJAX call.

    I can’t say I see any benefit to HTMX at all. The good part is I don’t need to invest time into learning it then.

  • Making Bolded Text Accessible

    Bold or italics in text is often used by sighted users as described in style guides to represent book titles or other citations. How does this impact users with disabilities and users that access your site with a screen reader?

    What not to use

    Both <b> and <i> tags are considered WCAG compliance errors. Don’t use them.

    Using a style element style="font-weight:bold" is not accessible. It only changes the appearance of the text and does nothing to convey meaning to any screen reader user. If this is a purely visual decoration this should be in your stylesheet not inline in your content.

    Headings are often bold, but they should not be use simply to bold text. They are used by many users to navigate the content of a page and provide structure to your content.

    Screen Readers and Bold Text

    While a sighted user can see text marked with <em> or <strong> tags most non-sighted screen reader users won’t have the same notation available since screen readers don’t acknowledge <em> or <strong> unless they use a specific one and turn on the setting. Most users don’t change the defaults on anything though so this is unlikely to be a use case that could be viably considered.

    What to do instead of bold?

    Gov.uk has a great look at this with their Warning text. If you click through to the HTML version you’ll see a visually hidden piece of text that says Warning.

    <div class="govuk-warning-text">
      <span class="govuk-warning-text__icon" aria-hidden="true">!</span>
      <strong class="govuk-warning-text__text">
        <span class="govuk-visually-hidden">Warning</span>
        You can be fined up to £5,000 if you do not register.
      </strong>
    </div>
    

    Using this Warning text means that users of screen readers get the semantic meaning of the text while visual users do not see the extra warning and instead see the styled text with a graphical element denoting that the piece of text is something to take extra note of. This is the same type of notation that is recommended by Deque University

    In general text your text-level semantics should be treated as a progressive enhancement instead of required to make what you’re writing understandable. If you need a bold element to make your content have the emphasis you feel it needs that’s likely a sign of bad writing. Your text should put the emphasis on the right point without the need for any extra bold markup

    Is the <em> or <strong> tag the best element to describe what you’re trying to emphasize?

    <cite> is intended for short citations such as book titles.

    <var> is designated to represent variables or computer code and mathematical equations.

    <code> is for longer blocks of code or for the code references you note here

    <kbd> is designated to specify key presses to be typed be a user.

  • Use nix search on macOS

    nix-shell is an excellent package manager for any macOS in large part because it doesn’t install things globally so you don’t end up with version conflicts. While you can always go to the web to search for nix packages, you can also do this from the command line if you enable the experimental features of nix.

    To do this on macOS open /etc/nix/nix.conf in your editor. I use NeoVim so my command is sudo nvim /etc/nix/nix.conf. Then add the following line and save.

    experimental-features = nix-commad flakes
    

    Now you can use nix search nixpkgs nodejs to search for all the available nodejs packages in the nix repository.

  • LocalWP and Homebrew Conflicts

    Like an idiot while I was going to do a “quick” bit of work to get ahead for next week on Friday. First I decided it was time to update Homebrew before I really got into diagnosing a problem for a client. Of course mayhem ensued with the wpcli commands no longer working with LocalWP after I ran brew update && brew upgrade.

    There were two main errors to search. First the icu4u library wasn’t loaded. This library is used for unicode characters and is a requirement of PHP, MySQL and a number of other things that Homebrew was managing. The specific error is below.

    Library not loaded: /opt/homebrew/opt/icu4c/lib/libicuio.73.dylib

    Further down in the same error block there was a note about PHP 8.0 not being installed.

    Error: php@8.0 has been disabled because it is a versioned formula!

    After searching and finding these general directions to remove and reinstall LocalWP, I was still out of luck. Worse, I no longer had any development sites installed locally as I removed them all during the uninstall process.

    Fixing with Homebrew

    Ultimately after a bunch of fruitless searching I came across this article saying that PHP 8.0 was no longer supported in Homebrew. To continue to have it installed I needed to get it from a different repository with the following commands.

    • Add the new repository to Homebrew – brew tap shivammathur/php
    • install php 8.0 – brew install shivammathur/php/php8.0

    Now my wpcli commands work as expected and I no longer see the errors from icu4c. The big problem with this, and anything installed with Homebrew, is that we’re messing with my main PHP version in my shell. I’m always running PHP 8.0 now, even on projects that may need earlier or later versions of PHP.

    Fixing it with nix-shell

    Of course you’ll need to install nix-shell first with these instructions.

    A far more portable and repeatable way to fix my issue is to use nix-shell. Instead of messing with the main versions of software running on my machine, I’m only messing with the current instance of the terminal that I’m working in.

    The easy way to test this is to run the following command which temporarily installs php8.1 and wpcli in my terminal.

    nix-shell -p php81 wp-cli
    

    You’ll still get an error connecting to the database because the shell doesn’t understand how to make the connection to LocalWP but that can be fixed by going to the Database tab in LocalWP and copying the socket value into the DB_HOST value. It should look something like this:

    define( 'DB_HOST', 'localhost:/Users/curtismchale/Library/Application Support/Local/run/CiNKEaTuy/mysql/mysqld.sock' );
    

    Now when I run a wp-cli command I have a connection which means the next step is to create a shell.nix file I can place in the root directory of my project which has the values I need setup already so I don’t have to manually run them each time. This is what my shell.nix file looks like.

    let
      nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11";
      pkgs = import nixpkgs { config = {}; overlays = []; };
    in
    
    pkgs.mkShellNoCC {
      packages = with pkgs; [
        wp-cli
    	php81
      ];
    }
    

    Then when I open up my terminal I run nix-shell and the current shell is setup with php81 and wp-cli every time. I can move the same shell.nix file to a new site, or a new computer and run nix-shell and I’ll get the same environment in my shell every time.