Идея переделать свою домашнюю страницу возникла у меня очень давно — старый вариант существовал уже более 10 лет и имел крайне примитивный дизайн, на уровне HTML версии 2.
Поскольку на нынешнем моем хостинге мне доступна только опция раздачи статических файлов, то это сразу наложило существенные ограничения на реализацию. Сначала я собрался писать генератор на чем-нибудь вроде Perl'а, но вовремя опомнился, и вспомнил, что видел сайты сделанные с помощью Emacs Muse, который я уже использовал для того, чтобы писать различные заметки и статьи.
Другой задачей был новый дизайн сайта. Поскольку художник/верстальщик из меня некудышный, я полез в Интернет в поиске подходящих бесплатных шаблонов сайтов. В итоге я набрел на сайт Andreas Viklund'а и взял понравившийся мне шаблон (правда немного пришлось его подкрутить для использования нужных шрифтов и т.п.).
Основное оформление шаблона прикрутилось к Muse без каких-либо проблем, и теперь я использую его для всех своих новых заметок и статей, некоторые из которых вы можете найти на сайте. Пришлось лишь немного попрограммировать для того, чтобы сайт обзавелся изменяемой частью в виде меню для навигации, но об этом в разделе "Реализация".
Для генерации сайта из исходных текстов Muse используется конфигурационный файл, состоящий из нескольких частей, описанных ниже.
Сначала мы инициализируем пакет и выставляем нужные переменные. Среди загружаемых пакетов
есть неиспользуемые непосредственно в генерации сайта, но этот же конфигурационный файл у
меня используется и для других проектов. А переменные определяют поведение пакета,
например, наличие расширения .muse
для всех документов, кодировку документов и
генерируемого html и т.п.
(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 () (auto-fill-mode 1) (flyspell-mode 1) (footnote-mode 1)) (add-hook 'muse-mode-hook 'my-muse-mode-hook)
Для генерации содержимого сайта я переопределил два стиля — для HTML & PDF, что позволяет мне использовать стили, отличающиеся от стилей, применяемых для других моих проектов — статей и всяческих записей.
(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")
Файлы используемые в этих стилях также лежат на моем сайте: HTML — header.tmpl &
footer.tmpl, PDF — header.tex & footer.tex. Исходные файлы Muse также лежат на сайте, и
вы можете увидеть их, заменив расширение .html
на .muse
, например, для данного расказа это
будет ./EmacsMuseMyPage.muse.
Затем определяется проект сайта:
(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[^/]*$"))))
Сайт выводится в тот же каталог, где находятся исходные файлы (с учетом их расположения в
иерархии каталогов проекта) — в данном случае это ~/projects/my-page-muse
.
muse-project-alist-dirs
генерирует список каталогов проекта для которых будет применяться
заданный стиль (my-page-html
). А две последних записи используются для генерации
PDF-версии английского и русского резюме используя стиль my-page-pdf
.
Для правильной генерации ссылок на стили и прочие файлы я написал небольшую функцию
muse-gen-relative-name
, которая принимает в качестве параметра имя файла относительно
корневого каталога проекта, и генерирует имя файла относительно текущего файла:
(defun muse-gen-relative-name (name) (concat (file-name-directory (muse-wiki-resolve-project-page (muse-project))) name))
Другая функция, muse-mp-detect-language
, используется для определения того, какой язык
используется для данного файла1:
(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))
Структура меню, генерируемая для каждой страницы, определяется переменной my-page-menu
, и
представляет собой ассоциативный список, каждый элемент которого состоит из названия языка
и списка пар, представляющих имя файла (или каталога) и соответствующее название пункта
меню.
(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")))))
И собственно функция для генерации меню на каждой странице, входящей в проект. Она определяет язык страницы, получает имя генерируемого файла, и в процессе генерации меню выделяет аттрибутом пункт, соответствующий нужному разделу сайта (например, этот рассказ находится в разделе "Статьи").
(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))
Кроме того, нужна еще одна функция, которая используется в footer для отображения даты последнего изменения страницы:
(defun generate-change-date (file) (when (file-exists-p file) (let* ((fa (file-attributes file)) (mod-time (nth 6 fa))) (format-time-string "%d.%m.%Y %R" mod-time))))
Вот и все. Все получилось достаточно просто и не требует никаких дополнительных действий по изменению сгенеренного html. Полученные файлы может сразу загружать на сервер.
1. Реализация этой функции не особо оптимальна и может приводить к ошибкам, но ее можно переписать не затрагивая остальной код.
Last change: 05.03.2013 16:54