diff --git a/cmd/sorg-build/main.go b/cmd/sorg-build/main.go index 84e5edb11..e6e4c364d 100644 --- a/cmd/sorg-build/main.go +++ b/cmd/sorg-build/main.go @@ -57,9 +57,9 @@ type Article struct { // Hook is a leading sentence or two to succinctly introduce the article. Hook string `yaml:"hook"` - // HookImage is a boolean indicating whether there's a preview image for - // the article that can be shown on the index page. - HookImage bool `yaml:"hook_image"` + // HookImageURL is the URL for a hook image for the article (to be shown on + // the article index) if one was found. + HookImageURL string `yaml:"-"` // Image is an optional image that may be included with an article. Image string `yaml:"image"` @@ -81,10 +81,6 @@ type Article struct { // included as YAML frontmatter, but rather calculated from the article's // content, rendered, and then added separately. TOC string `yaml:"-"` - - // TwitterImage is a boolean indicating whether there's an image for - // the article that can be shown in a Twitter card. - TwitterImage bool `yaml:"twitter_image"` } // PublishingInfo produces a brief spiel about publication which is intended to @@ -193,10 +189,6 @@ type Fragment struct { // Title is the fragment's title. Title string `yaml:"title"` - - // TwitterImage is a boolean indicating whether there's an image for - // the article that can be shown in a Twitter card. - TwitterImage bool `yaml:"twitter_image"` } // PublishingInfo produces a brief spiel about publication which is intended to @@ -583,12 +575,26 @@ func compileArticle(dir, name string, draft bool) (*Article, error) { return nil, err } + format, ok := pathAsImage( + path.Join(sorg.ContentDir, "images", article.Slug, "hook"), + ) + if ok { + article.HookImageURL = "/assets/" + article.Slug + "/hook." + format + } + + if err != nil && !os.IsNotExist(err) { + return nil, err + } + card := &twitterCard{ Title: article.Title, Description: article.Hook, } - if article.TwitterImage { - card.ImageURL = sorg.AbsoluteURL + "/assets/" + article.Slug + "/twitter@2x.jpg" + format, ok = pathAsImage( + path.Join(sorg.ContentDir, "images", article.Slug, "twitter@2x"), + ) + if ok { + card.ImageURL = sorg.AbsoluteURL + "/assets/" + article.Slug + "/twitter@2x." + format } locals := getLocals(article.Title, map[string]interface{}{ @@ -710,14 +716,15 @@ func compileFragment(dir, name string, draft bool) (*Fragment, error) { // A lot of fragments still have unwritten hooks, so only add a card where // a fragment has a configured Twitter image for the time being. var card *twitterCard - if fragment.TwitterImage { + format, ok := pathAsImage( + path.Join(sorg.ContentDir, "fragments", fragment.Slug, "twitter@2x"), + ) + if ok { card = &twitterCard{ + ImageURL: "/assets/fragments/" + fragment.Slug + "/twitter@2x." + format, Title: fragment.Title, Description: fragment.Hook, } - if fragment.TwitterImage { - card.ImageURL = sorg.AbsoluteURL + "/assets/fragments/" + fragment.Slug + "/twitter@2x.jpg" - } } locals := getLocals(fragment.Title, map[string]interface{}{ @@ -2113,6 +2120,25 @@ create: return os.Symlink(source, dest) } +// Checks if the path exists as a common image format (.jpg or .png only). If +// so, returns the discovered extension (e.g. "jpg") and boolean true. +// Otherwise returns an empty string and boolean false. +func pathAsImage(extensionlessPath string) (string, bool) { + // extensions must be lowercased + formats := []string{"jpg", "png"} + + for _, format := range formats { + _, err := os.Stat(extensionlessPath + "." + format) + if err != nil { + continue + } + + return format, true + } + + return "", false +} + func renderView(layout, view, target string, locals map[string]interface{}) error { log.Debugf("Rendering: %v", target) diff --git a/content/articles/acid.md b/content/articles/acid.md index 06151484c..eacbe5744 100644 --- a/content/articles/acid.md +++ b/content/articles/acid.md @@ -6,8 +6,6 @@ hook: On ensuring system integrity, operability, and correctness through a solid foundational database, and how ACID transactions and strong constraints work in your favor. Why to prefer Postgres over MongoDB. -hook_image: true -twitter_image: true --- In 1983, Andreas Reuter and Theo Härder coined the acronym diff --git a/content/articles/api-paradigms.md b/content/articles/api-paradigms.md index 4b8e7ecb4..7be31f033 100644 --- a/content/articles/api-paradigms.md +++ b/content/articles/api-paradigms.md @@ -5,8 +5,6 @@ location: San Francisco hook: Musings on the next API technology, and whether REST-ish JSON over HTTP is just "good enough" to never be displaced in a significant way. -hook_image: true -twitter_image: true hn_link: https://news.ycombinator.com/item?id=14003134 --- diff --git a/content/articles/breaktime.md b/content/articles/breaktime.md index 9603e8ef1..f61c3d97c 100644 --- a/content/articles/breaktime.md +++ b/content/articles/breaktime.md @@ -4,7 +4,6 @@ published_at: 2014-02-02T18:15:28Z location: San Francisco hook: In search of an alternative to BreakTime. The discovery of a very classical solution. -hook_image: true --- A few years ago I took the plunge and started using diff --git a/content/articles/heroku-values.md b/content/articles/heroku-values.md index ff595480f..7dc5623b5 100644 --- a/content/articles/heroku-values.md +++ b/content/articles/heroku-values.md @@ -4,8 +4,6 @@ image: "/assets/heroku-values/heroku-values.jpg" location: San Francisco published_at: 2015-11-05T06:20:16Z title: My Heroku Values -hook_image: true -twitter_image: true hn_link: https://news.ycombinator.com/item?id=14286143 --- diff --git a/content/articles/idempotency-keys.md b/content/articles/idempotency-keys.md index 8ea9fb73a..63a36e269 100644 --- a/content/articles/idempotency-keys.md +++ b/content/articles/idempotency-keys.md @@ -6,8 +6,6 @@ hook: Building resilient services by identifying foreign state mutations and grouping local changes into restartable atomic phases so that every request can be driven to completion. -hook_image: true -twitter_image: true hn_link: https://news.ycombinator.com/item?id=15569478 --- diff --git a/content/articles/interfaces.md b/content/articles/interfaces.md index f1d90a256..9add4cc9d 100644 --- a/content/articles/interfaces.md +++ b/content/articles/interfaces.md @@ -5,8 +5,6 @@ location: San Francisco hook: How we overvalue the wrong technology and novel aspects of interface design at the expense of substantial gains to our productivity. -hook_image: true -twitter_image: true hn_link: https://news.ycombinator.com/item?id=13733777 --- diff --git a/content/articles/minimalism.md b/content/articles/minimalism.md index ebca3937b..a0dd573ba 100644 --- a/content/articles/minimalism.md +++ b/content/articles/minimalism.md @@ -5,8 +5,6 @@ location: San Francisco hook: Practicing minimalism with the lofty goal of total ephemeralization to build coherent, stable, and operable stacks. -hook_image: true -twitter_image: true attributions: Photographs by Ben Harrington (SR-71), Robyn Jay (embers of a burning fire), and Md. Al Amin (boat and sky). Licensed under Creative Commons BY-NC-ND 2.0, BY-SA 2.0, and CC BY 2.0 respectively. --- diff --git a/content/articles/newsletters.md b/content/articles/newsletters.md index 0c488d3f1..61d26054f 100644 --- a/content/articles/newsletters.md +++ b/content/articles/newsletters.md @@ -3,8 +3,6 @@ title: "Pseudo-HTML and Pidgin CSS: Building an Email Newsletter" published_at: 2017-08-02T14:52:31Z hook: Building a toolchain for sending a newsletter, and the dismal state of HTML and CSS in email. -hook_image: true -twitter_image: true --- After a recent trip to Portland, I decided to try writing a diff --git a/content/articles/page.md b/content/articles/page.md index ec6a439c8..d9ec134c9 100644 --- a/content/articles/page.md +++ b/content/articles/page.md @@ -3,8 +3,6 @@ hook: How the page almost transitioned successfully to the digital world, but is in decline in new media. The lessons that we can learn from this age-old design element, and why we should hope for its re-emergence. -hook_image: true -twitter_image: true image: "/assets/page/page.jpg" location: San Francisco published_at: 2014-01-26T18:56:46Z diff --git a/content/articles/postgres-atomicity.md b/content/articles/postgres-atomicity.md index a7cfe356a..0a5fadd8d 100644 --- a/content/articles/postgres-atomicity.md +++ b/content/articles/postgres-atomicity.md @@ -5,8 +5,6 @@ location: San Francisco hook: A dive into the mechanics that allow Postgres to provide strong atomic guarantees despite the chaotic entropy of production. -hook_image: true -twitter_image: true hn_link: https://news.ycombinator.com/item?id=15027870 --- diff --git a/content/articles/postgres-reads.md b/content/articles/postgres-reads.md index 6df94eb2b..44bea2175 100644 --- a/content/articles/postgres-reads.md +++ b/content/articles/postgres-reads.md @@ -6,8 +6,6 @@ published_at: 2017-11-17T22:02:56Z hook: Scaling out operation with read replicas and avoiding the downside of stale reads by observing replication progress. -hook_image: true -twitter_image: true hn_link: https://news.ycombinator.com/item?id=15726376 --- diff --git a/content/articles/redis-streams.md b/content/articles/redis-streams.md index c85759f6b..8a9cdf200 100644 --- a/content/articles/redis-streams.md +++ b/content/articles/redis-streams.md @@ -5,8 +5,6 @@ location: San Francisco hook: Building a log-based architecture that's fast, efficient, and resilient on the new stream data structure in Redis. -hook_image: true -twitter_image: true hn_link: https://news.ycombinator.com/item?id=15653544 --- diff --git a/content/articles/stripe-running.md b/content/articles/stripe-running.md index 3cc43491e..d2502c981 100644 --- a/content/articles/stripe-running.md +++ b/content/articles/stripe-running.md @@ -3,7 +3,6 @@ hook: Crunching running data with prepared statements in Postgres. location: San Francisco published_at: 2015-10-24T20:55:32Z title: Running at Stripe -hook_image: true --- One pleasant surprise of Stripe's internal culture was the existence of a diff --git a/content/articles/webhooks.md b/content/articles/webhooks.md index e98bbeceb..75054ea6a 100644 --- a/content/articles/webhooks.md +++ b/content/articles/webhooks.md @@ -6,8 +6,6 @@ hook: When it comes to streaming APIs, there's now a lot of great options like SSE, GraphQL subscriptions, and GRPC streams. Let's examine whether webhooks are still a good choice in 2017. -hook_image: true -twitter_image: true attributions: Thanks to Spencer Dixon for review. diff --git a/content/articles/x100s-hack.md b/content/articles/x100s-hack.md index f45f30535..a80f3c156 100644 --- a/content/articles/x100s-hack.md +++ b/content/articles/x100s-hack.md @@ -4,8 +4,6 @@ hook: If you find that the price tag for a Fuji-official adapter ring for the X1 location: San Francisco published_at: 2014-08-03T16:50:19Z title: A Cheap X100S Filter Ring Hack -hook_image: true -twitter_image: true --- The [official Fujifilm X100/X100S 49 mm adapter ring](http://www.amazon.com/Fujifilm-AR-X100-Adapter-Ring-49mm/dp/B004MME69S) which allows you to mount extra filters onto your lens will run you about $40, which is a little steep considering that its entire role in life is to act as an expensive spacer. diff --git a/content/drafts/microservices-and-the-monolith.md b/content/drafts/microservices-and-the-monolith.md index c92851169..f7027ff5c 100644 --- a/content/drafts/microservices-and-the-monolith.md +++ b/content/drafts/microservices-and-the-monolith.md @@ -3,8 +3,6 @@ title: Microservices and the Monolith published_at: 2017-01-05T16:41:25Z hook: Microservices may be out of vogue, but we should be wary of overcompensation. -hook_image: true -twitter_image: true --- About three years ago, the idea of a service-oriented diff --git a/content/drafts/ruby-scale.md b/content/drafts/ruby-scale.md index e49f5e12b..1044c42c2 100644 --- a/content/drafts/ruby-scale.md +++ b/content/drafts/ruby-scale.md @@ -4,8 +4,6 @@ published_at: 2017-04-18T14:23:28Z location: San Francisco hook: The challenges of scaling and operating a big Ruby codebase (that are not related to performance). -hook_image: true -twitter_image: true --- Ruby is a beautiful language. Speaking from experience, diff --git a/content/fragments/airpods.md b/content/fragments/airpods.md index 5041de27a..abb1d3b10 100644 --- a/content/fragments/airpods.md +++ b/content/fragments/airpods.md @@ -2,7 +2,6 @@ title: AirPods published_at: 2017-03-29T14:44:33Z image: /assets/fragments/airpods/vista.jpg -twitter_image: true hook: I'm happy to be cheering from the bleachers as Apple makes their first home run in years. --- diff --git a/content/fragments/ipad-mini.md b/content/fragments/ipad-mini.md index 0cb680a16..34379944a 100644 --- a/content/fragments/ipad-mini.md +++ b/content/fragments/ipad-mini.md @@ -2,7 +2,6 @@ title: The iPad Mini published_at: 2016-06-12T22:26:17Z image: /assets/fragments/ipad-mini/vista.jpg -twitter_image: true hook: An ode to one of my favorite Apple devices. --- diff --git a/content/fragments/ivy.md b/content/fragments/ivy.md index e01d72374..8e71d1b3c 100644 --- a/content/fragments/ivy.md +++ b/content/fragments/ivy.md @@ -1,7 +1,6 @@ --- title: Ivy published_at: 2016-08-24T03:12:31Z -twitter_image: true image: /assets/fragments/ivy/vista.jpg hook: Stripe's new home in SOMA. --- diff --git a/content/fragments/monkeybrains.md b/content/fragments/monkeybrains.md index 00ce9b1af..8629db7e0 100644 --- a/content/fragments/monkeybrains.md +++ b/content/fragments/monkeybrains.md @@ -2,7 +2,6 @@ title: MonkeyBrains published_at: 2016-01-23T01:15:58Z image: /assets/fragments/monkeybrains/vista.jpg -twitter_image: true hook: A very brief review of the local San Francisco ISP. --- diff --git a/content/fragments/sprawl-blues.md b/content/fragments/sprawl-blues.md index fdfc04586..3dea6d7e1 100644 --- a/content/fragments/sprawl-blues.md +++ b/content/fragments/sprawl-blues.md @@ -2,7 +2,6 @@ title: The Sprawl Blues published_at: 2016-01-03T22:18:36Z image: /assets/fragments/sprawl-blues/vista.jpg -twitter_image: true hook: On sprawl and commute times in North America. --- diff --git a/content/fragments/wgt-2015.md b/content/fragments/wgt-2015.md index d5f91107f..1a676d74a 100644 --- a/content/fragments/wgt-2015.md +++ b/content/fragments/wgt-2015.md @@ -2,7 +2,6 @@ title: WGT 2015 Abstract published_at: 2015-05-29T13:34:59Z image: /assets/fragments/wgt-2015/vista.jpg -twitter_image: true hook: A whirlwind tour of Wave-Gotik-Treffen 2015. --- diff --git a/content/fragments/your-name.md b/content/fragments/your-name.md index 457fddb2b..d1a3dde20 100644 --- a/content/fragments/your-name.md +++ b/content/fragments/your-name.md @@ -2,7 +2,6 @@ title: Your Name published_at: 2017-04-11T01:26:59Z image: /assets/fragments/your-name/vista.jpg -twitter_image: true hook: A short review of Makoto Shinka's latest animated film. --- diff --git a/views/articles/index.ace b/views/articles/index.ace index 8ba057899..72b465c8a 100644 --- a/views/articles/index.ace +++ b/views/articles/index.ace @@ -25,9 +25,9 @@ ul {{range .Articles}} li - {{if .HookImage}} + {{if .HookImageURL}} a href="/{{.Slug}}" - img src="/assets/{{.Slug}}/hook.jpg" data-rjs="2" + img src="{{.HookImageURL}}" data-rjs="2" {{end}} .title a href="/{{.Slug}}"