Skip to content

Latest commit

 

History

History
413 lines (368 loc) · 17.8 KB

index.org

File metadata and controls

413 lines (368 loc) · 17.8 KB

Personal site

About

  • This is an example of a personal site built with org-thtml
  • It exemplifies the following features
    • Use of templates to create custom HTML.
    • Use of stylesheets.
    • Automatic creation of Facebook and Twitter cards.
    • Automatic configuration of links, icons and other personal references.

My personal blog

This list is automatically generated from blog/sitemap.inc

My recipes

This list is automatically generated from blog/recipes.inc

Creating the structure

This sample site is organized into a collection of directories

  • blog/ A directory with a few posts
  • recipes/ Another directory, to show that a project can have multiple components
  • images/ A folder with some pictures used in the site
  • css/ The HTML styles
  • templates/ The org-thtml templates used to build the pages
  • public_html/ The folder where the site is recreated

The code in this org file shows how to configure the site and how to publish it to public-html/. This org-file is automatically translated into

In order to see the actual code of the page, please look at the raw file

We begin our Emacs lisp script with some code to create the basic structure of the site. This downloads a few sample images and creates directories which are populated with components that are part of this org-mode file.

(dolist (d '("public_html" "images"))
  (unless (file-exists-p d)
    (make-directory d)))

(dolist (d '(("https://upload.wikimedia.org/wikipedia/commons/thumb/d/d8/Person_icon_BLACK-01.svg/200px-Person_icon_BLACK-01.svg.png" "images/me.png")
             ("https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Mini-poodle_companionship.jpg/800px-Mini-poodle_companionship.jpg" "images/a-dog.jpg")
             ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/ee/Grumpy_Cat_by_Gage_Skidmore.jpg/460px-Grumpy_Cat_by_Gage_Skidmore.jpg" "images/grumpy-cat.jpg")
             ("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b0/Bengal_tiger_%28Panthera_tigris_tigris%29_female_3_crop.jpg/800px-Bengal_tiger_%28Panthera_tigris_tigris%29_female_3_crop.jpg" "images/tiger.jpg")
             ("https://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Strawberry_and_lemon_smoothie_%2814430283996%29.jpg/800px-Strawberry_and_lemon_smoothie_%2814430283996%29.jpg" "recipes/smoothie.jpg")
             ("https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Strawberry_in_Dilshad_Garden%2C_India.jpg/800px-Strawberry_in_Dilshad_Garden%2C_India.jpg" "recipes/strawberries.jpg")))
  (unless (file-exists-p (cadr d))
    (url-copy-file (car d) (cadr d))))

(org-babel-tangle)

The templates

A full blog page

The template in templates/blog.html describes a full page, consisting of

  • the common HTML header section
  • a sidebar with personal information
  • a menubar with links to other sections of the page
  • a title header using the <h1> tag, followed by the content of the page, as exported by ox-html.
  • a footer
<!doctype html>
<html lang="en">
  <head>
    {{:include "header.html"}}
  </head>
  <body>

    <div id="layout" class="pure-g">
      {{:include "sidebar.html"}}
      <div class="content pure-u-1 pure-u-md-2-3">
        {{:include "menubar.html"}}

        <div id="content">
          {{:if with-title}}<h1>{{title}}</h1>{{:endif}}
          {{:if date}}<div class="post-meta">Published on {{format-time-string "%b %d, %Y" date}}</div>{{:endif}}
          {{contents}}
        </div>

        {{:include "footer.html"}}
      </div>
    </div>
  </body>
</html>

HTML header

Every HTML file has a header with metadata information about the page. The template template/header.html creates a header that informs the browser about

  • The title of the page, extracted from the page’s #+title: file or from templated-html-site-title
  • The description of the page, extracted from #+description: or from templated-html-site-description
  • A Facebook card with
    • The absolute path of this page, computed from the template-html-site-url
    • A link to the image shown in Facebook posts, computed from the image field, with details about size and width.
    • A copy of the page’s description.
  • A Twitter card with similar information, but only if personal-site-twitter is set.
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{{description}}">
{{:if title}}
  <title>{{title}}</title>
  <meta property="og:url" content="{{site-url}}{{input-url}}" />
  <meta property="og:title" content="{{title}}" />
  <meta property="og:type" content="article" />
  <meta property="og:description" content="{{description}}" />
  {{:if image}}
    <meta property="og:image" content="{{site-url}}{{image-url}}" />
    <meta property="og:image:width" content="{{image-width}}" />
    <meta property="og:image:height" content="{{image-height}}" />
  {{:endif}}
  {{:if personal-site-twitter}}
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:site" content="@{{personal-site-twitter}}">
    <meta name="twitter:title" content="{{title}}">
    <meta name="twitter:description" content="{{description}}">
    {{:if image}}
      <meta name="twitter:image" content="https://juanjose.garciaripoll.com{{image-url}}">
    {{:endif}}
  {{:endif}}
{{:endif}}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/1.0.1/pure-min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css">
<!--[if lte IE 8]>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/1.0.1/grids-responsive-old-ie-min.css">
<link rel="stylesheet" href="{{root}}css/layouts/blog-old-ie.css">
<![endif]-->
<!--[if gt IE 8]><!-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pure/1.0.1/grids-responsive-min.css">
<link rel="stylesheet" href="{{root}}css/layouts/blog.css">
<!--<![endif]-->

