emacs-wiki-discuss
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[emacs-wiki-discuss] planner-report.el


From: Andrew J. Korty
Subject: [emacs-wiki-discuss] planner-report.el
Date: Mon, 21 Jun 2004 18:03:51 -0500
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3.50 (darwin)

Here's something I wrote to generate timely status reports complete
with tasks and notes that pertain to a given time frame.  If you use
planner-authz.el, you can generate the reports based on planner pages
readable by certain users.  To use this feature, you must install the
updated version of planner-authz.el attached.

Again, I welcome any suggestions, patches, or comments on how to
improve my Lisp code.  If people like this stuff, I'll put it on the
web somewhere.

-- 
Andrew J. Korty, Principal Security Engineer, GCIA, GCFA
Office of the Vice President for Information Technology
Indiana University
;;; planner-report.el --- create a timely status report based on planner pages

;; Copyright 2004 by Andrew J. Korty <address@hidden>

;; Emacs Lisp Archive Entry
;; Filename: planner-authz.el
;; Version: $Revision: 1.4 $
;; Keywords: hypermedia
;; Author: Andrew J. Korty <address@hidden>
;; Maintainer: Andrew J. Korty <address@hidden>
;; Description: Create a timely status report based on planner pages
;; URL: 
;; Compatibility: Emacs21

;; 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:

;; This library creates a status report for a given timespan.  The
;; report itself is just another emacs-wiki page in your planner
;; directory.  Once generated, it contains tasks and notes culled from
;; active project pages.  Tasks are only shown if they are incomplete
;; or were completed within the timespan.  Notes are shown if they
;; were created during the timespan.  Tasks and notes are grouped
;; together under a heading for their corresponding project.

;; The idea is you have one of these status reports generated
;; periodically (say, every couple of weeks).  Perhaps you use cron to
;; run them automatically and then mail you a reminder that they've
;; been done.  Then you can edit the page, adding verbiage where it is
;; needed and removing irrelevant items.  This editing process is as
;; easy as editing any other emacs-wiki page.  Finally, you can
;; publish the page along with the rest of your planner using
;; M-x emacs-wiki-publish.

;; If you use planner-authz.el, you can tell planner-report.el only to
;; consult project pages that a given list of users
;; (planner-report-authz) can access when generating the report.  For
;; example, if you're preparing a status report for your boss, add
;; yourself and him to planner-report-authz.  The resulting status
;; report will only contain information the two of you are supposed to
;; have access to, and the report itself will be similarly restricted.

;; * Startup and Usage

;; Once this file is installed, add the following to your .emacs file:

