;;; emacs-wiki-journal.el --- Maintain a weblog style journal with emacs-wiki

;; Copyright 2003 Gary V. Vaughan (gary AT gnu DOT org)
;; This file has been hacked by Ole Arndt (ole AT sugarshark DOT com)

;; Emacs Lisp Archive Entry
;; Filename: emacs-wiki-journal.el
;; Version: 0.3
;; Date: Fri, 2 May 2003
;; Keywords: hypermedia
;; Author: Gary V. Vaughan (gary AT gnu DOT org)
;; Maintainer: Gary V. Vaughan (gary AT gnu DOT org)
;; Description: Maintain weblog style journal in a local Emacs Wiki
;; URL: http://www.oranda.demon.co.uk/dist/emacs-wiki-journal.el
;; Compatibility: XEmacs21

;; This file is not part of GNU Emacs.

;; This 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 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.

;;; Commentary:

;; I maintain the hypertext parts of my website with John Wiegley's
;; emacs-wiki.el, now maintained by Damien Elmes at
;; http://repose.cx/emacs/wiki.  You will need to install a copy of that
;; file before this one is of any use to you.

;; * Startup

;; To begin using emacs-wiki-journal, put this in your .emacs file:

;;   (load "emacs-wiki-journal")

;; Now you can add an entry to your journal with
;; M-x emacs-wiki-journal-add-entry, give it a WikiName for a category
;; to index the entry under (usually beginning "Category") and a header
;; line for the new entry.  A suitable header for today's entry will
;; added to emacs-wiki-journal-wiki where you add the body for the entry
;; and a cross reference will be added to the category index.  If you have
;; a PNG icon in the emacs-wiki-category-icons-url then the icon named
;; after the CategoryWiki will be inserted in the journal header.

;; You may need to set up a stylesheet to layout the journal page as you
;; want it.  Look at mine as an example:

;;   http://www.oranda.demon.co.uk/journal.css

;; You should also type M-x customize-group, and give the name
;; "emacs-wiki-journal".  Change it to suit your preferences.  Each of
;; the options has its own documentation.

;; * Key Bindings

;; You might want to bind emacs-wiki-journal-add-entry to a key in your
;; global keymap:

