Hi list,

Disclaimer: this is my first patch to the list, so I hope everything
is Ok with it's format and content.

This patch is an initial attempt to move mod_mbox to XML/XSLT
output. More improvements concerning mod_mbox's user interface will
come in the next days and weeks.

In order to use the new features coming with this patch, you'll need
to :

- copy the XSL and CSS stylesheets from a theme's directory (for the
  moment, only the basic ASF theme is available under themes/asf/) to
  your mailing lists archives root directory (for example,
  /var/www/archives/)
- add the following section to your Apache configuration :

    AddHandler mbox-handler .mbox
    <LocationMatch /archives/([^/]+)>
        MboxIndex On
        MboxStyle "/archives/style.xsl"
        MboxHideEmpty On
        MboxAntispam On
    </LocationMatch>

The last three directives are new ones. The first one tells where to
find the XSL transformation stylesheet. The next one hides empty
mailboxes from main listing. The third one enables email obfuscating
(as discussed within the corresponding thread on mbox-dev mailing list
[1]).

Review and comments are welcome !

- Sam

[1]
http://mail-archives.apache.org/mod_mbox/httpd-mbox-dev/200506.mbox/[EMAIL 
PROTECTED]

-- 
Maxime Petazzoni (http://www.bulix.org)
 -- gone crazy, back soon. leave message.
Index: module-2.0/mod_mbox_file.c
===================================================================
--- module-2.0/mod_mbox_file.c  (revision 208687)
+++ module-2.0/mod_mbox_file.c  (working copy)
@@ -22,25 +22,65 @@
  */
 
 #include "mod_mbox.h"
+#include <string.h>
 
-/*
- * This function prints one message
+/** Antispam protection.
+ *
  */
-static void print_message(request_rec *r, char* baseURI, Message *m)
+char *email_antispam(char *email)
 {
-    /* FIXME: HTML or TEXT formats? */
-    ap_rprintf(r, "<A HREF=\"%s/%s\">%s</A> %s (%s)",
-               baseURI,
-               URI_ESCAPE_OR_BLANK(r->pool, m->msgID),
-               ESCAPE_OR_BLANK(r->pool, m->subject),
-               ESCAPE_OR_BLANK(r->pool, m->str_from),
-               ESCAPE_OR_BLANK(r->pool, m->str_date));
+    char *pos;
+    int i, p;
+
+    pos = strrchr(email, '@');
+
+    if (!pos) {
+        return email;
+    }
+
+    p = pos - email - 1;
+
+    /* Wipe out at most three chars preceding the '@' sign */
+    for (i=0 ; i<3 ; i++) {
+        if ((p - i) > 0) {
+           email[p-i] = '.';
+       }
+    }
+
+    return email;
 }
 
-/* This function displays the index about the specified mbox file.
+
+/** Prints one message.
  *
- * The presentation is meant to emulate the old hypermail archives.
  */
+static void print_message(request_rec *r, Message *m,
+                         int linked, int depth)
+{
+    dir_cfg *conf;
+    char *from;
+
+    conf = ap_get_module_config(r->per_dir_config, &mbox_module);
+
+    from = ESCAPE_OR_BLANK(r->pool, m->str_from);
+    if (conf->antispam) {
+        from = email_antispam(from);
+    }
+
+    ap_rprintf(r, " <message id=\"%s\" linked=\"%d\" depth=\"%d\">\n"
+              "  <from>%s</from>\n"
+              "  <date>%s</date>\n"
+              "  <subject>%s</subject>\n"
+              " </message>\n",
+              URI_ESCAPE_OR_BLANK(r->pool, m->msgID),
+              linked, depth, from,
+               ESCAPE_OR_BLANK(r->pool, m->str_date),
+              ESCAPE_OR_BLANK(r->pool, m->subject));
+}
+
+/** This function displays the index about the specified mbox file.
+ *
+ */
 static apr_status_t display_index(request_rec *r, apr_file_t * f,
                                   int sortFlags)
 {
@@ -72,41 +112,33 @@
     while (head)
     {
         m = (Message*)head->value;
-
-        ap_rputs("<LI>", r);
-        print_message(r, baseURI, m);
-        ap_rputs("</LI>\n", r);
-
+        print_message(r, m, 1, 0);
         head = head->next;
     }
 
     return OK;
 }
 
-static void print_container(request_rec *r, char *baseURI, Container *c, int 
depth)
+static void print_container(request_rec *r, Container *c, int depth)
 {
-    ap_rputs("<LI>", r);
-
     /* Under the rules of our threading tree, if we do not have a
      * message, we MUST have at least one child.  Therefore, print
      * that child's subject when we don't have a message.
      */
     if (c->message)
-        print_message(r, baseURI, c->message);
+        print_message(r, c->message, 1, depth);
     else
-        ap_rprintf(r, "%s", c->child->message->subject);
+        print_message(r, c->child->message, 0, depth);
 
     if (c->child)
     {
-        ap_rputs("<UL>", r);
-        print_container(r, baseURI, c->child, depth+1);
-        ap_rputs("</UL>", r);
+        ap_rputs("<thread>\n", r);
+        print_container(r, c->child, depth+1);
+        ap_rputs("</thread>\n", r);
     }
 
-    ap_rputs("</LI>\n", r);
-
     if (depth && c->next)
-        print_container(r, baseURI, c->next, depth);
+        print_container(r, c->next, depth);
 }
 
 /* This function displays the index with threading.
@@ -144,7 +176,7 @@
 
     while (c)
     {
-        print_container(r, baseURI, c, 0);
+        print_container(r, c, 0);
         c = c->next;
     }
 
@@ -435,7 +467,7 @@
                     ap_set_content_type(f->r, apr_pstrdup(f->r->pool, ct));
                     rv = mbox_cte_filter(f, ctx->tbb, ccte, 1);
                 }
-                else if (ctx->get_part == 0){
+                else if (ctx->get_part == 0) {
                     /* Fetching the main message */
                     rv = mbox_cte_filter(f, ctx->tbb, ccte, 0);
                 }
