Contentful © 2022 Hugo 2020-10-20T21:36:34+08:00 https://foo-dogsquared.github.io/hugo-theme-contentful/https://foo-dogsquared.github.io/hugo-theme-contentful/icon.png John Doe johndoe@example.com https://foo-dogsquared.github.io/hugo-theme-contentful/articles/extending-contentful/ Extending Contentful 2020-05-12T17:25:55+08:00 2022-05-13T18:08:06+08:00 <div id="preamble"> <div class="sectionbody"> <div class="paragraph"> <p>Extending a Hugo theme is nothing new and a <a href="https://gohugo.io/content-management/sections/">few</a> <a href="http://hugocodex.org/add-ons/">places</a> provide a place for the most common extensions. In this post, I’ll be listing a few personal recipes I’ve always used for extending a Hugo theme. Though this only applies specifically to Contentful and may need some tweaking when applying it other themes.</p> </div> </div> </div> <div class="sect1"> <h2 id="_customizing_your_head">Customizing your <code>&lt;head&gt;</code></h2> <div class="sectionbody"> <div class="paragraph"> <p>Let’s start with the most basic and perhaps most useful customization: modifying the <code>&lt;head</code>&gt;. This is useful for adding your own CSS and JavaScript files, changing certain metadata, or adding icons.</p> </div> <div class="paragraph"> <p>First, copy the <code>head</code> partial from the theme (<code>theme/contentful/layouts/partials/head.html</code>) to your own (<code>layouts/partials/head.html</code>). We’re simply taking advantage of <a href="https://gohugo.io/templates/lookup-order/">Hugo’s lookup order</a> where we’ve override the <code>head</code> partial with our own copy.</p> </div> <div class="paragraph"> <p>Then, feel free to add your own (or others&#39;) scripts and stylesheets, <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML">icons and other metadata</a>, or whatever suitable things.</p> </div> <div class="paragraph"> <p>In my case, I often use certain JavaScript libraries like <a href="https://www.mathjax.org/">MathJax</a> for mathematical typesetting, <a href="https://prismjs.com/">Prism</a> for syntax highlighting, and <a href="https://github.com/francoischalifour/medium-zoom/">medium-zoom</a> for interactive image zooms.</p> </div> <div class="paragraph"> <p>Here’s the modified code. (The example code is snipped for brevity.)</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-go" data-lang="go">&lt;!--snip--&gt; {{- /* MathJax */ -}} &lt;script src=&#34;https://polyfill.io/v3/polyfill.min.js?features=es6&#34;&gt;&lt;/script&gt; &lt;script id=&#34;MathJax-script&#34; defer src=&#34;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&#34;&gt;&lt;/script&gt; {{- /* Prism.js */ -}} &lt;link rel=&#34;stylesheet&#34; href=&#34;https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/themes/prism-tomorrow.min.css&#34; type=&#34;text/css&#34;&gt; &lt;script defer src=&#34;https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/components/prism-core.min.js&#34;&gt;&lt;/script&gt; &lt;script defer src=&#34;https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/autoloader/prism-autoloader.min.js&#34;&gt; &lt;script defer src=&#34;https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/keep-markup/prism-keep-markup.min.js&#34;&gt; {{- /* medium-zoom */ -}} &lt;script defer src=&#34;https://cdn.jsdelivr.net/npm/medium-zoom@1.0.5/dist/medium-zoom.min.js&#34;&gt;&lt;/script&gt; &lt;script&gt;window.addEventListener(&#39;load&#39;, () =&gt; mediumZoom(&#39;article img&#39;, { &#39;background&#39;: &#39;rgba(0, 0, 0, 0.75)&#39; }))&lt;/script&gt;</code></pre> </div> </div> <div class="paragraph"> <p>Since most of the JavaScript libraries used here are not really a requirement (except for MathJax for mathematical typesetting), I’ve set them to be loaded at the end of the page loading with <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script"><code>defer</code> attribute</a>. If you have an inline script, you can simply wrap it in an event listener for page loading (<code>window.addEventListener(&#34;load&#34;, your_function_goes_here)</code>).</p> </div> <div class="paragraph"> <p>If you want document-specific libraries, you have to pass some raw HTML through the parser of the document. For Goldmark, the default Markdown parser starting <a href="https://gohugo.io/news/0.60.0-relnotes/">Hugo v0.60.0</a>, blocks raw HTML by default and you can disable it by setting <code>markup.goldmark.renderer.unsafe</code> to <code>true</code>.</p> </div> <div class="paragraph"> <p>For Blackfriday, it parses even the raw HTML just fine. Though, you have to set it as the default Markdown parser.</p> </div> <div class="paragraph"> <p>For <a href="https://asciidoctor.org/">Asciidoctor</a>, you can use <a href="https://asciidoctor.org/docs/user-manual/#passthroughs">passthroughs</a> to get raw HTML through.</p> </div> </div> </div> <div class="sect1"> <h2 id="_twitter_cards">Twitter cards</h2> <div class="sectionbody"> <div class="paragraph"> <p>This will add <a href="https://developer.twitter.com/en/docs/tweets/optimize-with-cards/guides/getting-started">Twitter cards</a> for your webpages. (Be sure to copy the <code>head</code> partial first in your own layout folder.)</p> </div> <div class="paragraph"> <p>Thankfully, Hugo already has <a href="https://gohugo.io/templates/internal/#twitter-cards">an internal template for Twitter cards</a>. Simply add <code>{{- template &#34;_internal/twitter_cards.html&#34; . -}}</code> somewhere in your own copy. (For reference, <a href="https://github.com/gohugoio/hugo/blob/25a6b33693992e8c6d9c35bc1e781ce3e2bca4be/tpl/tplimpl/embedded/templates/twitter_cards.html">here’s the source code for the internal template</a>.)</p> </div> <div class="paragraph"> <p>You could also roll your own Twitter cards but I recommend to modify the internal template instead fitting your specific needs. (Copy the internal template from the given link, create it as a partial in <code>layouts/partials/twitter_cards.html</code>, modify it, and insert the template with <code>{{- partial &#34;twitter_cards.html&#34; -}}</code>.)</p> </div> </div> </div> <div class="sect1"> <h2 id="_open_graph_protocol">Open Graph protocol</h2> <div class="sectionbody"> <div class="paragraph"> <p>Next up, we’re implementing <a href="https://opengraphprotocol.org/">Open Graph protocol</a> for our webpages. Commonly used for making suitable format when sharing the content on certain sites like Facebook. (Be sure to copy the <code>head</code> partial first in your own layout folder.)</p> </div> <div class="paragraph"> <p>Similar to Twitter cards, Hugo has <a href="https://gohugo.io/templates/internal/#open-graph">an internal template for this</a>. Simply add <code>{{- template &#34;_internal/opengraph.html&#34; . -}}</code> somewhere in your own copy. (For reference, <a href="https://github.com/gohugoio/hugo/blob/25a6b33693992e8c6d9c35bc1e781ce3e2bca4be/tpl/tplimpl/embedded/templates/opengraph.html">here’s the source code for the internal template</a>.)</p> </div> <div class="paragraph"> <p>If you want more control and customized version of the output, I recommend to copy the internal template and create a partial (e.g., <code>layouts/partials/opengraph.html</code>) and modify it.</p> </div> </div> </div> <div class="sect1"> <h2 id="_an_archive_page">An archive page</h2> <div class="sectionbody"> <div class="paragraph"> <p>This will add an archive page similar to archive pages <a href="https://davidtranscend.com/archives/">like</a> <a href="https://lukesmith.xyz/blogindex.html">these</a>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-go" data-lang="go">{{- define &#34;main&#34; -}} &lt;h1&gt;{{ .Title }}&lt;/h1&gt; {{ .Content }} &lt;hr&gt; {{- /* Creating a section that lists out regular pages by year */ -}} {{ range $.Site.RegularPages.GroupByPublishDate &#34;2006&#34; }} {{- /* Skip regular pages with an invalid creation date string. */ -}} {{- /* This is convenient if we want to exclude certain posts to be listed by giving no value to `date` in the frontmatter. */ -}} {{- /* We will also exclude hidden pages. */ -}} {{ if ne .Key &#34;0001&#34; }} &lt;section data-year=&#34;{{ .Key }}&#34;&gt; &lt;h2 id=&#34;{{ .Key }}&#34;&gt;{{ .Key }}&lt;/h2&gt; &lt;ul&gt; {{- range where .Pages &#34;Params.hidden&#34; &#34;!=&#34; true -}} &lt;li&gt; &lt;date&gt;{{ .Date.Format &#34;2006-01-02&#34; }}&lt;/date&gt; - &lt;a aria-label=&#34;{{ .Title }}&#34; href=&#34;{{ .Permalink }}&#34;&gt;{{ .Title }}&lt;/a&gt; &lt;/li&gt; {{- end -}} &lt;/ul&gt; &lt;/section&gt; {{- end }} {{ end }} {{- end -}}</code></pre> </div> </div> <div class="paragraph"> <p>We will simply add this as a layout in our customized theme. Let’s call it <code>archives</code> so we have to add a file in <code>layouts/_default/archives.html</code> then set a page of our project with the <code>layout</code> key in the frontmatter.</p> </div> <div class="paragraph"> <p>We want the archives page to be accessed at <code>$.Site.BaseURL/archives</code> so we’ll simply create <code>archives.adoc</code> (<a href="https://gohugo.io/content-management/formats/#list-of-content-formats">any valid content files with certain file extensions can do</a>, I’m using <a href="https://asciidoctor.org/">Asciidoctor</a>) with the following example content.</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-asciidoctor" data-lang="asciidoctor">--- title: &#34;Archives&#34; layout: &#34;archive&#34; --- = Archives This is the archives of the century.</code></pre> </div> </div> </div> </div> <div class="sect1"> <h2 id="_configurable_social_media_links">Configurable social media links</h2> <div class="sectionbody"> <div class="paragraph"> <p>Most themes offer quick social media links with site configuration. However, it is only limited to popular media sites such as Facebook, Twitter, Instagram, GitHub, etc.</p> </div> <div class="paragraph"> <p>To get around this, we’ll make use of <a href="https://gohugo.io/templates/data-templates/">data templates</a>. Let’s create a quick game plan how does it work.</p> </div> <div class="paragraph"> <p>The data is a top-level dictionary/object with each key contains an object with the following fields.</p> </div> <div class="ulist"> <ul> <li> <p><code>url</code> is the…​ contact link itself and it is required to have it.</p> </li> <li> <p><code>name</code> is the text to appear in the output. Also required to have.</p> </li> <li> <p><code>weight</code> is an integer similar to how Hugo sorts the pages with the lower weight having high precedence; if this key is absent, it will be interpreted as 0.</p> </li> </ul> </div> <div class="paragraph"> <p>And here’s the data example in TOML which is placed in <code>data/contact.toml</code>.</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-toml" data-lang="toml">[github] name = &#34;GitHub&#34; url = &#34;https://github.com/foo-dogsquared&#34; [gitlab] name = &#34;Gitlab&#34; url = &#34;https://gitlab.com/foo-dogsquared&#34; [keybase] name = &#34;Keybase&#34; url = &#34;https://keybase.io/foo_dogsquared&#34; weight = -1 [twitter] name = &#34;Twitter&#34; url = &#34;https://twitter.com/foo_dogsquared&#34;</code></pre> </div> </div> <div class="paragraph"> <p>I want my Keybase profile to appear first than anything else for whatever reason so the <code>weight</code> key is set to -1.</p> </div> <div class="paragraph"> <p>With this data, we can then create a template out of it. I’ve put the following template in a partial named <code>contacts</code> (i.e., <code>layouts/partials/contacts</code>).</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-go" data-lang="go">&lt;address&gt; {{- range (sort $.Site.Data.contact &#34;weight&#34; &#34;asc&#34;) -}} | &lt;a rel=&#34;me&#34; href=&#34;{{ .url }}&#34;&gt;{{- .name -}}&lt;/a&gt; | {{- end -}} &lt;/address&gt;</code></pre> </div> </div> <div class="paragraph"> <p>A suggestion (and an exercise) for extending this is to create image links. Maybe add another key named <code>image</code> that accepts either URL. The <code>name</code> would now be the image alternative text.</p> </div> </div> </div> https://foo-dogsquared.github.io/hugo-theme-contentful/articles/rss-atom-and-json-feed-support/ RSS, Atom, and JSON Feed Support John Doe Jane Doe 2019-09-04T17:22:44+08:00 2022-05-13T18:08:06+08:00<div class="paragraph"> <p>This theme supports RSS, Atom, and JSON feed output for more ways of publishing web content for your visitors. It is also suitable for reading content from feed readers.</p> </div> <div class="paragraph"> <p>Here are the following documents used as references for the creation of the output feed templates.</p> </div> <div class="ulist"> <ul> <li> <p><a href="https://tools.ietf.org/html/rfc4287">Atom 1.0 - IETF RFC4287</a></p> </li> <li> <p><a href="https://jsonfeed.org/version/1">JSON Feed version 1 specifications</a></p> </li> <li> <p><a href="https://cyber.harvard.edu/rss/rss.html">RSS 2.0 specifications</a></p> </li> </ul> </div> <div class="paragraph"> <p>In this demo, it is enabled and you should be able to see them through the following links:</p> </div> <div class="ulist"> <ul> <li> <p><strong>RSS</strong>: <code>$HUGO_URL/index.rss</code></p> </li> <li> <p><strong>Atom</strong>: <code>$HUGO_URL/index.atom</code></p> </li> <li> <p><strong>JSON</strong>: <code>$HUGO_URL/index.json</code></p> </li> </ul> </div> <div class="paragraph"> <p>For enabling output feeds, utilize the <a href="https://gohugo.io/templates/output-formats">output formats</a> in your site configuration.</p> </div> <div class="paragraph"> <p>If you’re settling with this option, here’s an example template for enabling all of the feed formats.</p> </div> <div class="listingblock"> <div class="content"> <pre class="highlight"><code class="language-toml" data-lang="toml"># Visit the following for more information: # https://gohugo.io/templates/output-formats # Defining the media type of the output formats # For JSON format, it doesn&#39;t need to be since it&#39;s already built-in into Hugo [mediaTypes] [mediaTypes.&#34;application/atom+xml&#34;] suffixes = [&#34;atom&#34;, &#34;atom.xml&#34;] # You can remove the &#34;atom.xml&#34; if you want # Redefining RSS media type for the additional suffix [mediaTypes.&#34;application/rss+xml&#34;] suffixes = [&#34;rss&#34;, &#34;rss.xml&#34;] # You can remove the &#34;rss.xml&#34; if you want # Including all of the feed output formats in the build [outputFormats] [outputFormats.Rss] mediaType = &#34;application/rss+xml&#34; baseName = &#34;feed&#34; [outputFormats.Atom] mediaType = &#34;application/atom+xml&#34; baseName = &#34;feed&#34; [outputFormats.Json] mediaType = &#34;application/json&#34; baseName = &#34;feed&#34; # Indicating what output formats shall be included # for the following kinds [outputs] # .Site.BaseURL/index.* is available home = [&#34;HTML&#34;, &#34;JSON&#34;, &#34;RSS&#34;, &#34;ATOM&#34;] # .Site.BaseURL/$section/index.* is available section = [&#34;HTML&#34;, &#34;JSON&#34;, &#34;RSS&#34;, &#34;ATOM&#34;]</code></pre> </div> </div>