• Fixing a broken ATOM Feed

    red amazon danbo on brown wooden surface

    My city is not known for being technologically adept, and I’m at least lucky they have a website with a CMS. Sadly though, the website offers only a broken ATOM 1.0 feed, a standard that’s old enough to drink in some countries.

    Unfortunately, this doesn’t work with NewsBlur, so I had to sort to building a proxy that would parse the XML and output a JSON Feed.

    Through the power of Phpfastcache (only for a little bit of caching), I am embarassed to show you this cobbled together mess:

    <?php
    define( 'DEBUG', false );
    
    if ( defined( 'DEBUG') && DEBUG ) {
    	ini_set('display_errors', 1);
    	ini_set('display_startup_errors', 1);
    	error_reporting(E_ALL);
    }
    
    use Phpfastcache\Helper\Psr16Adapter;
    require 'vendor/autoload.php';
    
    $cache     = new Psr16Adapter( 'Files' );
    $url       = 'https://www.cityoflinton.com/egov/api/request.egov?request=feed;dateformat=%25B%20%25d%20at%20%25X%23%23%25b%2B%2B%25d;featured=3;title=Upcoming%20Events;ctype=1;order=revdate';
    $cache_key = 'atom-feed_' . md5( $url );
    
    // Get and/or fill the cache.
    if ( ! $cache->has( $cache_key ) ) {
    	$atom_feed = file_get_contents( $url );
    	$cache->set( $cache_key, $atom_feed, 60 * 60 * 1 ); // 1 hour.
    } else {
    	$atom_feed = $cache->get( $cache_key );
    }
    
    $feed_data = new SimpleXMLElement( $atom_feed );
    
    $json_feed = array(
    	'version'       => 'https://jsonfeed.org/version/1.1',
    	'title'         => filter_var( trim( $feed_data->title ) ?? 'Upcoming Events for the City of Linton', FILTER_SANITIZE_STRING ),
    	'home_page_url' => 'https://www.cityoflinton.com/',
    	'feed_url'      => 'https://decarbonated.org/tools/cityoflinton-rss/',
    	'language'      => 'en-US',
    	'items'         => array(),
    );
    
    foreach( $feed_data->entry as $entry ) {
    	$json_feed['items'][] = array(
    		'id'            => md5( $entry->id ),
    		'url'           => filter_var( trim( $entry->link['href'] ) ?? 'NO LINK FOUND', FILTER_SANITIZE_URL ),
    		'title'         => filter_var( trim( $entry->title ) ?? 'NO TITLE FOUND', FILTER_SANITIZE_STRING ),
    		'content_text'  => filter_var( trim( $entry->summary ) ?? 'NO CONTENT FOUND', FILTER_SANITIZE_STRING ),
    		'date_modified' => ( new DateTime( trim( $entry->updated ) ?? now(), new DateTimeZone( 'America/New_York' ) ) )->format( DateTimeInterface::RFC3339 ),
    	);
    }
    
    if ( defined( 'DEBUG ' ) && DEBUG ) {
    	header( 'Content-Type: text/plain' );
    	var_dump( $json_feed );
    } else {
    	header( 'Content-Type: application/feed+json' );
    	echo json_encode( $json_feed );
    }
    

    This will convert the XML from (prettified):

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
    	<title>Upcoming Events</title>
    	<link rel="self" href="https://www.cityoflinton.com/egov/api/request.egov?request=feed;dateformat=%25B%20%25d%20at%20%25X%23%23%25b%2B%2B%25d;featured=3;title=Upcoming%20Events;ctype=1;order=revdate" />
    	<updated>2021-12-09T10:14:40</updated>
    	<id>https://www.cityoflinton.com/egov/api/request.egov?request=feed;dateformat=%25B%20%25d%20at%20%25X%23%23%25b%2B%2B%25d;featured=3;title=Upcoming%20Events;ctype=1;order=revdate</id>
    	<author>
    		<name>Organization Information</name>
    	</author>
    	<entry>
    		<title>City Hall Closed</title>
    		<link rel="alternate" href="https://www.cityoflinton.com/egov/apps/events/calendar.egov?view=detail;id=501" />
    		<updated>2021-12-09T10:14:40</updated>
    		<id>https://www.cityoflinton.com/egov/apps/events/calendar.egov?view=detail;id=501</id>
    		<featured>0</featured>
    		<summary type="html">City Hall Closed</summary>
    	</entry>
    </feed>

    to JSON like:

    {
      "version": "https://jsonfeed.org/version/1.1",
      "title": "Upcoming Events",
      "home_page_url": "https://www.cityoflinton.com/",
      "feed_url": "https://decarbonated.org/tools/cityoflinton-rss/",
      "language": "en-US",
      "items": [
        {
          "id": "9b0bcc229cdc4266e539a785d77b4a8f",
          "url": "https://www.cityoflinton.com/egov/apps/events/calendar.egov?view=detail;id=501",
          "title": "City Hall Closed",
          "content_text": "City Hall Closed",
          "date_modified": "2021-12-09T10:14:40-05:00"
        }
      ]
    }

    That’s all ¯\_(ツ)_/¯

  • Quick Tip: Bash CLI params

    blur bright business codes

    While working on a bash script, I stumbled upon what I think may be the cleanest and simplest way to add CLI params to a bash script so far:

    This lets you use short (-V), long (--version), space separated (--user john), and equal separated (--user=john) arguments.

    It’s not perfect, but for a quick bash script hack, I’ve found it very useful!

  • iOS Reminders to Habitica To Do’s via IFTTT

    After digging around for a while trying to see how I could link up iOS’s Reminders with Habitica‘s To Do’s to help keep me organized, I finally found an easy way through IFTTT.

    This works easily because Habitica offers a wonderful API💥

    Specifically we’re looking at the “Create a new task belonging to the user” API endpoint:

    https://habitica.com/api/v3/tasks/user

    With this, we’ll need to make a POST request with some special headers to authenticate and then a body payload made of JSON:

    Headers:

    X-Client: my-user-id-IFTTTiOSRemindersSync
    X-API-User: my-user-id
    X-API-Key: my-api-key

    Body:

    {
    	"text": "{{Title}}",
    	"type": "todo",
    	"notes": "{{Notes}} (Imported from iOS Reminders via IFTTT)"
    }

    From here, IFTTT will fill in the title, notes, and ship it off to Habitica for me to check off for some sweet XP!

  • Quick Tip: Get Size of Revisions in WordPress

    landfill near trees

    One thing that you might not think of when watching the size of a large WordPress site grow, is unnecessary data in the database. With the introduction of the block editor years ago, there has been a large increase in the number of revisions a post makes when being edited.

    This can create a lot of revisions in the database if you’re not setting a limit.

    If you’d like to do a quick and dirty audit of your revision data, you can use a very ugly SQL query like this:

    SELECT COUNT( ID ) as revision_count, ( SUM( CHAR_LENGTH( ID ) ) + SUM( CHAR_LENGTH( post_author ) ) + SUM( CHAR_LENGTH( post_date ) ) + SUM( CHAR_LENGTH( post_date_gmt ) ) + SUM( CHAR_LENGTH( post_content ) ) + SUM( CHAR_LENGTH( post_title ) ) + SUM( CHAR_LENGTH( post_excerpt ) ) + SUM( CHAR_LENGTH( post_status ) ) + SUM( CHAR_LENGTH( comment_status ) ) + SUM( CHAR_LENGTH( ping_status ) ) + SUM( CHAR_LENGTH( post_password ) ) + SUM( CHAR_LENGTH( post_name ) ) + SUM( CHAR_LENGTH( to_ping ) ) + SUM( CHAR_LENGTH( pinged ) ) + SUM( CHAR_LENGTH( post_modified ) ) + SUM( CHAR_LENGTH( post_modified_gmt ) ) + SUM( CHAR_LENGTH( post_content_filtered ) ) + SUM( CHAR_LENGTH( post_parent ) ) + SUM( CHAR_LENGTH( guid ) ) + SUM( CHAR_LENGTH( menu_order ) ) + SUM( CHAR_LENGTH( post_type ) ) + SUM( CHAR_LENGTH( post_mime_type ) ) + SUM( CHAR_LENGTH( comment_count ) ) ) as post_size FROM wp_posts WHERE post_type='revision' GROUP BY post_type ORDER BY revision_count DESC;

    This will give you the number of revisions you have, and the approximate amount of data its using in the database:

    revision_count post_size
    441419 2842450412

    You can see now a very large WordPress site can amass a lot of unnecessary data in its database over a number of years. 2.8 Gigabytes of revisions is a lot of stuff if you’re never going to use them again.

  • Cool WordPress Plugins: Embed Extended

    four white travel adapters

    If you saw my last post, you may have noticed some cool embeds. These are coming from the Embed Extended plugin. This plugin takes OpenGraph data and treats it more like oEmbed data for WordPress. It works great with the block editor as well!

  • My Favorite Firefox Addons

    For my own posterity in case I ever lose them, or if anyone is curious, here’s what I use:

  • Photos: Asheville, NC

    I’m currently away on sabbatical for three months, because my company is awesome (and we’re hiring!)

    I recently took a trip to Asheville, NC. It’s the first time I’ve ever been that far East, and the first time in North Carolina. It was AWESOME!

    Please enjoy a small selection of pictures:

  • Quick Tip: Script Debugging in WordPress

    If you’re debugging core WordPress scripts, one thing you might run into is dealing with cached copies of the script. Due to how script-loader.php enqueues the core files, their versions are “hard coded” and short of editing script-loader.php as well, there’s a way to fix this via a filter:

    add_filter( 'script_loader_src', function( $src, $handle ) {
         if ( false !== strpos( $src, 'ver=' ) ) {
             $src = remove_query_arg( 'ver', $src );
             $src = add_query_arg( array( 'ver', rawurlencode( uniqid( $handle ) . '-' ) ), $src );
         }
         
         return $src;
     }, -1, 2 );

    This will apply a unique ver argument to the core scripts on each refresh, so no matter what you’re editing you should get the most recent version from both any page cache you may have and also the browser cache (🤞).

    Also, don’t forget to enable SCRIPT_DEBUG if you’re hacking away at core scripts to debug issues.

    I couldn’t find a good related image, so enjoy this delicious toilet paper.

  • Quick Tip: Disable WordPress Block Editor Fullscreen Mode

    I don’t know why, but any time I edit posts on this site, the block editor always goes into fullscreen mode. Even if I disable it, the next time I edit a post or refresh, it goes right back. My preferences aren’t being saved.

    Oh well, we can fix that with some PHP!

    if ( is_admin() ) {
         function jba_disable_editor_fullscreen_by_default() {
             $script = "jQuery( window ).load(function() { const isFullscreenMode = wp.data.select( 'core/edit-post' ).isFeatureActive( 'fullscreenMode' ); if ( isFullscreenMode ) { wp.data.dispatch( 'core/edit-post' ).toggleFeature( 'fullscreenMode' ); } });";
             wp_add_inline_script( 'wp-blocks', $script );
         }
         add_action( 'enqueue_block_editor_assets', 'jba_disable_editor_fullscreen_by_default' );
     }

    Many thanks to Jean-Baptiste Audras for this snippet he shared on his site last year 🥳

  • Raspberry Pi: February 2021

    I often feel like I don’t get enough use out of my Raspberry Pi devices. I have a Raspberry Pi Model B Revision 2.0 (512MB) and a Raspberry Pi 4 (4GB).

    Their current uses are as follows

    Raspberry Pi Model B:

    Raspberry Pi 4:

    I’d love to do more, but I’m really not sure what else I need. I don’t need some grandiose home server but I absolutely love playing with these little things!

%d bloggers like this: