Re: pyarchives_enhanced.py updated package

Klaus Trainer <klaus.trainer-S0/[email protected]> Sun, 05 Oct 2008 17:11:03 +0200
Newsgroups gmane.comp.web.pyblosxom.devel
Message-ID <[email protected]>
Hi!

I've tried to address your feedback, Ryan.
With 'gendiff' I've generated the patch that is attached. It can be
applied to the files in '/trunk/contrib/plugins/archives'. However, you
probably first need to set back the '__version__' docstring in
pyarchives.py to "$Id$" in the case you have checked it out with svn.


So, what I _not_ did:

> we've started leaning harder on unit testing recently, e.g.
> http://pyblosxom.sourceforge.net/wiki/index.php/Tests and
> http://pyblosxom.sourceforge.net/wiki/index.php/Framework_for_testing_plugins

Yeah, I also need to improve on creating unit tests. Maybe I can deliver
such in addition.

> i like that you made the html customizable through the config variables.
> it's complex enough that it's probably worth using a full-blown flavour
> template instead. a couple other plugins do this, e.g. comments, which
> you could use as an example.

I've also thought about flavour templates. For this plugin I prefer not
to have templates, because it is simpler without; As it is, the plugin
is 100% compatible with the old archives plugin, i.e. you don't need to
make any customizations if you don't want to. With the default html
template, the default version should well integrate into most blogs even
 if users don't define the CSS classes.
My opinion is that flavour templates aren't relly needed for it.


What I did:

> finally, thanks for the detailed file docstring in pyarchives.py! it's
> really useful. i'd actually maybe even consider putting more of it in
> the README.

I've added two sentences to the README. I like it, when the actual
plugin file contains all information needed to deploy it correctly.
On the other hand I think the README tells the reader briefly what the
plugin is about, so replicating the documentation from pyarchives.py
doesn't make too much sense, as the last sentence is referring to the
documentation in pyblosxom.py.

> [...] on a similar note, would you mind adding docstrings to the
> new functions? they don't have to be big.