@@ -483,10 +515,11 @@
                     }
 
                     apr_brigade_printf(ctx->bb, NULL, NULL,
-                                   "<hr/>Attachment "
-                                   "<a href='%s/%d'>#%d</a> (%s) (%s)<hr/>",
-                                   f->r->uri, ctx->mp_count+1, ctx->mp_count,
-                                   ct, cte_e_to_char(ccte));
+                                      "<attachment href=\"%s/%d\" id=\"%d\" 
type=\"%s\" encoding=\"%s\" />\n",
+                                      URI_ESCAPE_OR_BLANK(f->r->pool, 
f->r->uri),
+                                      ctx->mp_count+1,
+                                      ctx->mp_count,
+                                      ct, cte_e_to_char(ccte));
 
                     ctx->status = 1;
                     ctx->mp_count++;
@@ -757,16 +790,14 @@
     return ap_pass_brigade(r->output_filters, bb);
 }
 
-#define MBOX_INDEX_TYPE_FMT "[<A HREF=\"%s/index.html\">Date</A>] " \
-        "[<A HREF=\"%s/authors.html\">Author</A>] " \
-        "[<A HREF=\"%s/threads.html\">Thread</A>]"
-
 apr_status_t mbox_out_index_filter(ap_filter_t *f, apr_bucket_brigade *bb)
 {
     apr_bucket *b, *bucket;
     mbox_filter_ctx *ctx;
     char *baseURI, *temp, *header, *footer;
+    dir_cfg *conf;
 
+    conf = ap_get_module_config(f->r->per_dir_config, &mbox_module);
     ctx = (mbox_filter_ctx*) f->ctx;
 
     /* We want to be nice and display a link back to the index from whence
@@ -790,15 +821,15 @@
     {
         apr_table_unset(f->r->headers_out, "Content-Length");
 
-        header = apr_psprintf(f->r->pool,
-            DOCTYPE_HTML_4_0T
-            "<HTML>\n<HEAD>\n<TITLE>%s</TITLE>\n</HEAD>\n<BODY>"
-            "Message Index: "
-            MBOX_INDEX_TYPE_FMT
-            "\n<HR>\n<UL>\n",
-            f->r->uri,
-            ctx->baseURI, ctx->baseURI, ctx->baseURI);
+        header = apr_psprintf(f->r->pool, "<?xml version=\"1.0\" 
encoding=\"UTF-8\"?>");
 
+       if (conf->style_path)
+         header = apr_psprintf(f->r->pool, "%s\n<?xml-stylesheet 
type=\"text/xsl\" href=\"%s\"?>",
+                               header, conf->style_path);
+
+       header = apr_psprintf(f->r->pool, "%s\n<archive>\n<index 
base=\"%s\">\n",
+                             header, ctx->baseURI);
+
         b = apr_bucket_pool_create(header, strlen(header), f->r->pool,
                                   f->c->bucket_alloc);
         APR_BRIGADE_INSERT_HEAD(bb, b);
@@ -810,13 +841,8 @@
     b = APR_BRIGADE_FIRST(bb);
 
     while (b != APR_BRIGADE_SENTINEL(bb)) {
-        if (APR_BUCKET_IS_EOS(b))
-        {
-            footer = apr_psprintf(f->r->pool,
-            "</UL>\n<HR>\nMessage Index: "
-            MBOX_INDEX_TYPE_FMT
-            "\n</BODY>\n</HTML>",
-            ctx->baseURI, ctx->baseURI, ctx->baseURI);
+        if (APR_BUCKET_IS_EOS(b)) {
+           footer = apr_psprintf(f->r->pool, "</index>\n</archive>\n");
 
             bucket = apr_bucket_pool_create(footer, strlen(footer),
                                             f->r->pool, f->c->bucket_alloc);
@@ -836,9 +862,12 @@
     apr_bucket *b;
     mbox_filter_ctx *ctx = f->ctx;
     Message *m = ctx->m;
-    char *baseURI, *temp;
+    char *baseURI, *temp, *from, *str_from;
 
+    dir_cfg *conf;
 
+    conf = ap_get_module_config(f->r->per_dir_config, &mbox_module);
+
     /* We want to be nice and display a link back to the index from whence
      * they came.  We want the NON r->path_info part, but that isn't a
      * part of the request_rec (no reason why not - could be added?) - so
@@ -868,33 +897,34 @@
 
         ctx->tbb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
 
-        temp = URI_ESCAPE_OR_BLANK(f->r->pool, m->msgID);
+        apr_brigade_printf(ctx->tbb, NULL, NULL,
+                          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
 
+       if (conf->style_path)
+         apr_brigade_printf(ctx->tbb, NULL, NULL,
+                            "<?xml-stylesheet type=\"text/xsl\" 
href=\"%s\"?>\n",
+                            conf->style_path);
+
+       from = ESCAPE_OR_BLANK(f->r->pool, m->from);
+       str_from = ESCAPE_OR_BLANK(f->r->pool, m->str_from);
+       if (conf->antispam) {
+           from = email_antispam(from);
+           str_from = email_antispam(str_from);
+       }
+
         apr_brigade_printf(ctx->tbb, NULL, NULL,
-            DOCTYPE_HTML_4_0T
-            "<HTML>\n<HEAD>\n<TITLE>%s</TITLE>\n</HEAD>\n<BODY>"
-            "Message Index: "
-            MBOX_INDEX_TYPE_FMT
-            "\n<HR>\n"
-            "<STRONG>From:</STRONG> %s<BR>\n"
-            "<STRONG>Subject:</STRONG> %s<BR>\n"
-            "<STRONG>Date:</STRONG> %s<BR>\n"
-            "<A HREF=\"%s/raw?%s\">Raw Message</A> "
-            "<A HREF=\"%s/prev?%s\">Prev</A> "
-            "<A HREF=\"%s/next?%s\">Next</A> "
-            "<A HREF=\"%s/prev-thread?%s\">Prev by Thread</A> "
-            "<A HREF=\"%s/next-thread?%s\">Next by Thread</A><BR>\n"
-            "<HR>\n<PRE>\n",
-            ap_escape_html(f->r->pool, m->subject),
-            ctx->baseURI, ctx->baseURI, ctx->baseURI,
-            ESCAPE_OR_BLANK(f->r->pool, m->from),
-            ESCAPE_OR_BLANK(f->r->pool, m->subject),
-            ESCAPE_OR_BLANK(f->r->pool, m->str_date),
-            ctx->baseURI, temp,
-            ctx->baseURI, temp,
-            ctx->baseURI, temp,
-            ctx->baseURI, temp,
-            ctx->baseURI, temp);
+                          "<archive>\n"
+                          " <mail base=\"%s\" id=\"%s\">\n"
+                          "  <from>%s</from>\n"
+                          "  <strfrom>%s</strfrom>\n"
+                          "  <subject>%s</subject>\n"
+                          "  <date>%s</date>\n"
+                          "  <contents>\n",
+                          ctx->baseURI,
+                          ESCAPE_OR_BLANK(f->r->pool, m->msgID),
+                          from, str_from,
+                          ESCAPE_OR_BLANK(f->r->pool, m->subject),
+                          ESCAPE_OR_BLANK(f->r->pool, m->str_date));
 
         APR_BRIGADE_PREPEND(bb, ctx->tbb);
         /* mark as having sent the header */
@@ -906,21 +936,10 @@
     while (b != APR_BRIGADE_SENTINEL(bb)) {
         if (APR_BUCKET_IS_EOS(b))
         {
-            temp = URI_ESCAPE_OR_BLANK(f->r->pool, m->msgID);
             apr_brigade_printf(ctx->tbb, NULL, NULL,
-            "</PRE>\n<HR>\nMessage Index: "
-            MBOX_INDEX_TYPE_FMT
-            "<BR>\n"
-            "<A HREF=\"%s/prev?%s\">Prev</A> "
-            "<A HREF=\"%s/next?%s\">Next</A> "
-            "<A HREF=\"%s/prev-thread?%s\">Prev by Thread</A> "
-            "<A HREF=\"%s/next-thread?%s\">Next by Thread</A>\n"
-            "</BODY>\n</HTML>",
-            ctx->baseURI, ctx->baseURI, ctx->baseURI,
-            ctx->baseURI, temp,
-            ctx->baseURI, temp,
-            ctx->baseURI, temp,
-            ctx->baseURI, temp);
+                              "\n  </contents>\n"
+                              " </mail>\n"
+                              "</archive>\n");
 
             /* EOS is a special bucket not containing anything.
              * Insert us before EOS. */
@@ -987,18 +1006,18 @@
     }
 
     /* Set content type */
-    r->content_type = "text/html";
+    r->content_type = "text/xml";
 
     if (r->header_only)
         status = OK;
     else
     {
         /* Send index now or the actual body of the message */
-        if (strcmp(r->path_info,"/index.html") == 0)
+        if (strcmp(r->path_info,"/date") == 0)
             status = display_index(r, f, MBOX_SORT_DATE);
-        else if (strcmp(r->path_info,"/authors.html") == 0)
+        else if (strcmp(r->path_info,"/author") == 0)
             status = display_index(r, f, MBOX_SORT_AUTHOR);
-        else if (strcmp(r->path_info,"/threads.html") == 0)
+        else if (strcmp(r->path_info,"/thread") == 0)
             status = display_thread_index(r, f);
         else if (strcmp(r->path_info,"/prev") == 0)
             status = fetch_relative_message(r, f, MBOX_PREV);
@@ -1019,4 +1038,3 @@
 
     return status;
 }
-
Index: module-2.0/mod_mbox.c
===================================================================
--- module-2.0/mod_mbox.c       (revision 208687)
+++ module-2.0/mod_mbox.c       (working copy)
@@ -82,7 +82,9 @@
     dir_cfg *conf = apr_pcalloc(p, sizeof(dir_cfg));
 
     conf->enabled = 0;
+    conf->hide_empty = 0;
     conf->search_path = NULL;
+    conf->style_path = NULL;
 
     return conf;
 }
@@ -93,6 +95,7 @@
     dir_cfg *merge = addv;
     dir_cfg *to = apr_palloc(p, sizeof(dir_cfg));
 
+    /* Update 'enabled' */
     if (merge->enabled == 1) {
         to->enabled = 1;
     }
@@ -100,6 +103,23 @@
         to->enabled = from->enabled;
     }
 
+    /* Update 'hide_empty' */
+    if (merge->hide_empty == 1) {
+        to->hide_empty = 1;
+    }
+    else {
+        to->hide_empty = from->hide_empty;
+    }
+
+    /* Update 'antispam' */
+    if (merge->antispam == 1) {
+        to->antispam = 1;
+    }
+    else {
+        to->antispam = from->antispam;
+    }
+
+    /* Update 'search_path' */
     if (merge->search_path != NULL) {
         to->search_path = apr_pstrdup(p, merge->search_path);
     }
@@ -110,6 +130,18 @@
         to->search_path = NULL;
     }
 
+    /* Update 'style_path' */
+    if (merge->style_path != NULL) {
+        to->style_path = apr_pstrdup(p, merge->style_path);
+    }
+    else if (from->style_path != NULL) {
+        to->style_path = apr_pstrdup(p, from->style_path);
+    }
+    else {
+        to->style_path = NULL;
+    }
+
+
     return to;
 }
 