;;   (require 'planner-report)

;; Then you can use the following command to generate a status report:

;;   M-x planner-report-generate

;; You will be prompted for a beginning and ending date, and then the
;; status report will be generated.  You can then edit it to your
;; liking and publish it just like you would the rest of your planner.

;; * Customization

;; All user-serviceable options can be customized with
;; M-x customize-group RET planner-report RET.

;;; Code

;; $Id: planner-report.el,v 1.4 2004/06/21 22:58:36 ajk Exp $

(defgroup planner-report nil
  "A planner.el extension for generating timely status reports
based on planner pages."
  :group 'planner
  :prefix "planner-report")

(defcustom planner-report-authz nil
  "List of users a status report should be restricted to.  When
status reports are generated, only planner pages accessible by
these users will be consulted, and the resulting status report
will be similarly restricted."
  :group 'planner-report
  :type '(repeat string))

(defvar planner-report-version "$Revision: 1.4 $"
  "Version of of planner-report.el.")

(defun planner-report-generate (begin end)
  "Generate a status report spanning a period from BEG to END,
which are in the format YYYY.MM.DD."
  (interactive
   (let ((planner-expand-name-favor-future-p
                (or planner-expand-name-favor-future-p
                    planner-task-dates-favor-future-p)))
           (list (planner-report-read-date "Start date")
                 (planner-report-read-date "End date"))))
  (save-some-buffers nil (lambda () (derived-mode-p 'emacs-wiki-mode)))
  (cd planner-directory)
  (with-temp-buffer
    (when planner-report-authz
      (require 'planner-authz)
      (insert "#authz " (mapconcat 'identity planner-report-authz " ") "\n"))
    (insert "#title Status report for " begin " to " end "\n")
    (let ((pages (if planner-report-authz
                     (planner-authz-file-alist planner-report-authz)
                   (planner-file-alist)))
          notes tasks)
      (while pages
        (when (caar pages)
          ;; Add only project pages, and skip other status reports
          (unless (or (string-match planner-name-regexp (caar pages))
                      (string-match "^StatusReport" (caar pages)))
            (with-temp-buffer
              (insert-file-contents-literally (cdar pages))
              (setq tasks (planner-report-find-tasks (caar pages) begin end))
              (setq notes (planner-report-find-notes (caar pages) begin end))))
          ;; Insert a linked heading if we found anything
          (if (or notes tasks)
              (insert "\n* [[" (caar pages) "]["
                      (or (emacs-wiki-get-title-fast (cdar pages))
                          (emacs-wiki-prettify-title (caar pages)))
                      "]]\n\n"))
          (when tasks
              (insert tasks "\n\n")
              (setq tasks nil))
          (when notes
              (insert notes "\n")
              (setq notes nil)))
        (setq pages (cdr pages))))
    (write-file
     (concat "StatusReport"
             (planner-date-to-filename (decode-time (current-time)))) t)))

(defun planner-report-find-notes (page begin end)
  "Find notes on planner page PAGE that were created between BEG
and END, which are formatted as YYYY.MM.DD."
  (goto-char (point-min))
  (let (result)
    (while (re-search-forward "^\\.#[0-9]+\\s-+" nil t)
      (let ((note
              (buffer-substring
               (line-beginning-position)
               (progn
                 ;; Find the end of this note (maybe EOF)
                 (re-search-forward "^\\(\\.#[0-9]+\\s-+\\|\\*\\*?\\s-+\\)"
                                    nil 1)
                 (goto-char (line-beginning-position))
                 (point))))
             (info (planner-current-note-info)))
        (when info
          (let* ((link (planner-note-link info))
                 (date (if link (emacs-wiki-wiki-base link))))
            ;; Snarf if note is associated with a date that is in range
            (and date
                 (not (string< date begin))
                 (not (string< end date))
                 (setq result (if result (concat note result) note)))))))
    result))

(defun planner-report-find-tasks (page begin end)
  "Find tasks on planner page PAGE that were created between BEG
and END, which are formatted as YYYY.MM.DD."
  (goto-char (point-min))
  (let (result)
    (while (re-search-forward "^#[A-C]" nil t)
      (let* ((task (buffer-substring (line-beginning-position)
                                     (line-end-position)))
             (info (planner-task-info-from-string page task)))
        (when info
          (let ((date (planner-task-date info)))
            ;; If the task isn't completed and has a date, snarf.  If it
            ;; has been completed and the date is in range, snarf.
            (and date
                 (or (not (string= (planner-task-status info) "X"))
                     (and (not (string< date begin))
                          (not (string< end date))))
                 (setq result (if result (concat result "\n" task) task)))))))
    result))

(defun planner-report-read-date (prompt)
  "Prompt for a date string in the minibuffer using PROMPT."
  (save-excursion
    (save-window-excursion
      (calendar)
      (let ((old-map (current-local-map)))
        (unwind-protect
            (let ((map (copy-keymap calendar-mode-map)))
              (use-local-map map)
              (define-key map (kbd "RET") 'planner-calendar-select)
              (define-key map [mouse-1] 'planner-calendar-select)
              (setq planner-calendar-selected-date nil)
              (let ((text (read-string (format "%s %s" prompt
                           (format-time-string
                            "(%Y.%m.%d, %m.%d, %d): ")))))
                (or planner-calendar-selected-date
                    (planner-expand-name text))))
          (use-local-map old-map))))))
;;; planner-authz.el --- control access to portions of published planner pages

;; Copyright 2004 by Andrew J. Korty <address@hidden>

;; Emacs Lisp Archive Entry
;; Filename: planner-authz.el
;; Version: $Revision: 1.14 $
;; Keywords: hypermedia
;; Author: Andrew J. Korty <address@hidden>
;; Maintainer: Andrew J. Korty <address@hidden>
;; Description: Control access to portions of published planner pages
;; URL: 
;; Compatibility: Emacs21

;; 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:

;; This library lets you publish your planner pages while controlling
;; access to certain portions of them to users you specify.  When you
;; load this library, you gain access to two additional markup
;; directives to use in your planner pages.  The <authz> tag lets you
;; restrict access to arbitrary content as follows:

;;   Here is a sentence everyone should see.  This sentence also
;;   contains no sensitive data whatsoever.  <authz users="ajk">This
;;   sentence, however, talks about my predilection for that French
;;   vanilla instant coffee that comes in the little tin, and I'm
;;   embarrassed for anyone else to know about that.</authz> And
;;   here's some more perfectly innocuous content.

;; You can use <authz> tags to mark up entire paragraphs, tasks,
;; notes, and anything else.  The tags are replaced with Mason code 

;; The #authz directive restricts access to an entire page.  A Mason
;; call is added to this page to generate a 403 error when someone not
;; listed tries to access it.  Any notes or tasks on a
;; #authz-protected page are also wrapped in Mason code on linked
;; pages.

;; * Startup

;; Add the following to your .emacs file to cause
;; M-x emacs-wiki-publish to automatically use planner-authz features.

;;   (require 'planner-authz)

;; * Customization

;; All user-serviceable options can be customized with
;; M-x customize-group RET planner-authz RET.

;; * Defaults

;; The following customization options let you set default access
;; lists for pages that don't have explicit settings:

;; planner-authz-project-default

;;   Default access list for project pages (not day pages).  If a
;;   given project page doesn't contain a #authz tag, it will receive
;;   the access list defined here.  If this variable is nil, all users
;;   will be allowed to view the page.  No corresponding variable is
;;   provided for day pages because it doesn't seem like you'd ever
;;   want to control access based on what day it was.  (But I will
;;   accept patches. :-) Notes and tasks referencing pages without
;;   #authz tags will also be restricted to the users listed here.

;; planner-authz-day-note-default

;;   Default access list for notes on day pages not associated with
;;   any project.  There is way to set a default for notes on project
;;   pages for the reason above; they would only be associated with
;;   date pages anyway.

;; planner-authz-day-task-default

;;   Same as above but for tasks.

;;; Todo

;; - Make more specific tags override less specific ones, rather than
;;   more restrictive overriding less restrictive
;; - Support something other than Mason

;;; Code

;; $Id: planner-authz.el,v 1.14 2004/06/21 22:28:32 ajk Exp $

(require 'planner)

;; Customization options

(defgroup planner-authz nil
  "A planner.el extension for restricting portions of your
published pages to specified users."
  :group 'planner
  :prefix "planner-authz")

(defcustom planner-authz-mason-component-contents
  "<%once>
sub authz {
        my $r_user = $r ? $r->connection->user : $ENV{REMOTE_USER} or return 0;
        foreach (@_) { return 1 if $r_user eq $_ }
        return 0;
}
</%once>        
<%method content>
<%args>@users</%args>
% if (authz @users) {
        <% $m->content %>
% }
</%method>
<%method page>
<%args>@users</%args>
<%perl>
unless (authz @users) {
        $m->clear_buffer;
        $m->abort(404);
}
</%perl>
</%method>
"
  "Mason code to be stored in component named
`planner-authz-mason-component-name'."
  :group 'planner-authz
  :type 'string)

(defcustom planner-authz-mason-component-name "authz.mas"
  "Name of Mason component that restricts content."
  :group 'planner-authz
  :type 'string)

(defcustom planner-authz-day-note-default nil
  "Default list of users for restricting notes on day pages that
are not linked to project pages."
  :group 'planner-authz
  :type '(repeat string))

(defcustom planner-authz-day-task-default nil
  "Default list of users for restricting tasks on day pages that
are not linked to project pages."
  :group 'planner-authz
  :type '(repeat string))

(defcustom planner-authz-project-default nil
  "Default list of users for restricting project pages if #authz
is unspecified."
  :group 'planner-authz
  :type '(repeat string))

(defcustom planner-authz-publishing-markup-first
  '(["^#authz\\s-+\\(.+\\)\n+" 0 ""]
    ["^#\\([A-C]\\)\\([0-9]*\\)\\s-*\\([_oX>]\\)\\s-*\\(.+\\)" 0
     planner-authz-markup-task]
    ["^\\.#\\([0-9]+\\)" 0 planner-authz-markup-note])
  "List of emacs-wiki markup rules to apply when publishing pages
with planner-authz in effect.  This list of rules will be applied
before any planner or emacs-wiki rules."
  :group 'planner-authz
  :type '(repeat
          (vector :tag "Markup rule"
                  (choice regexp symbol)
                  integer
                  (choice string function symbol))))

(defcustom planner-authz-publishing-markup-last
  '(
    ;; Change : to | (the | would cause table markup).

    ["<&:" 0 "<&|"]

    ;; Move Masonry to outside of list item tags.

    ["\\(<li>\\)\\(<&[^&]*&>\\)" 0 "\\2\\1"]
    ["\\(</&>\\)\\(\\s-*\\)\\(</li>\\)" 0 "\\2\\3\\1"]

    ;; Remove <p> tags from inserted Masonry.

    ["<p>\\s-*\\(</?&[^&]*>\\)" 0 "\\1"]
    ["\\(</?&[^&]*>\\)\\s-*</p>" 0 "\\1"]
    )
  "List of emacs-wiki markup rules to apply when publishing pages
with planner-authz in effect.  This list of rules will be applied
after any planner or emacs-wiki rules."
  :group 'planner-authz
  :type '(repeat
          (vector :tag "Markup rule"
                  (choice regexp symbol)
                  integer
                  (choice string function symbol))))

;; Non-customizable variables

(defvar planner-authz-pages nil
  "Alist of planner pages and the space-separated string of users
authorized to view them.  This variable is internal to
planner-authz; do not set it manually.")
(defvar planner-authz-pages-to-republish nil
  "Queue of planner pages to republish when finished with the
current round of publishing, used to markup planner day pages
that wouldn't ordinarily get republished because they haven't
explicitly changed.  This variable is internal to planner-authz;
do not set it manually.")

(defvar planner-authz-mason-page-tag "<& authz.mas:page, 'users', [qw(%s)] &>"
  "Mason component call inserted in published page to restrict
the entire page by aborting or not.  Don't be tempted to use the
=> Perl operator; the = could cause emacs-wiki to insert <code>
tags.")
(defvar planner-authz-mason-content-tag-begin
  "<&: authz.mas:content, 'users', [qw(%s)] &>"
  "Mason component call that restricts content to a
space-separated list of users.  If this variable contains
\"<&:\", it will be replaced by \"<&|\" at the end of the
publishing process to avoid being marked up as a table by
emacs-wiki.  Don't be tempted to use the => Perl operator; the =
could cause emacs-wiki to insert <code> tags.")
(defvar planner-authz-mason-content-tag-end "</&>"
  "Mason tag to insert at the end of content to be restricted.")

(defvar planner-authz-version "$Revision: 1.14 $"
  "Version of of planner-authz.el.")

;; Cleanup function to run in buffer after markup

(setq emacs-wiki-after-markup-hook
      (delq 'planner-authz-after-markup emacs-wiki-after-markup-hook)
      planner-custom-variables
      (assq-delete-all 'emacs-wiki-after-markup-hook planner-custom-variables))
(add-to-list
 'planner-custom-variables
 `(emacs-wiki-after-markup-hook
   . ,(cons 'planner-authz-after-markup emacs-wiki-after-markup-hook)))

;; Run after all files are published: republish files that still need
;; authz markup and generate Mason component.  If
;; emacs-wiki-publish-index appears, replace it with our own index
;; publisher.

(let ((match
       (memq 'emacs-wiki-publish-index emacs-wiki-after-wiki-publish-hook)))
  (if match
      (setcar match 'planner-authz-publish-index)))
(setq emacs-wiki-after-wiki-publish-hook
      (delq 'planner-authz-after-wiki-publish
            emacs-wiki-after-wiki-publish-hook)
      planner-custom-variables
      (assq-delete-all 'emacs-wiki-after-wiki-publish-hook
                       planner-custom-variables))
(add-to-list
 'planner-custom-variables
 `(emacs-wiki-after-wiki-publish-hook
   . ,(cons 'planner-authz-after-wiki-publish
            emacs-wiki-after-wiki-publish-hook)))

;; Add our <authz> tag

(defvar planner-authz-rule '("authz" t t nil planner-authz-tag)
  "Rule to add to `emacs-wiki-dangerous-tags' for <authz> tag support.")
(setq emacs-wiki-dangerous-tags
      (delq 'planner-authz-rule
            emacs-wiki-dangerous-tags)
      planner-custom-variables
      (assq-delete-all 'emacs-wiki-dangerous-tags
                       planner-custom-variables))
(add-to-list
 'planner-custom-variables
 `(emacs-wiki-dangerous-tags
   . ,(cons planner-authz-rule emacs-wiki-dangerous-tags)))

;; Replace emacs-wiki-publish-function to hook the beginning of the
;; publishing process

(setq planner-custom-variables
      (assq-delete-all 'emacs-wiki-publish-function
                       planner-custom-variables))
(add-to-list
 'planner-custom-variables
 '(emacs-wiki-publish-function . planner-authz-publish-current))

;; Add our markup rules, preserving those of planner and emacs-wiki

(setq emacs-wiki-publishing-markup
      (delq 'planner-authz-publishing-markup-first
            (delq 'planner-publishing-markup
                  (delq 'planner-authz-publishing-markup-last
                        emacs-wiki-publishing-markup)))
      planner-custom-variables
      (assq-delete-all 'emacs-wiki-publishing-markup
                       planner-custom-variables))
(add-to-list
 'planner-custom-variables
 `(emacs-wiki-publishing-markup
   . ,(append
       planner-authz-publishing-markup-first
       planner-publishing-markup
       emacs-wiki-publishing-markup
       planner-authz-publishing-markup-last)))

;; Make all our tweaks take effect

(planner-option-customized 'planner-custom-variables planner-custom-variables)

;;; Functions

(defun planner-authz-after-markup ()
  "Function to run in a buffer after it has been marked up,
currently used to remove the page from the queue of pages to
republish and to enforce default access controls for project
pages."
  (let ((page (emacs-wiki-page-name)))
    (when page
      (delete page planner-authz-pages-to-republish)
      (let ((users (planner-authz-users)))
        (when users
          (goto-char (point-min))
          (insert (format planner-authz-mason-page-tag users) "\n"))))))

(defun planner-authz-after-wiki-publish ()
  "Function to run in buffer after all pages have been published
by emacs-wiki, currently used to republish pages that reference
restricted pages and to generate Mason code."
  (emacs-wiki-publish-files planner-authz-pages-to-republish t)
  (planner-authz-generate-mason-component))

(defun planner-authz-day-p ()
  "Return non-nil if the current page is a day page (matches
`planner-name-regexp')."
  (save-match-data
    (string-match planner-name-regexp (emacs-wiki-page-name))))

(defun planner-authz-default (page)
  "Return a space-sparated string of users associated with the
project page PAGE."
  (and planner-authz-project-default
       (not (save-match-data
              (string-match planner-name-regexp page))) ; not on day pages
       (mapconcat 'identity planner-authz-project-default " ")))

(defun planner-authz-file-alist (users)
  "Generate a list of planner files that the list of users in
USERS have access to."
  (let ((pages (planner-file-alist))
        result)
    (while pages
      (let (not-found-p)
        (with-temp-buffer
          (insert-file-contents-literally (cdar pages))
          (when (re-search-forward "^#authz\\s-+\\(.+\\)\n+" nil t)
            (let ((users-iter users)
                  (authz (split-string (match-string 1))))
              (while (and users-iter (not not-found-p))
                (unless (member (car users-iter) authz)
                  (setq not-found-p t))
                (setq users-iter (cdr users-iter)))))
          (unless not-found-p
            (setq result (append (list (car pages)) result))))
        (setq pages (cdr pages))))
    result))

(defun planner-authz-generate-mason-component ()
  "Generate the Mason component named
`planner-authz-mason-component-name' and containing
`planner-authz-mason-component-contents' that restricts content
to users specified by <authz> and #authz tags."
  (with-temp-buffer
    (insert planner-authz-mason-component-contents)
    (let ((backup-inhibited t))
      (write-file
       (concat (file-name-as-directory planner-publishing-directory)
               planner-authz-mason-component-name)))))

(defun planner-authz-initial-directive-preprocess ()
  "Build `planner-authz-pages' from all planner pages containing
#authz directives.  Pages that are subsequently published get
removed from this list; all others are forcibly republished."
  (setq planner-authz-pages nil
        planner-authz-pages-to-republish nil)
  (dolist (elt (planner-file-alist))
    (let ((page (car elt)))
      (with-temp-buffer
        (insert-file-contents-literally (cdr elt))
        (while (re-search-forward "^#authz\\s-+\\(.+\\)\n+" nil t)
          (push `(,page . ,(match-string 1)) planner-authz-pages))))))

(defun planner-authz-markup-note ()
  "If a note is linked to a restricted page, markup the note to
restrict it as well.  If this page is restricted and the note is
linked to another page, remember to republish that page later and
restrict the note as it appears there."
  (let* ((link (save-match-data
                 (planner-note-link (planner-current-note-info))))
         (linked-page (if link (emacs-wiki-wiki-base link)))
         (linked-users
          (if linked-page
              (planner-authz-users linked-page)
            (and planner-authz-day-note-default
                 (planner-authz-day-p)
                 (mapconcat 'identity planner-authz-day-note-default " ")))))

    ;; "Inherit" from planner-markup-note, which inserts text rather
    ;; than returning a string

    (planner-markup-note)

    ;; If this note is linked to another page, republish that page
    ;; later to restrict the note as it appears there, providing that
    ;; page has an authz restriction

    (if linked-page
        (planner-authz-republish-page-maybe linked-page))

    ;; If the linked page has an authz restriction, restrict this note

    (when linked-users
      (goto-char (match-beginning 0))
      (planner-authz-surround-text
       linked-users
       (lambda ()
         (save-match-data
           (if (re-search-forward "^\\*\\{1,3\\}\\s-+" nil 0 2)
               (goto-char (match-beginning 0)))))
       t))
    nil))

(defun planner-authz-markup-task ()
  "If a task is linked to a restricted page, markup the task to
restrict it as well.  If this page is restricted and the task is
linked to another page, remember to republish that page later and
restrict the task as it appears there."
  (let* ((link (save-match-data
                 (planner-task-link (planner-current-task-info))))
         (linked-page (if link (emacs-wiki-wiki-base link)))
         (linked-users
          (if linked-page
              (planner-authz-users linked-page)
            (and planner-authz-day-task-default
                 (planner-authz-day-p)
                 (mapconcat 'identity planner-authz-day-task-default " ")))))

    ;; "Inherit" from planner-markup-task, which inserts text rather
    ;; than returning a string

    (planner-markup-task)

    ;; If this task is linked to another page, republish that page
    ;; later to restrict the task as it appears there, providing that
    ;; page has an authz restriction

    (if linked-page
        (planner-authz-republish-page-maybe linked-page))

    ;; If the linked page has an authz restriction, restrict this note

    (when linked-users
      (let ((start-tag
             (format planner-authz-mason-content-tag-begin linked-users))
            (end (point)))
        (goto-char (+ 2 (match-end 0))) ; skip past "- "
        (emacs-wiki-surround-text
         start-tag
         planner-authz-mason-content-tag-end
         (lambda () (goto-char (+ end (length start-tag)))))))
    ""))

(defun planner-authz-publish-current (file output-path)
  "Preprocess planner files for #authz directives and then call
`emacs-wiki-publish-current'."
  (planner-authz-initial-directive-preprocess)
  (emacs-wiki-publish-current file output-path))

(defun planner-authz-publish-index ()
  "Publish an index for the planner marked up with Mason code so
that only those links to pages which the remote user is
authorized to access will be shown."
  (interactive)
  (with-current-buffer (emacs-wiki-generate-index t t t)
    (message "Marking up index...")
    (goto-char (point-min))
    (while (re-search-forward "^- \\(.*\\)" nil t)
      (message "match: %s" (match-string 1))
      (let* ((match (match-string 1))
             (users (if match (planner-authz-users match))))
        (if users
            (replace-match
             (concat
              (format planner-authz-mason-content-tag-begin users)
              match planner-authz-mason-content-tag-end)
             t nil nil 1))))
    (emacs-wiki-replace-markup emacs-wiki-index-page)
    (let ((backup-inhibited t))
      (write-file (emacs-wiki-published-file emacs-wiki-index-page)))
    (kill-buffer (current-buffer))))

(defun planner-authz-republish-page-maybe (linked-page)
  "Remember LINKED-PAGE to be republished later iff the current
page is restricted."
  (if (planner-authz-users)
      (add-to-list 'planner-authz-pages-to-republish
                   (emacs-wiki-page-file linked-page))))

(defun planner-authz-surround-text (users move-func &optional block-p)
  "Insert Mason code for <authz> and #authz.  Restricts text to
USERS between point and the position moved to by MOVE-FUNC."
  (unless users (setq users ""))
  (emacs-wiki-surround-text
   (concat
    (format planner-authz-mason-content-tag-begin users)
    (if block-p "\n" ""))
   (concat
    planner-authz-mason-content-tag-end
    (if block-p "\n" ""))
   move-func))

(defun planner-authz-tag (beg end attrs)
  "Insert Mason code into published `plan' files to restrict
access to users specified in \"users\" attribute of <authz> tag."
  (let ((users (cdr (assoc "users" attrs))))
    (planner-authz-surround-text users (lambda () (goto-char end)))))

(defun planner-authz-users (&optional page)
  "Return a list of users PAGE is restricted to as a
space-separated string, based on a #authz directive appearing in
the page.  If PAGE contains no #authz directive and is a project
page (it doesn't match `planner-name-regexp'), return
`planner-authz-project-default' as a space-separated string.

If PAGE is nil, return a list of users associated with the
current page."
  (unless page (setq page (emacs-wiki-page-name)))
  (or (cdr (assoc page planner-authz-pages))
      (planner-authz-default page)))

(provide 'planner-authz)

reply via email to

[Prev in Thread] Current Thread [Next in Thread]