Yes, I've added some documentation. Now the class 'Record' contains more
documentation than code, which makes sense, because the reader can learn
about the structure the archives are stored (you can call it "composite
pattern"), which would be more difficult to gather when looking at the
code in 'genEnhancedArchive()'.


Many thanks for your feedback!


Regards,

Klaus



Ryan Barrett wrote:
> awesome, thank you! (sorry for the delay.)
> 
> overall, this looks great. i have a few comments and requests, and i
> made a couple minor stylistic changes, but nothing major. let me know if
> you'd like to address the feedback first, or if i should check this in
> as is and you'll send more patches later.
> 
> first, it looks like pyarchives_enhanced.py is no longer used. i should
> delete that from trunk, right?
> 
> i like that you made the html customizable through the config variables.
> it's complex enough that it's probably worth using a full-blown flavour
> template instead. a couple other plugins do this, e.g. comments, which
> you could use as an example.
> 
> we've started leaning harder on unit testing recently, e.g.
> http://pyblosxom.sourceforge.net/wiki/index.php/Tests and
> http://pyblosxom.sourceforge.net/wiki/index.php/Framework_for_testing_plugins
> .
> would you mind adding some unit tests?
> 
> finally, thanks for the detailed file docstring in pyarchives.py! it's
> really useful. i'd actually maybe even consider putting more of it in
> the README. on a similar note, would you mind adding docstrings to the
> new functions? they don't have to be big.
> 
> 
> On Sat, 20 Sep 2008, Klaus Trainer wrote:
> 
>> [after a loong delay...]
>>
>> On Mon, 28 Jul 2008, Ryan Barrett wrote:
>>
>>> one request: i'd like to see the common code in pyarchives.py and
>>> pyarchives_enhanced.py enhanced. ideally, pyarchives_enhanced users
>>> would
>>> install both, and pyarchives_enhanced.py would enable itself, disable
>>> pyarchives.py, and reuse the common pieces.
>>>
>>
>> I've finally realized your proposal. The plugin is now 100% backward
>> compatible with the old pyarchives.py plugin. People using the newer
>> pyarchives_enhanced.py plugin just need to define
>> py['archives_enhanced'] = True
>> in their config.py, so it's also 100% backward compatible to the former
>> pyarchives_enhanced.py then.
>>
>> What I've done in pyarchives_enhanced.py was to rename the method
>> "genLinearArchive" to "genEnhancedArchive" and paste the method
>> "genLinearArchive" from pyarchives.py into it. Then I added a check
>> whether "archives_enhanced" is set True, depending on this,
>> "genEnhancedArchive" or "genLinearArchive" will be invoked.
>> After all I've renamed pyarchives_enhanced.py to pyarchives.py, so there
>> will be no separate enhanced version anymore.
>>
>> I've also made minor changes to pyarchives.js, the toggling
>> functionality is now even more similar to blogspot, i.e. the most recent
>> archives are now open by default.
>>
>> example.css (the version from the previous attached package) has been
>> renamed to pyarchives.css.
>>
>> README has also been updated according to the changes.
>>
>>
>> Regards,
>>
>> Klaus
>>
> 
> -Ryan
> 
> -- 
> http://snarfed.org/

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

_______________________________________________
Pyblosxom-devel mailing list
Pyblosxom-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/pyblosxom-devel
pyarchives.diff (text/x-diff, 26 KB)
diff -up ./pyarchives.js.old ./pyarchives.js
--- ./pyarchives.js.old	2008-10-05 14:25:28.000000000 +0200
+++ ./pyarchives.js	2008-10-05 14:29:48.000000000 +0200
@@ -44,8 +44,9 @@ function toggleNextOpenClose(el) {
 	var next = el.nextSibling;
 	el.className = el.className.replace(new RegExp(opcn + "\\b"), "");
 	el.className = el.className.replace(new RegExp(clcn + "\\b"), "");
-	while(next.nodeType != 1)
+	while(next.nodeType != 1) {
 		next = next.nextSibling;
+	}
 	next.style.display = (next.style.display == "none" ? "block" : "none");
 	el.className +=
 		(next.style.display == "block" ? " " + opcn : " " + clcn);
@@ -53,7 +54,15 @@ function toggleNextOpenClose(el) {
 
 function toggleNextByTagAndClassName(tag, cname) {
 	clickers = getElementsByTagAndClassName(tag, cname);
-	for (i = 0; i < clickers.length; i++) {
+	if (clickers.length < 1) {
+		return;
+	}
+	clickers[0].className += " " + ccn;
+	clickers[0].className += " " + opcn;
+	clickers[0].onclick = function() {
+		toggleNextOpenClose(this);
+	}
+	for (i = 1; i < clickers.length; i++) {
 		clickers[i].className += " " + ccn;
 		clickers[i].className += " " + clcn;
 		clickers[i].onclick = function() {
@@ -91,23 +100,23 @@ var EventCache = function() {
 			var i, item;
 			for (i = listEvents.length - 1; i >= 0; i = i - 1) {
 				item = listEvents[i];
-				if(item[0].removeEventListener) {
+				if (item[0].removeEventListener) {
 					item[0].removeEventListener(item[1],
 						item[2], item[3]);
-				};
+				}
 				if (item[1].substring(0, 2) != "on") {
 					item[1] = "on" + item[1];
-				};
+				}
 				if (item[0].detachEvent) {
 					item[0].detachEvent(item[1], item[2]);
-				};
+				}
 				item[0][item[1]] = null;
-			};
+			}
 		}
 	};
 }();
-	
-	
+
+
 function pageLoaders(e) {
 	toggleNextByTagAndClassName("div", "archiveYear");
 	toggleNextByTagAndClassName("div", "archiveMonth");
diff -up ./example.css.old ./example.css
diff -up ./README.old ./README
--- ./README.old	2008-10-05 14:24:32.000000000 +0200
+++ ./README	2008-09-19 23:45:30.000000000 +0200
@@ -1,25 +1,34 @@
-This README is for pyarchives_enhanced.py, which is an enhanced version of the
-old pyarchives plugin.
+This README is for pyarchives.py, which is a plugin with optional enhanced
+functionality. It walks through your blog root and generates archives from all
+available entries.
+The generated html information is stored in the $archivelinks variable which
+can be used in your head or foot flavour templates.
 
-Added functionality:
+The basic version (as default) simply generates a list of monthly archives.
+
+Optional enhanced functionality:
     - yearly archives
     - number of an archive's entries can be displayed
-    - toggling functionality (open/close) for monthly archives is available
-      through javascript and CSS
+    - toggling functionality (open/close) for archives is available through
+      Javascript and CSS
 
 
-HOW TO ENABLE THE TOGGLING FUNCTIONALITY:
+HOW TO ENABLE THE ENHANCED FUNCTIONALITY:
 
-1) Make sure the content of pyarchives.js or the file itself is included in
-   your head flavor template's head section, for example:
-     <script type="text/javascript" src="/js/pyarchives.js"></script>
+1) Set the variable "archives_enhanced" to True in your config.py:
+   	py['archives_enhanced'] = True
 
 2) Copy opened.png and closed.png to your images directory.
 