@@ -117,9 +149,18 @@
     AP_INIT_FLAG("mboxindex", ap_set_flag_slot,
                  (void *)APR_OFFSETOF(dir_cfg, enabled), OR_INDEXES,
                 "Enable mod_mbox to create directory listings of .mbox 
files."),
+    AP_INIT_FLAG("mboxantispam", ap_set_flag_slot,
+                 (void *)APR_OFFSETOF(dir_cfg, antispam), OR_INDEXES,
+                "Enable mod_mbox email obfuscation."),
     AP_INIT_TAKE1("mboxsearch", ap_set_string_slot,
                  (void *)APR_OFFSETOF(dir_cfg, search_path), OR_INDEXES,
                  "Set the Directory that contains Search Data"),
+    AP_INIT_TAKE1("mboxstyle", ap_set_string_slot,
+                 (void *)APR_OFFSETOF(dir_cfg, style_path), OR_INDEXES,
+                 "Set the path to Css stylesheet file."),
+    AP_INIT_FLAG("mboxhideempty", ap_set_flag_slot,
+                 (void *)APR_OFFSETOF(dir_cfg, hide_empty), OR_INDEXES,
+                "Whether to display empty mboxes in index listing."),
     {NULL}
 };
 
Index: module-2.0/mod_mbox.h
===================================================================
--- module-2.0/mod_mbox.h       (revision 208687)
+++ module-2.0/mod_mbox.h       (working copy)
@@ -69,7 +69,10 @@
 
 typedef struct dir_cfg {
     int enabled;
+    int antispam;
+    int hide_empty;
     const char* search_path;
+    const char* style_path;
 } dir_cfg;
 
 
Index: module-2.0/mod_mbox_index.c
===================================================================
--- module-2.0/mod_mbox_index.c (revision 208687)
+++ module-2.0/mod_mbox_index.c (working copy)
@@ -20,77 +20,36 @@
 
 #include "mod_mbox.h"
 
-static int show_list_info_row(request_rec* r,
-                              const char* title, const char* sub,
-                              const char* list, const char* domain) {
-    return ap_rprintf(r,
-                      "<tr>\n"
-                      "<th>%s:</th>\n"
-                      "<td><a href=\"mailto:[EMAIL PROTECTED]">"
-                      "[EMAIL PROTECTED]</a></td>\n</tr>\n",
-                      title, list, sub, domain,
-                      list, sub, domain);
-}
-
 static int show_list_info(request_rec *r, mbox_cache_info* mli)
 {
-    char dstr[APR_RFC822_DATE_LEN];
     char* list;
     char* domain;
 
     list = ESCAPE_OR_BLANK(r->pool, mli->list);
     domain = ESCAPE_OR_BLANK(r->pool, mli->domain);
 
-    ap_rprintf(r,
-               "[EMAIL PROTECTED] Archives",
-               list, domain);
-    ap_rputs("</title>\n</head>\n"
-             "<body\n bgcolor=\"#FFFFFF\" text=\"#000000\" "
-             "link=\"#0000FF\" vlink=\"#000080\" alink=\"#FF0000\">\n",
-             r);
+    /* Output header and list information */
+    ap_rputs("<listinfo>\n", r);
+    ap_rprintf(r, " <list>%s</list>\n", list);
+    ap_rprintf(r, " <domain>%s</domain>\n", domain);
+    ap_rprintf(r, " <subscribe>[EMAIL PROTECTED]</subscribe>\n", list, domain);
+    ap_rprintf(r, " <digest>[EMAIL PROTECTED]</digest>\n", list, domain);
+    ap_rprintf(r, " <unsubscribe>[EMAIL PROTECTED]</unsubscribe>\n", list, 
domain);
+    ap_rprintf(r, " <help>[EMAIL PROTECTED]</help>\n", list, domain);
+    ap_rputs("</listinfo>\n", r);
 
-    ap_rprintf(r,
-               "<h2>[EMAIL PROTECTED]</h2>\n"
-               "<table style='text-align: left;'>\n",
-               list, domain);
-
-    show_list_info_row(r, "Subscription address", "subscribe",
-                       list, domain);
-    show_list_info_row(r, "Digest subscription address", "digest-subscribe",
-                       list, domain);
-    show_list_info_row(r, "Unsubscription addresses", "unsubscribe",
-                       list, domain);
-    show_list_info_row(r, "Getting help with the list", "help",
-                       list, domain);
-
-    apr_rfc822_date(dstr, mli->mtime);
-    ap_rprintf(r, "</table><hr/><I>Last Updated: %s</I><hr/>\n", dstr);
-
     return OK;
 }
 