The sidebar

The template template/sidebar.html provides a menu for various sources of personal information, including picture of the user, name, organization, accounts in GitHub, Linkedin, Twitter, Google Scholar, etc. They are configured using variables that are defined below.

<div class="sidebar pure-u-1 pure-u-md-1-3">
  <div class="header">
    <a href="{{root}}"><img class="portrait" src="{{root}}images/me.png" alt="Juan José García Ripoll"/></a>
    <h2 class="brand-title">{{personal-site-full-name}}</h2>
    <h2 class="brand-tagline">Senior scientist</h2>
    {{:if personal-site-organization}}
      <h2 class="brand-tagline"><a href="{{personal-site-organization-url}}">{{personal-site-organization}}</a></h2>
    {{:endif}}

    <nav class="nav">
      <ul class="nav-list">
        {{:if personal-site-google-scholar-id}}
          <li class="nav-item">
            <a class="pure-button" href="https://scholar.google.es/citations?user={{personal-site-google-scholar-id}}&hl=en"><i class="fab fa-google big-icon"></i></a>
          </li>
        {{:endif}
        {{:if personal-site-twitter}}
          <li class="nav-item">
            <a class="pure-button" href="https://twitter.com/{{personal-site-twitter}}"><i class="fab fa-twitter big-icon"></i></a>
          </li>
        {{:endif}}
        {{:if personal-site-github}}
          <li class="nav-item">
            <a class="pure-button" href="https://github.com/{{personal-site-github}}"><i class="fab fa-github big-icon"></i></a>
          </li>
        {{:endif}}
        {{:if personal-site-linkedin}}
          <li class="nav-item">
            <a class="pure-button" href="https://www.linkedin.com/in/{{personal-site-linkedin}}/"><i class="fab fa-linkedin big-icon"></i></a>
          </li>
        {{:endif}}
        {{:if personal-site-email}}
          <li class="nav-item">
            <a class="pure-button" href="mailto:{{personal-site-email}}"><i class="fas fa-envelope big-icon"></i></a>
          </li>
        {{:endif}}
        <li class="nav-item">
          <a class="pure-button" href="https://juanjose.garciaripoll.com/blog/rss.xml"><i class="fas fa-rss big-icon"></i></a>
        </li>
      </ul>
    </nav>
  </div>
</div>

Menubar

The template template/menubar.html produces a line with a few global links at the top.

<div class="pure-menu pure-menu-horizontal">
  <ul class="pure-menu-list">
    <li class="pure-menu-item"><a href="{{root}}index.html#blog" class="pure-menu-link">Blog</a></li>
    <li class="pure-menu-item"><a href="{{personal-site-organization-url}}" class="pure-menu-link">Work</a></li>
    <li class="pure-menu-item"><a href="{{root}}index.html#recipes" class="pure-menu-link">Cooking</a></li>
  </ul>
</div>

Footer

template/footer.html footer is included at the end of all pages.

<div class="footer">
  <hr>
  <div>Created using Emacs and <a href="https://github.com/juanjosegarciaripoll/org-thtml">org-mode</a></div>
  <div>(c) Juan José García Ripoll 2019</div>
</div>

A separate blog for cooking

We create a different template, recipe.html, for our cooking blog, because we want to customize the CSS.

<!doctype html>
<html lang="en">
  <head>
    {{:include "header.html"}}
    <style>
.figure {
    float: left;
    max-width: 40%;
    padding: 1em;
}
.figure img {
    border-radius: 1em;
    border: 1px solid #ddd;
    padding: 0.5em;
}
    </style>
  </head>
  <body>

    <div id="layout" class="pure-g">
      {{:include "sidebar.html"}}
      <div class="content pure-u-1 pure-u-md-2-3">
        {{:include "menubar.html"}}

        <div id="content">
          {{:if with-title}}<h1>{{title}}</h1>{{:endif}}
          {{:if date}}<div class="post-meta">Published on {{format-time-string "%b %d, %Y" date}}</div>{{:endif}}
          {{contents}}
        </div>

        {{:include "footer.html"}}
      </div>
    </div>
  </body>
</html>

A template for all other pages

The template/index.html is applied to the index page at the top, as well as to other non-blog pages

<!doctype html>
<html lang="en">
  <head>
    {{:include "header.html"}}
  </head>
  <body>
    <div id="layout" class="pure-g">
      {{:include "sidebar.html"}}
      <div class="content pure-u-1 pure-u-md-2-3">
        {{:include "menubar.html"}}

        <div id="content">
          {{:if with-title}}<h1>{{title}}</h1>{{:endif}}
          {{contents}}
        </div>

        {{:include "footer.html"}}
      </div>
    </div>
  </body>
</html>

Building the site

Configuration

We set the variables that our templates expect.

(defvar personal-site-full-name "Johnny Nobody"
  "A string with your full name.")

(defvar personal-site-position "Senior programmer"
  "A string with your full position or job appointment.")

(defvar personal-site-github "juanjosegarciaripoll"
  "If not NIL, your GitHub id.")

(defvar personal-site-google-scholar-id nil
  "If not NIL, your Google Scholar id.")

(defvar personal-site-email "nobody@nowhere.org"
  "If not NIL, your Google Scholar id.")

(defvar personal-site-linkedin nil
  "If not NIL, your Linkedin id.")

(defvar personal-site-organization "Emacs"
  "If not NIL, a string with the name of your organization, without the @ sign.")

(defvar personal-site-organization-url "https://www.gnu.org/software/emacs/"
  "If not NIL, the URL of your organization.")

(defvar personal-site-twitter "jjgarciaripoll"
  "If not NIL, a string with a Twitter handle, without the @ sign.")

Declaring the structure

We create a structure with four subgroups:

  • homepage-blog for all files under blog/
  • homepage-recipes for all files under recipes/
  • homepage-pages for all other pages.
  • homepage-assets for all non-HTML files, such as images and other resources.
(setq org-publish-project-alist
      `(("homepage-blog"
         :base-directory ,(expand-file-name "./blog")
         :root-directory ,(expand-file-name "./")
         :recursive t
         :base-extension "org"
         :publishing-directory ,(expand-file-name "./public_html/blog")
         ;; Exclude the blog archive index autogenerated below
         ;; Note that the regexp is relative to :base-directory
         :exclude "^index.org"
         :section-numbers nil
         :with-toc nil
         :with-date nil
         :html-template ,(templated-html-load-template "templates/blog.html")
         :publishing-function org-html-publish-to-templated-html
         :auto-sitemap t
         :sitemap-folders ignore
         :sitemap-style list
         :sitemap-title ,(concat personal-site-full-name "'s blog")
         :sitemap-filename "sitemap.inc"
         :sitemap-sort-files anti-chronologically
         )
        ("homepage-recipes"
         :base-directory ,(expand-file-name "./recipes")
         :root-directory ,(expand-file-name "./")
         :recursive t
         :base-extension "org"
         :publishing-directory ,(expand-file-name "./public_html/recipes")
         ;; Exclude the blog archive index autogenerated below
         ;; Note that the regexp is relative to :base-directory
         :exclude "^\\(index.org\\|*.inc\\)"
         :section-numbers nil
         :with-toc nil
         :with-date nil
         :html-template ,(templated-html-load-template "templates/recipe.html")
         :publishing-function org-html-publish-to-templated-html
         :auto-sitemap t
         :sitemap-folders ignore
         :sitemap-style list
         :sitemap-title ,(concat personal-site-full-name "'s cooking blog")
         :sitemap-filename "recipes.inc"
         :sitemap-sort-files anti-chronologically
         )
        ,(org-simple-rss-alist
          "homepage-rss"
          :base-directory (expand-file-name "./blog")
          :root-directory (expand-file-name "./")
          :recursive t
          :base-extension "org"
          :publishing-directory (expand-file-name "./public_html/blog")
          ;; Exclude the blog archive index autogenerated below
          ;; Note that the regexp is relative to :base-directory
          :exclude "^index.org"
          ;; The location of the RSS file is relative to :base-directory
          ;; Unfortunately, this is a limitation of using org-publish-sitemap
          :rss-filename "rss.xml"
          :rss-title "Juanjo García-Ripoll's blog"
          :rss-description "My blog of Emacsy things, programming and science"
          :rss-root "https://juanjose.garciaripoll.com/blog/")
        ("homepage-pages"
         :base-directory ,(expand-file-name "./")
         :recursive t
         :base-extension "org"
         :include ("blog/index.org")
         :exclude ,(regexp-opt '("blog" "recipes"))
         :publishing-directory ,(expand-file-name "./public_html")
         :section-numbers nil
         :with-toc nil
         :with-date nil
         :html-template ,(templated-html-load-template "templates/index.html")
         :publishing-function org-html-publish-to-templated-html
         )
        ("homepage-assets"
         :base-directory "./"
         :publishing-directory "./public_html"
         :recursive t
         :exclude "^\\(public_html\\|templates\\).*"
         :base-extension "\\(jpg\\|gif\\|png\\|css\\|js\\|el\\|nb\\|ipynb\\|pdf\\|xml\\)"
         :publishing-function org-publish-attachment)
        ("homepage" :components ("homepage-blog" "homepage-recipes" "homepage-pages"
                                 "homepage-rss" "homepage-assets"))
        ))

Build the site

We build all web pages.

(let ((enable-local-variables :all)
      (find-file-hook nil))
  (org-publish "homepage" t))

Build the sitemap

Many search engines rely on a file called sitemap.xml to index your site. We build it here.

(templated-html-create-sitemap-xml "public_html/sitemap.xml" "public_html" templated-html-site-url)