-3) Update your CSS file. For an example, see example.css. Make sure that the
-   url-path in the closed and open classes' background-property refers to the
-   files you have copied in step 2.
+3) Make sure the content of pyarchives.js or the file itself is included in
+   your head flavor template's head section, for example:
+	<script type="text/javascript" src="/js/pyarchives.js"></script>
+
+4) Make sure the content of pyarchives.css or the file itself is included in
+   your head flavor template's head section, for example:
+   	<link href="/css/pyblosxom.css" rel="stylesheet" type="text/css" />
+   You may eventually need to adjust the images' path names.
 
 
 For further information about how to configure the plugin's output, please
-read the documentation in the top section of the file pyblosxom_enhanced.py.
+read the documentation in the top section of the file pyblosxom.py.
diff -up ./pyarchives.py.old ./pyarchives.py
--- ./pyarchives.py.old   2008-10-05 14:49:50.000000000 +0200
+++ ./pyarchives.py	2008-10-05 15:05:25.000000000 +0200
@@ -1,22 +1,100 @@
-# vim: tabstop=4 shiftwidth=4
 """
-Walks through your blog root figuring out all the available monthly archives in
-your blogs.  It generates html with this information and stores it in the
-$archivelinks variable which you can use in your head or foot templates.
+Walks through your blog root and generates archives from all available entries.
+The generated html information is stored in the $archivelinks variable which
+you can use in your head or foot templates.
+
+The basic version (as default) simply generates a list of monthly archives.
+
+You can however optionally enable the enhanced version which generates yearly
+and monthly archives combined with a dynamic toggling functionality (i.e.
+open/close archives).
+To enable the enhanced version you need to set the variable "archives_enhanced"
+to True in your config.py. You also need to copy the provided files
+    - closed.png
+    - opened.png
+    - pyblosxom.css
+    - pyarchives.js
+into appropriate directories and include pyarchives.css and pyarchives.js in
+your head flavour template.
+You may eventually need to adjust the images' path names.
+These instructions can be found with examples in the README file.
+
+You can format the output by setting these variables in your config.py, whereas
+this variable applies only to the basic version:
+    "archive_template"
+and these variables apply only to the enhanced version:
+    "archives_start",
+    "archive_template_year" .
+    "archive_template_month",
+    "archive_template_post",
+    "archives_finish" .
+
+"archives_start" starts the archive listing and is only used at the very
+beginning. The "archive_template_year" and "archive_template_month" properties
+are the templates for each year or rather month item. "archive_template_post"
+represents a single post. Then, after all the
+archives are printed, "archives_finish" ends the archives listing.
 
-You can format the output with the key "archive_template".
 
 A config.py example:
 
-    py['archive_template'] = '<li><a href="%(base_url)s/%(Y)s/%(b)s">%(m)s/%(y)s</a></li>'
+py['archives_enhanced'] = True
 
-Displays the archives as list items, with a month number slash year number, like 06/78.
+py['archives_start'] = '<ul id="archives"><li>'
 
-The vars available with typical example values are:
+py['archive_template_year'] = '<div class="archiveYear"><a href="%(base_url)s/%(Y)s">%(Y)s</a> (%(c)s)</div>'
+
+py['archive_template_month'] = '<div class="archiveMonth"><a href="%(base_url)s/%(Y)s/%(m)s">%(B)s %(Y)s</a> (%(c)s)</div>'
+
+py['archive_template_post'] = '<div class="archivePost"><a href="%(base_url)s/%(post)s.%(default_flavour)s">%(title)s</a></div>'
+
+py['archives_finish'] = '</li></ul>'
+
+The vars available through this plugin with typical example values are:
     b      'Jun'
+    B      'June'
     m      '6'
     Y      '1978'
     y      '78'
+    c      '(7)'
+where "%(c)s" is the number of blog entries in a particular archive.
+
+
+Available CSS elements:
+    #archives
+    .closed
+    .open
+    .clicker
+    .archiveYear
+    .archiveMonth
+    .archivePost
+For the enhanced version it is important to stick to the above element names,
+otherwise the Javascript toggling functionality won't be available.
+
+CSS example:
+#archives {
+}
+.closed {
+    background: url(/images/closed.png) left center no-repeat;
+}
+.open {
+    background: url(/images/opened.png) left center no-repeat;
+}
+.clicker {
+    cursor: pointer;
+}
+.archiveYear {
+    text-indent: 1.5em;
+    margin: 5px 0px 2px 0px;
+}
+.archiveMonth {
+    text-indent: 2em;
+}
+.archivePost {
+    text-indent: 2.5em;
+    font-size : 0.9em;
+    margin: 2px 0px;
+}
 
 
 Permission is hereby granted, free of charge, to any person
@@ -39,22 +117,65 @@ ACTION OF CONTRACT, TORT OR OTHERWISE, A
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 
-Copyright 2004, 2005 Wari Wahab
+Copyright 2004, 2005 Wari Wahab, 2008 Klaus Trainer
 """