-static int show_index_file_info(request_rec *r,
-                                mbox_cache_info* mli, char* path)
-{
-    int count = 0;
-    mbox_cache_get_count(mli, &count, path);
-    ap_rprintf(r, "<tr><td>%.2s/%.4s</td><td>"
-               "<a href=\"%s/threads.html\">Threads</a> "
-               "<a href=\"%s/index.html\">Date</a> "
-               "<a href=\"%s/authors.html\">Authors</a></td>"
-               "<td>%d</td></tr>\n",
-               path+4, path, path, path, path, count);
-    return OK;
-}
-
 static int file_alphasort(const void *fn1, const void *fn2)
 {
     /* reverse order */
     return strcmp(*(char**)fn2, *(char**)fn1);
 }
 
-static int generate_mbox_index(request_rec *r)
+static int generate_mbox_index(request_rec *r, dir_cfg *conf)
 {
+    char dstr[APR_RFC822_DATE_LEN];
     apr_status_t rv = APR_SUCCESS;
     char* file;
     char* etag;
@@ -100,6 +59,11 @@
     mbox_cache_info* mli;
     int i;
 
+    /* Don't serve index if this directory is not enabled */
+    if (!conf->enabled) {
+        return DECLINED;
+    }
+
     rv = mbox_cache_get(&mli, r->filename, r->pool);
     if (rv != APR_SUCCESS) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
@@ -108,7 +72,7 @@
         return HTTP_FORBIDDEN;
     }
 
