Меню:


Idea of recreate my home was pretty old — old variant was created more than 10 years ago, and had very primitive design, like sites of HTML, version 2.

Preparatory work

As on my current hosting I able to host only static files, this restricted my choice of technologies, that could be used for implementation. First, I wanted to write html generator myself, using something like Perl, but I just remembered, that I had seen sites, that was created with Emacs Muse, that I already use for my notes and articles.

Another task for this site was a creation of new design. As I'm not so good designer/html coder, I searched Internet for good free site's design. During surfing, I found site of Andreas Viklund and took suitable template (I change it in the end, but this was mostly changes in fonts size, etc.)

Main parts of design was plugged to Muse without any problem, and I use it for all my notes and articles, some of them you can find on my site. To create a dynamic parts of pages — menus for navigation between parts of site, I wrote some amount of code in elisp.

Implementation

For generation of site from Muse source files I create the configuration file, consisting from several parts, described below.

First we need to initialize package and set needed variables. Some loadable packages doesn't used for site generation, but they used for my other projects. Variables changes behaviour of package, for example, .muse extension for all Muse files, encoding for files & generated html, etc.

(require 'muse-mode)
(require 'muse-html)
(require 'muse-colors)
(require 'muse-wiki)
(require 'muse-latex)
(require 'muse-texinfo)
(require 'muse-docbook)
(require 'muse-project)

(add-to-list 'auto-mode-alist '("\\.muse$" . muse-mode))

(custom-set-variables
 '(muse-html-encoding-default (quote utf-8))
 '(muse-html-meta-content-encoding (quote utf-8))
 '(muse-html-charset-default "utf-8")
 '(muse-file-extension "muse")
 '(muse-mode-auto-p nil)
 '(muse-wiki-allow-nonexistent-wikiword nil)
 '(muse-wiki-use-wikiword nil)
 '(muse-ignored-extensions (quote ("bz2" "gz" "[Zz]" "rej" "orig" "png" "hgignore" "gif"
                                   "css" "jpg" "html" "sh" "lftp" "pdf")))
)

(defun my-muse-mode-hook ()
  (setq auto-fill-mode t)
  (flyspell-mode 1)
  (footnote-mode 1)
  )
(add-hook 'muse-mode-hook 'my-muse-mode-hook)

For site generation, I redefine two style — for HTML & PDF — this allows me to use styles, that differ from styles, used for other projects.

(muse-derive-style "my-page-html" "html"
                   :header "~/projects/my-page-muse/header.tmpl"
                   :footer "~/projects/my-page-muse/footer.tmpl")

(muse-derive-style "my-page-pdf" "pdf"
                   :header "~/projects/my-page-muse/header.tex"
                   :footer "~/projects/my-page-muse/footer.tex")

These files you can find on my site: HTML — header.tmpl & footer.tmpl, PDF — header.tex & footer.tex. Muse source files also uploaded to site, and you can find them if you replace .html to .muse, for example, for this article this file is ./EmacsMuseMyPage.muse.

You also need to define project for this site:

(setq muse-project-alist
      `(
        ("my-page"
         (,@(muse-project-alist-dirs "~/projects/my-page-muse") :default "index")
         ,@(muse-project-alist-styles "~/projects/my-page-muse"
                                      "~/projects/my-page-muse"
                                      "my-page-html")
         (:base "my-page-pdf"
                :path "~/projects/my-page-muse/en"
                :include "/alexott-cv-en[^/]*$")
         (:base "my-page-pdf"
                :path "~/projects/my-page-muse/ru"
                :include "/alexott-cv-ru[^/]*$"))))

Generated files are also placed in catalog with source files — in my case this is ~/projects/my-page-muse. muse-project-alist-dirs generate list of directories for which given style (my-page-html) will used. And two last records used for generation of PDF-files for english & russian versions of CV (using style my-page-pdf).

For proper generation of links to style files (.css) and images I wrote small function muse-gen-relative-name, that accept file name (relative to root directory of project) as parameter, and generate proper file name relative to current file:

(defun muse-gen-relative-name (name)
  (concat
   (file-name-directory (muse-wiki-resolve-project-page (muse-project)))
   name))

Other function, muse-mp-detect-language, is used for detecting which language used for current file1:

(defun muse-mp-detect-language ()
  (let ((lang "NN")
        (cur-dir (file-name-directory (muse-current-file)))
        )
    (let ((smatch (string-match "/\\(ru\\|en\\|de\\)/" cur-dir)))
      (when smatch
        (setq lang (substring cur-dir (+ smatch 1) (+ smatch 3)))))
    lang))

Structure of navigation menu, generated for every page, is defined by variable my-page-menu, and represented as alist, every element of which consists from language tag, and list of pair, representing file (or directory) name and corresponding title for menu item.

(setq my-page-menu '(("ru" . ( ("index.html" . "Главная")
                               ("cf/" . "Информационная безопасность")
                               ("fp/" . "Функциональное программирование")
                               ("scheme/" . "Scheme")
                               ("lisp/" . "emacs-lisp")
                               ("cpp/" . "C++")
                               ("oss/" . "Open Source Projects")
                               ("emacs/" . "Emacs")
                               ("writings/" . "Статьи")))
                     ("en" . ( ("index.html" . "Main")
                               ("cf/" . "Information Security")
                               ("fp/" . "Functional programming")
                               ("cpp/" . "C++")
                               ("oss/" . "Open Source Projects")
                               ("emacs/" . "Emacs")
                               ("writings/" . "Articles")))))

And at the end, function for menu generation, that called for every page in project. This function detect language for page, get name of generated file and set attribute for menu item, matching current path (for example, this note is in section "Articles").

(defun muse-mp-generate-menu ()
  (let* ((menu-lang (muse-mp-detect-language))
         (menu-struct (assoc menu-lang my-page-menu))
         (menu-string "")
         (rel-dir (file-name-directory (muse-wiki-resolve-project-page (muse-project))))
         (rel-path (if (> (length rel-dir) 2)   (substring rel-dir 3) ""))
         (cur-path-muse (muse-current-file))
         (cur-path-html (replace-regexp-in-string "\\.muse" ".html" cur-path-muse))
         )
      (when menu-struct
        (let ((menu-list (if (not (null menu-struct)) (cdr menu-struct))))
          (setq menu-string
                (concat "<ul class=\"avmenu\">"
                        (apply #'concat
                               (mapcar
                                (lambda (x)
                                  (concat "<li><a href=\"" rel-path (car x)
                                          (if (string-match (concat "/" menu-lang "/" (car x))
                                                            cur-path-html)
                                              "\" class=\"current\""
                                            "\"")
                                          ">" (cdr x) "</a></li>"))
                                menu-list))
                        "</ul>"))))
      menu-string))

That's all. All is pretty simple and doesn't require manual changes in generated pages. Generated files are ready to upload to server.


1. Implementation of this function is not optimal and it could fail, but it could be changed without affecting other code.