index ] [ cedega ] [ code ] [ emacs-wiki ] [ ewiki-bits ] [ key ] [ shots ] [ thinkpad-x40 ] [ wireless ]

emacs wiki examples

One extremely useful feature of emacs-wiki is the ability to embed elisp code in your pages which is evaluated during publishing to produce HTML. The shots and code sections of this site make extensive use of this - they are almost completely generated by elisp code. This is something of a "low maintenance" site.

For example, the following elisp fragment generates the shots page:


(labels ((file-contents (f)
           (if (file-exists-p f)
               (with-temp-buffer
                 (insert-file f)
                 (buffer-string))
             nil))
         (group-list (list num)
           "Group LIST into sublists of length NUM"
           (cond ((null list) nil)
                 ((null (nthcdr num list)) (list list))
                 (t (cons (subseq list 0 num)
                          (group-list (nthcdr num list) num)))))

         (make-gallery (src-dir web-root image-destination-dir
                                &optional thumb-size columns)
           "Output a HTML image gallery containing thumbnails and links
           for all image files in SRC-DIR. WEB-ROOT should be a
           path to the directory tree representing the web site,
           IMAGE-DESTINATION-DIR is the directory (relative to
           WEB-ROOT) that will contain the images and thumbnails.

           If specified, THUMB-SIZE is a cons whose car represents the
           width of thumbnails, and whose cdr represents their height.
           COLUMNS is the number of columns that should be produced in
           the HTML output.

           Requires the Imagemagick `convert' program to do the actual
           image manipulation."

           (let ((files (directory-files src-dir t
                                         "\\.\\(jpg\\|png\\|gif\\)$")))
             (when emacs-wiki-publishing-p
               ;; create thumbnails and copy the files
               (mapc (lambda (f)
                       (let ((destination (format "%s/%s/%s"
                                                  web-root
                                                  image-destination-dir
                                                  (file-name-nondirectory f)))
                             (thumb-size (or thumb-size (cons 300 3000))))
                         (unless (and (file-exists-p destination)
                                      (file-newer-than-file-p destination f))
                           (ignore-errors (copy-file f destination t)))
                         (let ((thumbnail (format "%s/%s/thumbs/%s"
                                                  web-root
                                                  image-destination-dir
                                                  (file-name-nondirectory f))))
                           (unless (and (file-exists-p thumbnail)
                                        (file-newer-than-file-p thumbnail f))
                             (shell-command (format
                                             "convert -geometry %sx%s %s %s"
                                             (car thumb-size) (cdr thumb-size)
                                             f thumbnail))))))
                     files))

             ;; generate one HTML table per shot
             (let ((shots
                    (mapcar
                     (lambda (f)
                       (let ((web-filename
                              (format "%s/%s"
                                      image-destination-dir
                                      (file-name-nondirectory f)))
                             (thumbnail
                              (format "%s/thumbs/%s"
                                      image-destination-dir
                                      (file-name-nondirectory f)))
                             (caption
                              (file-contents (format "%s.caption" f))))
                         (concat
                          "\n<table class=\"shottable\" width=\"50%\" border=\"0\">\n"
                          "<tr><td align=center>\n"
                          (format (concat "<a class=\"shot\" href=\"%s\">\n"
                                          "<img src=\"%s\" alt=\"%s\">\n")
                                  web-filename thumbnail
                                  (file-name-nondirectory
                                    (file-name-sans-extension thumbnail)))
                          "</a>"
                          "</td></tr>\n"

                          "<tr><td class=\"caption\">\n"
                          (if caption
                              (format "%s<br>\n" caption)
                            "<br>")
                          (format "<span class=\"size\">Size:%s bytes</span>\n"
                                  (nth 7 (file-attributes f)))
                          "</td></tr>"
                          "</table>")))
                     files)))

               (let ((columns (or columns 2)))
                 (if shots
                     (mapconcat
                      (lambda (items)
                        (concat
                         "<table class=\"shotrow\" border=0><tr>"
                         (mapconcat
                          (lambda (item)
                            (format "\n<td align=\"center\">%s</td>"
                                    item))
                          items
                          " ")
                         "\n</tr></table>\n"))
                      (group-list shots columns)
                      " ")
                   "Nothing at the moment\n"))))))

  (make-gallery "/home/mst/projects/site/shots/" "/home/mst/projects/site"
                 "shots" '(300 . 225)))

For each of the image files in my "shots" directory, it creates a HTML table containing a thumbnail hyperlink to the image, an optional caption (taken from a .caption file in my "shots" directory), and the size of the file in bytes. Then, the list of these tables is separated into pairs, and each pair is wrapped into a table of its own (to give the two column display). If no images were available, the generated page simply reads "Nothing at the moment".

This elisp generates the elisp section of the code page:

(mapconcat
 (lambda (file)
   (let ((header
          (with-temp-buffer
            (insert-file (concat "~/.elisp/homebrew/" file))
            (delete-non-matching-lines
             (concat "^;+ " file " --\\(-\\)? "))
            (replace-regexp-in-string
             (concat "^;+ " file " --- \\(.*\\)$") "\\1"
             (buffer-string))))
         (functions
          (with-temp-buffer
            (insert-file (concat "~/.elisp/homebrew/" file))
            (delete-non-matching-lines "^ *(defun [^ ]+ ")
            (let ((acc (mapconcat
                        'identity
                        (split-string
                         (replace-regexp-in-string
                          "^.*(defun \\([^ ]+\\) .*$"
                          "\\1"
                          (buffer-string))) ", ")))
              (if (string= acc "")
                  nil
                acc))))
         (macros
          (with-temp-buffer
            (insert-file (concat "~/.elisp/homebrew/" file))
            (delete-non-matching-lines "^ *(defmacro [^ ]+ ")
            (let ((acc (mapconcat
                        'identity
                        (split-string
                         (replace-regexp-in-string
                          "^.*(defmacro \\([^ ]+\\) .*$"
                          "\\1"
                          (buffer-string))) ", ")))
              (if (string= acc "")
                  nil
                acc)))))

     (concat "<li><a href=\"elisp/" file "\">" file "</a>"
             (if (> (length header) 0)
                 (concat " - <b>" header "</b>")
               "")
             "<br>\n"
             (format "<strong>Size:</strong> %s bytes<br>\n"
                     (nth 7 (file-attributes ;; size
                             (file-truename
                              (concat "~/.elisp/homebrew/" file)))))
             "<div style=\"text-align: left\">\n"
             (if functions
                 (concat "<strong>Functions:</strong> " functions "\n")
                 "")
             (if macros
                 (concat (if functions "<br>" "")
                         "<strong>Macros:</strong> "
                         macros "\n")
               "")
             "</div><br></li>")))
 (remove-if-not
  (lambda (file) (string-match "^[^.].*\\.el$\\|^dot[^~]+$" file))
  (sort (directory-files "~/.elisp/homebrew/") 'string<)) "\n")

For each of the ".el" or "dot*" files in my ~/.elisp/homebrew/ directory, this code determines two pieces of information: a description of what the file does (which is the text after the "---" in a line like: ";; foo.el --- do foo") and the functions that the file provides (which is done by looking for defuns in the file). It then outputs a list item containing a link to the file, the description, the size of the file and the functions it provides.

The navigation bar across the top of each page is controlled by some lisp code contained in the 'emacs-wiki-publishing-header' variable. It basically constructs the header out of the contents of my wiki directory. The header is:

"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
  <head>
    <title><lisp>(emacs-wiki-page-title)</lisp></title>
    <meta name="generator" content="emacs-wiki.el">
    <meta http-equiv="<lisp>emacs-wiki-meta-http-equiv</lisp>"
          content="<lisp>emacs-wiki-meta-content</lisp>">
    <link rev="made" href="<lisp>emacs-wiki-maintainer</lisp>">
    <link rel="home" href="<lisp>(emacs-wiki-published-name
                                     emacs-wiki-home-page)</lisp>">
    <link rel="index" href="<lisp>(emacs-wiki-published-name
                                      emacs-wiki-index-page)</lisp>">
    <lisp>
      (replace-regexp-in-string " />" ">" emacs-wiki-style-sheet)
    </lisp>
  </head>
  <body>
    <p><a id="top"></a></p>
    <div style="text-align: right">
      <lisp>
        (let ((files (remove-if (lambda (f) (member f '("WikiMarkup")))
                                (mapcar 'car
                                       (cadr
                                         (assoc emacs-wiki-current-project
                                                emacs-wiki-file-alist))))))
          (mapconcat
           (lambda (file)
             (if (string= file
                          (file-name-nondirectory
                           (or (buffer-file-name) "")))
                 (format "[&nbsp<span class=active>%s</span>&nbsp]" file)
               (format "[&nbsp<a href=%S>%s</a>&nbsp]"
                       (emacs-wiki-published-name file)
                       file)))
           (sort files
                 (lambda (e1 e2)
                   (cond ((string= e1 "index") t)
                         ((string= e2 "index") nil)
                         (t (string< e1 e2)))))
           " "))
      </lisp>
    </div>
    <h1><lisp>(emacs-wiki-page-title)</lisp></h1>
    <!-- Page published by Emacs Wiki begins here -->
"

The key page is also generated during publishing, extracting my public key from my GPG keyring with the following:


<pre> <command file="/bin/bash">
gpg -a --export 3A2EC93E 
</command> </pre>

To use these yourself, open a wiki page and disable font-lock mode (c-c c-l in GNU emacs and I believe also XEmacs). Paste the lisp fragment into your buffer, and surround it with <lisp> ... </lisp> tags. Then just modify any of the paths that I've impolitely hard-coded in, publish and you should be good to go.


The above code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version.

This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.