-    ap_set_content_type(r, "text/html; charset=utf-8");
+    ap_set_content_type(r, "text/xml; charset=utf-8");
 
     rv = apr_dir_open(&dir, r->filename, r->pool);
     if (rv != APR_SUCCESS) {
@@ -139,51 +103,55 @@
 
     apr_dir_close(dir);
 
-    if (files->nelts != 0) {
-        qsort((void *) files->elts, files->nelts,
-              sizeof(char *), file_alphasort);
+    qsort((void *) files->elts, files->nelts, sizeof(char *), file_alphasort);
+    
+    ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", r);
+    if (conf->style_path) {
+        ap_rprintf(r, "<?xml-stylesheet type=\"text/xsl\" href=\"%s\"?>\n", 
conf->style_path);
     }
 
-    /* FIXME: Alternative Languages. Always forcing to english isn't truthful. 
*/
-    ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-             DOCTYPE_XHTML_1_0T
-             "<html xmlns=\"http://www.w3.org/1999/xhtml\"; xml:lang=\"en\">\n"
-             "<head>\n<title>", r);
-
-
+    apr_rfc822_date(dstr, mli->mtime);
+    ap_rprintf(r, "<archive updated=\"%s\">\n", dstr);
+    
+    show_list_info(r, mli);
+    
     if (files->nelts != 0) {
-        show_list_info(r, mli);
-    }
-    else {
-        ap_rputs("No List Information", r);
-        ap_rputs("</title>\n</head>\n"
-                 "<body\n bgcolor=\"#FFFFFF\" text=\"#000000\" "
-                 "link=\"#0000FF\" vlink=\"#000080\" alink=\"#FF0000\">\n",
-                 r);
-    }
-    ap_rputs("<table width=\"100%\">\n", r);
-    ap_rputs("<tr><th align=\"left\" width=\"15%\">Date</th>"
-             "<th align=\"left\" width=\"85%\">Sorted 
by</th><th>Messages</th></tr>", r);
+      int year_header = 0;
 
-    if (files->nelts != 0) {
+      for (i = 0; i < files->nelts; i++) {
+           int count = 0;
 
-        for (i = 0; i < files->nelts; i++) {
             file = ((char**)files->elts)[i];
-            show_index_file_info(r, mli, file);
-            if (i+1 < files->nelts) {
+
+           mbox_cache_get_count(mli, &count, file);
+
+           if (count || !conf->hide_empty)
+             {
+               if (!year_header)
+                 {
+                   ap_rprintf(r, "<mboxes year=\"%.4s\">\n", 
((char**)files->elts)[i]);
+                   year_header = 1;
+                 }
+
+               ap_rprintf(r, " <mbox month=\"%.2s\" year=\"%.4s\" 
msgcount=\"%d\" />\n",
+                          file+4, file, count);
+             }
+
+           /* Year separation */
+            if (i+1 < files->nelts && year_header) {
                 if(((char**)files->elts)[i][3] != 
((char**)files->elts)[i+1][3]) {
-                    ap_rputs("<tr><td colspan='3'><hr/></td></tr>", r);
+                    ap_rputs("</mboxes>\n", r);
+                   year_header = 0;
                 }
             }
         }
     }
     else {
-        ap_rputs("<tr><td colspan=\"2\">"
-                 "No messages have been archived for this list."
-                 "</td></tr>", r);
+        ap_rputs("<empty />\n", r);
     }
 
-    ap_rputs("</table>\n</body>\n</html>", r);
+    ap_rputs("</mboxes>\n", r);
+    ap_rputs("</archive>", r);
 
     return OK;
 }
@@ -198,10 +166,5 @@
 
     conf = ap_get_module_config(r->per_dir_config, &mbox_module);
 
-    if (!conf->enabled) {
-        return DECLINED;
-    }
-
-    return generate_mbox_index(r);
+    return generate_mbox_index(r, conf);
 }
-
Index: themes/asf/style.xsl
===================================================================
--- themes/asf/style.xsl        (revision 0)
+++ themes/asf/style.xsl        (revision 0)
@@ -0,0 +1,469 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0">
+
+  <xsl:output method="html" />
+
+  <xsl:template match="*" />
+
+  <xsl:template match="archive">
+    <html>
+      <head>
+       <title><xsl:text>Archives</xsl:text></title>
+        <link rel="stylesheet" type="text/css" href="/archives/style.css" />
+      </head>
+
+      <body>
+       <h1><xsl:text>Mailing list archive</xsl:text></h1>
+
+       <xsl:apply-templates />
+
+       <xsl:if test="@updated">
+         <p id="lastupdated">
+           <xsl:text>Last updated on </xsl:text>
+           <xsl:value-of select="@updated" />
+         </p>
+       </xsl:if>
+      </body>
+    </html>
+  </xsl:template>
+
+  <xsl:template match="listinfo">
+    <h2><xsl:text>Mailing list information</xsl:text></h2>
+     <table>
+      <tr>
+       <td class="left">List name</td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:text>mailto:</xsl:text>
+             <xsl:value-of select="list" />
+             <xsl:text>@</xsl:text>
+             <xsl:value-of select="domain" />
+           </xsl:attribute>
+           <xsl:attribute name="title">
+             <xsl:text>List</xsl:text>
+           </xsl:attribute>
+           <xsl:value-of select="list" />
+           <xsl:text>@</xsl:text>
+           <xsl:value-of select="domain" />
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left">Subscribe</td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:text>mailto:</xsl:text>
+             <xsl:value-of select="subscribe" />
+           </xsl:attribute>
+           <xsl:attribute name="title">
+             <xsl:text>Subscribe</xsl:text>
+           </xsl:attribute>
+           <xsl:value-of select="subscribe" />
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left">Unsubscribe</td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:text>mailto:</xsl:text>
+             <xsl:value-of select="unsubscribe" />
+           </xsl:attribute>
+           <xsl:attribute name="title">
+             <xsl:text>Unsubscribe</xsl:text>
+           </xsl:attribute>
+           <xsl:value-of select="unsubscribe" />
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left">Digest</td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:text>mailto:</xsl:text>
+             <xsl:value-of select="digest" />
+           </xsl:attribute>
+           <xsl:attribute name="title">
+             <xsl:text>Digest</xsl:text>
+           </xsl:attribute>
+           <xsl:value-of select="digest" />
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left">Help</td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:text>mailto:</xsl:text>
+             <xsl:value-of select="help" />
+           </xsl:attribute>
+           <xsl:attribute name="title">
+             <xsl:text>Help</xsl:text>
+           </xsl:attribute>
+           <xsl:value-of select="help" />
+         </xsl:element>
+       </td>
+      </tr>
+   </table>
+  </xsl:template>
+
+  <xsl:template match="mboxes">
+    <h2>
+      <xsl:text>Year </xsl:text>
+      <xsl:value-of select="@year" />
+      <xsl:text> archives</xsl:text>
+    </h2>
+
+    <table id="index">
+      <thead>
+       <td class="month"><xsl:text>Date</xsl:text></td>
+       <td class="right"><xsl:text>Browse by</xsl:text></td>
+       <td class="msgcount"><xsl:text>Messages</xsl:text></td>
+      </thead>
+
+      <xsl:apply-templates />
+    </table>
+  </xsl:template>
+
+  <xsl:template name="month_string">
+    <xsl:param name="month" select="0" />
+
+    <xsl:choose>
+      <xsl:when test="$month='01'">
+       <xsl:text>Jan</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='02'">
+       <xsl:text>Feb</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='03'">
+       <xsl:text>Mar</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='04'">
+       <xsl:text>Apr</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='05'">
+       <xsl:text>May</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='06'">
+       <xsl:text>Jun</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='07'">
+       <xsl:text>Jul</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='08'">
+       <xsl:text>Aug</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='09'">
+       <xsl:text>Sep</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='10'">
+       <xsl:text>Oct</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='11'">
+       <xsl:text>Nov</xsl:text>
+      </xsl:when>
+      <xsl:when test="$month='12'">
+       <xsl:text>Dec</xsl:text>
+      </xsl:when>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template match="mbox">
+    <tr>
+      <td class="month">
+       <xsl:call-template name="month_string">
+         <xsl:with-param name="month" select="@month" />
+       </xsl:call-template>
+       <xsl:text> </xsl:text>
+       <xsl:value-of select="@year" />
+      </td>
+
+      <td class="right">
+       <xsl:element name="a">
+         <xsl:attribute name="href">
+           <xsl:value-of select="@year" /><xsl:value-of select="@month" />
+           <xsl:text>.mbox/thread</xsl:text>
+         </xsl:attribute>
+         <xsl:text>Thread</xsl:text>
+       </xsl:element>
+       <xsl:text>, </xsl:text>
+
+       <xsl:element name="a">
+         <xsl:attribute name="href">
+           <xsl:value-of select="@year" /><xsl:value-of select="@month" />
+           <xsl:text>.mbox/date</xsl:text>
+         </xsl:attribute>
+         <xsl:text>Date</xsl:text>
+       </xsl:element>
+       <xsl:text>, </xsl:text>
+
+       <xsl:element name="a">
+         <xsl:attribute name="href">
+           <xsl:value-of select="@year" /><xsl:value-of select="@month" />
+           <xsl:text>.mbox/author</xsl:text>
+         </xsl:attribute>
+         <xsl:text>Author</xsl:text>
+       </xsl:element>
+      </td>
+      <td class="msgcount">
+       <xsl:value-of select="@msgcount" />
+      </td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="index">
+    <h2><xsl:text>Navigation</xsl:text></h2>
+    <table>
+      <tr>
+       <td class="left"><xsl:text>List index</xsl:text></td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="substring(@base, 1, 
string-length(@base)-11)" />
+           </xsl:attribute>
+           <xsl:text>Archives</xsl:text>
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left"><xsl:text>Browse by</xsl:text></td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="@base" /><xsl:text>/thread</xsl:text>
+           </xsl:attribute>
+           <xsl:text>Thread</xsl:text>
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left"></td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="@base" /><xsl:text>/date</xsl:text>
+           </xsl:attribute>
+           <xsl:text>Date</xsl:text>
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left"></td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="@base" /><xsl:text>/author</xsl:text>
+           </xsl:attribute>
+           <xsl:text>Author</xsl:text>
+         </xsl:element>
+       </td>
+      </tr>
+    </table>
+
+    <h2>
+      <xsl:text>Message index : </xsl:text>
+
+      <xsl:variable name="s" select="substring-before(@base, '.mbox')" />
+
+      <xsl:call-template name="month_string">
+       <xsl:with-param name="month" select="substring($s, 
(string-length($s)-2)+1, 2)" />
+      </xsl:call-template>
+
+      <xsl:text> </xsl:text>
+      <xsl:value-of select="substring($s, (string-length($s)-6)+1, 4)" />
+    </h2>
+
+    <table>
+      <xsl:apply-templates />
+    </table>
+  </xsl:template>
+
+  <xsl:template match="thread">
+    <xsl:apply-templates />
+  </xsl:template>
+
+  <xsl:template name="loop">
+    <xsl:param name="start" select="0" />
+    <xsl:param name="end" select="0" />
+
+    <xsl:if test="$start = 1">
+      <xsl:text>\</xsl:text>
+    </xsl:if>
+
+    <xsl:if test="$start != 0">
+      <xsl:text>--</xsl:text>
+    </xsl:if>
+
+    <xsl:if test="$start = $end">
+       <xsl:text> </xsl:text>
+    </xsl:if>
+
+    <xsl:if test="$start &lt; $end">
+    <xsl:call-template name="loop">
+      <xsl:with-param name="start" select="($start)+1" />
+      <xsl:with-param name="end" select="$end" />
+    </xsl:call-template>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="message">
+    <tr>
+      <td class="left"><xsl:value-of select="from" /></td>
+      <td class="right fixed">
+       <xsl:variable name="linked" select="@linked" />
+
+       <xsl:call-template name="loop">
+         <xsl:with-param name="start" select="0" />
+         <xsl:with-param name="end" select="@depth" />
+       </xsl:call-template>
+
+       <xsl:choose>
+         <xsl:when test="$linked=1">
+           <xsl:element name="a">
+             <xsl:attribute name="href">
+               <xsl:value-of select="/archive/index/@base" />
+               <xsl:text>/</xsl:text>
+               <xsl:value-of select="@id" />
+             </xsl:attribute>
+             <xsl:value-of select="subject" />
+           </xsl:element>
+         </xsl:when>
+         <xsl:otherwise>
+           <xsl:value-of select="subject" />
+         </xsl:otherwise>
+       </xsl:choose>
+      </td>
+      <td class="date"><xsl:value-of select="date" /></td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="mail">
+    <h2><xsl:text>Navigation</xsl:text></h2>
+    <table>
+      <tr>
+       <td class="left"><xsl:text>List index</xsl:text></td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="substring(@base, 1, 
string-length(@base)-11)" />
+           </xsl:attribute>
+           <xsl:text>Archives</xsl:text>
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left"><xsl:text>Message index</xsl:text></td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="@base" />
+             <xsl:text>/thread</xsl:text>
+           </xsl:attribute>
+
+           <xsl:variable name="s" select="substring-before(@base, '.mbox')" />
+
+           <xsl:call-template name="month_string">
+             <xsl:with-param name="month" select="substring($s, 
(string-length($s)-2)+1, 2)" />
+           </xsl:call-template>
+
+           <xsl:text> </xsl:text>
+           <xsl:value-of select="substring($s, (string-length($s)-6)+1, 4)" />
+         </xsl:element>
+       </td>
+      </tr>
+
+      <tr>
+       <td class="left"><xsl:text>Previous</xsl:text></td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="@base" />
+             <xsl:text>/prev?</xsl:text>
+             <xsl:value-of select="@id" />
+           </xsl:attribute>
+           <xsl:text>By date</xsl:text>
+         </xsl:element>
+         <xsl:text>, </xsl:text>
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="@base" />
+             <xsl:text>/prev-thread?</xsl:text>
+             <xsl:value-of select="@id" />
+           </xsl:attribute>
+           <xsl:text>By thread</xsl:text>
+         </xsl:element>
+       </td>
+      </tr>
+      <tr>
+       <td class="left">Next</td>
+       <td class="right">
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="@base" />
+             <xsl:text>/next?</xsl:text>
+             <xsl:value-of select="@id" />
+           </xsl:attribute>
+           <xsl:text>By date</xsl:text>
+         </xsl:element>
+         <xsl:text>, </xsl:text>
+         <xsl:element name="a">
+           <xsl:attribute name="href">
+             <xsl:value-of select="@base" />
+             <xsl:text>/next-thread?</xsl:text>
+             <xsl:value-of select="@id" />
+           </xsl:attribute>
+           <xsl:text>By thread</xsl:text>
+         </xsl:element>
+       </td>
+      </tr>
+    </table>
+
+    <h2><xsl:text>View mail : </xsl:text><xsl:value-of select="subject" /></h2>
+    <table>
+      <tr>
+       <td class="left"><xsl:text>From</xsl:text></td>
+       <td class="right fixed"><xsl:value-of select="from" /></td>
+      </tr>
+      <tr>
+       <td class="left"><xsl:text>Date</xsl:text></td>
+       <td class="right fixed"><xsl:value-of select="date" /></td>
+      </tr>
+      <tr>
+       <td class="left"><xsl:text>Subject</xsl:text></td>
+       <td class="right fixed"><xsl:value-of select="subject" /></td>
+      </tr>
+      <tr>
+       <td class="left"><xsl:text>Contents</xsl:text></td>
+       <td class="data">
+         <xsl:apply-templates />
+       </td>
+      </tr>
+    </table>
+  </xsl:template>
+
+  <xsl:template match="contents">
+    <pre>
+      <xsl:apply-templates />
+    </pre>
+  </xsl:template>
+
+  <xsl:template match="attachment">
+    <xsl:element name="a">
+      <xsl:attribute name="href"><xsl:value-of select="@href" 
/></xsl:attribute>
+      <xsl:text>Attachment #</xsl:text>
+      <xsl:value-of select="@id" />
+    </xsl:element>
+
+    <xsl:text> (</xsl:text>
+    <xsl:value-of select="@type" />
+    <xsl:text>) (</xsl:text>
+    <xsl:value-of select="@encoding" />
+    <xsl:text>)</xsl:text>
+  </xsl:template>
+
+</xsl:stylesheet>
Index: themes/asf/style.css
===================================================================
--- themes/asf/style.css        (revision 0)
+++ themes/asf/style.css        (revision 0)
@@ -0,0 +1,117 @@
+body
+{
+  margin: 0;
+  padding: 0;
+  font-size: small;
+  background-color: white;
+  background-image: url("/archives/asf_logo_wide.gif");
+  background-repeat: no-repeat;
+  background-position: 1em 1em;
+}
+
+h1
+{
+  font-size: xx-large;
+  text-align: right;
+  margin: 0.5em;
+  padding: 0.3em;
+}
+
+h2
+{
+  font-size: 110%;
+  text-align: left;
+  margin: 4em 5% 0 5%;
+  padding: 0.3em;
+  padding-left: 1em;
+  background: #900;
+  color: white;
+
+  -moz-border-radius: 0.3em 0 0 0;
+}
+
+p#lastupdated
+{
+  font-style: italic;
+  color: #ccc;
+  border-top: 1px #aaa solid;
+  margin: 1em;
+  padding: 0.3em;
+  text-align: right;
+}
+
+table
+{
+  margin: 0 auto;
+  width: 90%;
+  padding: 0;
+  border-spacing: 0;
+}
+
+/* Left column */
+table td.left
+{
+  width: 15%;
+  font-weight: bold;
+  text-align: right;
+  background: #dedede;
+  padding: 0.2em 1em;
+  vertical-align: top;
+}
+
+table td.month
+{
+  width: 15%;
+  text-align: center;
+  background: #dedede;
+  padding: 0.2em 1em;
+}
+
+table td.right
+{
+  text-align: left;
+  background: #f4f4f4;
+  padding: 0.2em 0 0.2em 1em;
+}
+
+table td.data
+{
+  padding: 0.2em 0 0.2em 1em;
+  background: white;
+  text-align: left;
+}
+
+table td.msgcount
+{
+  width: 10%;
+  text-align: center;
+  background: #dedede;
+}
+
+table td.date
+{
+  width: 20%;
+  text-align: right;
+  background: #f4f4f4;
+  font-style: italic;
+  padding-right: 1em;
+  color: #888;
+}
+
+table td.fixed
+{
+  font-family: monospace;
+}
+
+thead td
+{
+  font-weight: bold;
+  border-bottom: 1px black solid;
+}
+
+pre
+{
+  font-family: monospace;
+  padding: 0;
+  margin: 0;
+}
\ No newline at end of file

Attachment: signature.asc
Description: Digital signature

Reply via email to