-__author__ = "Wari Wahab - wari at wari dot per dot sg"
+__author__ = ["Wari Wahab - wari at wari dot per dot sg",
+              "Klaus Trainer - klaus.trainer at web dot de"]
 __version__ = "$Id$"
+__url__ = "http://pyblosxom.sourceforge.net"
+__description__ = "Creates yearly and monthly archives for PyBlosxom."
 
 from Pyblosxom import tools
-import time, os
+import re, time, os, os.path
 
 def verify_installation(request):
     config = request.getConfiguration()
-    if not config.has_key("archive_template"):
-        print "missing optional config property 'archive_template' which "
-        print "allows you to specify how the archive links are created.  "
-        print "refer to pyarchive plugin documentation for more details."
+    if not config.get("archives_enhanced", False):
+        if not config.has_key("archives_enhanced"):
+            print "Missing optional config property 'archives_enhanced'."
+            print "Refer to pyarchive plugin documentation for more details."
+            return 1
+        else:
+            if not config.has_key("archive_template"):
+                print "Missing optional config property 'archive_template' "
+                print "which allows you to specify how the archive links are "
+                print "created.\nRefer to pyarchive plugin documentation for "
+                print "more details."
+            return 1
+    if not config.has_key("archives_start"):
+        print "Missing optional config property 'archives_start' which "
+        print "allows you to specify how the archive links are created.\n"
+        print "Refer to pyarchive plugin documentation for more details."
+    if not config.has_key("archive_template_year"):
+        print "Missing optional config property 'archive_template_year' which "
+        print "allows you to specify how the archive links are created.\n"
+        print "Refer to pyarchive plugin documentation for more details."
+    if not config.has_key("archive_template_month"):
+        print "Missing optional config property 'archive_template_month' which"
+        print " allows you to specify how the archive links are created.\n"
+        print "Refer to pyarchive plugin documentation for more details."
+    if not config.has_key("archive_template_post"):
+        print "Missing optional config property 'archives_template_post' which"
+        print " allows you to specify how the archive links are created.\n"
+        print "Refer to pyarchive plugin documentation for more details."
+    if not config.has_key("archives_finish"):
+        print "Missing optional config property 'archives_finish' which "
+        print "allows you to specify how the archive links are created.\n"
+        print "Refer to pyarchive plugin documentation for more details."
     return 1
 
+class Record:
+    """
+    The class used for archive records. A record consists of an year entry in
+    the 'archives' dictionary which consists of one or more month entries which
+    in turn consists of one or more post entries.
+    This implementation equates to the "composite pattern".
+    """
+    def __init__(self, repr):
+        self.count = 0
+        self.archives = {}
+        self.repr = repr
+
 class PyblArchives:
     def __init__(self, request):
         self._request = request
@@ -62,10 +183,17 @@ class PyblArchives:
 
     def __str__(self):
         if self._archives == None:
-            self.genLinearArchive()
+            if self._request.getConfiguration().get('archives_enhanced', False):
+                self.genEnhancedArchive()   # enhanced version
+            else:
+                self.genLinearArchive()     # basic version (default)
         return self._archives
 
+
     def genLinearArchive(self):
+        """
+        Generate monthly archives (basic version, default)
+        """
         config = self._request.getConfiguration()
         data = self._request.getData()
         root = config["datadir"]
@@ -95,6 +223,95 @@ class PyblArchives:
             result.append(archives[key])
         self._archives = '\n'.join(result)
 
