Archives

Category: Development

  • 2024 Redesign


    This redesign started with a simple note in Obsidian:

    Simplify. Keep the orange, but have less of it. Too much in nav. Move most of the extra info into pages linked off the About page.

    I also wanted something with a sidebar again.

    I wanted to stick with the Site Editor, so I used a child-theme of the Twenty Twenty-Four theme generated with Create Block Theme. I went with Twenty Twenty-Four because it purports to be extremely flexible and I wanted to use a theme with the most up-to-date Site Editor tooling and best practices.

    The first thing I did was set up my own template parts, overwriting the defaults. It was a bit time consuming, but TT4 doesn’t have a left sidebar or sidebar navigation template or pattern. I made as much as I could reusable as a named template part so I can easily make changes later and keep them consistent across my various templates.

    I used some very minor custom CSS (roughly 50 lines), mostly relating to my footer animation and some mobile styles for the navigation.

    I decided to go with default system fonts rather than loading a web font. Simplify.

    Everything non-essential got removed from the main navigation and either linked in the footer or somewhere more contextually appropriate on a subpage. Navigations on personal websites don’t need dropdowns.

    I removed the custom PHP template I had for my Reading page and turned it into a regular page in the editor, and pretty soon I’ll probably remove the custom post type and custom fields that it relied on, too. I don’t think I really need a separate post type, just a list will do. Simplify.

    Here is what it looks like:

    Places I took inspiration from:

    • From Manuel Moreale and Steph Ango‘s post lists on their homepages. I kind of like how both of them have their latest post at the top, which I might adopt. We’ll see.
    • From Jeremy Felt, the right aligned titles and how to handle my h-card
    • From Footer.design, a bold footer that has a surprise
    • From James G, not being afraid to highlight specific sections of my site in my footer that I want people to look at. Things like /now, /uses, /meta, /blogroll
    • From my own digital garden, the idea that having the last updated date on pages is useful to understand how fresh or stale they are.

    I’m sure there are more folks I took unconscious inspiration from, too!

    I included a Meta page in the footer that explains how this site is built and has a rough history of the changes this site has gone through since 2007. It was inspired by Anh, Shea Fitzpatrick, and Jeremy Felt. Since I’m a digital hoarder, I had backups of almost every iteration.

    I know it isn’t perfect, but I wanted to ship it and get it live.

    Where I want to go next with this:

    • Customize some category archive pages with additional info. Things like Photography and Woodworking can use helpful contextful info on the archive pages.
    • Continuing to refine pages like /now, /uses, /about, and /blogroll
    • Keep improving the mobile styling
    • Refining block styles as I use them
    • Fixing old posts that now look wonky
    • Keep figuring out what I like and what is useful on the homepage
    • Style webmentions, pingbacks, and comments better

    If you find something broken, please let me know. There are some templates I haven’t touched (though I don’t think anything actually uses them, but I should verify that.) 🚢

  • Notes on making a Digital Garden with WordPress


    Andy SylvesterArchived Link wrote me asking about my digital garden:

    I followed links to your site from Dave Winer’s Scripting News site, your digital garden site is cool! I am interested in what theme you started with to create that site.

    Andrew Shell has developed some tools for creating feeds for Federated Wiki installations (https://feeds.fedwikiriver.com/). I am interested in your thoughts about how to create feeds for wikis.

    Andy Sylvester
    andysylvester.com

    Thanks, Andy! I hope you don’t mind me writing a full post as a reply instead of an email. I’ve received other questions about this recently and I find it useful to document this stuff so it is searchable and linkable.

    If you want to use the theme and templates I’ve set up, I can try to package it up for you. I didn’t want to do that here because it isn’t in a state I feel comfortable releasing publicly, but happy to share it privately and get your feedback on what would be good to add. I consider it an ongoing experiment and subject to change at any time.

    Theme and templates

    For notes.cagrimmett.com I used the Blockbase theme. I picked that one because it is barebones but has nice Site Editor support, and I didn’t want to fight existing conventions. I wanted something that would stay out of my way but give me modern WP tools.

    If I were to do it again, I’d probably use the new Twenty Twenty-Four theme. It is super flexible and has the lastest-and-greatest Site Editor and blocks support.

    As you can tell, I’m all-in on the block editor.

    If you haven’t used the Site Editor yet, the main way it differs from classic themes is that it gives you the ability to customize templates right in the block editor, which is very powerful.

    I customized the templates in the Site Editor for the Blockbase theme, so I thought I’d call those out:

    • Homepage
      • I have two lists at the top of my homepage: Recently Created and Recently Updated.
        • One is a list generated by published date and one is a list generated by post modified date. I like to be able to see not only what is new, but what has been updated recently. I use the more powerful query loop in Generate Blocks to sort by post modified.
        • In the garden metaphor, I think of these as Planted and Tended.
      • Page Index
        • I opted to use Pages instead of Posts because I liked the hierarchy of Pages and thought I’d use that. I think this might have been a mistake and I’m considering moving everything to Posts and forgetting the hierarchy altogether. The hierarchy does make things easy to find, but so would better tags and categories.
        • I do have some private pages that only I can see when logged in, so there is a section on the homepage for that, too.
        • This is generated by the Page List block. If I moved to posts, I’d probably have a bunch of query loops for different categories.
      • Categories
        • Self explanatory
      • Tags
        • Self explanatory
    • Single Page
      • At the top I’m trying to surface relevant metadata:
        • Hierarchy (basically breadcrumbs from the hierarchical pages)
        • Categories and Tags
        • Published date
        • Last Modified date
      • Some pages have a sidebar with links to child pages to try to show the note’s context within a similar group. I don’t think I’ve nailed this yet.
      • Pingbacks and trackbacks to surface cross-linking between posts. I don’t think the bottom is the right place for this… I think I should put this in the sidebar instead.
        • Cross/backlinking are handled via enabling trackbacks and pingbacks on pages.
          • add_post_type_support( 'page', 'trackbacks' );
        • I use Webmentions here, too!

    Plugins

    • Tags are topical and freeform, auto-linked by TaxoPress
      • TaxoPress only has posts turned on by default. You need to go into the settings and enable for pages.
    • Making use of the GenerateBlocks plugin, which has a great replacement for the core Query Loop block with the ability to customize the query just like you can in code with WP_Query
    • Breadcrumbs via Breadcrumb NavXT
    • Redirection to monitor permalink changes so I can easily reorganize things without breaking links.
    • Webmentions as an addition to pingbacks and trackbacks for the indieweb.
    • Post Modified Time Block for easily displaying the post modified date on a page
    • Bookmark Card for nice looking bookmark cards
    • Child Pages Card for displaying child pages in the sidebar

    Feeds

    I am interested in your thoughts about how to create feeds for wikis.

    I have some thoughts in the WordPress ecosystem, and some cross over to wikis.

    • WordPress has incredible feed support already. Categories, tags, and search queries have their own feeds, which is quite helpful for people only interested in subscribing to certain topics.
      • Wikis have categories, so they could have category feeds, too.
    • Pages do not have feeds by default, but you can add them with this plugin: https://wordpress.org/plugins/rss-includes-pages/
    • It would be great if RSS feeds could surface updates to existing content, too. I’d like a feed for my recently updated list. My colleagues at Newspack have a plugin that includes an <updated> tag, which is a step in the right direction. It might require a different sorting mechanism for the feed reader. I’ll float it by Dave for FeedLand 🙂
      • Wikis have a great history page, so it is theoretically possible to have an updated element in the feed for the most recent updated time.

    Future additions/ongoing work

    • I want to better surface cross-linking within the site and inbound links from around the internet.
    • I want neat previews for outgoing links. Transclusion. I have some code for this that Jeremy Felt wrote for perell.com, but need to integrate it.
    • I want better context. On each page I want to show related content by category and show page ancestry siblings rather than just children. This would be easier if I moved to posts rather than pages.
    • I want to show revisions and/or changelog to show how notes have changed over time. I’m working with the WordPress Gutenberg team to figure out how to do this.
  • Apple Shortcuts for posting to WordPress via the REST API and XML-RPC MetaWeblog API


    Jim Willis asks,

    it seems that the iOS WordPress app’s “Post to WordPress” is no longer working, so I’d like to fallback to the rpc endpoint. Any chance you could share a link to your shortcut so I could take a look what you did here?

    Certainly! I’m replying as a post because I think it might help other people, too.

    I made two versions, one using the REST API and one using the XML-RPC MetaWeblog API.

    REST API

    Docs for making posts with the WordPress REST API: https://developer.wordpress.org/rest-api/reference/posts/

    Shortcut link: https://www.icloud.com/shortcuts/182b3a80dff54b27a4d7464cfc139b81

    How it works:

    • Takes input for a title and content. These are turned into variables.
      • If you don’t want a title, you can change this, but make sure your theme supports not having titles.
    • When you first set up the shortcut, add your username and an Application password in the format: username:password in the provided text block. This gets base64 encoded and passed to the cURL request as a basic authorization header.
    • The Get Content of URL block makes a POST to /wp-json/wp/v2/posts (Update your domain here!)
      • The variables for title and content get passed into the appropriate key in the JSON request body. I included title, content, and status, as those are the three that are needed. You can include tags, categories, slug, excerpt, etc. Anything in the docs. All pretty simple, you just need to make more variables and pass them along in the JSON body.
    • I included a block that gets the link back from the successful POST and prompts you to view it. Feel free to remove it if you don’t like it.

    XML-RPC MetaWeblog API

    Docs for the WordPress XML-RPC MetaWeblog API: https://codex.wordpress.org/XML-RPC_MetaWeblog_API

    Shortcut link: https://www.icloud.com/shortcuts/4a199bb32d654721a4aac9a773c664f1

    How it works:

    • Takes input for a title and content. These are turned into variables.
      • If you don’t want a title, you can change this, but make sure your theme supports not having titles.
    • When you first set up the shortcut, add your username and an Application password in the provided text blocks. These get turned into variables and passed to the XML that we send to xmlrpc.php.
    • The XML is stored as a text block, which is then turned into a variable and passed as a file along with the request.
      • I only included the username, password, post title, and post content, but you can add whatever is supported by the API. You just need more variables and more members of the struct.
    • The Get Contents of URL block makes a POST to the /xmlrpc.php endpoint (change the value for your domain!) with Content-Type of text/xml and the request body as a file.

    If you run into any issues, let me know! I’m happy to help.

  • Setting up PagePark on a DigitalOcean Droplet


    A couple days ago I asked Dave Winer how he was able to point news.scripting.com to a FeedLand news product, because CNAME records have to point to a hostname and can’t include a path. I wanted to point a subdomain at one of my own news products on FeedLand and couldn’t figure it out.

    The answer was PageParkArchived Link, a simple web server he wrote in 2014 to park his unused domains, and later expanded to include useful tools like serving redirects, content from GitHub and S3, and aliasing content from another site.

    I realized that I had to set that up for myself. I have a bunch of domains I don’t use, some old sites that I should just turn into a static archive and stick somewhere, and domains that I’d like to point to other services, such as FeedLand news products.

    That night I decided to set up PageParkArchived Link on a DigitalOcean droplet. I thought I’d write out the steps in case I or anyone else want to set it up on DigitalOcean in the future.

    Assuming you have a DigitalOcean account and are logged in:

    1. Set up a NodeJS droplet. I used DO’s NodeJS droplet template, which as of the time of this writing uses Node 18.12.1 and Ubuntu 20.04. I don’t expect to need a high powered server due to the low traffic to these domains, so I started with a $4/mo basic shared 512MB CPU with 10GB SSD and 500GB transfer. Basically the lowest tier.
    2. After the server came online, I SSH’d in.
    3. At the root, I cloned the PagePark repo: git clone https://github.com/scripting/pagePark.git
    4. Go into the pagePark directory: cd pagePark
    5. Install the app and dependencies: npm install
    6. Start the app: sudo -u root pm2 start pagepark.js
    7. Edit the nginx config to serve the app to the world. The default hello.js app that comes with the droplet is set to serve from port 3000. PagePark is set to serve from 1339, so we need to edit the location / block in nginx:
      • nano /etc/nginx/sites-available/default
      • Look for the block that looks like this:
    location / {
            proxy_http_version 1.1;
            proxy_cache_bypass $http_upgrade;
    
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    
            proxy_pass http://localhost:3000;
    }
    1. That second to last line, proxy_pass http://localhost:3000; is what we want to edit. Simple change 3000 to 1339. The line should now read: proxy_pass http://localhost:1339;
    2. Exit and save: ^X to exit, y to confirm save, hit enter to save it to the same filename
    3. Restart nginx: sudo systemctl restart nginx
    4. Delete the default app included with the droplet: sudo -u root pm2 delete hello
    5. Schedule the PagePark app to run at launch and stop the default hello app from running at launch: sudo -u root pm2 save
    6. Point a domain at the droplet to use for easy CNAME pointing for other domains. I used pagepark.cagrimmett.com and pointed it via A record to the IP address listed in the DO dashboard under the project I added the droplet to > Resources > Domains.
    7. Back on the server, I went to ~/pagePark/domains and added a folder for pagepark.cagrimmett.com, then added an index.html with a simple message to test my setup. I also moved the ~/pagePark/prefs/error.html and ~/pagePark/templates/* to the pagepark.cagrimmett.com folder to make the accessible, then edited them and changed the path for the ~/pagePark/config.json keys for the following items to serve them from this server rather than Dave’s:
      • urlDefaultMarkdownTemplate
      • urlDefaultOpmlTemplate
      • urlDefaultErrorPage

    That was it! pagepark.cagrimmett.com started working, then I set up a couple more domains by adding new folders to the domains folder (follow the docsArchived Link) and adding a cname record for the domains to pagepark.cagrimmett.com.

    • peekskill.cagrimmett.com shows my Peekskill news product, running on FeedLand. It pulls in feeds from local news sources and the City of Peekskill.
    • sideproject.showArchived Link shows the contents of a simple markdown file
    • behindtheart.xyz – an interview website I set up on WordPress where I interviewed generative artists in 2021, then abandoned. I want the interviews to stay live, but I’m probably not going to do them anymore, so I generated a static site and put the HTML files here instead of keeping the WP site live. I’m probably going to do this with a few more old WP sites.
    • More to come soon!

    Thanks for open sourcing PagePark, Dave! I’m already finding it useful.

  • iOS Shortcut Actions for Micropub posting


    I recently lamented about how few options there are for Micropub posting on iOS now that Indigenous was pulled from the app store. I had a hunch that the Shortcuts app could be a solution since Micropub takes pretty simple cURL requests, and after some testing to figure it out I put together a set of three Shortcuts to do the most popular Micropub actions. You can install them from the iCloud links:

    1. Like, Reply, Bookmark, or Repost a URL
    2. Post a Note
    3. Post a Note with an Image

    The first time you install one of these Shortcuts, you’ll be prompted to provide your Micropub endpoint and an IndieAuth token with the scope create profile update media.

    How do they work?

    They essentially take inputs (a URL or text) and make a curl POST request to your Micropub endpoint with a Bearer token that you provide on initial setup. Then it gives you the option to open up the URL for your post to take a look at it.

    The Like/Reply/Bookmark/Repost action has a bunch of conditionals to handle each action a little differently.

    The Post a Note with an Image action makes two separate curl requests: One to the Media endpoint for posting the image (which is automatically converted to a JPEG since most sites and browsers don’t support HEIF), the URL for which is then stored as a variable, then one to the regular endpoint with your note text and the image you wanted to post. I couldn’t actually get the “photo” parameter to work in the curl request, so I did it the old fashioned way and appended an HTML img tag to the “content” parameter. I also add a class micropub-img for easy styling. If anyone figures out how to get the photo parameter to work with the curl request, I’d be happy to update the action accordingly.

    Some screenshots

    The Like and Reply actions:

    Regular Note action:

    The Note with Image action:

    Videos of the Shortcuts in action

    Demonstrating the Like action on a URL:

    Demonstrating posting a regular Note from a Shortcut added to my Home Screen:

    Demonstrating posting an Image with a Note:

    Syndicated to IndieNews
  • Apple Shortcut to upload photos to WordPress Media Library


    UPDATE 11 Feb 2023:

    Good news! I refactored this to work with the Share Sheet and to prompt you for credentials and a media endpoint on the initial setup.

    Here is the new link: https://www.icloud.com/shortcuts/cf31d94107e24e5e947a801fb9d8132c

    Here is how easy it is to use on macOS.

    And here is how easy it is to use on iOS:


    Tonight I sat down and figured out something that has been in my head for months: Uploading photos to my WordPress Media Library with an Apple Shortcut. It works on both macOS and iOS.

    I use the REST API to upload them to the /wp/v2/media endpoint via a POST request. For authentication I use a username and Application Password that I base64 encode.

    Here is the basic workflow:

    • Select the photos
    • Loop through them and convert them to JPEGs. This is necessary because WordPress does not currently handle HEIC images, the default iOS image format.
    • Use a POST request to upload the converted JPEGs to the /wp/v2/media endpoint.

    You currently have to run this from Shortcuts.app. Share sheet support does not work on macOS Monterey, so I still have the step in there to pick images from Photos.app. I know they added this in Ventura, but I haven’t upgraded yet. Once I do, I’ll change this to only take images from the Share sheet so you can start in Photos.app rather than Shortcuts. I know share sheet support works on iOS, but since I blog most on my Mac, I wanted to make a shortcut that works in both places.

    If you’d like to use this Shortcut, you can get it here: https://www.icloud.com/shortcuts/e473f76d1692444896077ebcdbc4c893

    If you use it, you’ll need to make two changes:

    1. In the Text area at the top, put your WordPress Username and Application Password in this format: username:password
      • Application passwords are found under wp-admin -> Users -> your user profile -> Application passwords
    2. Change the domain from example.com to your website’s domain.
  • My Indie Likes Workflow


    As part of trying to implement a website-first POSSE workflow, I wanted to start with posting Likes to my website and sending out webmentions from them. That is a lot of what I used to tweet out. Why should a record of what I like be stored elsewhere?

    It took me a little while to figure out what I wanted this system to look like, but once I landed on it and verified it could work with a couple quick tests, I got to work building it out and it has been running smoothly for a couple of weeks.

    Inputs and Outputs

    Where do I read content the most and how can I get links from there into my website? This was my research question before Christmas.

    When I’m at my computer, posting likes is fairly painless, though it is a multistep process:

    1. Go to my website
    2. Log in
    3. Click to add a new like
    4. Paste in the URL
    5. Add commentary
    6. Click publish

    There is more friction on mobile, which is where I tend to read a lot of content.

    Jan Boddez pointed out that using Micropub reduces that friction. Unfortunately I couldn’t find clients that reliably worked for me on iOS.

    So, that put me back in the realm of looking for solutions. I did what I do on most projects: Look where the inputs are coming from.

    • I spend most of my time reading articles on mobile.
    • 50% of what I want to post as Likes comes from content already in my RSS feed reader.
    • The rest come from a mix of social, email, general browsing (things like news.ycombinator.com, pinboard.in/popular), and Slack groups.

    Whatever I choose has to incorporate all of these channels and has to work from all of my devices (macOS, iOS, and iPadOS).

    I decided to handle likes coming from the various sources in two ways:

    1. A solution specifically for my RSS reader
    2. A solution for everything else

    From the RSS Reader

    My RSS reader of choice is NetNewsWire (I used it before the Black Pixel era too), and I use Feedbin as my feed syncing service (and the email -> RSS functionality).

    Feedbin and NetNewsWire supports stars, so I decided that anything I star in Feedbin should be posted as a Like on my site.

    Feedbin has an API that makes starred entries available in two ways:

    • As a filter option on the Entries endpoint
    • As its own endpoint that returns a list of IDs, which then need a follow-up API call to fetch the contents

    I thought about a couple ways of fetching those starred items and turning them into posts. What I landed on is a simple plugin that polls the API once an hour, posts new items as Likes, then saves the IDs of the posts it processed so they won’t be processed again.

    This was the first proper WordPress plugin I’ve build and I learned a lot in the process:

    • The proper way to set up and tear down dependencies on install and uninstall hooks.
    • Working with WP Cron.
    • Setting up plugin settings pages and saving options.

    The Likes are posted using Jan Boddez’s IndieBlocks plugin context block, which also handles sending out webmentions.

    Here is the plugin, free to use or remix:

    GitHub – cagrimmett/feedbin-stars-to-indie-likes: Takes starred posts from Feedbin and turns them into Indie Likes on a WordPress site
    Takes starred posts from Feedbin and turns them into Indie Likes on a WordPress site – GitHub – cagrimmett/feedbin-stars-to-indie-likes: Takes starred posts from Feedbin and turns them into Indie Likes on a WordPress site
    github.com

    If I were to remake this from scratch, I’d probably save the post permalinks instead of the post IDs to the database to check. That seems slightly more hardened, and also ensures I’m not posting duplicate Likes if a Feedbin ID ever changes.

    An improvement I’d like to make: Add a filter to load the plugin updates from GitHub instead of WP.org with the new Update URI header.

    Everything Else

    For everything else I decided to piggyback off of a bookmarking solution I use. Bookmarking is pretty fast and shared across all of my devices, so I set up a specific folder called Likes and any time something gets added to that folder it gets turned into a Like.

    I currently use Larder.io for my bookmarking, which supports making folder contents accessible via RSS. This is perfect for my use case: No authentication, just fetch a feed and parse it. WordPress was born for this.

    Side note: I change bookmarking apps as often as I change email apps. I’ve used Pocket, Instapaper, Raindrop, Evernote web clips, Notion, browser built-in options, etc. I know one day I’m going to migrate everything to Pinboard and then it will live there for the rest of my days. For now, I’m still using Larder.

    I made another plugin very similar to the Feedbin one above, except that it fetches and parses an RSS feed with WordPress’s built in fetch_feed function. Like the other plugin, it fetches new posts once an hour and posts new Likes, then saves the permalinks of the posts it processed to the database so it skips those next time.

    Since bookmarks can have a description, it optionally outputs a paragraph block after the Like with the description I created. Again it uses Jan Boddez’s IndieBlocks plugin context block.

    GitHub – cagrimmett/rss-to-indie-likes: WordPress plugin that takes posts from an RSS feed and turns them into Indie Likes on your site.
    WordPress plugin that takes posts from an RSS feed and turns them into Indie Likes on your site. – GitHub – cagrimmett/rss-to-indie-likes: WordPress plugin that takes posts from an RSS feed and turns them into Indie Likes on your site.
    github.com

    The main thing I learned working on this version is the default feed cache when you are using fetch_feed() in WordPress is 12 hours and you can override it with a hook: wp_feed_cache_transient_lifetime

    Both have been running on my website for a couple weeks now without a hitch. I’ve yet to link Likes and Notes in the nav or on the homepage because I want to redesign how they are output, but I linked them here if you are interested.

  • Switching from Jekyll back to WordPress


    I set up this blog on WordPress in 2008. In 2015 I moved it to Jekyll and back filled those old posts in 2018. Now I’m back on WordPress with the entire archive. Why? I want to post more and WordPress is more conducive to that.

    With Jekyll I had to create a new markdown file with all of the yaml front matter I wanted and my automated templates were never quite right, then I had to get the Jekyll site running to preview the post (running the inevitable Jekyll and gem upgrades and troubleshooting issues with some plugin that broke), then run the custom build process I made to commit the changes, push them to Github, build the site, then deploy the changes to S3 and Cloudfront with s3_website, which is no longer being maintained. It was “rickety as hell” as an old family saying goes.

    With WordPress, Pressable keeps core up to date for me, so I just open the site, write the post, and click publish. No messing around with build tools, no finicky yaml templates, and no annoying Ruby and Java dependencies to fight with.

    Also, with WordPress, basic things like Search and Category Archives are available by default instead of something you need to hack on. The Plugin ecosystem is much better, too.

    The Jekyll site build and all the tools I built around it was a fun project, but I’m glad to be back on WordPress. I recently started working at Automattic, which of course played a role in my decision to switch back, but I had planned to make the switch long before I had applied.

    The speed of the 100% static Jekyll site served via Cloudfront was slightly faster than this WordPress site, but the difference isn’t big enough to matter to me. The Pressable CDN and object caching is pretty dang fast, and as I said above, I care more about being able to update the site faster and easier than I could with Jekyll.

    Getting started

    I had big ambitions for this project: I wanted to build a new theme from scratch adhering to the WordPress-Extra ruleset for PHPCS and the starter theme my team uses. I kept putting it off, preferring to be offline when not working. I was itching to get my site migrated, so I decided to modify an existing theme with a child theme and focus my energy on the migration.

    Migrations

    I essentially combined three sites into one.

    • Old WordPress posts: I’m a digital hoarder, so I still have the database for my 2008 WordPress site. I loaded it up in a local instance, ran some upgrades so it would work with WordPress 5.5, and then used the typical WordPress WXR files to import them to the new site. I temporarily put my old /wp-content/uploads/ folder on the server hosting cagrimmett.com so that the import would also pull in the featured images.
    • Microblog posts: cagrimmett.com was still live and I keep WordPress up to date there
    • Jekyll posts: I heavily customized my Jekyll RSS feed to include all kinds of extra metadata that it normally wouldn’t. I then used the excellent WP All Import Pro to parse the XML and map it to WordPress fields and download and import the media.
      • Attempting to do this and getting stuck? Shoot me an email and I’ll do what I can to help!

    Reading list

    I built out a cool reading list feature in Jekyll using yaml data files and a template that grouped the books I read by year and displayed the count. I was able to rebuild it pretty quickly with a custom post type, custom fields, and two queries: One to group the books by year and sort by date read, and the other to display the counts.

    Check out the reading page, my favorite page on the site!

    Redirects

    I thought I’d have a big issue with redirects, but with some careful planning about 95% of old links just work.

    By going back to the old databases from existing WordPress sites, I was able to use existing post IDs, which is great because my old links used the post ID permalink structure. Post ID permalinks redirect to the post by default, even if you have a different permalink structure.

    For the Jekyll posts, I was able to match the permalink structure I had on the Jekyll site, category/year/month/day/slug, so those work, too. The only issue I ran into is that I had some uncategorized posts in Jekyll and Jekyll leaves uncategorized out of the permalink while WordPress doesn’t. So I installed the Redirection plugin to add some redirects for those and monitor 404s.

    Ongoing issues

    I do have a few issues left to clean up:

    1. Code blocks with the Rouge syntax highlighter I used in Jekyll are a bit garbled. They need some styling to make them pretty again.
    2. JavaScript embeds in posts don’t work. I had a bunch of d3.js tutorials with interactive examples that don’t work right now. As a stop-gap I’ve linked to the old Jekyll version that is still accessible on S3, but I need to go through and fix those.

    I plan to eventually build out my own theme from scratch and I want to build some of my own custom blocks for a few ideas I have.

    But first, back to regular blogging.

  • Advent of Code, Day 12: Ember Simple Auth


    Day 12!

    I tried to add user authentication with EmberFire and Ember Simple Auth. I get through the provider workflow with Twitter, see a new user in Firebase, see the correct user data in the network panel in Chrome, and the ember_simple_auth-session cookie has “authenticated” in the content field. But session.isAuthenticated never seems to be true.

    I must be doing something wrong, but I don’t have the time to figure it out today. Time to keep packing. I’m buying a house tomorrow and moving in this weekend!

  • Advent of Code, Day 11: Rendering Charts from Ember Data


    Day 11!

    After some frustrating trial and error and searching through docs with lots of words I don’t quite understand, Dave Wasmer kindly helped me figure out how to get Ember Objects (the results of Ember data queries) in a format I’m used to working with: Something that looks like a regular array of objects, or “POJO” as Dave said.

    The solution ended up being calling the map method on the array-but-not-really-array and toJSON on each item in that. Then I get something back that looks like [{name: ..., startDate:...}, {...}]

    model.plants.map(c => c.toJSON()); 

    This allowed me to render the charts with Ember Data stored in Firebase.

    Tomorrow: Digging back in to D3’s update pattern and getting the chart automatically updating when adding a new plant. Dave’s helpful suggestion was to look into the “data down, action up” pattern.

    Thanks, Dave!

  • Advent of Code, Day 10: Setting up Firebase


    Day 10!

    I had a conversation on Twitter with Sam Selikoff and Ilya Radchenko, then later on Slack with Ilya and Dave Wasmer about using Firebase + EmberFire + Ember Data vs GraphQL + Hasura (Sam’s suggestion) vs GraphQL + Fauna (Ilya’s suggestion) for my data needs. I appreciate their inputs and arguments, but I ultimately went with Firebase + EmberFire for two reasons:

    1. I don’t want to set up and maintain an API server for this project. That knocks out Hasura.
    2. Fauna looks cool, but I don’t know either Ember Data or GraphQL that well, so I’ll have to learn one. Firebase + EmberFire + Ember Data is a little more battle tested and there is more documentation and search results for it vs Fauna, so I’m going to stick with that. I don’t want to be stuck in the dark on this project or constantly bug Ilya or Dave about stuff I don’t know. I do that enough at work.

    So that means I set up Firebase and EmberFire today. It was surprisingly easy! The quickstart guide was solid. I was saving Ember Data to Firebase and retrieving it in my routes in no time.

    I modified my Add a Garden form to save to Firebase, then transitioned to the Edit route where I passed along the ID of the garden I just created an retrieved the garden record’s data in the route. Then I read the Ember Data relationships docs and was able to both save and retrieve a garden’s related plants.

    Then I got stuck: I tried to pass the plant data to the calendar component to render the chart and I can’t get it to work. The data isn’t coming through as I expect. I assume it has something to do with relationships in Ember data as promises, but haven’t quite figured the solution out yet. So that will be tomorrow’s project.

    I feel like I’m making progress, though! It was super satisfying to see my collections and documents appear in Firebase.

  • Advent of Code, Day 9: More Ember Data


    Day 9!

    • My problems with yesterday’s ember data store not being recognized magically disappeared when deleting node_modules and running yarn to install them again. Node, man. So good, but so frustrating sometimes.
    • I got plants and gardens showing up in the ember data store after form submissions, which I verified with the Ember Inspector plugin.
    • I added a form for creating gardens.

    garden data plant data

    I thought I’d be able to get it all working locally before setting up EmberFire and Firebase, but after seeing that IDs for Ember Data are usually assigned on the server and having a talk with with Dave Wasmer and Ilya Radchenko, it sounds like I might be better off setting up Firebase now instead of trying to get Mirage to work. That is a little more than I have time for today, so that will be tomorrow’s goal.

  • Advent of Code, Day 8: Learning Ember Data


    Day 8! Another short session of work on a plane before I go home and pack up my apartment. Today I finished separating different functional parts of my components into individual components and started integrating Ember data.

    I hit an issue I wasn’t able to research since I wrote it only a place without internet access, so the below might be wrong.

    My models:

    export default DS.Model.extend({   garden: DS.belongsTo('garden'),   startDate: DS.attr('date'),   name: DS.attr('string'),   color: DS.attr('string'),   daysToMaturity: DS.attr('number') });  export default DS.Model.extend({   name: DS.attr('string'),   startDate: DS.attr('date'),   plants: DS.hasMany('plant') }); 

    Tomorrow’s goal:

    • Figure out why store is throwing errors for me
  • Advent of Code, Day 7: Figure out routes


    Day 7! Today I’m traveling, but I got some work done on the plane: I figured out my routes and started breaking apart components to their individual functions.

    My routes:

    Router.map(function() {   this.route('login');   this.route('signup');   this.route('gardens', function() {     this.route('edit', { path: ':slug' });     this.route('new');   });   this.route('public-profile', { path: ':user_slug' }, function() {     this.route('garden', { path: ':garden_slug' });   }); }); 

    Tomorrow’s goal:

    • Finish breaking the components apart
    • Get the new routes working
  • Advent of Code, Day 6: Plant Addition Flow


    Day 6! Short update today after tons of phone calls, move packing prep, and packing for a trip.

    Today’s progress

    • Removed the mocked plant data for my testing and built out the plant addition flow:
      • If there are no plants, hide the chart, show the form, and show a header
      • After adding a plant, hide the form, render the chart, and show the Add a Plant button
      • Only add the resize listener after the chart renders

    I also tried to fix a bug with the color picker where it wouldn’t close, but didn’t solve it.

    Amanda gave me another good idea today: For items like tomatoes, it would be good if there was another bar at the end that was lighter to signify a harvesting time window.

    Today’s commits:

    1. first use flow

    Tomorrow’s goal:

    • Dig in further to tailwind for styling
    • Read the Ember Data docs
    • Use D3’s update() to update the chart instead of destroying and redrawing it
    • Break the components apart
  • Advent of Code, Day 5: Form Components


    Day 5! I didn’t think I’d make this much progress today, but I’m happy I did.

    Today’s progress

    • Built reusable form components (button, input, form, label) with the help of EmberMap
    • Updated the chart
    • Integrated a color picker addon ember-pickr
    • Discovered and fixed a bug with the way I was calculating the scale.
    • I didn’t realize how much stuff I thought was just regular Ember in our app is actually addons. A bunch of things I use at Crash every day didn’t work at first in plant-gantt until I tracked it back to an addon. For example: ember-truth-helpers and ember-decorators

    The scale bug: the scale bug You see here that blueberries are going off the right side. This is because I was calculating the scale based on the last plant in the list, which isn’t always the one that will be harvested last, only the one that will be planted last.

    Instead, I had to use d3.max and offset to create a custom function to look at all of the plants and see which one would be harvested last:

    let lastToHarvest = maxIndex(sortedData, d =>   timeDay.offset(new Date(d.start), d.daysToMaturity) ); 

    Today’s commits:

    1. upgrade ember, add form components, add some helpers
    2. updates to chart with form data
    3. get the color picker working
    4. fixed end data calculation bug

    Here’s how it works so far!

    plant gantt with forms

    Tomorrow’s goal:

    • Improve the form UI
    • Use D3’s update() to update the chart instead of destroying and redrawing it
    • Show a different screen on the first time you land instead of showing the mocked out chart
    • Break the components apart
  • Advent of Code, Day 4: Responsive D3 Chart in an Ember Component


    Day 4!

    Today’s progress

    • Sorted vegetables by start date
    • Updated the data model to calculate the harvest date by the days to maturity
    • Changed the scale to start 15 days before the first planting and end 15 days after the last harvest to save space on screen. This also allows things to start before January and end after December.
    • Made the chart responsive

    Making the chart responsive took me down a weird rabbit hole. I tried using a computed property and then a bunch of different addons, but none of that worked. So I went the old-school route and wrapped the chart drawing in a function and then added an event listener to remove the chart and redraw it. There is probably a more elegant way to handle it, but I don’t know what it is.

    window.addEventListener('resize', function() {   selectAll('svg').remove();   drawChart(); }); 

    Today’s commits:

    1. change tick format to months and sort data
    2. move to relative scales
    3. responsive chart

    Tomorrow’s goal:

    • Add a form to add new elements and update the chart
  • Advent of Code, Day 3: D3 chart basics


    Day 3!

    Today’s progress

    • set up pods because that is what I’m used to working with at Crash
    • configured Tailwind in Ember
    • successfully rendered yesterday’s D3 mock inside a component
    • Talked to my first user (my wife!) and got feedback about the chart. She says vegetables should be sorted by when you plant them by default, not when you added them to the chart.

    Resources that helped me:

    Today’s commits:

    1. configure tailwind, move to pods
    2. add calendar component and add D3 mock to it

    Tomorrow’s goal:

    • change the calendar component to be responsive
    • sort the vegetables
    • Change the data to be closer to user-input data (plant date and duration, not just plant and harvest dates
    • watch more embermap videos to learn how to update with changes to user input data
  • Advent of Code, Day 2: D3 chart basics


    Day 2! I woke up excited to work on this and spent an hour on it before work.

    Today’s progress

    Figured out the basics of the D3 implementation for Plant Gantt:

    • Learned how to use Observable
    • The week-based X axis
    • Stacking each plant similar to a horizontal bar chart and starting the bar at the correct start date
    • Lining the labels up correctly

    Here is the Observable notebook.

    Tomorrow’s goal

    Get back into Ember land and figure out how to incorporate a chart with mocked out data in an Ember component.

  • Advent of Code, Day 1: Picking a Project


    I’ve wanted to do Advent of Code for a few years, but I just can’t get excited about random puzzles. Not knocking all the folks involved in it–more power to you! It just isn’t for me. So I haven’t participated.

    Last week while flipping through a seed catalog to figure out what I’m going to plan in my garden next year, I had an idea: I want to build a plant starting/harvesting calendar for my garden. Building that will be my own Advent of Code project this year.

    Today was Day 1, planning day.

    Name

    Plant Gantt. Amanda came up with it and it is perfect. A gantt chart for plants.

    Goals

    1. Build an interactive planting/harvesting calendar for vegetables.
    2. Deploy my own Ember app with a user login system on something like EmberFire
    3. Beef up on my D3 and Ember skills.
    4. Use Tailwind for the first time
    5. Make progress every day.

    Features

    • Ability to add a plant name, seed start date, days to maturity, and pick a label color for it.
    • Visualize the plant’s growing period on a 52 week calendar
    • See the current day marked across the full calendar
    • Ability to hover over a plant and see how long it has been growing and how long left until harvest
    • Ability to create an account and save your calendar
    • Ability to print the calendar you created
    • Ability to create multiple calendars with unique names

    I may not get this done in 25 days, but I intend to make progress every day. It will be tough with the month ahead: Two out of state trips, moving into our new house, and holiday parties. I’ll carve out time every day to get some work done, though.

    Today’s progress

    1. Picked a name and got a domain
    2. Planned out the features and my goals for the project
    3. Set up the git repo and basic Ember app
    4. Install add-ons: ember-d3, ember-cli-tailwind

    Tomorrow’s goal

    • Figure out the basics of the D3 gantt-style chart