;;   (define-key ctl-x-4-map
;;               "j" 'emacs-wiki-journal-add-entry-other-window)

;; And also in emacs-wiki mode:

;;   (add-hook 'emacs-wiki-mode-hook
;;             (lambda ()
;;               (local-set-key "C-cj" 'emacs-wiki-journal-add-entry)))


;;; Code:

;; The parts of this code:
;;
;; * Customization group setup
;; * Category Index maintenance
;; * JournalWiki maintenance

;; TODO:
;;
;; Only add category icons if the icon file exists
;; Search for category icon using emacs-wiki-image-regexp
;; Read in the CategoryWiki using emacs-wiki-read-name
;; Split the JournalWiki up by year and generate annual chronological indices


(require 'cl)
(require 'emacs-wiki)

;;; Options:

(defgroup emacs-wiki-journal nil
  "Options controlling the behaviour of Emacs Wiki journaling.
See `emacs-wiki-journal-add-entry' for more information."
  :group 'emacs-wiki)

(defcustom emacs-wiki-journal-wiki "MyJournal"
  "*Default name of the file to which journal entries are added."
  :type 'string
  :group 'emacs-wiki-journal)

(defcustom emacs-wiki-journal-category-directory nil
  "*Default directory to search for category wiki files."
  :type 'directory
  :group 'emacs-wiki-journal)

(defcustom emacs-wiki-journal-icons-subdirectory "images"
  "*Default base url to a directory containing category icons."
  :type 'string
  :group 'emacs-wiki-journal)

(defcustom emacs-wiki-journal-time-format "%a, %e %b. %2y"
  "*Format for the date string of journal entries.
See `format-time-string' for more information."
  :type 'string
  :group 'emacs-wiki-journal)

(defcustom emacs-wiki-journal-category-regexp "^Category"
  "*Each of the category index Wiki files start with this prefix."
  :type 'string
  :group 'emacs-wiki-journal)


;;; Category Index maintenance:

(defun emacs-wiki-journal-category-alist (&optional no-check-p)
  "Return possible category index Wikis in `emacs-wiki-directories'.
If NO-CHECK-P is non-nil, then don't check for changes in the directories
to decide whether to re-read the cached alist, just re-read the disk."
  (let ((file-alist (emacs-wiki-file-alist no-check-p))
        (category-alist nil))
    (save-match-data
      (while file-alist
        (if (string-match emacs-wiki-journal-category-regexp
                          (caar file-alist))
            (setq category-alist (cons (car file-alist)
                                       category-alist)))
        (setq file-alist (cdr file-alist))))
    category-alist))


(defun emacs-wiki-journal-prompt-for-category-wiki ()
  "Prompt for a category index file."
  (let* ((directory (or emacs-wiki-journal-category-directory
                        (car emacs-wiki-directories)))
         (file-alist (emacs-wiki-journal-category-alist))
         (emacs-wiki-default-page (or (caar file-alist) "CategoryEmacs")))
    (emacs-wiki-read-name file-alist "Category Wiki: ")))


(defun emacs-wiki-journal-make-extended-link (target-url
                                              &optional link-description)
  "Make an Emacs Wiki extended link from TARGET-URL and LINK-DESCRIPTION,"
  (concat "[[" target-url
          (and link-description (concat "][" link-description))
          "]]"))

(defun emacs-wiki-journal-add-category-entry (wiki target-url
                                              &optional link-description)
  "Find category index file and add an entry for today."
  (emacs-wiki-find-file wiki)
  (undo-boundary)
  (goto-char (point-min))

  ;; skip to first heading
  (goto-char 
   (or (search-forward-regexp "^\\* " (point-max) t) (point)))
  (beginning-of-line)
  (forward-line 1)

  (goto-char 
   (or (search-forward-regexp "^- " (point-max) t) (point)))
  (beginning-of-line)
  (insert (concat "- "
                  (format-time-string emacs-wiki-journal-time-format)
                  " "
                  (emacs-wiki-journal-make-extended-link
                   target-url link-description)
                  "\n")))


;;; JournalWiki maintenance:

(defun emacs-wiki-journal-1+-string (value)
  "Increment an ascii encoded number."
  (int-to-string (1+ (string-to-int value))))

;;;###autoload
(defun emacs-wiki-journal-add-entry (&optional other-window)
  "Find journal file and add an entry and category index for today."
  (interactive)
  (let* ((category-wiki (emacs-wiki-journal-prompt-for-category-wiki))
         (journal-entry-heading (read-from-minibuffer "Journal Heading: "))
         (category-anchor-base (downcase category-wiki))
         (anchor-regexp (concat "^#" (regexp-quote category-anchor-base)
                                "\\([0-9][0-9]*\\)"))
         (anchor-ord "0"))

    (emacs-wiki-find-file emacs-wiki-journal-wiki)
    (goto-char (point-min))
    (save-excursion
      (if (search-forward-regexp anchor-regexp (point-max) t)
          (setq anchor-ord 
                (emacs-wiki-journal-1+-string
                 (buffer-substring (match-beginning 1)
                                   (match-end 1))))))
    (save-excursion
      (emacs-wiki-journal-add-category-entry
       category-wiki
       (concat emacs-wiki-journal-wiki "#" category-anchor-base anchor-ord)
       journal-entry-heading))

    ;; skip # lines and blanks at the start of the buffer
    (while (and (looking-at "^\\(#\\|\n\\)")
                (equal 0 (forward-line 1))))

    ;; skip to first heading
    (goto-char 
     (or (search-forward-regexp "^\\* " (point-max) t) (point)))
    (beginning-of-line)

    (let* ((time-string (format-time-string
                         emacs-wiki-journal-time-format))
           (icon-file-name (concat emacs-wiki-journal-icons-subdirectory "/"
                                   category-wiki ".png"))
           (icon-link (if (file-exists-p icon-file-name)
                          (concat (emacs-wiki-journal-make-extended-link
                            icon-file-name) " ")
                        "")))
      ;; only add a new date if different from the top entry
      (if (not (looking-at (regexp-quote (concat "* " time-string))))
          (insert (concat "\n* " time-string "\n\n"))
        (forward-line 1)
        (insert "\n"))

      (open-line 1)
      (insert (concat "#" category-anchor-base anchor-ord " " icon-link "\n** " 
                      journal-entry-heading "\n*** " category-wiki "\n\n\n")))

    (emacs-wiki-find-file emacs-wiki-journal-wiki)
    (forward-line -1)                   ; move to insertion point
    ))

;;;###autoload
(defun emacs-wiki-journal-add-entry-other-window ()
  "Find category index file in another window and add an entry for today."
  (interactive)
  (emacs-wiki-journal-add-entry t))

(provide 'emacs-wiki-journal)

;;; emacs-wiki-journal.el ends here