+
+    def genEnhancedArchive(self):
+        """
+        Generate yearly and monthly archives with post titles (enhanced version)
+        """
+        config = self._request.getConfiguration()
+        root = config["datadir"]
+        year_archives = {}
+        archiveList = tools.Walk(self._request, root)
+        fulldict = {}
+        fulldict.update(config)
+
+        year_template = (config.get('archive_template_year',
+            '<div class="archiveYear"><a href="%(base_url)s/%(Y)s">%(Y)s</a> (%(c)s)</div>'))
+        month_template = (config.get('archive_template_month',
+            '<div class="archiveMonth"><a href="%(base_url)s/%(Y)s/%(m)s">%(B)s %(Y)s</a> (%(c)s)</div>'))
+        post_template = (config.get('archive_template_post',
+            '<div class="archivePost"><a href="%(base_url)s/%(post)s.%(default_flavour)s">%(title)s</a></div>'))
+
+        for mem in archiveList:
+            timetuple = tools.filestat(self._request, mem)
+            timedict = {}
+            # "%(c)d" will be correctly replaced
+            # later by the number of archive entries...
+            timedict["c"] = "%(c)d"
+            for x in ["B", "b", "m", "Y", "y", "d", "H", "M", "S"]:
+                timedict[x] = time.strftime("%" + x, timetuple)
+            fulldict.update(timedict)
+
+            # Create year entry if needed, and update count
+            if not year_archives.has_key(timedict['Y']):
+                year_archives[timedict['Y']] = Record(year_template % fulldict)
+
+            year_entry = year_archives[timedict['Y']]
+            year_entry.count += 1
+
+            # Create month entry if needed, update count
+            month_archives = year_entry.archives
+
+            if not month_archives.has_key(timedict['m']):
+                month_archives[timedict['m']] = Record(month_template % fulldict)
+
+            month_entry = month_archives[timedict['m']]
+            month_entry.count += 1
+
+            # Save title of the post
+            post_archives = month_entry.archives
+            text = open(mem).readline()
+            title = re.match(".+", text).group()
+            post, ext = os.path.splitext(mem[len(root):])
+            post = post.lstrip('/')
+            fulldict.update({ 'title' : title, 'post' : post})
+            post_archives[timedict['d'] + timedict['H'] + timedict['M'] + timedict['S']] = post_template % fulldict
+
+        year_keys = year_archives.keys()
+        year_keys.sort()
+        year_keys.reverse()
+
+        # Generate output
+        result = [ config.get('archives_start', '<ul id="archives"><li>') ]
+        for year_key in year_keys:
+            year_entry = year_archives[year_key]
+
+            result.append(year_entry.repr % {"c": year_entry.count})
+            result.append('<div>')
+
+            month_keys = year_entry.archives.keys()
+            month_keys.sort()
+            month_keys.reverse()
+
+            for month_key in month_keys:
+                month_entry = year_entry.archives[month_key]
+                result.append(month_entry.repr % {"c": month_entry.count})
+                result.append('<div>')
+ 
+                post_keys = month_entry.archives.keys()
+                post_keys.sort()
+                post_keys.reverse()
+
+                for post_key in post_keys:
+                    post_entry = month_entry.archives[post_key]
+                    result.append(post_entry)
+
+                result.append('</div>')
+            result.append('</div>')
+
+        result.append(config.get('archives_finish', '</li></ul>'))
+        self._archives = '\n'.join(result)
+
 def cb_prepare(args):
     request = args["request"]
     data = request.getData()
diff -up ./pyarchives_enhanced.py /dev/null
--- ./pyarchives_enhanced.py	2008-10-05 14:23:27.000000000 +0200
+++ /dev/null	2008-05-08 15:17:18.000000000 +0200
@@ -1,236 +0,0 @@
-"""
-Walks through your blog root and generates yearly and monthly archives from all
-available entries.  The generated html information is stored in the
-$archivelinks variable which you can use in your head or foot templates.
-
-You can format the output by setting these variables in your config.py:
-"archives_start",
-"archive_template_year",
-"archive_template_month",
-"archive_template_post" and
-"archives_finish" .
-
-"archives_start" starts the archive listing and is only used at the very
-beginning. The "archive_template_year" and "archive_template_month" properties
-are the templates for each year or rather month item. "archive_template_post"
-represents a single post. Then, after all the
-archives are printed, "archives_finish" ends the archives listing.
-
-A config.py example:
-
-py['archives_start'] = '<ul id="archives"><li>'
-
-py['archive_template_year'] = '<div class="archiveYear"><a href="%(base_url)s/%(Y)s">%(Y)s</a> (%(c)s)</div>'
-
-py['archive_template_month'] = '<div class="archiveMonth"><a href="%(base_url)s/%(Y)s/%(m)s">%(B)s %(Y)s</a> (%(c)s)</div>'
-
-py['archive_template_post'] = '<div class="archivePost"><a href="%(base_url)s/%(post)s">%(title)s</a> </div>'
-
-py['archives_finish'] = '</li></ul>'
-
-The vars available through this plugin with typical example values are:
-    b      'Jun'
-    B      'June'
-    m      '6'
-    Y      '1978'
-    y      '78'
-    c      '(7)'
-where "%(c)s" is the number of blog entries in a particular archive.
-
-Available CSS elements:
-    #archives
-    .closed
-    .open
-    .clicker
-    .archiveYear
-    .archiveMonth
-    .archivePost
-
-CSS example:
-#archives {
-}
-.closed {
-  background: url(/images/closed.png) left center no-repeat;
-}
-.open {
-  background: url(/images/opened.png) left center no-repeat;
-}
-.clicker {
-  cursor: pointer;
-}
-.archiveYear {
-  text-indent: 1.5em;
-  margin: 5px 0px 2px 0px;
-}
-.archiveMonth {
-  text-indent: 2em;
-}
-.archivePost {
-  text-indent: 2.5em;
-  font-size : 0.9em;
-  margin: 2px 0px;
-}
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without restriction,
-including without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell copies of the
-Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-
-Copyright 2004, 2005 Wari Wahab, 2008 Klaus Trainer
-"""
-__author__ = ["Wari Wahab - wari at wari dot per dot sg",
-              "Klaus Trainer - klaus.trainer at web dot de"]
-__version__ = "$Id$"
-__url__ = "http://pyblosxom.sourceforge.net"
-__description__ = "Creates yearly and monthly archives for PyBlosxom."
-
-from Pyblosxom import tools
-import re, time, os, os.path
-
-def verify_installation(request):
-    config = request.getConfiguration()
-    if not config.has_key("archives_start"):
-        print "Missing optional config property 'archives_start' which "
-        print "allows you to specify how the archive links are created.\n"
-        print "Refer to pyarchive plugin documentation for more details."
-    if not config.has_key("archive_template_year"):
-        print "Missing optional config property 'archive_template_year' which "
-        print "allows you to specify how the archive links are created.\n"
-        print "Refer to pyarchive plugin documentation for more details."
-    if not config.has_key("archive_template_month"):
-        print "Missing optional config property 'archive_template_month' which"
-        print " allows you to specify how the archive links are created.\n"
-        print "Refer to pyarchive plugin documentation for more details."
-    if not config.has_key("archives_template_post"):
-        print "Missing optional config property 'archives_template_post' which"
-        print " allows you to specify how the archive links are created.\n"
-        print "Refer to pyarchive plugin documentation for more details."
-    if not config.has_key("archives_finish"):
-        print "Missing optional config property 'archives_finish' which "
-        print "allows you to specify how the archive links are created.\n"
-        print "Refer to pyarchive plugin documentation for more details."
-    return 1
-
-class Record:
-    def __init__(self, repr):
-        self.count = 0
-        self.archives = {}
-        self.repr = repr
-
-class PyblArchives:
-    def __init__(self, request):
-        self._request = request
-        self._archives = None
-
-    def __str__(self):
-        if self._archives == None:
-            self.genLinearArchive()
-        return self._archives
-
-    def genLinearArchive(self):
-        config = self._request.getConfiguration()
-        root = config["datadir"]
-        year_archives = {}
-        archiveList = tools.Walk(self._request, root)
-        fulldict = {}
-        fulldict.update(config)
-
-        year_template = (config.get('archive_template_year',
-            '<div class="archiveYear"><a href="%(base_url)s/%(Y)s">%(Y)s</a> (%(c)s)</div>'))
-        month_template = (config.get('archive_template_month',
-            '<div class="archiveMonth"><a href="%(base_url)s/%(Y)s/%(m)s">%(B)s %(Y)s</a> (%(c)s)</div>'))
-        post_template = (config.get('archive_template_post',
-            '<div class="archivePost"><a href="%(base_url)s/%(post)s">%(title)s</a> </div>'))
-
-        for mem in archiveList:
-            timetuple = tools.filestat(self._request, mem)
-            timedict = {}
-            # "%(c)d" will be correctly replaced
-            # later by the number of archive entries...
-            timedict["c"] = "%(c)d"
-            for x in ["B", "b", "m", "Y", "y", "d", "H", "M", "S"]:
-                timedict[x] = time.strftime("%" + x, timetuple)
-            fulldict.update(timedict)
-
-            # Create year entry if needed, and update count
-            if not year_archives.has_key(timedict['Y']):
-                year_archives[timedict['Y']] = Record(year_template % fulldict)
-
-            year_entry = year_archives[timedict['Y']]
-            year_entry.count += 1
-
-            # Create month entry if needed, update count
-            month_archives = year_entry.archives
-
-            if not month_archives.has_key(timedict['m']):
-                month_archives[timedict['m']] = Record(month_template % fulldict)
-
-            month_entry = month_archives[timedict['m']]
-            month_entry.count += 1
-
-            # Save title of the post
-            post_archives = month_entry.archives
-            text = open(mem).readline()
-            title = re.match(".+", text).group()
-            post = os.path.basename(mem)
-            fulldict.update({ 'title' : title, 'post' : post})
-            post_archives[timedict['d'] + timedict['H'] + timedict['M'] + timedict['S']] = post_template % fulldict
-
-        year_keys = year_archives.keys()
-        year_keys.sort()
-        year_keys.reverse()
-
-        # Generate output
-        result = [ config.get('archives_start', '<ul id="archives"><li>') ]
-        for year_key in year_keys:
-
-            year_entry = year_archives[year_key]
-
-            result.append(year_entry.repr % {"c": year_entry.count})
-            result.append('<div>')
-
-            month_keys = year_entry.archives.keys()
-            month_keys.sort()
-            month_keys.reverse()
-
-            for month_key in month_keys:
-
-                month_entry = year_entry.archives[month_key]
-                result.append(month_entry.repr % {"c": month_entry.count})
-                result.append('<div>')
-
-                post_keys = month_entry.archives.keys()
-                post_keys.sort()
-                post_keys.reverse()
-
-                for post_key in post_keys:
-
-                    post_entry = month_entry.archives[post_key]
-                    result.append(post_entry)
-
-                result.append('</div>')
-            result.append('</div>')
-
-        result.append(config.get('archives_finish', '</li></ul>'))
-        self._archives = '\n'.join(result)
-
-def cb_prepare(args):
-    request = args["request"]
-    data = request.getData()
-    data["archivelinks"] = PyblArchives(request)
diff -up ./example.css /dev/null
--- ./example.css	2008-10-05 14:26:30.000000000 +0200
+++ /dev/null	2008-05-08 15:17:18.000000000 +0200
@@ -1,26 +0,0 @@
-/*	Archives	*/
-
-#archives {
-}
-.closed {
-	background: url(/images/closed.png) left center no-repeat;
-}
-.open {
-	background: url(/images/opened.png) left center no-repeat;
-}
-.clicker {
-	cursor: pointer;
-}
-.archiveYear {
-        text-indent: 1.5em;
-        margin: 5px 0px 2px 0px;
-}
-.archiveMonth {
-        text-indent: 2em;
-	margin: 4px 0px 2px 10px;
-}
-.archivePost {
-	text-indent: 2.5em;
-	font-size: 0.9em;
-	margin: 0px 0px 3px 15px;
-}
diff -up /dev/null ./pyarchives.css
--- /dev/null	2008-05-08 15:17:18.000000000 +0200
+++ ./pyarchives.css	2008-09-20 11:09:48.000000000 +0200
@@ -0,0 +1,27 @@
+/*	Archives	*/
+
+#archives {
+}
+.closed {
+	background: url(/images/closed.png) left center no-repeat;
+}
+.open {
+	background: url(/images/opened.png) left center no-repeat;
+}
+.clicker {
+	cursor: pointer;
+}
+.archiveYear {
+        text-indent: 1.5em;
+        margin: 5px 0px 2px 0px;
+}
+.archiveMonth {
+        text-indent: 2em;
+	margin: 4px 0px 2px 10px;
+}
+.archivePost {
+	padding-left: 50px;
+	text-indent: -15px;
+	font-size: small;
+	margin: 0px 0px 3px 0px;
+}