leonardo-0.7b1/0000755000076500007650000000000010344513411014526 5ustar jtauberjtauber00000000000000leonardo-0.7b1/APACHE_HOWTO0000644000076500007650000000111210344474163016357 0ustar jtauberjtauber00000000000000 SETTING UP LEONARDO WITH APACHE 1. change leonardo_lib in leonardo-cgi.py to point to the full path of Leonardo's lib directory 2. change data_dir in leonardo.config to point to the full path of Leonardo's data directory. If you aren't going to run Leonardo at / do: 3. change cgi_root in leonardo.config to /cgi-bin/leonardo-cgi.py/ 4. copy the two files in cgi-bin into your Apache cgi-bin directory. 5. DONE! or, if you are going to run Leonardo at / 3. add something like ScriptAlias / to your Apache config. 4. DONE! leonardo-0.7b1/bin/0000755000076500007650000000000010344513407015303 5ustar jtauberjtauber00000000000000leonardo-0.7b1/bin/convert-db-to-pickle.py0000755000076500007650000000304310340021773021604 0ustar jtauberjtauber00000000000000#!/usr/bin/env python # # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ This script converts shelve property_db files to property_pickle """ import sys import os import os.path import shelve import cPickle as pickle def usage(): print 'Usage:', sys.argv[0], '' sys.exit(0) if len(sys.argv) != 2: usage() lfs_root = sys.argv[1] for dirpath, dirnames, filenames in os.walk(lfs_root): if "/.svn" not in dirpath: if "property_db" in filenames: db_filename = os.path.join(dirpath, "property_db") pickle_filename = os.path.join(dirpath, "property_pickle") print "converting %s..." % db_filename, d = dict(shelve.open(db_filename)) f = file(pickle_filename, "w") pickle.dump(d, f) f.close() print "done." leonardo-0.7b1/bin/create_user.py0000755000076500007650000000316110340255737020167 0ustar jtauberjtauber00000000000000#!/usr/bin/env python # # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ This script is used for creating or modifying users in the user database. """ USER_DB = "../data/users" def create_user(name, email, password): """ Creates (or overwrites) a new user entry in the user database. """ import sha user_db = file(USER_DB, "a") user_db.write("%s:%s:%s\n" % (email, name, sha.sha(password).hexdigest())) from getpass import getpass if __name__ == "__main__": name = raw_input("User's Full Name: ") email = raw_input("User's Email Address (used as user id): ") while True: password = getpass("User's Password: ") password_confirm = getpass("Confirm Password: ") if password == password_confirm: break print "Passwords didn't match." print "Creating account...", create_user(name, email, password) print "done." leonardo-0.7b1/bin/regen_index.py0000644000076500007650000000351010343060523020136 0ustar jtauberjtauber00000000000000#!/usr/bin/env python # # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ This script regenerates the property index. """ import sys import os import os.path # adjust accordingly if need be sys.path.append("../lib/") from leonardo import filesystem def usage(): print 'Usage:', sys.argv[0], '' sys.exit(0) if len(sys.argv) != 2: usage() lfs_root = sys.argv[1] lfs = filesystem.LeonardoFileSystem(lfs_root) index_filename = os.path.join(lfs_root, "property_pickle") if os.access(index_filename, os.F_OK): os.remove(index_filename) def reindex(lfs, key=""): directories, files = lfs.get_children(key) for filename in files: # we use os.path.join because if key="" we don't want initial slash f = lfs.get(os.path.join(key, filename)) print f.key for name in f.get_properties(): value = f.get_property(name) if value != None: print "\t", name, value lfs.add_property(f.key, name, value) for directory in directories: reindex(lfs, os.path.join(key, directory)) reindex(lfs)leonardo-0.7b1/cgi-bin/0000755000076500007650000000000010344513410016035 5ustar jtauberjtauber00000000000000leonardo-0.7b1/cgi-bin/leonardo-cgi.py0000755000076500007650000000262410343023112020753 0ustar jtauberjtauber00000000000000#!/usr/bin/env python # # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ## Leonardo main CGI script import cgitb; cgitb.enable() from os.path import join, dirname, abspath # location of leonardo modules (full path to leonardo lib directory) # the following is just to get you started - you'll probably want to change it # to the literal path leonardo_lib = abspath(join(dirname(__file__), "../lib")) # location of main configuration file for this instance main_config = join(dirname(__file__), "leonardo.config") import sys; sys.path.append(leonardo_lib) from leonardo import core from leonardo import config leonardo = core.Leonardo(config.Configurator(main_config)) leonardo.dispatch()leonardo-0.7b1/cgi-bin/leonardo.config0000644000076500007650000000221510344513332021032 0ustar jtauberjtauber00000000000000## Configuration file for Leonardo # # You will almost certainly want to change the settings under [page], [blog] # and [atom03] / [atom10] # # For actual deployment you will likely want to change data_dir as well. # Without a ScriptAlias, you will probably also need to change cgi-root. [page] site_title : Leonardo site_sub_title : sample site rights : All content Copyright 2003-2005, James Tauber [blog] blog_title : Leonardo Sample Blog site_url : http://localhost:8000/ [atom03] blog_author : James Tauber [atom10] blog_author : James Tauber [DEFAULT] # location of data directory # uncomment and change value if data_dir not parallel to lib # note: must end with a slash # data_dir : # location of lfs directory # uncomment and change value if not under data_dir # note: must end with a slash # lfs_root : # http path to the cgi-bin script cgi_root : / # usually blank path_prefix : # http path to the home page # must end with a slash when expanded home_href : %(cgi_root)s%(path_prefix)s leonardo-0.7b1/CREDITS0000644000076500007650000000032410344474257015563 0ustar jtauberjtauber00000000000000Principal Developer James Tauber Developers Bryan Lawrence Dave Warnock Contributors Peter Sefton Min Sik Kim C. J. Wagenius Grigory Bakunov Xavier Verges Tim Wegener leonardo-0.7b1/data/0000755000076500007650000000000010344513410015436 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/0000755000076500007650000000000010344513411016223 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/__home__.ldv/0000755000076500007650000000000010344513411020533 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/__home__.ldv/__content__.wiki040000644000076500007650000000526610334325330024043 0ustar jtauberjtauber00000000000000It is recommended you read this page to get started. Further enquires should be directed to the [http://pyworks.org/mailman/listinfo/leonardo Leonardo Mailing List]. ===Users and Login=== To be able to edit pages or create new ones, you will need to first of all create a user account by running create_user.py in the Leonardo bin directory. You will then be able to log in via the Login link at the bottom of the menu to the left. ===Editing Existing Pages=== To edit an existing page, click on the Edit Page link at the bottom of the menu. You can find out more about the wiki formatting used in the [Wiki Formatting Guide]. ===Creating a New Page=== A page may be created one of two ways. If you go to a non-existent page while logged in, you can click on Edit Page to create the page directly. Alternatively, you can use the draft facility. Click on Drafts at the bottom of the menu (while logged in) and you can create a draft page. Once you have created a draft page, you can promote it to a real page (which includes as a blog entry). ===Blog Entries=== A blog entry is simply a page under blog////. Entries here will show up in daily, monthly, yearly and overall blog lists as well as in the Atom feed. ===Atom Feeds=== A titles-only atom feed is available at /atom and a full-entry atom feed at /atom/full. Note that these paths can be changed by overriding the relevant atom provider settings in your leonardo.config file. ===Comments and Trackbacks=== When you create or edit a page, you will have the option to turn on comments and/or trackbacks. ===Categories=== When you create or edit a page, you can specify a list of categories separated by whitespace. Categories in Leonardo are just page names. For example, a category "python" corresponds to page "python". If you would like a list of all the pages in category foo, insert "category/foo". See the [Wiki Formatting Guide] for how to insert. ===Non-Wiki Files=== Non-wiki files such as images or code can be uploaded using the "Upload File" page linked from the bottom of the menu when logged in. It is also possible to edit such files if they are text. An example is at [:/static/sample]. ===Customization=== Customisation can be done in a number of places: * the leonardo.config file in cgi-bin * the menu (see below) * the css stylesheet (see below) * individual templates under the lib/providers packages ===Editing the Menu=== To change the menu to the left, go to [:edit?resource=__menu__ /edit?resource=__menu__] while logged. ===Editing the CSS=== To make a change to the CSS stylesheet, go to [:edit?resource=css /edit?resource=css] while logged in.������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__home__.ldv/property_pickle������������������������������������������������0000644�0000765�0000765�00000000306�10340021773�023671� 0����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������(dp1 S'allow_comments' p2 S'NO' p3 sS'last_modified' p4 F1132470581.8110039 sS'page_title' p5 S'Welcome to Leonardo!!' p6 sS'allow_trackbacks' p7 S'NO' p8 sS'author' p9 S'jtauber@jtauber.com' p10 s.��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/������������������������������������������������������������������0000755�0000765�0000765�00000000000�10344513410�017665� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/creation_time/����������������������������������������������������0000755�0000765�0000765�00000000000�10344513410�022507� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/creation_time/2005/�����������������������������������������������0000755�0000765�0000765�00000000000�10344513410�023075� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/creation_time/2005/11/��������������������������������������������0000755�0000765�0000765�00000000000�10344513410�023316� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/creation_time/2005/11/11/�����������������������������������������0000755�0000765�0000765�00000000000�10344513410�023537� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/creation_time/2005/11/11/KEYS�������������������������������������0000644�0000765�0000765�00000000044�10340021773�024235� 0����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������blog/2005/11/11/my_first_blog_entry ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/����������������������������������������������������0000755�0000765�0000765�00000000000�10344513410�022470� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/2005/�����������������������������������������������0000755�0000765�0000765�00000000000�10344513410�023056� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/2005/11/��������������������������������������������0000755�0000765�0000765�00000000000�10344513410�023277� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/2005/11/09/�����������������������������������������0000755�0000765�0000765�00000000000�10344513410�023527� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/2005/11/09/KEYS�������������������������������������0000644�0000765�0000765�00000000011�10334325330�024216� 0����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������__home__ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/2005/11/11/�����������������������������������������0000755�0000765�0000765�00000000000�10344513410�023520� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/2005/11/11/KEYS�������������������������������������0000644�0000765�0000765�00000000103�10340021773�024212� 0����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������blog/2005/11/11/my_first_blog_entry __home__ wiki_formatting_guide �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/2005/11/20/�����������������������������������������0000755�0000765�0000765�00000000000�10344513410�023520� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__INDEX__/last_modified/2005/11/20/KEYS�������������������������������������0000644�0000765�0000765�00000000011�10340021773�024210� 0����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������__home__ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__menu__.ldv/���������������������������������������������������������������0000755�0000765�0000765�00000000000�10344513410�020546� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__menu__.ldv/__content__.wiki04���������������������������������������������0000644�0000765�0000765�00000000441�10305760536�024056� 0����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������===[:/ Home Page]=== ===General=== [http://jtauber.com/leonardo Leonardo Home Page] [Wiki Formatting Guide] [Wiki Formatting Guide 2] ===[Blog]=== [insert:calendar] [:/atom/ title-only atom feed] [:/atom/full/ full-entry atom feed] [:/blog/all/ all blog entries]�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__page_template__.ldv/������������������������������������������������������0000755�0000765�0000765�00000000000�10344513411�022412� 5����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������leonardo-0.7b1/data/lfs/__page_template__.ldv/__content__.xhtml�������������������������������������0000644�0000765�0000765�00000001064�10307020060�025727� 0����������������������������������������������������������������������������������������������������ustar �jtauber�������������������������jtauber�������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/TD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>%(title)s %(head_links)s

%(site_title)s

%(sub_title)s

%(title_html)s

%(content)s
%(lastmod_display)s %(author_display)s
%(rights)s
leonardo-0.7b1/data/lfs/blog/0000755000076500007650000000000010344513410017145 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2004/0000755000076500007650000000000010344513410017532 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2004/12/0000755000076500007650000000000010344513410017754 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2004/12/16/0000755000076500007650000000000010344513410020202 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2004/12/16/my_first_blog_entry.ldv/0000755000076500007650000000000010344513410025046 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2004/12/16/my_first_blog_entry.ldv/__content__.wiki040000644000076500007650000000003410242537427030355 0ustar jtauberjtauber00000000000000This is my first blog entry.leonardo-0.7b1/data/lfs/blog/2004/12/16/my_first_blog_entry.ldv/property_pickle0000644000076500007650000000016210340021773030205 0ustar jtauberjtauber00000000000000(dp1 S'allow_comments' p2 S'NO' p3 sS'page_title' p4 S'My First Blog Entry' p5 sS'allow_trackbacks' p6 S'NO' p7 s.leonardo-0.7b1/data/lfs/blog/2005/0000755000076500007650000000000010344513410017533 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2005/11/0000755000076500007650000000000010344513410017754 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2005/11/11/0000755000076500007650000000000010344513410020175 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2005/11/11/my_first_blog_entry.ldv/0000755000076500007650000000000010344513410025041 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog/2005/11/11/my_first_blog_entry.ldv/__content__.wiki040000644000076500007650000000004710340021773030343 0ustar jtauberjtauber00000000000000This is a draft of my first blog entry.leonardo-0.7b1/data/lfs/blog/2005/11/11/my_first_blog_entry.ldv/property_pickle0000644000076500007650000000035510340021773030204 0ustar jtauberjtauber00000000000000(dp1 S'allow_comments' p2 S'NO' p3 sS'page_title' p4 S'My First Blog Entry' p5 sS'author' p6 S'jtauber@jtauber.com' p7 sS'creation_time' p8 F1131687450.1338899 sS'allow_trackbacks' p9 S'NO' p10 sS'last_modified' p11 F1131687450.194092 s.leonardo-0.7b1/data/lfs/blog.ldv/0000755000076500007650000000000010344513411017732 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/blog.ldv/__content__.wiki040000644000076500007650000000002210242537427023235 0ustar jtauberjtauber00000000000000This is your blog!leonardo-0.7b1/data/lfs/blog.ldv/property_pickle0000644000076500007650000000015010340021773023065 0ustar jtauberjtauber00000000000000(dp1 S'allow_comments' p2 S'NO' p3 sS'page_title' p4 S'Your Blog' p5 sS'allow_trackbacks' p6 S'NO' p7 s.leonardo-0.7b1/data/lfs/css.ldv/0000755000076500007650000000000010344513411017577 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/css.ldv/__content__.css0000644000076500007650000000646210224230044022562 0ustar jtauberjtauber00000000000000body { color : #333; background-color : #FFF; font-family : verdana, arial, helvetica, sans-serif; font-size : 0.8em; margin : 0px; } div#top { margin : 0px; padding-top : 5px; padding-bottom : 5px; background-color : #454; color : #996; } div#top h1 { font : 2.56em "trebuchet ms", verdana, arial, helvetica, sans-serif; padding-left : 15px; margin-top : 8px; margin-bottom : 8px; text-shadow: 3px 3px 3px #000; } div#top h2 { font : 1.6em "trebuchet ms", verdana, arial, helvetica, sans-serif; padding-left : 20px; margin-bottom : 8px; margin-top : 0px; font-style : italic; text-shadow: 3px 3px 3px #000; } div#main { margin-left : 22%; margin-right : 10%; margin-top : 0px; padding-top : 20px; padding-left : 10px; padding-bottom : 20px; } div#main h2 { margin-top : 0px; margin-bottom : 5px; padding : 0px; font : 1.92em "trebuchet ms", verdana, arial, helvetica, sans-serif; } div#main h3 { margin-top : 0px; margin-bottom : 5px; padding : 0px; font : 1.5em arial, helvetica, sans-serif; } div#main h4 { margin-top : 20px; margin-bottom : 0px; padding : 0px; border-bottom : 1px solid #444; font : bold 1.2em arial, helvetica, sans-serif; } div#main p { margin-top : 0px; margin-bottom : 10px; padding : 0px; } div#menu { float : left; width : 20%; margin-top : 0px; margin-right : 0px; padding-left : 15px; padding-top : 20px; padding-right : 10px; padding-bottom : 20px; border-right : 1px solid #ddd; } div#menu h3 { margin-bottom : 3px; padding-top : 0px; margin-top : 5px; border-bottom : 1px solid #ccc; font : bold 1em verdana, arial, helvetica, sans-serif; } div#menu p { margin-top : 0px; margin-bottom : 0px; font : 0.9em "trebuchet ms", verdana, arial, helvetica, sans-serif; line-height : 1.1em; } a:link, a:visited, a:active { color : #996; text-decoration : none; background-color : transparent; } a:hover { text-decoration : underline; background-color : transparent; } a.external { font-style : italic; } img { border : 0px; } div#bottom { clear : left; font-size : 0.8em; border-top : 1px solid #DDD; margin-top : 0px; padding-bottom : 5px; text-align : right; padding-right : 15px; color : #AAA; } .debug { font-size : 0.8em; color : #AAA; } tt { font-size : 1.21em; } p.blog_date { font-size : 0.8em; color : #666; } div.comment, div.trackback { border-top: 1px solid #999; margin: 10px 5px 5px 5px; padding: 3px; } p.comment_citation, p.trackback_citation { font-size : 0.8em; font-style: italic; } .footnotes { font-size : smaller; border-top : 1px solid #DDD; padding-top : 10px; } leonardo-0.7b1/data/lfs/draft/0000755000076500007650000000000010344513410017322 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/draft/my_first_blog_entry.ldv/0000755000076500007650000000000010344513410024166 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/draft/my_first_blog_entry.ldv/__content__.wiki040000644000076500007650000000004710242537427027501 0ustar jtauberjtauber00000000000000This is a draft of my first blog entry.leonardo-0.7b1/data/lfs/draft/my_first_blog_entry.ldv/property_pickle0000644000076500007650000000016210340021773027325 0ustar jtauberjtauber00000000000000(dp1 S'allow_comments' p2 S'NO' p3 sS'page_title' p4 S'My First Blog Entry' p5 sS'allow_trackbacks' p6 S'NO' p7 s.leonardo-0.7b1/data/lfs/property_pickle0000644000076500007650000000301510343077171021367 0ustar jtauberjtauber00000000000000(dp1 S'allow_comments' p2 (dp3 S'NO' p4 (lp5 S'wiki_formatting_guide' p6 aS'foo' p7 aS'blog' p8 aS'wiki_formatting_guide_2' p9 aS'__home__' p10 aS'draft/my_first_blog_entry' p11 aS'blog/2005/11/11/my_first_blog_entry' p12 aS'blog/2004/12/16/my_first_blog_entry' p13 assS'page_title' p14 (dp15 S'Welcome to Leonardo!!' p16 (lp17 S'__home__' p18 asS'My First Blog Entry' p19 (lp20 S'draft/my_first_blog_entry' p21 aS'blog/2005/11/11/my_first_blog_entry' p22 aS'blog/2004/12/16/my_first_blog_entry' p23 asS'Wiki Formatting Guide (wikiBNL)' p24 (lp25 S'wiki_formatting_guide_2' p26 asS'Your Blog' p27 (lp28 S'blog' p29 asS'Wiki Formatting Guide (wiki04)' p30 (lp31 S'wiki_formatting_guide' p32 asS'foo' p33 (lp34 S'foo' p35 assS'author' p36 (dp37 S'jtauber@jtauber.com' p38 (lp39 S'wiki_formatting_guide' p40 aS'__home__' p41 aS'blog/2005/11/11/my_first_blog_entry' p42 asS'asdf' p43 (lp44 S'foo' p45 assS'creation_time' p46 (dp47 F1131687450.1338899 (lp48 S'blog/2005/11/11/my_first_blog_entry' p49 asF1133271475.7060931 (lp50 S'foo' p51 assS'allow_trackbacks' p52 (dp53 S'NO' p54 (lp55 S'wiki_formatting_guide' p56 aS'foo' p57 aS'blog' p58 aS'wiki_formatting_guide_2' p59 aS'__home__' p60 aS'draft/my_first_blog_entry' p61 aS'blog/2005/11/11/my_first_blog_entry' p62 aS'blog/2004/12/16/my_first_blog_entry' p63 assS'last_modified' p64 (dp65 F1131687841.146055 (lp66 S'wiki_formatting_guide' p67 asF1131687450.194092 (lp68 S'blog/2005/11/11/my_first_blog_entry' p69 asF1133271475.755336 (lp70 S'foo' p71 asF1132470581.8110039 (lp72 S'__home__' p73 ass.leonardo-0.7b1/data/lfs/static/0000755000076500007650000000000010344513411017512 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/static/sample.ldv/0000755000076500007650000000000010344513411021557 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/static/sample.ldv/__content__.txt0000644000076500007650000000006010212476201024561 0ustar jtauberjtauber00000000000000This is an example of a statically served file. leonardo-0.7b1/data/lfs/wiki_formatting_guide.ldv/0000755000076500007650000000000010344513411023361 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/wiki_formatting_guide.ldv/__content__.wiki040000644000076500007650000000242410242537427026674 0ustar jtauberjtauber00000000000000===Headings=== ==Second Level Heading== ===Third Level Heading=== ====Fourth Level Heading==== ===Lists=== asterix at the start of a line give you * this ===Blocks=== : a colon and a space at the start of a line for block quotes Four spaces at start of line for pre Empty lines mark paragraph boundaries ===Character Formatting=== Surround with asterixes for *blog* and two single quotes for ''italic'' Three dashes --- for emdash. ===Links=== Internal wiki links are surround by square brackets, e.g. [wiki formatting guide]. External links are surrounded by squared brackets and begin with the URI followed by a space followed by a title, e.g. [http://jtauber.com/leonardo Leonardo Home Page]. Internal links that involve / or need to be titled differently are surround by [: and ] with the path followed by a space followed by the title to use, e.g. [:blog/2004 the 2004 blog]. Books can be linked via ISBN to amazon using [amazon:]. ===Insertions=== Images can be inserted using [image: and ] surrounding the image path followed by a space followed by the alt text. Other wiki pages can be inserted by using prefixing the wiki link with insert: as demonstrated below: [insert:blog/2004/12/16/my_first_blog_entry]leonardo-0.7b1/data/lfs/wiki_formatting_guide.ldv/property_pickle0000644000076500007650000000031610340021773026520 0ustar jtauberjtauber00000000000000(dp1 S'allow_comments' p2 S'NO' p3 sS'last_modified' p4 F1131687841.146055 sS'page_title' p5 S'Wiki Formatting Guide (wiki04)' p6 sS'allow_trackbacks' p7 S'NO' p8 sS'author' p9 S'jtauber@jtauber.com' p10 s.leonardo-0.7b1/data/lfs/wiki_formatting_guide_2.ldv/0000755000076500007650000000000010344513410023601 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/wiki_formatting_guide_2.ldv/__content__.wikiBNL0000644000076500007650000000755110242537427027313 0ustar jtauberjtauber00000000000000This is a guide to Bryan Lawrence's modified wiki format designated "wikiBNL". Syntax supported includes: * Links (local, and external, labelled or otherwise, including images) * Lists (obviously), including bulleted and enumerated (to four deep) * Tables * Highlighting, Italics, Math mode (simple and latex if available) * Code Higlighting (where source-highlight is also available) * Preformatted Text and Blockquoting * Footnotes are supported too [fn: All you need do is wrap the footnote text in between square brackets with fn following the first one, e.g.\[fn: text\]]. Note that both \[ and \* can be escaped by using a \ before the \[ or \*. (Sometimes you'll also need to escape the return ],e.g. in a footnote), but generally not). Paragraphs are delineated by a blank line. === Links === * Simple local links are done with \[WikiFormat] to give you [WikiFormat]. * Local links can be renamed using \[:WikiFormat local wiki instructions] to give [:WikiFormat local wiki instructions]. * Remote Links are simply \[http:blah] or \[http:blah with local label]. * Images are included by using [image: linktoimage] === Bold and Italic Text === You can easily produce *bold* and ''italic'' text using \*bold\* and a pair of single quotes either side of the required text, e.g. [pre You can easily produce *bold* and ''italic'' pre] === Preformatted Text and Blockquotes === For preformatted text, simply surround your material with [pre and pre] each on their own lines, without following spaces befor the carriage returns, to get [pre exactly \[&>><<<999666444111///,,,***'''%%%###  M.@tRNS -8@HOU[`fkotx|„ˆŒ“—𡤧ª­°³¶¹»¾ÁÃÆÉËÎÐÓÕØÚÜßáãåèêìîðóõ÷ùûýÿ³ÏDûbKGDˆHtIMEÕ-¯†:4tEXtSoftwaredvi2bitmap 0.13b2¹mÁ-tEXtCommentConverted from DVI file tmpzJfMiu.dviáúÊDtEXtCommentSee http://www.astro.gla.ac.uk/users/norman/star/dvi2bitmap/Ž(°¤IDATX…½W‹vª:D PZ´ž >ÐÚb/Zm¥Z¤ ÿÿWw’ðˆˆ¶¬{z÷Z’L&ÉdfOPàä¸j„¬ÜøæÍ¯²LÄßÇ[pSÒ ûrâåMÁ)¼´õmn+†ØºZÑš.–«°€lðk‘µø{°r ¥V è ¦¦Çg'0€òVþv?ƒ§êYŒÎÌ_ÆÃÅÞO|fQµ[(ô€~ÿ¼„²®(vmY‚ =Ê‚eYpƒÆ¹?=êb3>Sí‘Dàïà\óöäâ6˜S8ø èg$mQ*A¥€÷ ‹ÊŽèÚ†ïÐù™|sg¥_b =Š~ëǰ«¡£‘ªè ?ÙƒKÚKraHZK”²¥^"¡O…á‰ü£$†RÏ’&qpJ7ᆊUSÖ°à®ñt@·¿O]ŸI³ò³ØY!¾ô&Ử¢¿ã °×,X7¥®â<‰%ûx3,z™ùb¾øû:Ö˜3WÎÙÈ qmÓëY þry~Š£qÝØñ±Ã2‘}0ór/Êù]S ª:2©@_]]¯eÏL…`tCã õÔÚL¦BýU“àèƒV+Ê’Ôü´Î¼–PN"æhAËò‘œÂÂU8Á! ¡¢Õ’ºvBŸ §\#70gÚÕyÅ “·9lÃ5~Z lÉÏþЇÉì©Iò@E¶4U°0JWGj™¥é~Z ie‘ZA¬ ço]¿¡SÜq0«bˆ|rùFH¾4ú±¥0$Gùô‹GO›÷–²†ffÍŽjÔ¥_/âç—ói`â µÃÇ”ß"¶š÷ª[‰yrϸNy™7¢ƒk&ÔJùgé¶Ëœeƒ›aÖggFݼ¾Â}©ž_u×9‡år‰žúRcÄ·èm6ùByw­Þ¶uIüoÀ˜›Ç SP³4 *#?-©•SjƒÞª‰uÔbd>™l†ÝÅlOéû0¹-®CÈ5Ð(-b!%zòɃ-t1r1žë¤·ÿ°#‘NÇŠ˜ƒÿ3ab±Æo¢¼-×ÚQ½¶êdÕõ:߇ b9åÿ ä¦k«@™4 ¢ %TYwédQÌúÊ2ïªú¶.c¸®l¦óà•iŸ±^Ñ;JέÜ<'¯GÈ ïÀ¾>3ˆDUÚùþ:_©²'¿jTX• OáOqN‚æh7Aߦ®Òôr¤7ÈáNCÚáË üÁd¼ãG«ÇíKÊÛ‰Ü]\‹ôÊOD¹Çó—º2+ Õùb·ôi˜Š;²“™»Ç¯t­Ly:jž(ªÂhÍ~,M+Að§á¦UÅÊ!f¡‡¦vízøÓ|0/4•Qâ“ßç#Ü:§[l;âÐ Ô¼%F•ã±vÛ‘Ã]¿-t e·|ã«ÿ¿Š‘ÄÙg?"ëg5_ãhdã!™›x2‹çõ~*~›y•Ÿ†„™”‘Ó’ŒÀîa3”jùgaé—SƒÃ£Ë}ã–8~+›ö=P„ôúÒÃ¾Õ /.¦ñ|üÎÅ¢!³|àf6$›»_™¢_É9îýßð/lêa¯7nûIEND®B`‚leonardo-0.7b1/data/lfs/wiki_formatting_guide_2.ldv/__embedded__2/0000755000076500007650000000000010344513410026210 5ustar jtauberjtauber00000000000000leonardo-0.7b1/data/lfs/wiki_formatting_guide_2.ldv/__embedded__2/__content__.png0000644000076500007650000000255410224230044031166 0ustar jtauberjtauber00000000000000‰PNG  IHDR¥ü©¬ÀPLTEÿÿÿßßßÒÒÒÇÇÇ¿¿¿···°°°ªªª¤¤¤ŸŸŸ™™™”””‹‹‹‡‡‡ƒƒƒ~~~{{{wwwsssooolllhhheeebbb^^^[[[XXXUUURRROOOLLLIIIFFFDDDAAA>>><<<999666444111///,,,***'''%%%###  M.@tRNS -8@HOU[`fkotx|„ˆŒ“—𡤧ª­°³¶¹»¾ÁÃÆÉËÎÐÓÕØÚÜßáãåèêìîðóõ÷ùûýÿ³ÏDûbKGDˆHtIMEÕ7ƒÆFŽtEXtSoftwaredvi2bitmap 0.13b2¹mÁ-tEXtCommentConverted from DVI file tmpKrkoSk.dviJ4¯JDtEXtCommentSee http://www.astro.gla.ac.uk/users/norman/star/dvi2bitmap/Ž(°LIDATH‰¥U‹rª0]@,Œ¢­Š^«â£jt´(ÁJÿÿ¯înBhT¸uæî8„ÝÍÙ³Še&«oØ‚I‹A‰jÉy ð`Ã+€“®²wXhòš½u˜€k¹xð¤ÒŒûèe»×X{°þªc¡êiÅH[E÷NȲ!ì¥èa¤vf§Ñ1"¥ŠÚ¤åR2ó…Ï÷9wÀŸ“G[øt2Ÿ‹B&JHÝ^o{EZq©u í;·YHi•BšÏø<Ô1kTpq¬Dø`_e2ð½!ÞZÈ9^·"¾m ùkc¹Ï¶¾¤Ïe%÷‰©«G3˜&úÄä6µ†ã*>wð¡ø4šð|û½Ôä¢Ë«à)¬†ká¥ýÇü‰ËÑ’Jÿ3+ èpwÂû@2ýú;mÛTœ,ºìZŽd˜íR^µ¾) žÈ¥ƒÅ`ëMê O«‰-Cù¡Úµ\ôƒN=ÒÀ¢*€Jo.š¸'âcÁH(°¼7Èåì*nÆ48^5½©`¼dq3ŠÏà_¾xïæ1£iF²þ‘¡Ë/åŒ7VŽ2¦Ôào ŒJÄ™Àe¥ ta ÊTp©oÌ^Υťí[@¤uÇ#A ¶tj*—~EåRœÛŒÈR.0ãï¿)s)µ¹ÈéV2=ŠÄþ4Ë?jëýì17ƒlòv‚Ó“ùi?~ÿlçÿ®ÔŠŠæv.Õô± k9å ̸•õymxÓK 1ù'•Ia¬-Þ£óy2—‡A^ï‹ãñRΦ½¹œÅ¬ÛòX(]ñ°ÚL‰Ýܤû÷vŽªþQ‰œ÷é&‡Þò½«0t/IX½¼í[±Æ·ª²:D›Ó _WSÄ»ÂÀÈEƒõn×iµ¤ò×cd0àƒÈøþéëV³ûu}÷0ìfH5†Þ• #'ÜÝ¡rW¢ Copyright (C) This program 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 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. leonardo-0.7b1/lib/0000755000076500007650000000000010344513377015307 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/0000755000076500007650000000000010344513407017104 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/__init__.py0000644000076500007650000000000210343045633021205 0ustar jtauberjtauber00000000000000# leonardo-0.7b1/lib/leonardo/auth.py0000755000076500007650000001224710343017712020425 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ This module deals with session and user management. """ import os import os.path # session status codes OK = "ok" WRONG_IP = "different ip address" UNKNOWN_SID = "unknown sid" NO_SID = "no sid in cookie" NO_COOKIE = "no cookie" class SessionManager: def __init__(self, request, data_dir): self.request = request self.data_dir = data_dir self.session_db = None def check_session(self): """Returns the current session status as triple: code, user_id, session_id""" if self.request.cookie: import Cookie ip_address = self.request.remote_address cookie = Cookie.SimpleCookie(self.request.cookie) if "session_id" in cookie: sid = cookie["session_id"].value if self.has_session(sid): session_uid, session_ip_address = self.get_session(sid) if ip_address == session_ip_address: return OK, session_uid, sid else: # spoof return WRONG_IP, None, sid else: # unknown session ID return UNKNOWN_SID, None, sid else: # no session ID in cookie return NO_SID, None, None else: # no cookie return NO_COOKIE, None, None def get_sessions(self): """Get the session database, loading from disk if necessary""" if not self.session_db: import anydbm self.session_db = anydbm.open(self.data_dir + "sessions_db", "c") return self.session_db def create_session(self, uid, ip_address): """Create a new session for the given user at the given IP address""" sid = generate_id(uid, ip_address) self.get_sessions()[sid] = " ".join((uid, ip_address)) if hasattr(self.get_sessions(), "sync"): self.get_sessions().sync() return sid def get_session(self, sid): """Get the session info for the given session ID. Returned as pair: user_id, ip_address""" return self.get_sessions()[sid].split(" ") def has_session(self, sid): """Does the given session ID exist?""" return self.get_sessions().has_key(sid) def delete_session(self, sid): """Delete the session with the given ID. Has effect of logging user out""" del self.get_sessions()[sid] if hasattr(self.get_sessions(), "sync"): self.get_sessions().sync() def generate_id(uid, ip_address): """Generate a new session ID""" import md5, time, base64 m = md5.new() m.update('this is just a fun little sentence to get things started') m.update(str(time.time())) m.update(uid) m.update(ip_address) return base64.encodestring(m.digest())[:-3].replace("/", "$") class UserManager: def __init__(self, request, data_dir): self.request = request self.users = {} self.data_dir = data_dir def get_users(self): """Get the user database, loading from disk if necessary""" users_filename = os.path.join(self.data_dir, "users") if not self.users and os.access(users_filename, os.R_OK): for line in file(users_filename): user_id, name, password = line.strip().split(":") self.users[user_id] = (user_id, name, password) return self.users def known_user(self, user_id): """Is the given user known?""" return self.get_users().has_key(user_id) def get_password(self, user_id): """Get the (hashed) password for given user""" return self.get_userinfo(user_id)[2] def get_name(self, user_id): """Get the name of the user with given id""" if self.known_user(user_id): return self.get_userinfo(user_id)[1] else: return None def get_userinfo(self, user_id): """Get info for the given user. Returned as triple: user_id, name, (hashed) password""" return self.get_users()[user_id] def correct_password(self, user_id, passwd): """Is the given password correct for the given user?""" import sha return (self.known_user(user_id) and self.get_password(user_id) == sha.sha(passwd).hexdigest()) leonardo-0.7b1/lib/leonardo/config.py0000644000076500007650000000271610343023112020715 0ustar jtauberjtauber00000000000000 import ConfigParser import os import os.path class Configurator: def __init__(self, main_file): self.config = ConfigParser.ConfigParser() self.main_file = main_file def add(self, package_name): package = __import__(package_name) components = package_name.split('.') for comp in components[1:]: package = getattr(package, comp) package_paths = package.__path__ module_name = package_name.split('.')[-1] for package_path in package_paths: config_filename = os.path.join(package_path, module_name + ".config") self.config.read(config_filename) def read_main(self): self.config.read(self.main_file) # default data_dir for quick start if not self.config.has_option("DEFAULT", "data_dir"): this_directory = os.path.dirname(__file__) abs_path = os.path.abspath(os.path.join(this_directory, "../../data")) self.config.set("DEFAULT", "data_dir", abs_path + os.sep) # default lfs_root if not self.config.has_option("DEFAULT", "lfs_root"): data_dir = self.config.get("DEFAULT", "data_dir") self.config.set("DEFAULT", "lfs_root", os.path.join(data_dir, "lfs") + os.sep) def get(self, section, key): return self.config.get(section, key) def getint(self, section, key): return self.config.getint(section, key) leonardo-0.7b1/lib/leonardo/core.py0000755000076500007650000000371210343017712020411 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import sys import request_response import filesystem from providers.manager import ProviderManager class Leonardo: def __init__(self, configurator): self.configurator = configurator self.resource_manager = ProviderManager(self.configurator) def dispatch(self): request = request_response.Request(self.configurator) resource_id = request.relative_path head_only = False if request.method == "GET": resource = self.resource_manager.get(resource_id, request, None) elif request.method == "POST": resource = self.resource_manager.post(resource_id, request, None) elif request.method == "HEAD": resource = self.resource_manager.get(resource_id, request, None) head_only = True else: # @@@ need better error handling resource = None if request.not_modified(resource): response = request_response.Response(status="304 Not Modified") else: response = request_response.Response(resource, header_only=head_only) response.send(sys.stdout) leonardo-0.7b1/lib/leonardo/filesystem.py0000644000076500007650000003417710343017712021653 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ This module abstracts the access to file contents and file attributes. Alternative backend data stores such as databases or version control systems could be implemented by implementing an identical interface. This implementation works by storing each "file" as a directory with the extension ".ldv". The directory may then contain not only the content (potentially in multiple forms) but additional metadata, annotations, etc. @@@ document enclosures @@@ document property indicies """ import os import re import time import cPickle as pickle class LeonardoFileSystem: """ The entry point to the file system. From here one can request file objects and their children as well as look up the property index. """ def __init__(self, root): """ Initialise the file system physically located at the given path. """ self.root = root # check access to filesystem root if not os.access(self.root, os.R_OK): # @@@ improve response to user raise "can't access filesystem root" self.property_index_db = None def get(self, key): """ Retrieve the file object of the given key. Raises a ValueError if the key is invalid. """ if self.valid(key): return LeonardoFile(self, key) else: raise ValueError def valid(self, key): """ Check whether the key is valid. Note, this doesn't mean the file exists, merely that the key is well-formed. """ root = os.path.abspath(self.root) path = os.path.abspath(os.path.normpath(self.root + "/" + key)) return path.startswith(root) def get_children(self, key): """ Returns a tuple of: - list of directories that are children of the key - list of leonardo pages that are children of the key A ValueError is raised if the key is invalid. """ if not self.valid(key): raise ValueError directories = [] files = [] # @@@ factor out commonalities with LeonardoFile d = self.root + key.strip("/") if os.path.isdir(d) and not d.endswith(".ldv"): for f in os.listdir(d): # @@@ factor out ignore list if os.path.isdir(d + "/" + f) and f not in [".svn"]: if f.endswith(".ldv"): # @@@ change to creation time sort_key = os.path.getmtime(os.path.join(d, f)) files.append((sort_key, f[:-4])) else: directories.append(f) directories.sort() directories.reverse() files.sort() files.reverse() files = [c[1] for c in files] return directories, files def find_property(self, property_name): """ Find a property in the index. Returns a dictionary mapping values for that property to a list of keys to file objects with that value for the property. """ property_pickle_filename = os.path.join(self.root, "property_pickle") if os.access(property_pickle_filename, os.F_OK): f = file(property_pickle_filename) property_index = pickle.load(f) f.close() else: property_index = {} return property_index.get(property_name, {}) def list_properties(self): """ List the properties in the index. """ property_pickle_filename = os.path.join(self.root, "property_pickle") if os.access(property_pickle_filename, os.F_OK): f = file(property_pickle_filename) property_index = pickle.load(f) f.close() else: property_index = {} return property_index.keys() def add_property(self, key, name, value): """ Add a property to the index. """ property_pickle_filename = os.path.join(self.root, "property_pickle") if os.access(property_pickle_filename, os.F_OK): f = file(property_pickle_filename) property_index = pickle.load(f) f.close() else: property_index = {} if type(value) == list: for item in value: property_index.setdefault(name, {}).setdefault(item, []).append(key) else: property_index.setdefault(name, {}).setdefault(value, []).append(key) f = file(property_pickle_filename, "w") pickle.dump(property_index, f) f.close() def remove_property(self, key, name): """remove a property from the index""" property_pickle_filename = os.path.join(self.root, "property_pickle") if os.access(property_pickle_filename, os.F_OK): f = file(property_pickle_filename) property_index = pickle.load(f) f.close() else: property_index = {} x = property_index.get(name, {}) for value in property_index.get(name, {}): y = x[value] if key in y: y.remove(key) property_index[name] = x f = file(property_pickle_filename, "w") pickle.dump(property_index, f) f.close() def add_time_property(self, key, name, value): """ Add a key to the time index. """ index_key = "__INDEX__/%s/%s" % (name, value) if self.valid(index_key): directory = self.root + index_key if not os.access(directory, os.F_OK): os.makedirs(directory) filename = directory + "/KEYS" if os.access(filename, os.F_OK): f = file(filename) keys = [line.strip() for line in f] f.close() else: keys = [] if key not in keys: keys.append(key) f = file(filename, "w") for key in keys: f.write("%s\n" % key) f.close() else: raise ValueError def remove_time_property(self, key, name, value): """ Remove a key from the time index. """ index_key = "__INDEX__/%s/%s" % (name, value) if self.valid(index_key): directory = self.root + index_key if not os.access(directory, os.F_OK): os.makedirs(directory) filename = directory + "/KEYS" if os.access(filename, os.F_OK): f = file(filename) keys = [line.strip() for line in f] f.close() else: return if keys.count(key) > 0: keys.remove(key) f = file(filename, "w") for key in keys: f.write("%s\n" % key) f.close() else: raise ValueError class Bundle: """ A bundle is the parent class of file objects and enclosure objects. """ def __init__(self): self.property_db = None def get_content_filename_(self, content_type): return self.get_directory_() + "__content__." + content_type def get_content_filename_glob_(self): return self.get_content_filename_("*") def get_content_files_(self): import glob return glob.glob(self.get_content_filename_glob_()) def exists(self): files = self.get_content_files_() if files: return True else: return False def load_content_(self): files = self.get_content_files_() if files: f = file(files[0]) s = f.read() f.close() self.content = s def load_content_type_(self): files = self.get_content_files_() if files: self.content_type = files[0].split(".")[-1] else: self.content_type = None def get_content(self): if not self.content: self.load_content_() return self.content def get_content_type(self): if not self.content_type: self.load_content_type_() return self.content_type def get_lastmod(self): lastmod = self.get_property("last_modified", -1) if lastmod == -1: files = self.get_content_files_() ST_MTIME = 8 if files: lastmod = os.stat(files[0])[ST_MTIME] else: lastmod = -1 return lastmod def get_creation_time(self): t = self.get_property("creation_time", "-1") return int(t) def set_content(self, content, content_type, update_lastmod=True): self.make_bundle_() self.content = content self.content_type = content_type path = self.get_content_filename_(content_type) f = file(path, "w") f.write(content) f.close() if update_lastmod: self.set_property("last_modified", time.time()) self.set_time_property("last_modified", time.time()) def delete(self): for root, dirs, files in os.walk(self.get_directory_(), topdown=False): for name in files: os.remove(os.path.join(root, name)) for name in dirs: os.rmdir(os.path.join(root, name)) os.rmdir(self.get_directory_()) def make_bundle_(self): directory = self.get_directory_() if not os.access(directory, os.F_OK): os.makedirs(directory) self.set_property("creation_time", time.time()) self.set_time_property("creation_time", time.time()) def get_property(self, name, default=None): property_pickle_filename = self.get_directory_() + "property_pickle" if self.exists() and os.access(property_pickle_filename, os.R_OK): f = file(property_pickle_filename) property_pickle = pickle.load(f) f.close() return property_pickle.get(name, default) else: return default def get_properties(self): """Get a list of the properties (i.e. property names) on this file""" property_pickle_filename = self.get_directory_() + "property_pickle" if self.exists() and os.access(property_pickle_filename, os.R_OK): f = file(property_pickle_filename) property_pickle = pickle.load(f) f.close() return property_pickle.keys() else: return [] def set_property(self, name, value): self.make_bundle_() property_pickle_filename = self.get_directory_() + "property_pickle" if os.access(property_pickle_filename, os.F_OK): f = file(property_pickle_filename) property_pickle = pickle.load(f) f.close() else: property_pickle = {} property_pickle[name] = value f = file(property_pickle_filename, "w") pickle.dump(property_pickle, f) f.close() def set_time_property(self, name, value): pass def convert_time(t): return "%04d/%02d/%02d" % time.localtime(t)[:3] class LeonardoFile(Bundle): def __init__(self, filesystem, key): Bundle.__init__(self) self.filesystem = filesystem self.key = key self.content = None self.content_type = None def get_directory_(self): return os.path.join(self.filesystem.root, self.key.strip("/") + ".ldv/") def set_property(self, name, value): Bundle.set_property(self, name, value) self.filesystem.remove_property(self.key, name) self.filesystem.add_property(self.key, name, value) def set_time_property(self, name, value): old_value = self.get_property(name) if old_value is not None: self.filesystem.remove_time_property(self.key, name, convert_time(old_value)) Bundle.set_time_property(self, name, value) self.filesystem.add_time_property(self.key, name, convert_time(value)) def delete(self): for name in self.get_properties(): self.filesystem.remove_property(self.key, name) Bundle.delete(self) def enclosure(self, enctype, index): return Enclosure(self, enctype, index) def enclosures(self, enctype): enc_list = [] for d in os.listdir(self.get_directory_()): match = re.match("__(\w+)__(\d+)", d) if match and enctype == match.group(1): index = match.group(2) enc_list.append(self.enclosure(enctype, index)) return enc_list class Enclosure(Bundle): def __init__(self, parent, enctype, index): Bundle.__init__(self) self.parent = parent self.enctype = enctype self.index = index self.content = None self.content_type = None self.key = self.parent.key.strip("/") + "/__" + self.enctype + "__" + str(self.index) def get_directory_(self): return self.parent.filesystem.root + \ self.parent.key.strip("/") + ".ldv/" + \ "__" + self.enctype + "__" + str(self.index) + "/" leonardo-0.7b1/lib/leonardo/formatters/0000755000076500007650000000000010344513401021264 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/formatters/__init__.py0000644000076500007650000000147410207002212023372 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Package for Leonardo wiki formatters. """leonardo-0.7b1/lib/leonardo/formatters/manager.py0000755000076500007650000000353510244201167023263 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import re import wiki04.formatter as wiki04 import wikiBNL.formatter as wikiBNL import xhtml.formatter as xhtml class FormatterManager: def __init__(self, config): home_href = config.get("DEFAULT", "home_href") self.formatters = { "wiki04": wiki04.WikiFormatter(home_href), "wikiBNL": wikiBNL.WikiFormatter(home_href), "xhtml": xhtml.XHTMLFormatter(home_href), } self.default_formatter = DefaultFormatter() def known(self, content_type): return content_type in self.formatters def format(self, content, content_type, leofile, resource_getter): formatter = self.formatters.get(content_type, self.default_formatter) return formatter.format(content, leofile, resource_getter) class DefaultFormatter: def format(self, content, leofile, resource_getter): if content: content = re.sub("&", "&", content) content = re.sub("<", "<", content) else: content = "" return content leonardo-0.7b1/lib/leonardo/formatters/wiki04/0000755000076500007650000000000010344513401022373 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/formatters/wiki04/__init__.py0000644000076500007650000000145710235321046024514 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ wiki04 formatter plugin. """leonardo-0.7b1/lib/leonardo/formatters/wiki04/formatter.py0000755000076500007650000001234410250652066024766 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import re class WikiFormatter: def __init__(self, link_prefix): self.link_prefix = link_prefix def __sub_link(self, matchobj): wiki_word = matchobj.group(1) wiki_word = re.sub(r'\\(.)', '\\1', wiki_word) link = self.link_prefix + re.sub(' ', '_', wiki_word).lower() return """%s""" % (link, wiki_word) def extract_title(self, page_content): m = re.match(r'(?m)^==([^=]+)==', page_content) if m: return m.group(1) else: return None def __insert(self, resource_id, resource_getter): resource = resource_getter(resource_id) if resource.exists(): if hasattr(resource, "get_html"): return resource.get_html() else: return """

invalid insert : %s

""" % resource_id else: return """

unknown insert : %s

""" % resource_id # @@@ templatize def format(self, page_content, leo_file=None, resource_getter=None): s = page_content s = re.sub(r'&', '&', s) s = re.sub(r'<', '<', s) s = re.sub(r'>', '>', s) s = re.sub(r'"', '"', s) # ==...== to h2 s = re.sub(r'(?m)^==([^=]+)==', '

\\1

', s) # ===...=== to h3 s = re.sub(r'(?m)^===([^=]+)===', '

\\1

', s) # ====...==== to h4 s = re.sub(r'(?m)^====([^=]+)====', '

\\1

', s) # * ... to list s = re.sub(r'(?m)^\* (.+)$', '
  • \\1
  • ', s) # ": " at start of line to blockquote s = re.sub(r'(?m)^: (.+)$', '
    \\1
    ', s) s = re.sub(r'(\s*)
    ', '\\1', s) # four spaces at start of line to pre s = re.sub(r'(?m)^ (.+)$', '
    \\1
    ', s) s = re.sub(r'(\s*)
    ', '\\1', s)
        
            # *...* bold
            s = re.sub(r'\*([^\*]+)\*', '\\1', s)
        
            # ''...'' italic
            s = re.sub(r"''([^']+)''", '\\1', s)
        
            # --- to emdash
            s = re.sub(r'---', '—', s)
        
            # empty lines -> paragraph boundaries
            s = re.sub(r'(?m)^\s*$', '

    \n

    ', s) s = "

    " + s + "

    " s = re.sub(r'

    \s*

  • ', '
    • ', s) s = re.sub(r'
    • \s*

      ', '
    ', s) s = re.sub(r'

    \s*

    ', '

    ', s) s = re.sub(r'

    \s*

    ', '', s) s = re.sub(r'

    \s*

    ', '

    ', s) s = re.sub(r'

    \s*

    ', '', s) s = re.sub(r'

    \s*

    ', '

    ', s) s = re.sub(r'

    \s*

    ', '', s) s = re.sub(r'

    ', '', s) URL = r'((?:ftp|http|https):[^ \]]+)' # [http:..] to external link s = re.sub(r'\['+URL+r'\]', '\\1', s) # [http:... ...] to titled and entitled external link s = re.sub(r'\['+URL+r' "([^"]+)" ([^\]]+)\]', '\\3', s) # [http:... ...] to titled external link s = re.sub(r'\['+URL+r' ([^\]]+)\]', '\\2', s) # [:...] to local link s = re.sub(r'\[:/([^ \]]+)\]', '%s\\1' % (self.link_prefix, self.link_prefix), s) s = re.sub(r'\[:([^ \]]+)\]', '\\1', s) # [:... ...] to titled local link s = re.sub(r'\[:/([^ \]]+) ([^\]]+)\]', '\\2' % self.link_prefix, s) s = re.sub(r'\[:([^ \]]+) ([^\]]+)\]', '\\2', s) # [image:... ...] to local image s = re.sub('\[image:/([^ \]]+) ([^\]]+)\]', '\\2' % self.link_prefix, s) s = re.sub('\[image:([^ \]]+) ([^\]]+)\]', '\\2', s) # [insert:...] to insert a resource s = re.sub(r'\[insert:([^\]]+)\]', lambda x: self.__insert(x.group(1), resource_getter), s) # [amazon:... ...] to link to Amazon using ISBN and link title s = re.sub(r'\[amazon:([0-9X]+) ([^\]]+)\]', '\\2', s) # [..] to wiki link s = re.sub(r'(? for xhtml1.1 # V0.44 March 25, 2005: Add support for access to source-highlight command line # Changes default to not support numbering ... # import os, tempfile, popen2, glob from xml.dom import minidom class embedhandler: def __init__(self, link_prefix, embed_prefix='/__embedded__', workingpath='/tmp/leoscratch',binarypath='/usr/local/bin'): ''' Input arguments include link_prefix necessary to find the location of the html file and any embedded inclusions embed_prefix used to indicate any further additions to the URI for the path to the inclusions workingpath is a directory location for temporary files binarypath is an additional directory for embeddding binaries like source-highlight and/or dvi2bitmap ''' self.link_prefix = link_prefix self.embed_prefix = embed_prefix self.item=0 # # check and see if binarypath is in path, if not add it # path=os.environ['PATH'] pathdirs=path.split(':') if binarypath not in pathdirs: os.environ['PATH']+=':'+binarypath # self.workingpath=workingpath if not os.path.exists(workingpath): os.mkdir(workingpath) # # we care about the current directory for use when standalone ... self.currentdir=os.getcwd() def execmd(self,command): ''' wraps up subprocess error handling''' # has to be linuxacious because process handling is uncool in python #V0.33 and earlier: try: r=popen2.Popen3(command +' >& /dev/null ') res=r.wait() return res except: return 1 def latex(self,payload,tmpfilebase): ''' Given a latex payload, and base path for tmpfiles, produce a png file which is the output of dvi2bitmap and then return the content of that file for inclusion in the "official" output file ''' latex=r'''\documentclass{article} \usepackage{amssymb,amsmath,amscd,concmath} \pagestyle{empty} \begin{document} \begin{equation*}%s\end{equation*} \end{document} ''' % payload tmplatex=tmpfilebase+'.tex' tmpdvi=tmpfilebase+'.dvi' tmppng=tmpfilebase+'.png' t=file(tmplatex,"w") t.write(latex) t.close() if self.execmd('latex --interaction=batchmode '+tmplatex): return self.preformat(payload,'Latex Error ') cmd=''.join(["dvi2bitmap ",tmpfilebase, " --magnification=2 " "--scale=6 --font-mode=nechi --resolution=360 ", "--process=blur,crop,transparent ", "--output=",tmppng]) if self.execmd(cmd): return self.preformat(payload,'dvi2bitmap error ') try: f=open(tmppng,'rb') content=f.read() self.cleanup() return content except: return self.preformat(payload,'Unable to find '+tmppng) def codehighlight(self,payload,language,tmpfilebase): ''' takes a set of code lines, and a language definition, and uses the external source-highlight code operating on temporary files to produce some highlighted code ''' # March 25, we expect the 'language' string to look like either # python # python -n (and any other source-highlight command except # the -f and --src-lang ) # so the default is now no numbering tmpcode=tmpfilebase+'.code' tmpout=tmpcode+'.html' t=file(tmpcode,'w') t.write(payload) t.close() # access source-highlight args from language variable blank=language.find(' ') if blank!=-1: args=language[blank+1:] language=language[0:blank] else: args='' if self.execmd('source-highlight '+args+' -f xhtml --src-lang=' +language+' '+tmpcode+' >& /dev/null'): self.preformat(payload,'highlight error ') try: t=file(tmpout,'r') content=t.read() t.close() self.cleanup() return content except: return self.preformat(payload,'highlight file error') def embed(self,xmlstring,leofile=''): ''' Take an xml document consisting of a script command and a payload to be executed by the script (if necessary) and if necessary, embed the output in leofile. Outside the context of leonardo, leofile is not necessary ''' os.chdir(self.workingpath) doc=minidom.parseString(xmlstring) command=doc.getElementsByTagName('script')[0].firstChild.data payload=doc.getElementsByTagName('payload')[0].firstChild.data # two cases, return html pointing to an image, or return html rimage={'late':1,'code':0}[command[0:4]] firstchild,parent=-1,0 # hack for comparisons if rimage: self.item+=1 if leofile=='': key='' leo=0 else: leo=1 self.leofile=leofile key=self.leofile.key.strip('/') embedded_uri=''.join([self.link_prefix,key, self.embed_prefix,str(self.item)]) if leo: # we can evaluate whether we need to do anything or not parent=self.leofile.get_lastmod() firstchild=self.leofile.enclosure('embedded', self.item).get_lastmod() if firstchild<=parent: tmpfilebase=tempfile.mktemp() tmpfilebase=os.path.basename(tmpfilebase) self.tmpfilebase=tmpfilebase if leofile is None: return self.preformat(payload,' Leofile is None ') if command[0:5]=='latex': content=self.latex(payload,tmpfilebase) if content[0:5]=='
    ': rimage=0
                elif command[0:4]=='code':
            
                    content=self.codehighlight(payload,command[5:].rstrip(),tmpfilebase)
                else:
                    return self.preformat(payload,'Unknown command '+command)
    
                #would prefer not to copy it ... could do directly ... but if the lfs isn't a
                #"real" file system then we probably have to do it this way ... perhaps better
                #for lfs to return a "file-type" object ... but difficult to see how
                #we could use it in external scripts ...
    
                if rimage:
                    if leo:
                        self.leofile.enclosure('embedded',
                                               self.item).set_content(content, 'png')
                    else:
                        os.chdir(self.currentdir)
                        ff=embedded_uri
                        fo=open(ff,'w')
                        fo.write(content)
                        fo.close()
    
            if rimage:
                wrap=('
    ','
    ') html=''.join([wrap[0],'Latex Embedded Image',wrap[1]]) else: html=content os.chdir(self.currentdir) return html def preformat(self,payload,message): self.cleanup() html=''.join(['
    ',payload,'\n***\n',message,'\n***\n','
    ']) return html def cleanup(self): files=glob.glob(self.tmpfilebase+'*') for file in files: os.remove(file) leonardo-0.7b1/lib/leonardo/formatters/wikiBNL/formatter.py0000644000076500007650000005375710310246354025164 0ustar jtauberjtauber00000000000000# Copyright Bryan Lawrence and James Tauber, 2004 # Covered by the GPL since based on GPL code. # # eventually make all this xml compliant ... but not now ... # V0.1 (Dec 17, 2004) - for Leonardo 0.4rc1 (v67) # V0.2 (Jan 06, 2005) - more exact backwards compatiability with JT code # - simple greek inserts # - for Leonardo v111 up # V0.3 (Jan 10, 2005) - inline math mode ... # V0.4 (Jan 11, 2005) - stricter XHTML # V0.41 (Jan 12, 2005) - verbatim/preformat handled using [pre: pre] # V0.42 (Jan 17, 2005) - working source highlighter # V0.50 (Jan 18, 2005) - Modify to James' new insert syntax # V0.51 (Jan 24, 2005) - tiny change to support for _ in URL's .. # V0.52 (Jan 28, 2005) - support doi redirects # V0.53 (Feb 07, 2005) - Minor bug fix for preformat and embed in column 1 # Also sort out break on table close ... # V0.54 (Feb 14, 2005) - Minor bug in [:... ...] syntax # Actually, this was less a bug, more a difference # of opinion about how this should work. An ongoing # issue. Currently this formatter does not support # "local" links. # V0.55 (Feb 19, 2005) - Support rooted links, ie [:/blah xx] (but assume # this is still the same as [:blah xx] for now. # - Fix bug in [asdf] syntax, so that a space cannot # occur within a hyperlink. (Broke some inline maths) # V0.56 (Feb 22, 2005) - Pass embed scratch dir # V0.57 (Feb 25, 2005) - More modifications for strict XHTML1.1 #

    within blockquote, , # (nb can't use this at xhthml1.1):

    ->
    # V0.59 Feb 26, ugly hack for images xhtml ...means that for now, images # can only occur in paragraph mode ... this doesn't seem to matter. # V0.591, Mar 08, support for https links # V0.592, Mar 24, somehow had missed delta!! # V0.593, Mar 25, escaping [ and * # V0.60, Mar 26, Adding support for footnotes # V0.61, Mar 26, Also allow math mode to start with [m: for consistency # V0.611 Apr 10, Reorder image/url to allow import of external images and # add a local/external check. Allow [doi: blah or doi:blah] # V0.62 May 16, Added support for local images linked externally with: # [linkimage: localimage externallink] # V0.63 May 23 Change default embedscratchdir to be more mulitplatform. # V0.64 Jun 01 Bug fix (short para list) for close para error on list # V0.641 Jun 22 (Ensure no print statements) # V0.65 Sep 02 Starting work for insert css modifications import re, tempfile, os.path from MultiSplit import MultiSplit class WikiFormatter: ''' Provides wiki page formatting to produce XHTML output ''' def __init__(self, link_prefix, external_embedhandler=None, embedscratchdir=None): ''' On instantiation provide any external link prefixes, something to get the contents of external page inserts, and an external embedhandler if required (it shouldnt be) ''' self.link_prefix = link_prefix if embedscratchdir is None: embedscratchdir=os.path.join(tempfile.gettempdir(),'leoscratch') if external_embedhandler is None: from embedhandler import embedhandler self.embedhandler=embedhandler(link_prefix, workingpath=embedscratchdir) else: self.embedhandler=external_embedhandler #wrap images up as tables to help the css self.imagewrapper= ['

    ','

    '] def __sub_link(self, matchobj): wiki_word = matchobj.group(1) link = self.link_prefix + re.sub(' ', '_', wiki_word).lower() return """%s""" % (link, wiki_word) def __remlinkimage(self,matchobj): return self.__linkimage(matchobj,0) def __linkimage(self,matchobj,local=1): ''' Returns xhtml for a linked image, locally or externally ''' m1,m2=matchobj.group(1),matchobj.group(2) prefix={0:'',1:self.link_prefix}[local] c={0:'class="external"',1:''}[local] wrapper=[self.imagewrapper[0]+'', ''+self.imagewrapper[1]] s=wrapper[0]+'Image:'+m1+''+wrapper[1] return s def __insert(self, matchobj): ''' need to handle inserting resources via this to ensure that we flag to the formatter that this must be a new piece of block level html and to allow user driven css directives''' # self.resource_getter set in formatter # concept is that user does # [insert:url a b c] # to get url content inserted matchString=matchobj.group(1) words=matchString.split(':') wiki_word = words[0] stuff=self.resource_getter(wiki_word).get_html() if len(words)==2: stuff=''.join(['<',words[1],'>',stuff,'']) elif len(words)==4: stuff=''.join(['<',words[1],' ',words[2],'="',words[3],'">', stuff,'']) return ''+stuff+'' def __embedder(self,matchobj): ''' takes the embedded text and sends off to the embedhandler and replaces with simple html expressions ''' #self.leofile set in formatter s=''.join(['', matchobj.group(2),'']) s=''+self.embedhandler.embed(s,self.leofile)+'' return s def __footnoter(self,matchobj): ''' takes some maaterial and adds it to the footnote list and returns appropriate html for a marker ''' fnumber=len(self.footnotelist)+1 sfnum=str(fnumber) address1='fn'+sfnum address2=address1+'call' html=''.join(['', '',sfnum,'']) text=''.join(['

    ','',sfnum,': ',matchobj.group(1), ' (ret).
    \n']) self.footnotelist.append(text) return html def extract_title(self, page_content): m = re.match(r'(?m)^==([^=]+)==', page_content) if m: return m.group(1) else: return None def escapewikimarkup(self,s): ## bnl this is where I put the escaped wiki markup characters s = re.sub(r'\\\[','',s) s = re.sub(r'\\\]','',s) s = re.sub(r'\\\*','',s) ## and these are undone after formatting is complete return s def escapereturn(self,s): s=re.sub(r'',r'[',s) s=re.sub(r'',r']',s) s=re.sub(r'','*',s) return s def htmlspecial(self,s): ''' replace html special characters ''' # Why don't I do this in stateless format? In case we need these # characters to go into a latex preparser ... s = re.sub(r'&', '&', s) s = re.sub(r'<', '<', s) s = re.sub(r'>', '>', s) return s def htmlgreek(self,matchobj): ''' takes a string and inserts into it based on "latex-like" escaped greek (e.g \alpha) the xhtml code for the required symbol ''' s=matchobj.group(1) # handle super/sub scripts sup,sub=r'\^(\S+)',r'_(\S+)' s=re.sub(sup,'\\1',s) s=re.sub(sub,'\\1',s) # can't imagine doing it more inefficiently than this ... lexicon = ('alpha','beta','gamma','delta','epsilon','zeta', 'eta','theta','iota','kappa','lambda','mu', 'nu','xi','omicron','pi','rho','sigma','tau', 'upsilon','phi','chi','psi','omega', 'Alpha','Beta','Gamma','Delta','Epsilon','Zeta', 'Eta','Theta','Iota','Kappa','Lambda','Mu', 'Nu','Xi','Omicron','Pi','Rho','Sigma','Tau', 'Upsilon','Phi','Chi','Psi','Omega') for item in lexicon: target='\\'+item replacement='&'+item+';' s=s.replace(target,replacement) #should add a regular expression to make normal letters italics return s def statelessformat(self, s): ''' This method simply parses s and does the substitutions that do not have any state carried to subsequent lines ''' # ==...== to h2 s = re.sub(r'(?m)^==([^=]+)==', '

    \\1

    ', s) # ===...=== to h3 s = re.sub(r'(?m)^===([^=]+)===', '

    \\1

    ', s) # ====...==== to h4 s = re.sub(r'(?m)^====([^=]+)====', '

    \\1

    ', s) # --- to emdash s = re.sub(r'---', '—', s) # [..] to wiki link s = re.sub(r'\[([\._A-Za-z0-9]+)\]',self.__sub_link, s) image=r'image: ([^ \]]+)' remoteimage=r'image: http://([^ \]]+)' # [image: filename] insert an image from the static directory # [image: http://blah] insert a remote linked image s=re.sub(r'\['+remoteimage+r'\]',self.imagewrapper[0]+ 'Image: \\1'%'http://'+self.imagewrapper[1],s) s=re.sub(r'\['+image+r'\]',self.imagewrapper[0]+ 'Image: \\1'%self.link_prefix+ self.imagewrapper[1],s) # [image: filename alternate] insert an image with user alternate # [image: http://blah altenrate] insert a remote linked image s=re.sub(r'\['+remoteimage+r' ([^\]]+)\]',self.imagewrapper[0]+ '\\2'%'http://'+self.imagewrapper[1],s) s=re.sub(r'\['+image+r' ([^\]]+)\]',self.imagewrapper[0]+ '\\2'%self.link_prefix+self.imagewrapper[1],s) imagelink=r'imagelink: ([^ \]]+)' imagerem=r'imagerem: ([^ \]]+)' # [imagelink: filename link] insert a local image with a local link # [imagerem: filename remlink] insert a local image with a rem link # Scraping someone else's image and linking seems a bit much ... s=re.sub(r'\['+imagelink+r' ([^\]]+)\]',self.__linkimage,s) s=re.sub(r'\['+imagerem+r' ([^\]]+)\]',self.__remlinkimage,s) URL = r'((?:ftp|http|https):[^ \]]+)' # [http:..] to external link s = re.sub(r'\['+URL+r'\]', '\\1', s) # [http:... ...] to titled and entitled external link s = re.sub(r'\['+URL+r' "([^"]+)" ([^\]]+)\]', '\\3', s) # [http:... ...] to titled external link s = re.sub(r'\['+URL+r' ([^\]]+)\]', '\\2', s) # [:... ...] to titled local link s = re.sub(r'\[:/([^ \]]+) ([^\]]+)\]', '\\2' % self.link_prefix, s) s = re.sub(r'\[:([^ \]]+) ([^\]]+)\]', '\\2' % self.link_prefix, s) #s = re.sub(r'\[:([^\]]+)\]', '\\1', s) # [amazon:... ...] to link to Amazon using ISBN and link title s = re.sub(r'\[amazon:(\d+) ([^\]]+)\]', '\\2', s) # [doi:doistring title] pop in the dx.doi.org redirect, can't rely on #browsers. s = re.sub(r'\[doi:(\S+) ([^\]]+)\]', '\\2',s) # allow either [doi:blah or [doi: blah] s = re.sub(r'\[doi: (\S+) ([^\]]+)\]', '\\2',s) # handle simple inline mathematics (greek, super, subscripts) s=re.sub(r'\[m ([^\]]+)\]',self.htmlgreek,s) s=re.sub(r'\[m: ([^\]]+)\]',self.htmlgreek,s) # do bold and italic # actually italics don't really work properly ... s=re.sub(r'\*(?! )([^\*]+)\*', '\\1', s) s=re.sub(r"''([^']+)''", '\\1',s) # last, but not least, now do the footnotes ... s = re.sub(r'\[fn: ([^\]]+)\]',self.__footnoter,s) return s def statefulformat(self,para,reset=0): # * 0 ... to list and | tables # the members of the key of the following dictionary when present # at the beginning of a line represent a state change # the tuple has the state start and close html # current state is carried between verbatim and embedding incidents, # but not between paragraph incidents ... # variables holding state between invocations include: # self.current_state # self.NewPara # self.ListOpen - terminated unless we continue list tokens={'* ':('
      ','
    \n'), '** ':('
      ','
    \n'), '*** ':('
      ','
    \n'), '**** ':('
      ','
    \n'), '0 ':('
      ','
    \n'), '00 ':('
      ', '
    \n'), '000 ':('
      ', '
    \n'), '0000 ':('
      ', '
    \n'), '|':('\n', '

    \n'), ': ':('

    ','

    \n')} current_state=self.current_state p='' #code for bugfix 0.64 follows #(note also definition of self.PageStart in self.format) self.ParaClose=0 if self.PageStart: if para[0:para.find(' ')+1] in tokens: para='\n'+para self.PageStart=0 #code for bugfix 0.64 concludes if self.NewPara or reset: #we may need to close some lists or tables if len(current_state)>1: while len(current_state)>1: type=current_state.pop() p+=tokens[type][1] current_state=[[0]] # implies no current state self.NewPara=0 self.ListOpen=0 if reset: return p states=['\n'+x for x in tokens.keys()] tokeniser=MultiSplit(para,states) for state in tokeniser: if state[0] not in states: # make sure that it's not a heading ... if state[1].find('0: current_state.append(statetype) p+=tokens[statetype][0] else: #close previous ones for i in range(-nl): type=current_state.pop() p+=tokens[type][1] p+='
  • ' # and close the last one else: p+='' p+='%s%s'%('
  • ',state[1]) #needed to ensure that orphans are not created within lists if '*' in current_state[-1] or '0' in current_state[-1]: self.ListOpen=1 elif statetype == '|': # Handle tables, assume never part of lists! # We assume that tables continue until the end # of the paragraph, and that new rows start when # there is a | in column 1 ... a space in column # 1 indicates a continuation from previous row. # not that i've tested this properly yet if current_state[-1] not in [[0],'|']: p+=''' Tables cannot be part of a paragraph or list (It is too hard to ensure xhtml compliance in a wiki). Please leave a blank line before tables ''' if current_state[-1]!='|': current_state.append('|') p+=tokens[statetype][0] # No default for colspan line=re.sub(r'\| ',r'|1',statetype+state[1]) line=''+re.sub(r'^\|(\d)', r'  ',line) line=re.sub( r'\|(\d)', r'    ',line) # need two for offline and online, why? # emacs shows lines ending in ^M ... which the second # one catches ... line=re.sub(r'\|$',' \n',line) line=re.sub(r'\|\s',' \n',line) p+=line elif statetype ==': ': # handle block quotes p+=tokens[': '][0]+state[1]+tokens[': '][1] self.current_state=current_state if self.ParaClose: p+='

    ' self.ParaClose=0 return p def format(self, page_content, leofile=None, resource_getter=None ): ''' Parse page_content and produced xhtml formatted output ''' self.leofile=leofile self.resource_getter=resource_getter self.footnotelist = [] # We'll build a list of useful words for a later searching as well # but it is not yet implemented ... because it will have to wait # until we have page formatting on submission not on extraction self.searchable=[] # Bryan knows we can improve much of this with regular expressions, # but not right now ... s = page_content s = self.htmlspecial(s) # # general structure # # preformatted text # embedded material external html # parsed and formatted wiki instructions # # concept then is three passes through the material, rather # than event driven parsing. # first pass gets preformatted text # second finds embedded material # third does any wiki formatting # First we need to identify the preformatted text, and ensure that # no tokenising is done on that ... # Note that preformatted text cannot be part of an html

    , # but it can be part of lists etc, so we can't treat # preformatted items as new paragraphs. s = re.sub(r'[\n$]\[pre[\n\r\f\v]([\s\S]+?)[\n\r]pre\]', '

    \\1
    ',s) s = re.sub(r'
  • (\s*)', '\\1', s) # # then get the other chunks that will be preformatted # preblocks=MultiSplit(s,['
    ','
    ']) news='' for block in preblocks: news+=block[0] if block[0]<>'
    ':
                    s=block[1]
                    # Now find anything for embedding and get it done
                    s=re.sub(r'\[embed\s(.+)\s([\s\S]+?)[\n]embed\]',
                             self.__embedder,s)
                    # [insert:...] to insert a resource
                    s = re.sub(r'\[insert:([^\]]+)\]',self.__insert, s)
                    news+=s
                else:
                    news+=block[1]
            #
            # now we can do the wiki formatting proper
            #
    
            #yes we could avoid doing this twice, but it's simple
            blocks=MultiSplit(news,['','
    ','
    ','
    ']) self.PageStart=1 #bf0.64 self.NewPara=1 self.current_state=[[0]] new='' for block in blocks: if block[0] not in ('
    ',''):
    
                    ss=''
                    s=block[1]
    
                    # empty lines -> paragraph boundaries 
                    s = re.sub(r'(?m)^\s*$', '', s)
    
                    subblocks=MultiSplit(s,[''])
    
                    for sub in subblocks:
                        
                        s=sub[1]
                        
                        if s<>'':
                            if sub[0]=='': self.NewPara=1
                            s=self.escapewikimarkup(s)
                            s=self.statelessformat(s)
                            s=self.statefulformat(s)
                            s=self.escapereturn(s)
                            
                        ss+=s
    
                    block=[{'
    ':'
    ','':'','':''}[block[0]],ss] elif block[0]=='': block[0]='' elif block[0]=='
    ':
    
                    pass
                    
                new+=block[0]+block[1]
                
            new+=self.statefulformat('',1)  # close any environments
    
            # now add any footnotes
            if len(self.footnotelist)!=0:
                new+='
    ' for item in self.footnotelist:new+=self.escapereturn(item) new+='
    ' return new leonardo-0.7b1/lib/leonardo/formatters/wikiBNL/MultiSplit.py0000644000076500007650000000323410235321564025253 0ustar jtauberjtauber00000000000000# Version 0.1 BNL, December 2, 2004 # This code is in the public domain, no warranty etc, use it at your own risk from UserList import UserList class MultiSplit(UserList): ''' Take a string,s, and a list containing items to split on, and when instantiated, return a list of lists identifying substrings and what precedes it.''' def __init__(self,s,listofsplitters): slist=[x for x in listofsplitters] UserList.__init__(self) result=[(None,s)] while len(slist) > 0: results=[] item=slist.pop() for edge,substring in result: #if substring != '': splitted=substring.split(item) if len(splitted)<>1: results.append([edge,splitted[0]]) for word in splitted[1:]: results.append([item,word]) else: results.append([edge,substring]) #else: # results.append(edge,substring) result=results #get rid of meaningless first term if result[0][0]==None: result[0]=['',results[0][1]] if result[0]==['',''] and len(result)>1: result=result[1:] self.data=result def join(self): ''' Return version of self as a concatenated string ''' result='' for item in self.data: result='%s%s%s'% (result,item[0],item[1]) return result if __name__=='__main__': # test it input = 'abcn def 12 ghin xy adsf' result=MultiSplit(input,['n','12','xy']) print result print result.join() input = '12341231' x=MultiSplit(input,['1','4']) print x print x.join() print MultiSplit('',['1']) leonardo-0.7b1/lib/leonardo/formatters/xhtml/0000755000076500007650000000000010344513401022420 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/formatters/xhtml/__init__.py0000644000076500007650000000145610235321046024540 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ xhtml formatter plugin. """leonardo-0.7b1/lib/leonardo/formatters/xhtml/formatter.py0000755000076500007650000000227610235321046025010 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import re class XHTMLFormatter: def __init__(self, link_prefix): self.link_prefix = link_prefix def extract_title(self, page_content): m = re.match(r'(?m)^

    ([^<]+)

    ', page_content) if m: return m.group(1) else: return None def format(self, page_content, leo_file=None, resource_getter=None): return page_content leonardo-0.7b1/lib/leonardo/overview.txt0000644000076500007650000000336310343017712021515 0ustar jtauberjtauber00000000000000 Overview of Leonardo Internals Let us begin by tracing the flow of a simple GET request. When a request is made, the dispatch() method on core.Leonardo is called. Leonardo.dispatch() does four things: 1. constructs a Request object representing the request from the client 2. asks the resource manager for the Resource corresponding to the request 3. constructs a Response object from the Resource 4. sends the Response back to the client The resource manager is asked for the Resource via a resource id. When a browser makes a "GET /foo" request, the resource manager is asked to provide the Resource with resource id "foo". The resource manager is implemented by the ProviderManager class in providers/manager.py When a get request is made, each provider is asked, in a specific order, for the Resource with the desired id. If the provider returns a Resource, no more providers are asked. This continues until one provider finally returns a Resource. (The last provider in the list is expected to always return a Resource, even if it is just a 404 Not Found.) How does a provider go about providing the right Resource? Many providers are configured to respond to a request for a specific resource id or set of resource ids (e.g. the login_logout provider). Others are looking for ids with a particular pattern (e.g. the blog provider which is looking for any id that starts with "blog") The page provider will respond to requests for any resource id which is why it is last in the list of providers that ProviderManager will ask. Let us say that our request for "foo" hasn't been dealt with by any other provider and so it is left up to the page provider. What does it do? At this point we need to bring in the Leonardo File System or LFS. [to be continued]leonardo-0.7b1/lib/leonardo/providers/0000755000076500007650000000000010344513407021121 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/__init__.py0000644000076500007650000000147710160166761023246 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Package for Leonardo resource providers. """leonardo-0.7b1/lib/leonardo/providers/atom03/0000755000076500007650000000000010344513406022223 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/atom03/__init__.py0000644000076500007650000000145410235315303024333 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Atom provider plugin. """leonardo-0.7b1/lib/leonardo/providers/atom03/atom03.config0000644000076500007650000000051310266377370024527 0ustar jtauberjtauber00000000000000# Configuration file for atom 03 provider # Note that blog_author always comes from main configuration file. [atom03] atom_path : %(path_prefix)satom full_atom_path : %(path_prefix)satom/full atom_all_path : %(path_prefix)satom/all full_atom_all_path : %(path_prefix)satom/all/full atom_recent_count : 10 leonardo-0.7b1/lib/leonardo/providers/atom03/atom03_provider.py0000755000076500007650000001426710343023112025614 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "Atom03Provider" __contributions__ = {"head_links": "head_link"} import time import urllib from leonardo.web_resource import TopLevelResource from leonardo.providers.blog.blog_provider import Walker, ALL import atom03_templates def head_link(request, resource, main_resource, config): # @@@ this should probably only be added on certain pages atom_href = config.get("DEFAULT", "home_href") + config.get("atom03", "full_atom_path") blog_title = config.get("blog", "blog_title") return atom03_templates.auto_discovery_link % locals() class Atom03Provider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.atom_path = config.get("atom03", "atom_path") self.full_atom_path = config.get("atom03", "full_atom_path") self.atom_all_path = config.get("atom03", "atom_all_path") self.full_atom_all_path = config.get("atom03", "full_atom_all_path") self.atom_recent_count = config.getint("atom03", "atom_recent_count") def get(self, resource_id, request, main_resource): if resource_id == self.atom_path: return Atom03Resource(resource_id, self.resource_manager, self.lfs, request, self.config, full=False, number=self.atom_recent_count) elif resource_id == self.full_atom_path: return Atom03Resource(resource_id, self.resource_manager, self.lfs, request, self.config, full=True, number=self.atom_recent_count) elif resource_id == self.atom_all_path: return Atom03Resource(resource_id, self.resource_manager, self.lfs, request, self.config, full=False, number=ALL) elif resource_id == self.full_atom_all_path: return Atom03Resource(resource_id, self.resource_manager, self.lfs, request, self.config, full=True, number=ALL) else: return None def post(self, resource_id, request, main_resource): return None class Atom03Resource(TopLevelResource): def __init__(self, resource_id, resource_manager, lfs, request, config, full, number): TopLevelResource.__init__(self, resource_id, "text/xml; charset=utf-8") if full: formatter = FullAtom03Formatter(resource_manager, config) else: formatter = Atom03Formatter(resource_manager, config) walker = Walker(formatter, config.get("blog", "blog_path"), lfs) if number == ALL: self.content = walker.all(request, self) else: self.content = walker.recent(request, self, number) def get_content(self): return self.content class Atom03Formatter: def __init__(self, resource_manager, config): self.resource_manager = resource_manager self.atom_path = config.get("atom03", "atom_path") self.site_url = config.get("blog", "site_url") self.blog_title = config.get("blog", "blog_title") self.blog_path = config.get("blog", "blog_path") self.blog_author = config.get("atom03", "blog_author") self.shown_modified = False def header(self): blog_title = self.blog_title blog_url = self.site_url + self.blog_path blog_author = self.blog_author return atom03_templates.atom_header % locals() def footer(self): return atom03_templates.atom_footer % locals() def no_entries(self): return atom03_templates.atom_no_entries % locals() def entry(self, year, month, day, entry, request, main_resource): page_key = "%s/%s/%s/%s/%s" % (self.blog_path, year, month, day, entry) page = self.resource_manager.get(page_key, request, main_resource) iso_time = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(page.get_lastmod())) title = page.get_title() html_link = self.site_url + urllib.quote(page_key) if not self.shown_modified: self.shown_modified = True s = atom03_templates.atom_modified % locals() # this is the overall feed lastmod, so tell the main resource main_resource.lastmod = time.gmtime(page.get_lastmod()) else: s = "" modified = iso_time issued = iso_time s += atom03_templates.atom_entry % locals() return s class FullAtom03Formatter(Atom03Formatter): def entry(self, year, month, day, entry, request, main_resource): page_key = "%s/%s/%s/%s/%s" % (self.blog_path, year, month, day, entry) page = self.resource_manager.get(page_key, request, main_resource) iso_time = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(page.get_lastmod())) title = page.get_title() html_link = self.site_url + urllib.quote(page_key) if not self.shown_modified: self.shown_modified = True s = atom03_templates.atom_modified % locals() # this is the overall feed lastmod, so tell the main resource main_resource.lastmod = time.gmtime(page.get_lastmod()) else: s = "" modified = iso_time issued = iso_time content = page.get_html() s += atom03_templates.atom_full_entry % locals() return s leonardo-0.7b1/lib/leonardo/providers/atom03/atom03_templates.py0000644000076500007650000000340110266376650025767 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # auto_discovery_link = """\ """ atom_header = """\ %(blog_title)s %(blog_author)s """ atom_footer = """ """ atom_no_entries = "" atom_modified = """ %(iso_time)s """ atom_entry = """ %(title)s %(modified)s %(issued)s %(html_link)s """ atom_full_entry = """ %(title)s %(modified)s %(issued)s %(html_link)s """ leonardo-0.7b1/lib/leonardo/providers/atom10/0000755000076500007650000000000010344513405022220 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/atom10/__init__.py0000644000076500007650000000147610344473526024352 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber, Dave Warnock # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Atom 1.0 provider plugin. """leonardo-0.7b1/lib/leonardo/providers/atom10/atom10.config0000644000076500007650000000115410344513332024510 0ustar jtauberjtauber00000000000000# Configuration file for atom 10 provider # Note that blog_author always comes from main configuration file. [atom10] atom_path : %(path_prefix)satom10 full_atom_path : %(path_prefix)satom10/full atom_all_path : %(path_prefix)satom10/all full_atom_all_path : %(path_prefix)satom10/all/full atom_recent_count : 10 # only set if the *_feed_id's are different to the feed urls. # That will be the case if the feeds have been published before from a # different domain or from other software for example # title_feed_id : http://my-original/feed/url title_feed_id : full_feed_id : leonardo-0.7b1/lib/leonardo/providers/atom10/atom10_provider.py0000755000076500007650000001771010344513332025615 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber, Dave Warnock # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "Atom10Provider" __contributions__ = {"head_links": "head_link"} import time import urllib from leonardo.web_resource import TopLevelResource from leonardo.providers.blog.blog_provider import Walker, ALL import atom10_templates from leonardo.version import VERSION def head_link(request, resource, main_resource, config): # @@@ this should probably only be added on certain pages atom_href = config.get("DEFAULT", "home_href") + config.get("atom10", "full_atom_path") blog_title = config.get("blog", "blog_title") return atom10_templates.auto_discovery_link % locals() class Atom10Provider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.atom_path = config.get("atom10", "atom_path") self.full_atom_path = config.get("atom10", "full_atom_path") self.atom_all_path = config.get("atom10", "atom_all_path") self.full_atom_all_path = config.get("atom10", "full_atom_all_path") self.atom_recent_count = config.getint("atom10", "atom_recent_count") def get(self, resource_id, request, main_resource): if resource_id == self.atom_path: return Atom10Resource(resource_id, self.resource_manager, self.lfs, request, self.config, full=False, number=self.atom_recent_count) elif resource_id == self.full_atom_path: return Atom10Resource(resource_id, self.resource_manager, self.lfs, request, self.config, full=True, number=self.atom_recent_count) elif resource_id == self.atom_all_path: return Atom10Resource(resource_id, self.resource_manager, self.lfs, request, self.config, full=False, number=ALL) elif resource_id == self.full_atom_all_path: return Atom10Resource(resource_id, self.resource_manager, self.lfs, request, self.config, full=True, number=ALL) else: return None def post(self, resource_id, request, main_resource): return None class Atom10Resource(TopLevelResource): def __init__(self, resource_id, resource_manager, lfs, request, config, full, number): TopLevelResource.__init__(self, resource_id, "text/xml; charset=utf-8") if full: formatter = FullAtom10Formatter(resource_manager, config) else: formatter = Atom10Formatter(resource_manager, config) walker = Walker(formatter, config.get("blog", "blog_path"), lfs) if number == ALL: self.content = walker.all(request, self) else: self.content = walker.recent(request, self, number) def get_content(self): return self.content class Atom10Formatter: def __init__(self, resource_manager, config): self.resource_manager = resource_manager self.title_feed_id = config.get("atom10","title_feed_id") self.full_feed_id = config.get("atom10","full_feed_id") self.atom_path = config.get("atom10", "atom_path") self.full_atom_path = config.get("atom10", "full_atom_path") self.site_url = config.get("blog", "site_url") self.blog_title = config.get("blog", "blog_title") self.blog_path = config.get("blog", "blog_path") self.blog_author = config.get("atom10", "blog_author") self.rights = config.get("page", "rights") self.shown_modified = False def header(self): feed_self_url = "%s%s"%(self.site_url,self.atom_path) if self.title_feed_id: feed_id = self.title_feed_id else: feed_id = feed_self_url blog_title = self.blog_title blog_url = self.site_url + self.blog_path blog_author = self.blog_author rights = self.rights return atom10_templates.atom_header % locals() def footer(self): return atom10_templates.atom_footer % locals() def no_entries(self): return atom10_templates.atom_no_entries % locals() def entry(self, year, month, day, entry, request, main_resource): page_key = "%s/%s/%s/%s/%s" % (self.blog_path, year, month, day, entry) page = self.resource_manager.get(page_key, request, main_resource) iso_time = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(page.get_lastmod())) title = page.get_title() entry_author_email = page.get_property("author") if entry_author_email: entry_author = request.get_name(entry_author_email) if entry_author: author_element = "%s" % entry_author else: author_element = "" else: author_element = "" #log("VERSION:%s"%VERSION) html_link = self.site_url + urllib.quote(page_key) if not self.shown_modified: self.shown_modified = True s = atom10_templates.atom_modified % locals() # this is the overall feed lastmod, so tell the main resource main_resource.lastmod = time.gmtime(page.get_lastmod()) else: s = "" modified = iso_time issued = iso_time s += atom10_templates.atom_entry % locals() return s class FullAtom10Formatter(Atom10Formatter): def header(self): feed_self_url = "%s%s"%(self.site_url,self.full_atom_path) if self.full_feed_id: feed_id = self.full_feed_id else: feed_id = feed_self_url blog_title = self.blog_title blog_url = self.site_url + self.blog_path blog_author = self.blog_author rights = self.rights return atom10_templates.atom_header % locals() def entry(self, year, month, day, entry, request, main_resource): page_key = "%s/%s/%s/%s/%s" % (self.blog_path, year, month, day, entry) page = self.resource_manager.get(page_key, request, main_resource) iso_time = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(page.get_lastmod())) title = page.get_title() html_link = self.site_url + urllib.quote(page_key) entry_author_email = page.get_property("author") if entry_author_email: entry_author = request.get_name(entry_author_email) if entry_author: author_element = "%s" % entry_author else: author_element = "" else: author_element = "" #log("VERSION:%s"%VERSION) if not self.shown_modified: self.shown_modified = True s = atom10_templates.atom_modified % locals() # this is the overall feed lastmod, so tell the main resource main_resource.lastmod = time.gmtime(page.get_lastmod()) else: s = "" modified = iso_time issued = iso_time content = page.get_html() s += atom10_templates.atom_full_entry % locals() return s leonardo-0.7b1/lib/leonardo/providers/atom10/atom10_templates.py0000644000076500007650000000357110344513332025756 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber, Dave Warnock # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # auto_discovery_link = """\ """ atom_header = """\ %(feed_id)s %(blog_title)s %(blog_author)s %(rights)s """ atom_footer = """ """ atom_no_entries = "" atom_modified = """ %(iso_time)s """ atom_entry = """ %(title)s %(author_element)s %(modified)s %(issued)s %(html_link)s """ atom_full_entry = """ %(title)s %(author_element)s %(modified)s %(issued)s %(html_link)s """ leonardo-0.7b1/lib/leonardo/providers/blog/0000755000076500007650000000000010344513402022037 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/blog/__init__.py0000644000076500007650000000145410235315303024153 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Blog provider plugin. """leonardo-0.7b1/lib/leonardo/providers/blog/blog.config0000644000076500007650000000035210241506056024154 0ustar jtauberjtauber00000000000000# Configuration file for blog provider # Note that blog_title and site_url always come from main configuration file. [blog] blog_path : %(path_prefix)sblog all_blog_path : %(path_prefix)sblog/all blog_recent_count : 10 leonardo-0.7b1/lib/leonardo/providers/blog/blog_provider.py0000755000076500007650000002337310343023112025252 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "BlogProvider" __contributions__ = {} import urllib from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, HTMLPage404 import blog_templates ALL = "all" YEARS = ["2004", "2005"] MONTHS = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"] DAYS = [ "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"] class BlogProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.blog_path = config.get("blog", "blog_path") self.all_blog_path = config.get("blog", "all_blog_path") self.site_url = config.get("blog", "site_url") self.blog_recent_count = config.getint("blog", "blog_recent_count") def get(self, resource_id, request, main_resource): if resource_id == self.all_blog_path: page_part = BlogPagePart(resource_id, self.resource_manager, request, self.lfs, self.config, number=ALL) return HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) if not resource_id.startswith(self.blog_path): return None blog_path = resource_id[len(self.blog_path):] year, month, day, entry = None, None, None, None path_components = blog_path.split("/") if len(path_components) == 1: pass elif len(path_components) == 2: year = path_components[1] elif len(path_components) == 3: year, month = path_components[1:] elif len(path_components) == 4: year, month, day = path_components[1:] elif len(path_components) == 5: year, month, day, entry = path_components[1:] else: return HTMLPage404(resource_id, self.resource_manager, request, main_resource, self.config) if year and year not in YEARS: return HTMLPage404(resource_id, self.resource_manager, request, main_resource, self.config) if month and month not in MONTHS: return HTMLPage404(resource_id, self.resource_manager, request, main_resource, self.config) if day and day not in DAYS: return HTMLPage404(resource_id, self.resource_manager, request, main_resource, self.config) if entry: return None # we don't need to handle it @@@ although we could to add date/permalink else: if year: page_part = BlogPagePart(resource_id, self.resource_manager, request, self.lfs, self.config, year, month, day) else: if main_resource != None: # if not a top-level request, return None # don't handle it here page_part = TopBlogPagePart(resource_id, self.resource_manager, request, self.lfs, self.config, number=self.blog_recent_count) return HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) def post(self, resource_id, resource, main_resource): return None class BlogPagePart(PagePart): def __init__(self, resource_id, resource_manager, request, lfs, config, year=None, month=None, day=None, number=None): PagePart.__init__(self, resource_id) formatter = HTMLFormatter(resource_manager, config) walker = Walker(formatter, config.get("blog", "blog_path"), lfs) blog_title = config.get("blog", "blog_title") if day: title = blog_templates.blog_title_day % locals() content = walker.day(year, month, day, request, self) elif month: title = blog_templates.blog_title_month % locals() content = walker.month(year, month, request, self) elif year: title = blog_templates.blog_title_year % locals() content = walker.year(year, request, self) elif number == ALL: title = blog_templates.blog_title_all % locals() content = walker.all(request, self) else: # latest n title = blog_templates.blog_title_latest % locals() content = walker.recent(request, self, number) self.title = title self.html = content class TopBlogPagePart(BlogPagePart): def __init__(self, resource_id, resource_manager, request, lfs, config, year=None, month=None, day=None, number=None): BlogPagePart.__init__(self, resource_id, resource_manager, request, lfs, config, year, month, day, number) page_part = resource_manager.get(resource_id, request, self) if page_part.exists(): self.html = page_part.get_html() + self.html def is_read_only(self): return False class HTMLFormatter: def __init__(self, resource_manager, config): self.resource_manager = resource_manager self.site_url = config.get("blog", "site_url") self.blog_path = config.get("blog", "blog_path") self.config = config def header(self): return "" def footer(self): return "" def no_entries(self): return blog_templates.blog_no_entries % locals() def entry(self, year, month, day, entry, request, main_resource): page_key = "%s/%s/%s/%s/%s" % (self.blog_path, year, month, day, entry) page = self.resource_manager.get(page_key, request, main_resource) body = page.get_html() title = page.get_title() author_email = page.get_property("author") if author_email: author_name = request.get_name(author_email) if author_name: author_display = "by %s : " % author_name else: author_display = "" else: author_display = "" date = "%s/%s/%s" % (year, month, day) html_link = self.site_url + urllib.quote(page_key) blog_footer_extras = "" for provider in self.resource_manager.get_contributors("blog_entry_footer"): blog_footer_extras += provider(request, page, main_resource, self.config) return blog_templates.blog_entry % locals() class Walker: def __init__(self, formatter, blog_path, lfs): self.formatter = formatter self.blog_path = blog_path self.lfs = lfs def all(self, request, main_resource): return \ self.formatter.header() + \ self.descend(self.blog_path, lambda year: self.year(year, request, main_resource)) + \ self.formatter.footer() # contributed by Min Sik Kim # @@@ could be merged with descend with an extra parameter def recent(self, request, main_resource, num): import os content = "" years = self.lfs.get_children(self.blog_path)[0] for year in years: year_dir = os.path.join(self.blog_path, year) months = self.lfs.get_children(year_dir)[0] for month in months: month_dir = os.path.join(year_dir, month) days = self.lfs.get_children(month_dir)[0] for day in days: day_dir = os.path.join(month_dir, day) entries = self.lfs.get_children(day_dir)[1] for entry in entries: content += self.entry(year, month, day, entry, request, main_resource) num -= 1 if num == 0: break if num == 0: break if num == 0: break if num == 0: break return self.formatter.header() + content + self.formatter.footer() def year(self, year, request, main_resource): return self.descend(self.blog_path + "/" + year, lambda month: self.month(year, month, request, main_resource)) def month(self, year, month, request, main_resource): return self.descend("%s/%s/%s" % (self.blog_path, year, month), lambda day: self.day(year, month, day, request, main_resource)) def day(self, year, month, day, request, main_resource): return self.descend("%s/%s/%s/%s" % (self.blog_path, year, month, day), lambda entry: self.entry(year, month, day, entry, request, main_resource)) def entry(self, year, month, day, entry, request, main_resource): return self.formatter.entry(year, month, day, entry, request, main_resource) def descend(self, key, func): directories, files = self.lfs.get_children(key) children = directories + files content = "" if children: for child in children: content += func(child) else: content = self.formatter.no_entries() return content leonardo-0.7b1/lib/leonardo/providers/blog/blog_templates.py0000644000076500007650000000231510274511664025425 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # blog_title_day = "%(blog_title)s %(year)s/%(month)s/%(day)s" blog_title_month = "%(blog_title)s %(year)s/%(month)s" blog_title_year = "%(blog_title)s %(year)s" blog_title_latest = "%(blog_title)s" blog_title_all = "%(blog_title)s" blog_no_entries = """

    No Entries.

    """ blog_entry = """

    %(title)s

    %(body)s

    %(author_display)s %(date)s %(blog_footer_extras)s (permalink)

    """ leonardo-0.7b1/lib/leonardo/providers/blogcalendar/0000755000076500007650000000000010344513407023536 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/blogcalendar/__init__.py0000644000076500007650000000147710235315303025652 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Package for Leonardo resource providers. """leonardo-0.7b1/lib/leonardo/providers/blogcalendar/blogcalendar.config0000644000076500007650000000024110313136665027342 0ustar jtauberjtauber00000000000000# Configuration file for blogcalendar provider [blogcalendar] calendar_key : %(path_prefix)scalendar monthly_archive_key : %(path_prefix)smonthly-archiveleonardo-0.7b1/lib/leonardo/providers/blogcalendar/blogcalendar_provider.py0000644000076500007650000002041710343023112030427 0ustar jtauberjtauber00000000000000# # Copyright (C) 2004-2005 Bryan Lawrence and James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "CalendarProvider" __contributions__ = {} from leonardo.web_resource import PagePart import os, os.path, time class CalendarProvider: def __init__(self, resource_manager, lfs, config): self.config = config self.calendar_key = config.get("blogcalendar", "calendar_key") self.monthly_archive_key = config.get("blogcalendar", "monthly_archive_key") def get(self, resource_id, request, main_resource): if resource_id == self.calendar_key: return BlogCalendarPart(resource_id, request, self.config) elif resource_id == self.monthly_archive_key: return MonthlyArchivePart(resource_id, request, self.config) else: return None def post(self, resource_id, request, main_resource): return None class BlogCalendarPart(PagePart): def __init__(self, key, request, config): """ this is called by the menu but we get the context from the request itself. """ PagePart.__init__(self, key) self.relative_path = request.relative_path self.site_url = config.get("blog", "site_url") self.blog_path = config.get("blog", "blog_path") self.lfs_root = config.get("DEFAULT", "lfs_root") path = self.relative_path # five cases: # blog: - serve up latest month # blog/year - serve up Jan # blog/year/month - serve up year/month # blog/year/month/day - serve up year/month # anything else - serve up latest month # where of course blog is the config blog prefix ... bl = len(self.blog_path) if path[0:bl] != self.blog_path or path[bl:bl+1] != "/": # serve up latest month y, m = time.gmtime()[0:2] else: lookingfor = path[bl+1:] slash = lookingfor.find("/") try: if slash == -1: y = int(lookingfor) m = 1 else: y = int(lookingfor[0:4]) m = int(lookingfor[5:7]) except ValueError: y, m = time.gmtime()[0:2] year, last, this, next = self.title_links(y, m) self.html = HtmlCalendar(y, m, self.day_links(y, m), (last, next), this, year) def day_links(self,y,m): # Jump directly down to the month ... links = {} tail = "".join([self.blog_path, "/", str(y), "/", "%2.2i" % m]) monthdir = self.lfs_root + tail base = self.site_url + tail if os.path.isdir(monthdir): blogs = os.listdir(monthdir) for blog in blogs: if blog[0:1] != ".": links[int(blog)] = base + "/" + blog return links def title_links(self,y,m): def yeartitle(y): return "".join([self.site_url, self.blog_path, "/", str(y)]) def increment(y, m, i): mm = m + i yy = y if mm > 12: mm, yy = mm % 12, y + mm / 12 elif mm < 1: mm, yy = 12 + mm, y - 1 return yy, mm plus = increment(y, m, 1) minus = increment(y, m, -1) year = yeartitle(y) this = "%s%s%2.2i" % (year, "/", m) next = "%s%s%2.2i" % (yeartitle(plus[0]), "/", plus[1]) last = "%s%s%2.2i" % (yeartitle(minus[0]), "/", minus[1]) return year, last, this, next month_names = ["January ", "February ", "March ", "April ", "May ", "June ", "July ", "August ", "September ", "October ", "November ", "December "] class MonthlyArchivePart(PagePart): def __init__(self, key, request, config): PagePart.__init__(self, key) site_url = config.get("blog", "site_url") blog_path = config.get("blog", "blog_path") lfs_root = config.get("DEFAULT", "lfs_root") html = "" blog_dir = lfs_root + blog_path if os.path.isdir(blog_dir): years = os.listdir(blog_dir) years.sort() years.reverse() for year in years: if year == ".svn": continue year_dir = lfs_root + blog_path + "/" + year if os.path.isdir(year_dir): months = os.listdir(year_dir) months.sort() months.reverse() for month in months: if month == ".svn": continue link = site_url + blog_path + "/" + year + "/" + month html += '

    %s %s

    ' % (link, month_names[int(month) - 1], year) self.html = html def HtmlCalendar(year, month, links={}, arrows=None, monthlink=None, yearlink=None): """ Produces an html table representation of the month. If the links argument is passed then it should consist of a set of links keyed by day of the month. If the arrows argument is passed, then it should consist of a two member tuple which has the links to the previous and next month. If the monthlink argument is passed, then it should be used as a link from the month title, if the yearlink argument is passed it can be used as the yearlink title. """ months = ["January ", "February ", "March ", "April ", "May ", "June ", "July ", "August ", "September ", "October ", "November ", "December "] # Ok, I know I should be able to find someone elses code to do this, # but it'll take longer to find it than write it import calendar def makelink(target, link): return "".join(['',link,""]) def monthtitle(m, link, short=0): title = months[m - 1] if short: title = title[0:3] if link is None: return title else: return makelink(link, title) tr=("","") td=("""""","") if yearlink == None: yeartitle = str(year) else: yeartitle = makelink(yearlink, str(year)) if arrows == None: template = "".join([""""]) else: template = "".join(["
    """, monthtitle(month, monthlink), yeartitle, "
    ", td[0], makelink(arrows[0], "<<"), td[1], """", td[0], makelink(arrows[1], ">>"), td[1], tr[1]]) template += tr[0] for day in ("Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"): template += "".join([td[0], day, td[1]]) template += tr[1] matrix = calendar.monthcalendar(year, month) for week in matrix: row = tr[0] for day in week: if day != 0: if day not in links.keys(): row += "".join([td[0], str(day), td[1]]) else: row += "".join([td[0], makelink(links[day], str(day)), td[1]]) else: row += "".join(td) row += tr[1] template += row template += "
    """, monthtitle(month, monthlink, short=1), " ", yeartitle, "
    " return template leonardo-0.7b1/lib/leonardo/providers/category/0000755000076500007650000000000010344513407022736 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/category/__init__.py0000644000076500007650000000146010242550335025046 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Category provider plugin. """leonardo-0.7b1/lib/leonardo/providers/category/category_provider.py0000644000076500007650000001252210343023112027025 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "CategoryProvider" __contributions__ = {"edit_form": "category_edit_form", "draft_hidden": "category_draft_hidden", "page_end": "category_page_end", "blog_entry_footer": "category_blog_entry_footer", "post_processing": "category_post_processing"} from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage def category_edit_form(request, resource, main_resource, config): if resource: categories = " ".join(resource.get_property("categories", [])) else: categories = "" # @@@ templatize return """ Categories
    """ % locals() def category_draft_hidden(request, resource, main_resource, config): if resource: categories = " ".join(resource.get_property("categories", [])) else: categories = "" # @@@ templatize return """ """ % locals() # @@@ templatize def category_blog_entry_footer(request, resource, main_resource, config): s = "" prefix = config.get("DEFAULT", "home_href") for category in resource.get_property("categories", []): href = prefix + category s += """ %(category)s""" % locals() if s: s = ": Categories " + s return s # @@@ templatize def category_page_end(request, resource, main_resource, config): if main_resource == None: s = "" categories = "" prefix = config.get("DEFAULT", "home_href") for category in resource.get_property("categories", []): href = prefix + category categories += """ %(category)s""" % locals() if categories: s += "

    Categories: %(categories)s

    " % locals() return s else: return "" def category_post_processing(request, resource, main_resource, config): categories = request.fields.getvalue("categories", "").split() if categories and request.logged_in(): resource.set_property("categories", categories) return "" class CategoryProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config def get(self, resource_id, request, main_resource): if not resource_id.startswith("category"): return None page_part = CategoryPagePart(resource_id, self.lfs, request, self.resource_manager, self.config) return page_part def post(self, resource_id, request, main_resource): return None class CategoryPagePart(PagePart): def __init__(self, resource_id, lfs, request, resource_manager, config): PagePart.__init__(self, resource_id) parts = resource_id.split("/") prefix = config.get("DEFAULT", "home_href") draft_prefix = config.get("draft", "draft_prefix") self.html = "
      " if len(parts) != 2: self.title = "Categories" for value, resources in lfs.find_property("categories").items(): self.html += "
    • %(value)s
        " % locals() for resource in resources: if resource.startswith(draft_prefix): # @@@ continue href = prefix + resource page = resource_manager.get(resource, request, "hack") if page: title = page.get_property("page_title") or resource else: title = resource self.html += """
      • %(title)s
      • """ % locals() self.html += "
    • " else: category = parts[1] self.title = "Category '%(category)s'" % locals() for resource in lfs.find_property("categories").get(category, []): if resource.startswith(draft_prefix): # @@@ continue href = prefix + resource page = resource_manager.get(resource, request, "hack") if page: title = page.get_property("page_title") or resource else: title = resource self.html += """
    • %(title)s
    • """ % locals() self.html += "
    " leonardo-0.7b1/lib/leonardo/providers/comment/0000755000076500007650000000000010344513405022561 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/comment/__init__.py0000644000076500007650000000145710235315303024675 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Comment provider plugin. """leonardo-0.7b1/lib/leonardo/providers/comment/comment_provider.py0000644000076500007650000001765310343056112026517 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "CommentProvider" __contributions__ = {"edit_form": "comments_allowed_edit_form", "draft_hidden": "comments_allowed_draft_hidden", "page_end" : "comment_page_end", "blog_entry_footer" : "comment_blog_entry_footer", "post_processing": "comments_post_processing"} import urllib import re from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage import comment_templates def comments_allowed_edit_form(request, resource, main_resource, config): # @@@ templatize if resource: allow_comments = resource.get_property("allow_comments") else: allow_comments = "" s = """ Allow Comments?
    """ return s def comments_allowed_draft_hidden(request, resource, main_resource, config): # @@@ templatize if resource: allow_comments = resource.get_property("allow_comments") else: allow_comments = "" s = """ """ % locals() return s # @@@ templatize def comment_blog_entry_footer(request, resource, main_resource, config): if resource.get_property("allow_comments", "NO") != "NO": comment_count = len(resource.enclosures("comment")) if comment_count == 1: s = " : 1 comment" else: s = " : %s comments" % comment_count else: s = "" return s # @@@ templatize def comment_page_end(request, resource, main_resource, config): if main_resource == None and resource.get_property("allow_comments", "NO") != "NO": enclosures = resource.enclosures("comment") html = """

    Comments (%s)

    """ % len(enclosures) for comment in enclosures: author_name = comment.get_property("author_name") or "unknown" author_link = comment.get_property("author_link") content = comment.get_content() # @@@ utility function author_name = re.sub(r'&', '&', author_name) author_name = re.sub(r'<', '<', author_name) author_name = re.sub(r'>', '>', author_name) if author_link: author_link = re.sub(r'&', '&', author_link) author_link = re.sub(r'<', '<', author_link) author_link = re.sub(r'>', '>', author_link) author_link = re.sub(r'"', '"', author_link) content = re.sub(r'&', '&', content) content = re.sub(r'<', '<', content) content = re.sub(r'>', '>', content) content = re.sub(r'\n', '
    ', content) lastmod = comment.get_lastmod() if lastmod: import time lastmod_display = time.strftime("%A %d %B, %Y", time.gmtime(lastmod)) else: lastmod_display = "" if author_link: citation = """%(author_name)s""" % locals() else: citation = author_name if request.logged_in(): comment_path = comment.key delete_comment = """

    Delete this comment

    """ % comment_path # @@@ delete is hard-coded else: delete_comment = "" html += """

    %(citation)s on %(lastmod_display)s:

    %(content)s
    %(delete_comment)s
    """ % locals() if resource.get_property("allow_comments", "NO") == "YES": html += """

    Add a Comment

    Name
    URI
    Comment
    """ else: html += """

    Comments presently read-only.

    """ else: html = "" return html def comments_post_processing(request, resource, main_resource, config): allow_comments = request.fields.getvalue("allow_comments") if allow_comments and request.logged_in(): resource.set_property("allow_comments", allow_comments) if resource.get_property("allow_comments", "NO") != "YES": return None comment_content = request.fields.getvalue("comment_content", None) if comment_content: return add_comment(resource, request, main_resource) else: return None def add_comment(resource, request, main_resource): comment_content = request.fields.getvalue("comment_content", None) author_name = request.fields.getvalue("author_name", None) author_link = request.fields.getvalue("author_link", None) inner_resource = resource # @@@ could create comment object for this that wraps enclosure existing_comments = inner_resource.enclosures("comment") next_index = 0 for comment in existing_comments: if int(comment.index) > next_index: next_index = int(comment.index) next_index += 1 new_comment = inner_resource.enclosure("comment", next_index) new_comment.set_content(comment_content, "txt") if author_name: new_comment.set_property("author_name", author_name) if author_link: new_comment.set_property("author_link", author_link) class CommentProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config def get(self, resource_id, request, main_resource): return None def post(self, resource_id, request, main_resource): pass leonardo-0.7b1/lib/leonardo/providers/comment/comment_templates.py0000644000076500007650000000370110265653326026665 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # toggle_comments_confirm_title = "%(site_title)s : toggle comments on %(resource_to_toggle)s?" toggle_comments_confirm_form = """

    Toggle Comments on %(resource_to_toggle)s?

    Cancel """ #toggle_comments_menu = """ #

    toggle_comments Page

    #""" toggle_comments_missing_id_title = "Missing Resource ID" toggle_comments_missing_id_content = "Missing Resource ID." toggle_comments_page_not_exist_title = "%(site_title)s : %(resource_to_toggle)s does not exist" toggle_comments_page_not_exist_content = "%(resource_to_toggle)s does not exist." toggle_comments_read_only_title = "Can't toggle comments on a read-only resource" toggle_comments_read_only_content = "Can't toggle comments on a read-only resource." toggle_comments_done_title = "%(site_title)s: %(resource_to_toggle)s comments toggled" toggle_comments_done_content = """%(resource_to_toggle)s comments toggled to %(new_value)s."""leonardo-0.7b1/lib/leonardo/providers/css/0000755000076500007650000000000010344513403021705 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/css/__init__.py0000644000076500007650000000147710235315303024025 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Package for Leonardo resource providers. """leonardo-0.7b1/lib/leonardo/providers/css/css.config0000644000076500007650000000015510241506056023667 0ustar jtauberjtauber00000000000000# Configuration file for css provider [CSS] css_id : %(path_prefix)scss css_href : %(cgi_root)s%(css_id)s leonardo-0.7b1/lib/leonardo/providers/css/css_provider.py0000755000076500007650000000204510241514307024765 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = None __contributions__ = {"head_links": "head_link"} def head_link(request, resource, main_resource, config): css_href = config.get("CSS", "css_href") return """""" % locals() leonardo-0.7b1/lib/leonardo/providers/delete/0000755000076500007650000000000010344513405022361 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/delete/__init__.py0000644000076500007650000000145610235315303024474 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Delete provider plugin. """leonardo-0.7b1/lib/leonardo/providers/delete/delete.config0000644000076500007650000000026610241506056025016 0ustar jtauberjtauber00000000000000# Configuration file for delete provider [delete] delete_path : %(path_prefix)sdelete delete_href : %(cgi_root)s%(delete_path)s delete_resource_field : resource leonardo-0.7b1/lib/leonardo/providers/delete/delete_provider.py0000755000076500007650000001274310343023112026107 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ GETing the 'delete' resource confirms deletion of a resource. POSTing to the 'delete' resource is intended as an alternative to DELETEing a resource in cases like HTML forms where you can't directly DELETE. """ __provider__ = "DeleteProvider" __contributions__ = {"menu": "delete_menu_part"} import urllib from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage import delete_templates def delete_menu_part(request, resource, main_resource, config): if request.logged_in() and not resource.is_read_only(): delete_page_href = config.get("delete", "delete_href") + "?" + config.get("delete", "delete_resource_field") + "=" + urllib.quote(resource.get_id()) menu_part = delete_templates.delete_menu % locals() else: menu_part = "" return menu_part class DeleteProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.delete_path = config.get("delete", "delete_path") self.resource_name = config.get("delete", "delete_resource_field") def get(self, resource_id, request, main_resource): if resource_id != self.delete_path: return None if not request.logged_in(): return ForbiddenPage(resource_id, self.resource_manager, request, main_resource, self.config) resource_to_delete = request.fields.getvalue(self.resource_name) page_part = GetDeletePagePart(resource_id, resource_to_delete, self.resource_manager, request, self.config) return HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) def post(self, resource_id, request, main_resource): if resource_id != self.delete_path: return None if not request.logged_in(): return ForbiddenPage(resource_id, self.resource_manager, request, main_resource, self.config) resource_to_delete = request.fields.getvalue(self.resource_name) page_part = PostDeletePagePart(resource_id, resource_to_delete, self.resource_manager, request, self.config) return HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) class GetDeletePagePart(PagePart): def __init__(self, resource_id, resource_to_delete, resource_manager, request, config): PagePart.__init__(self, resource_id) site_title = config.get("page", "site_title") post_action = config.get("delete", "delete_href") resource_name = config.get("delete", "delete_resource_field") if resource_to_delete: cancel_action = urllib.quote(resource_to_delete) page = resource_manager.get(resource_to_delete, request, self) if not page.exists(): self.title = delete_templates.delete_page_not_exist_title % locals() self.html = delete_templates.delete_page_not_exist_content % locals() elif page.is_read_only(): self.title = delete_templates.delete_read_only_title % locals() self.html = delete_templates.delete_read_only_content % locals() else: self.title = delete_templates.delete_confirm_title % locals() self.html = delete_templates.delete_confirm_form % locals() else: self.title = delete_templates.delete_missing_id_title % locals() self.html = delete_templates.delete_missing_id_content % locals() class PostDeletePagePart(PagePart): def __init__(self, resource_id, resource_to_delete, resource_manager, request, config): PagePart.__init__(self, resource_id) site_title = config.get("page", "site_title") if resource_to_delete: page = resource_manager.get(resource_to_delete, request, self) if not page.exists(): self.title = delete_templates.delete_page_not_exist_title % locals() self.html = delete_templates.delete_page_not_exist_content % locals() elif page.is_read_only(): self.title = delete_templates.delete_read_only_title % locals() self.html = delete_templates.delete_read_only_content % locals() else: page.delete() self.title = delete_templates.delete_done_title % locals() self.html = delete_templates.delete_done_content % locals() else: self.title = delete_templates.delete_missing_id_title % locals() self.html = delete_templates.delete_missing_id_form % locals() leonardo-0.7b1/lib/leonardo/providers/delete/delete_templates.py0000644000076500007650000000315310337051142026252 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # delete_confirm_title = "DELETE %(resource_to_delete)s?" delete_confirm_form = """
    Cancel """ delete_menu = """

    Delete Page

    """ delete_missing_id_title = "Missing Resource ID" delete_missing_id_content = "Missing Resource ID." delete_page_not_exist_title = "%(resource_to_delete)s does not exist" delete_page_not_exist_content = "%(resource_to_delete)s does not exist." delete_read_only_title = "Can't delete read-only resource" delete_read_only_content = "Can't delete read-only resource." delete_done_title = "%(resource_to_delete)s DELETED" delete_done_content = "%(resource_to_delete)s DELETED."leonardo-0.7b1/lib/leonardo/providers/draft/0000755000076500007650000000000010344513404022216 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/draft/__init__.py0000644000076500007650000000145510235315303024331 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Draft provider plugin. """leonardo-0.7b1/lib/leonardo/providers/draft/draft.config0000644000076500007650000000025110241506056024504 0ustar jtauberjtauber00000000000000# Configuration file for draft provider [draft] draft_prefix : %(path_prefix)sdraft draft_href : %(cgi_root)s%(draft_prefix)s draft_type_default : wiki04 leonardo-0.7b1/lib/leonardo/providers/draft/draft_provider.py0000755000076500007650000001767710343023112025616 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "DraftProvider" __contributions__ = {"menu": "draft_menu_part"} import re import time import urllib from leonardo.formatters.manager import FormatterManager from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage import draft_templates def draft_menu_part(request, resource, main_resource, config): if request.logged_in(): draft_href = config.get("draft", "draft_href") menu_part = draft_templates.draft_menu % locals() else: menu_part = "" return menu_part # form field names page_id_name = "page_id" page_title_name = "page_title" page_content_name = "page_content" page_content_type_name = "page_content_type" # requires 'editform', 'blog' and 'put' modules # @@@ perhaps this could delegate more to the editform provider class DraftProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.draft_prefix = config.get("draft", "draft_prefix") self.formatter_manager = FormatterManager(config) def get(self, resource_id, request, main_resource): return self.handle(resource_id, request, main_resource) def post(self, resource_id, request, main_resource): return self.handle(resource_id, request, main_resource) def handle(self, resource_id, request, main_resource): """ handle both GET and POST. """ if not resource_id.startswith(self.draft_prefix): return None if not request.logged_in(): return ForbiddenPage(resource_id, self.resource_manager, request, main_resource, self.config) draft_path = resource_id[len(self.draft_prefix):].strip("/") if draft_path == "": # top level page_part = DraftListPagePart(resource_id, self.resource_manager, request, self.lfs, self.config, self.formatter_manager) return HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) else: # individual draft page if main_resource != None: # if not a top-level request, return None # don't handle it here outer_page_part = DraftPagePart(resource_id, self.resource_manager, request, self.config) return HTMLPage(resource_id, outer_page_part, self.resource_manager, request, main_resource, self.config) class DraftListPagePart(PagePart): def __init__(self, resource_id, resource_manager, request, lfs, config, formatter_manager): PagePart.__init__(self, resource_id) status = "" # @@@ how can this be avoided? page_content_name = globals()["page_content_name"] page_content_type_name = globals()["page_content_type_name"] page_title_name = globals()["page_title_name"] form_extras = "" for provider in resource_manager.get_contributors("edit_form"): form_extras += provider(request, None, None, config) form_content = draft_templates.draft_initial_form_content % locals() # @@@ posting should really be done as a separate method if request.method == "POST": page_content = request.fields.getvalue(page_content_name, "") page_content_type = request.fields.getvalue(page_content_type_name, "") page_title = request.fields.getvalue(page_title_name, None) if page_title: draft_resource_id = config.get("draft", "draft_prefix") + "/" + re.sub(' ', '_', page_title.lower()) page = resource_manager.get(draft_resource_id, request, self) if page.exists(): status = draft_templates.draft_existing_title % locals() form_content = page_content else: page.set_content(page_content, page_content_type) page.set_property("page_title", page_title) for provider in resource_manager.get_contributors("post_processing"): provider(request, page, None, config) status = draft_templates.draft_success % locals() else: status = draft_templates.draft_no_title % locals() form_content = page_content title = draft_templates.draft_title # get list of drafts drafts = lfs.get_children(config.get("draft", "draft_prefix"))[1] if drafts: draft_items = "" for draft_title in drafts: draft_link = config.get("draft", "draft_href") + "/" + urllib.quote(draft_title) draft_items += draft_templates.draft_item % locals() draft_list = draft_templates.draft_list % locals() else: draft_list = draft_templates.empty_draft_list % locals() action = config.get("draft", "draft_href") page_content_type = config.get("draft", "draft_type_default") content = draft_templates.draft_page % locals() self.title = title self.html = content class DraftPagePart(PagePart): def __init__(self, resource_id, resource_manager, request, config): PagePart.__init__(self, resource_id) page_part = resource_manager.get(resource_id, request, self) if page_part.exists(): page_title = page_part.get_title() content = page_part.get_html() page_content = page_part.get_content() page_content_type = page_part.get_content_type() page_content = re.sub(r'&', '&', page_content) page_content = re.sub(r'<', '<', page_content) page_content = re.sub(r'>', '>', page_content) page_content = re.sub(r'"', '"', page_content) draft_path = resource_id[len(config.get("draft", "draft_prefix")):].strip("/") post_action = config.get("put", "put_href") resource_name = config.get("put", "put_resource_field") page_content_name = config.get("put", "put_content_field") page_content_type_name = config.get("put", "put_content_type_field") # @@@ should be in put config, not editform page_title_name = config.get("editform", "edit_title_field") resource_id_to_put = config.get("blog", "blog_path") + "/%04d/%02d/%02d/" % tuple(time.localtime()[:3]) + draft_path form_extras = "" for provider in resource_manager.get_contributors("draft_hidden"): form_extras += provider(request, page_part, None, config) content = draft_templates.draft_put_form % locals() + content self.title = page_title self.html = content else: # @@@ really should return a 404 self.title = "Non-existent draft" # @@@ templatize self.html = "

    Non-existent draft.

    " # @@@ templatize def is_read_only(self): return False leonardo-0.7b1/lib/leonardo/providers/draft/draft_templates.py0000644000076500007650000000433510307023652025753 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # draft_initial_form_content = "Type content here." draft_existing_title = "Page with that title already exists in drafts." draft_no_title = "Please provide a title." draft_success = "Successfully created '%(draft_resource_id)s'." draft_title = "Drafts" draft_item = """
  • %(draft_title)s
  • """ draft_list = """
      %(draft_items)s
    """ empty_draft_list = """

    No drafts.

    """ draft_page = """

    %(status)s

    Existing

    %(draft_list)s

    New Draft Page

    Title:

    %(form_extras)s
    as
    Cancel """ draft_menu = """

    Drafts

    """ draft_put_form = """

    to as

    %(form_extras)s
    """leonardo-0.7b1/lib/leonardo/providers/editform/0000755000076500007650000000000010344513406022731 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/editform/__init__.py0000644000076500007650000000146010235315303025036 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Editform provider plugin. """leonardo-0.7b1/lib/leonardo/providers/editform/editform.config0000644000076500007650000000047410335027775025747 0ustar jtauberjtauber00000000000000# Configuration file for editform provider [editform] edit_path : %(path_prefix)sedit edit_href : %(cgi_root)s%(edit_path)s edit_resource_field : resource edit_title_field : title keep_last_modified_field : keep_last_modified editform_default_type : wiki04 leonardo-0.7b1/lib/leonardo/providers/editform/editform_provider.py0000755000076500007650000001127410343023112027023 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "EditFormProvider" __contributions__ = {"menu": "edit_menu_part"} import urllib from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage import editform_templates def edit_menu_part(request, resource, main_resource, config): if request.logged_in() and not resource.is_read_only(): edit_page_href = config.get("editform", "edit_href") + "?" + config.get("editform", "edit_resource_field") + "=" + urllib.quote(resource.get_id()) menu_part = editform_templates.edit_menu % locals() else: menu_part = "" return menu_part class EditFormProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.edit_path = config.get("editform", "edit_path") self.resource_name = config.get("editform", "edit_resource_field") def get(self, resource_id, request, main_resource): if resource_id != self.edit_path: return None if not request.logged_in(): return ForbiddenPage(resource_id, self.resource_manager, request, main_resource, self.config) resource_to_edit = request.fields.getvalue(self.resource_name) outer_page_part = EditFormPagePart(resource_id, resource_to_edit, self.resource_manager, request, self.config) html_page = HTMLPage(resource_id, outer_page_part, self.resource_manager, request, main_resource, self.config) html_page.expiry = 0 return html_page def post(self, resource_id, request, main_resource): return None class EditFormPagePart(PagePart): def __init__(self, resource_id, resource_to_edit, resource_manager, request, config): PagePart.__init__(self, resource_id) post_action = config.get("put", "put_href") page_content_name = config.get("put", "put_content_field") page_content_type_name = config.get("put", "put_content_type_field") resource_name = config.get("put", "put_resource_field") title_name = config.get("editform", "edit_title_field") if resource_to_edit: cancel_action = urllib.quote(resource_to_edit) page = resource_manager.get(resource_to_edit, request, self) if page.is_read_only(): self.title = editform_templates.edit_read_only_title % locals() self.html = editform_templates.edit_read_only_title % locals() else: time = None site_title = config.get("page", "site_title") if page.exists(): page_title = page.get_title() title = editform_templates.page_title % locals() page_content = page.get_content() page_content_type = page.get_content_type() time = page.get_lastmod() else: # new wiki word page_title = resource_to_edit title = editform_templates.new_page_title % locals() page_content = editform_templates.new_page_content % locals() page_content_type = config.get("editform", "editform_default_type") form_extras = "" for provider in resource_manager.get_contributors("edit_form"): form_extras += provider(request, page, None, config) content = editform_templates.edit_form % locals() self.title = title self.html = content else: self.title = editform_templates.edit_missing_id_title % locals() self.html = editform_templates.edit_missing_id_content % locals() leonardo-0.7b1/lib/leonardo/providers/editform/editform_templates.py0000644000076500007650000000337710335027775027215 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # page_title = "Edit %(page_title)s" new_page_title = "New Page" new_page_content = "Type content here." edit_form = """
    Title:

    %(form_extras)s as Keep previous last modified?
    Cancel """ edit_menu = """

    Edit Page

    """ edit_missing_id_title = "Missing Resource ID" edit_missing_id_content = "Missing Resource ID." edit_read_only_title = "Can't edit read-only resource" edit_read_only_content = "Can't edit read-only resource."leonardo-0.7b1/lib/leonardo/providers/enclosure/0000755000076500007650000000000010344513403023114 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/enclosure/__init__.py0000644000076500007650000000145110235315303025224 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Enclosures plugin. """leonardo-0.7b1/lib/leonardo/providers/enclosure/enclosure_provider.py0000755000076500007650000000656110343023112027402 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "EnclosureProvider" __contributions__ = {} import re import mimetypes from leonardo.formatters.manager import FormatterManager from leonardo.web_resource import TopLevelResource, FileObjectResource from leonardo.providers.page.page_provider import NonWikiPage, WikiPagePart, HTMLPage, HTMLPage404 # @@@ this is now too much like PageProvider not to be refactored class EnclosureProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.formatter_manager = FormatterManager(config) def get(self, resource_id, request, main_resource): match = re.match("(.*)/__(\w+)__(\d+)", resource_id) if not match: return None base = match.group(1) enctype = match.group(2) index = match.group(3) key = resource_id enclosure = self.lfs.get(base).enclosure(enctype, index) content_type = enclosure.get_content_type() if enclosure.exists() and not self.formatter_manager.known(content_type): mime_type, encoding = mimetypes.guess_type("dummy.%s" % content_type) if not mime_type: mime_type = "application/octet-stream" return NonWikiPage(key, enclosure, mime_type) page_part = EnclosurePagePart(key, self.resource_manager, request, main_resource, self.config, enclosure, self.formatter_manager, base) if main_resource: # if there's a parent resource, we're done return page_part if page_part.exists(): return HTMLPage(key, page_part, self.resource_manager, request, main_resource, self.config) else: return HTMLPage404(key, self.resource_manager, request, main_resource, self.config) # @@@ superclass should do this def post(self, resource_id, request, main_resource): return None class EnclosurePagePart(WikiPagePart): def __init__(self, resource_id, resource_manager, request, main_resource, config, file_object, formatter_manager, base_resource): WikiPagePart.__init__(self, resource_id, resource_manager, request, main_resource, config, file_object, formatter_manager) self.base_resource = base_resource def get_html(self): base_link = self.config.home_href + self.base_resource # @@@ templatize return """

    Return to parent.

    """ % base_link + WikiPagePart.get_html(self)leonardo-0.7b1/lib/leonardo/providers/login_logout/0000755000076500007650000000000010344513404023617 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/login_logout/__init__.py0000644000076500007650000000145310235315303025730 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Login/logout plugin. """leonardo-0.7b1/lib/leonardo/providers/login_logout/login_logout.config0000644000076500007650000000041110242522031027474 0ustar jtauberjtauber00000000000000# Configuration file for login/logout provider [login_logout] login_path : %(path_prefix)slogin logout_path : %(path_prefix)slogout login_href : %(cgi_root)s%(login_path)s logout_href : %(cgi_root)s%(logout_path)s log_return_field : return leonardo-0.7b1/lib/leonardo/providers/login_logout/login_logout_provider.py0000755000076500007650000001443510343023112030605 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "LoginLogoutProvider" __contributions__ = {"menu": "menu_part"} import urllib from leonardo.auth import OK from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage import login_logout_templates import leonardo.config def menu_part(request, resource, main_resource, config): if request.logged_in(): user_id = request.get_user() logout_href = config.get("login_logout", "logout_href") + "?" + config.get("login_logout", "log_return_field") + "=" + urllib.quote(request.relative_path) menu_part = login_logout_templates.logged_in_menu % locals() else: login_href = config.get("login_logout", "login_href") + "?" + config.get("login_logout", "log_return_field") + "=" + urllib.quote(request.relative_path) menu_part = login_logout_templates.logged_out_menu % locals() return menu_part class LoginLogoutProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.login_path = config.get("login_logout", "login_path") self.logout_path = config.get("login_logout", "logout_path") def get(self, resource_id, request, main_resource): return self.handle(resource_id, request, main_resource) def post(self, resource_id, request, main_resource): return self.handle(resource_id, request, main_resource) def handle(self, resource_id, request, main_resource): """ handles both GET and POST. """ if resource_id == self.login_path: page_part = LoginPagePart(resource_id, request, self.config) elif resource_id == self.logout_path: page_part = LogoutPagePart(resource_id, request, self.config) else: return None html_page = HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) html_page.expiry = 0 # @@@ put on HTMLPage itself if hasattr(page_part, "cookie"): html_page.cookie = page_part.cookie return html_page class LoginPagePart(PagePart): def __init__(self, resource_id, request, config): PagePart.__init__(self, resource_id) # form fields email_name = "email" password_name = "passwd" return_name = config.get("login_logout", "log_return_field") session_status = request.check_session() action = config.get("login_logout", "login_href") return_path = request.fields.getvalue(return_name, "") if request.method == "GET": status_code = session_status[0] if session_status[0] == OK: user_id = session_status[1] content = login_logout_templates.already_logged_in % locals() else: content = login_logout_templates.login_form % locals() else: # POST # log out if someone else session_status = request.check_session() if session_status[0] == OK: request.delete_session(session_status[2]) email = request.fields.getvalue(email_name, "") passwd = request.fields.getvalue(password_name, "") return_path = request.fields.getvalue(return_name, "") return_href = config.get("DEFAULT", "cgi_root") + return_path status_code = session_status[0] if request.correct_password(email, passwd): # @@@ should consider putting cookie creation code on # response object import Cookie ip_address = request.remote_address self.cookie = Cookie.SimpleCookie() self.cookie["session_id"] = request.create_session(email, ip_address) self.cookie["session_id"]["expires"] = 86400 # @@@ parameterize out self.cookie["session_id"]["path"] = "/" # @@@ should be home_href # bit of a hack, inserting the cookie after the fact request.cookie = self.cookie content = login_logout_templates.successful_login % locals() else: content = login_logout_templates.incorrect_password % locals() self.title = login_logout_templates.login_title self.html = content class LogoutPagePart(PagePart): def __init__(self, resource_id, request, config): PagePart.__init__(self, resource_id) return_name = config.get("login_logout", "log_return_field") action = config.get("login_logout", "logout_href") return_path = request.fields.getvalue(return_name, "") return_href = config.get("DEFAULT", "home_href") + return_path if request.method == "GET": content = login_logout_templates.confirm_logout % locals() else: # POST session_status = request.check_session() if session_status[0] == OK: request.delete_session(session_status[2]) user_id = session_status[1] content = login_logout_templates.successful_logout % locals() else: status_code = session_status[0] content = login_logout_templates.not_logged_in % locals() self.title = login_logout_templates.logout_title self.html = content leonardo-0.7b1/lib/leonardo/providers/login_logout/login_logout_templates.py0000644000076500007650000000450710265653326030770 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # login_title = "Login" logout_title = "Logout" already_logged_in = """

    You are already logged in as %(user_id)s

    """ login_form = """
    Email Address
    Password
     

    %(status_code)s

    """ successful_login = """

    Successful login, %(email)s from %(ip_address)s

    Return to %(return_path)s.

    """ incorrect_password = """

    Incorrect Email/Password.

    """ + login_form confirm_logout = """

    To confirm logout, press the button below.

    """ successful_logout = """

    You are now logged out, %(user_id)s.

    Return to %(return_path)s.

    """ not_logged_in = """

    You are not currently logged in.

    %(status_code)s

    """ logged_in_menu = """

    Logged in as: %(user_id)s

    Logout

    """ logged_out_menu = """

    Login

    """ leonardo-0.7b1/lib/leonardo/providers/manager.py0000755000076500007650000000735510343023112023106 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # provider_list = [ "leonardo.providers.enclosure", "leonardo.providers.login_logout", "leonardo.providers.category", "leonardo.providers.trackback", "leonardo.providers.comment", "leonardo.providers.editform", "leonardo.providers.put", "leonardo.providers.delete", "leonardo.providers.blog", "leonardo.providers.atom03", "leonardo.providers.atom10", "leonardo.providers.draft", "leonardo.providers.blogcalendar", "leonardo.providers.upload", "leonardo.providers.menu", "leonardo.providers.css", "leonardo.providers.propertylist", "leonardo.providers.page", ] from leonardo import filesystem class ProviderManager: def __init__(self, config): # initialise config for package_name in provider_list: config.add(package_name) config.read_main() self.providers = [] self.contributors = {} lfs_root = config.get("DEFAULT", "lfs_root") lfs = filesystem.LeonardoFileSystem(lfs_root) # register resource providers for provider_package_name in provider_list: provider_module_name = provider_package_name + "." + provider_package_name.split('.')[-1] + "_provider" provider_module = get_module(provider_module_name) if provider_module.__provider__: self.providers.append(getattr(provider_module, provider_module.__provider__)(self, lfs, config)) for key, contribution_function in provider_module.__contributions__.items(): self.contributors.setdefault(key, []).append(getattr(provider_module, contribution_function)) def get_contributors(self, key): return self.contributors.get(key, []) def get(self, resource_id, request, main_resource): """ Attempts to retrieve the resource with the given resource_id. request is also passed in to provide authentication information and HTTP parameters. @@@ This could be improved. main_resource is None if the requested resource is what the client directly requested. Otherwise it is the actual resource object requested by the client (and the resource in resource_id is just a component being retrieved to contribute to the the main_resource). """ for provider in self.providers: resource = provider.get(resource_id, request, main_resource) if resource: return resource return None def post(self, resource_id, request, main_resource): for provider in self.providers: resource = provider.post(resource_id, request, main_resource) if resource: return resource return None def get_module(name): module = __import__(name) components = name.split('.') for comp in components[1:]: module = getattr(module, comp) return module leonardo-0.7b1/lib/leonardo/providers/menu/0000755000076500007650000000000010344513406022064 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/menu/__init__.py0000644000076500007650000000145410235317737024211 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Menu provider plugin. """leonardo-0.7b1/lib/leonardo/providers/menu/menu.config0000644000076500007650000000016110241506056024214 0ustar jtauberjtauber00000000000000# Configuration file for menu provider [menu] menu_key : __MENU__ main_menu_key : %(path_prefix)s/__menu__ leonardo-0.7b1/lib/leonardo/providers/menu/menu_provider.py0000644000076500007650000000536610343023112025313 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "MenuProvider" __contributions__ = {} from leonardo.web_resource import PagePart import leonardo.providers.page.page_templates as page_templates # @@@ should be local class MenuProvider: def __init__(self, resource_manager, file_system, config): self.resource_manager = resource_manager self.file_system = file_system self.config = config self.menu_key = config.get("menu", "menu_key") def get(self, resource_id, request, main_resource): if resource_id == self.menu_key: return MenuPagePart(self.resource_manager, request, main_resource, self.config) else: return None def post(self, resource_id, request, main_resource): return None class MenuPagePart(PagePart): """ The page part representing the full menu, including the content provided by contributors to "menu". This resource is never requested directly, only as part of a page. The main (or inner) menu, which forms part of the content of this resource, is a resource in its own right with id config.main_menu_key. The main menu can be requested directly and edited like a normal page part resource. """ def __init__(self, resource_manager, request, main_resource, config): PagePart.__init__(self, config.get("menu", "menu_key")) self.resource_manager = resource_manager self.request = request self.main_resource = main_resource self.config = config self.main_menu = resource_manager.get(config.get("menu", "main_menu_key"), request, self) def get_html(self): html = self.main_menu.get_html() html += page_templates.menu_separator % locals() for provider in self.resource_manager.get_contributors("menu"): html += provider(self.request, self.main_resource, None, self.config) return html leonardo-0.7b1/lib/leonardo/providers/page/0000755000076500007650000000000010344513406022034 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/page/__init__.py0000644000076500007650000000144310235315303024142 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Page plugin. """leonardo-0.7b1/lib/leonardo/providers/page/page.config0000644000076500007650000000032210307017225024131 0ustar jtauberjtauber00000000000000# Configuration file for page provider # Note that site_title, site_sub_title and copyright_holder always come from # main configuration file. [page] home_key : __home__ page_template_key : __page_template__ leonardo-0.7b1/lib/leonardo/providers/page/page_provider.py0000755000076500007650000002502410343023112025227 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "PageProvider" __contributions__ = {} import re import mimetypes from leonardo.formatters.manager import FormatterManager from leonardo.web_resource import PagePart, TopLevelResource, FileObjectResource import page_templates class PageProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.home_key = config.get("page", "home_key") self.formatter_manager = FormatterManager(config) def get(self, resource_id, request, main_resource): page_part = self.get_page(resource_id, request, main_resource) if main_resource: # if there's a parent resource, we're done return page_part if isinstance(page_part, NonWikiPage): # if it's a non wiki page, we're done return page_part key = page_part.get_id() if page_part.exists(): return HTMLPage(key, page_part, self.resource_manager, request, main_resource, self.config) else: # new page if request.logged_in(): return PageDoesNotExist(key, self.resource_manager, request, main_resource, self.config) else: return HTMLPage404(key, self.resource_manager, request, main_resource, self.config) def post(self, resource_id, request, main_resource): page_part = self.get_page(resource_id, request, main_resource) for provider in self.resource_manager.get_contributors("post_processing"): result = provider(request, page_part, None, self.config) if result: return result # @@@ what can we do about duplication here if isinstance(page_part, NonWikiPage): # if it's a non wiki page, we're done return page_part key = page_part.get_id() if page_part.exists(): return HTMLPage(key, page_part, self.resource_manager, request, main_resource, self.config) else: # new page if request.logged_in(): return PageDoesNotExist(key, self.resource_manager, request, main_resource, self.config) else: return HTMLPage404(key, self.resource_manager, request, main_resource, self.config) def get_page(self, resource_id, request, main_resource): if resource_id == "": key = self.home_key else: key = resource_id file_object = self.lfs.get(key) content_type = file_object.get_content_type() if file_object.exists() and not self.formatter_manager.known(content_type): mime_type, encoding = mimetypes.guess_type("dummy.%s" % content_type) if not mime_type: mime_type = "application/octet-stream" return NonWikiPage(key, file_object, mime_type) return WikiPagePart(key, self.resource_manager, request, main_resource, self.config, file_object, self.formatter_manager) class NonWikiPage(FileObjectResource, TopLevelResource): def __init__(self, resource_id, file_object, mime_type): TopLevelResource.__init__(self, resource_id, mime_type) FileObjectResource.__init__(self, file_object) def is_read_only(self): return False class HTMLPage(TopLevelResource): def __init__(self, resource_id, page_part, resource_manager, request, main_resource, config): TopLevelResource.__init__(self, resource_id, "text/html; charset=utf-8") self.resource_manager = resource_manager self.request = request self.main_resource = main_resource self.config = config self.home_href = config.get("DEFAULT", "home_href") self.site_title = config.get("page", "site_title") self.site_sub_title = config.get("page", "site_sub_title") self.rights = config.get("page", "rights") self.home_key = config.get("page", "home_key") self.page_template_key = config.get("page", "page_template_key") self.menu_key = config.get("menu", "menu_key") self.make_html(page_part, request) def exists(self): return True def get_content(self): return self.html def is_read_only(self): return True def make_html(self, page_part, request): page_title = page_part.get_title() if page_title: title = "%s : %s" % (self.site_title, page_title) else: title = self.site_title content = page_part.get_html() lastmod = page_part.get_lastmod() author_email = page_part.get_property("author") if author_email: author_name = request.get_name(author_email) else: author_name = None menu = self.resource_manager.get(self.menu_key, request, page_part).get_html() self.html = self.page_template(title, page_title, menu, content, request, page_part, lastmod, author_name) def page_template(self, title, main_title, menu, content, request, page_part, lastmod=None, author=None): if lastmod: import time lastmod = time.strftime(page_templates.time_format, time.gmtime(lastmod)) lastmod_display = page_templates.lastmod_display % locals() else: lastmod_display = "" if self.site_sub_title: site_sub_title = self.site_sub_title sub_title = page_templates.sub_title % locals() else: sub_title = "" if author: author_display = "by %s" % author else: author_display = "" site_title = self.site_title home_page = self.home_href rights = self.rights head_links = "" for provider in self.resource_manager.get_contributors("head_links"): head_links += provider(self.request, self, self.main_resource, self.config) if main_title: title_html = page_templates.title_template % locals() else: title_html = "" page_template = self.resource_manager.get(self.page_template_key, request, "dummy") return page_template.get_html() % locals() class ForbiddenPage(HTMLPage): def __init__(self, resource_id, resource_manager, request, main_resource, config): HTMLPage.__init__(self, resource_id, None, resource_manager, request, main_resource, config) self.status = "403 Forbidden" def make_html(self, page_part, request): site_title = self.site_title title = page_templates.operation_not_allowed_title % locals() content = page_templates.operation_not_allowed % locals() time = None menu = self.resource_manager.get(self.menu_key, request, self).get_html() self.html = self.page_template(title, title, menu, content, request, page_part, time) class PageDoesNotExist(HTMLPage): def __init__(self, resource_id, resource_manager, request, main_resource, config): HTMLPage.__init__(self, resource_id, None, resource_manager, request, main_resource, config) self.status = "404 Not Found" def make_html(self, page_part, request): site_title = self.site_title title = page_templates.page_does_not_exist_title % locals() content = page_templates.page_does_not_exist % locals() time = None menu = self.resource_manager.get(self.menu_key, request, self).get_html() self.html = self.page_template(title, title, menu, content, request, page_part, time) def is_read_only(self): return False class HTMLPage404(HTMLPage): def __init__(self, resource_id, resource_manager, request, main_resource, config): HTMLPage.__init__(self, resource_id, None, resource_manager, request, main_resource, config) self.status = "404 Not Found" def make_html(self, page_part, request): site_title = self.site_title title = page_templates.title_404 % locals() content = page_templates.content_404 % locals() time = None menu = self.resource_manager.get(self.menu_key, request, self).get_html() self.html = self.page_template(title, title, menu, content, request, page_part, time) def is_read_only(self): return True class WikiPagePart(FileObjectResource, PagePart): # @@@ perhaps things like resource_manager, request, config should be moved to superclass __init__ def __init__(self, resource_id, resource_manager, request, main_resource, config, file_object, formatter_manager): PagePart.__init__(self, resource_id) FileObjectResource.__init__(self, file_object) self.resource_manager = resource_manager self.request = request self.main_resource = main_resource self.config = config self.formatter_manager = formatter_manager self.title = None def get_html(self): content = self.get_content() content_type = self.get_content_type() html = self.formatter_manager.format(content, content_type, self.file_object, self.resource_getter_) for provider in self.resource_manager.get_contributors("page_end"): html += provider(self.request, self, self.main_resource, self.config) return html def resource_getter_(self, resource_id): return self.resource_manager.get(resource_id, self.request, self) def get_title(self): return self.get_property("page_title") def is_read_only(self): return False leonardo-0.7b1/lib/leonardo/providers/page/page_templates.py0000644000076500007650000000253410307020060025370 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # page_does_not_exist = """

    Page does not exist. Create it with Edit Page if you like.

    """ page_does_not_exist_title = "Page Does Not Exist" operation_not_allowed = """

    Operation not allowed.

    """ operation_not_allowed_title = "Operation Not Allowed" title_404 = "File Not Found" content_404 = """

    File not found.

    """ time_format = "%A %d %B, %Y" lastmod_display = """This page last modified %(lastmod)s""" # @@@ these two are probably redundant now sub_title = "%(site_sub_title)s" title_template = """%(main_title)s""" menu_separator = """

     

    """ leonardo-0.7b1/lib/leonardo/providers/propertylist/0000755000076500007650000000000010344513402023674 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/propertylist/__init__.py0000644000076500007650000000146510242044142026010 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Property list provider plugin. """leonardo-0.7b1/lib/leonardo/providers/propertylist/propertylist_provider.py0000755000076500007650000000450610343023112030741 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ This just a little sample provider that lists all the properties across the site, their values and the resources that have them. """ __provider__ = "PropertyListProvider" __contributions__ = {} from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage class PropertyListProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config def get(self, resource_id, request, main_resource): if not resource_id.startswith("propertylist"): return None page_part = PropertyListPagePart(resource_id, self.lfs) return HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) def post(self, resource_id, request, main_resource): pass class PropertyListPagePart(PagePart): def __init__(self, resource_id, lfs): PagePart.__init__(self, resource_id) self.title = "Property List" self.html = "
      " for prop in lfs.list_properties(): self.html += "
    • %(prop)s
        " % locals() for value, resources in lfs.find_property(prop).items(): self.html += "
      • %(value)s
          " % locals() for resource in resources: self.html += "
        • %(resource)s
        • " % locals() self.html += "
      • " self.html += "
    • " self.html += "
    "leonardo-0.7b1/lib/leonardo/providers/put/0000755000076500007650000000000010344513406021730 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/put/__init__.py0000644000076500007650000000145310235315303024037 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Put provider plugin. """leonardo-0.7b1/lib/leonardo/providers/put/put.config0000644000076500007650000000037110241506056023727 0ustar jtauberjtauber00000000000000# Configuration file for put provider [put] put_path : %(path_prefix)sput put_href : %(cgi_root)s%(put_path)s put_resource_field : resource put_content_field : page_content put_content_type_field : content_type leonardo-0.7b1/lib/leonardo/providers/put/put_provider.py0000755000076500007650000001112010343023112025007 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ POSTing to the 'put' resource is intended as an alternative to PUTing a resource in cases like HTML forms where you can't directly PUT. """ __provider__ = "PutProvider" __contributions__ = {} import urllib from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage import put_templates class PutProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.put_path = config.get("put", "put_path") def get(self, resource_id, request, main_resource): if resource_id != self.put_path: return None return ForbiddenPage(resource_id, self.resource_manager, request, main_resource, self.config) def post(self, resource_id, request, main_resource): if resource_id != self.put_path: return None if not request.logged_in(): return ForbiddenPage(resource_id, self.resource_manager, request, main_resource, self.config) page_part = PutPagePart(resource_id, self.resource_manager, request, self.config) return HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) class PutPagePart(PagePart): def __init__(self, resource_id, resource_manager, request, config): PagePart.__init__(self, resource_id) resource_to_put = request.fields.getvalue(config.get("put", "put_resource_field")) if resource_to_put: page = resource_manager.get(resource_to_put, request, self) if page.is_read_only(): title = put_templates.put_read_only_title % locals() content = put_templates.put_read_only_content % locals() else: new_content = request.fields.getvalue(config.get("put", "put_content_field")) new_content_type = request.fields.getvalue(config.get("put", "put_content_type_field")) page_title = request.fields.getvalue(config.get("editform", "edit_title_field"), None) keep_last_modified = request.fields.getvalue(config.get("editform", "keep_last_modified_field"), None) if keep_last_modified: update_last_modified = False else: update_last_modified = True page.set_property("page_title", page_title) page.set_property("author", request.get_user()) for provider in resource_manager.get_contributors("post_processing"): provider(request, page, None, config) if page.exists(): replace = True else: replace = False page.set_content(new_content, new_content_type, update_last_modified) resource_link = config.get("DEFAULT", "cgi_root") + urllib.quote(resource_to_put) resource_label = resource_to_put resource_type = new_content_type if replace: title = put_templates.put_replace_title % locals() content = put_templates.put_replace_content % locals() else: title = put_templates.put_create_title % locals() content = put_templates.put_create_content % locals() else: # no resource_id provided title = put_templates.put_missing_id_title % locals() content = put_templates.put_missing_id_content % locals() self.title = title self.html = content leonardo-0.7b1/lib/leonardo/providers/put/put_templates.py0000644000076500007650000000241510265653326025202 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # put_replace_title = "Resource replaced" put_replace_content = 'Resource %(resource_label)s replaced as %(resource_type)s.' put_create_title = "Resource created" put_create_content = 'Resource %(resource_label)s created as %(resource_type)s.' put_missing_id_title = "Missing Resource ID" put_missing_id_content = "Missing Resource ID." put_read_only_title = "Can't PUT read-only resource" put_read_only_content = "Can't PUT read-only resource."leonardo-0.7b1/lib/leonardo/providers/static/0000755000076500007650000000000010344513403022404 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/static/__init__.py0000644000076500007650000000147710235315303024524 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Package for Leonardo resource providers. """leonardo-0.7b1/lib/leonardo/providers/static/provider.py0000755000076500007650000000560710235314561024626 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # from web_resource import TopLevelResource # ************************************************************** # THIS MODULE IS NOW DEPRECATED # non-wiki files now live under lfs and get served up by page.py # ************************************************************** class StaticFileProvider: def __init__(self, config): self.static_dir = config.static_dir def get(self, resource_id, request, main_resource): if resource_id[:4] not in ["1996", "1997", "1998", "1999", "2000", "2001", "2002", "2003", "2004", "2005"]: return None path = resource_id if path[-1] == "/": path += "index.html" # @@@ should extend this to other filenames path = path.strip("/") ext = path[path.rfind("."):] if ext in [".htm", ".html"]: mime_type = "text/html" elif ext in [".jpg", ".jpeg"]: mime_type = "image/jpeg" elif ext in [".css"]: mime_type = "text/css" else: mime_type = "text/plain" try: f = file(self.static_dir + path, "rb") x = f.read() f.close() return StaticFile(path, x, mime_type) except: return StaticFile404(path) def post(self, resource_id, request, main_resource): return None class StaticFile(TopLevelResource): def __init__(self, resource_id, content, mime_type): TopLevelResource.__init__(self, resource_id, mime_type) self.content = content def exists(self): return True def get_content(self): return self.content def get_lastmod(self): # @@@ return 0 def is_read_only(self): return True # @@@ for now class StaticFile404(StaticFile): def __init__(self, resource_id): StaticFile.__init__(self, resource_id, "File Not Found.", "text/plain") self.status = "404 Not Found" def exists(self): return False leonardo-0.7b1/lib/leonardo/providers/trackback/0000755000076500007650000000000010344513403023042 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/trackback/__init__.py0000644000076500007650000000146110235315303025153 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Trackback provider plugin. """leonardo-0.7b1/lib/leonardo/providers/trackback/trackback_provider.py0000644000076500007650000001450210343023112027245 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "TrackbackProvider" __contributions__ = {"edit_form": "trackbacks_allowed_edit_form", "draft_hidden": "trackbacks_allowed_draft_hidden", "page_end" : "trackback_page_end", "blog_entry_footer" : "trackback_blog_entry_footer", "post_processing": "trackbacks_post_processing"} import urllib from leonardo.web_resource import TopLevelResource, PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage import trackback_templates def trackbacks_allowed_edit_form(request, resource, main_resource, config): # @@@ templatize if resource: allow_trackbacks = resource.get_property("allow_trackbacks") else: allow_trackbacks = "" s = """ Allow Trackbacks?
    """ return s def trackbacks_allowed_draft_hidden(request, resource, main_resource, config): # @@@ templatize if resource: allow_trackbacks = resource.get_property("allow_trackbacks") else: allow_trackbacks = "" s = """ """ % locals() return s # @@@ templatize def trackback_blog_entry_footer(request, resource, main_resource, config): if resource.get_property("allow_trackbacks", "NO") != "NO": trackback_count = len(resource.enclosures("trackback")) if trackback_count == 1: s = " : 1 trackback" else: s = " : %s trackbacks" % trackback_count else: s = "" return s # @@@ templatize def trackback_page_end(request, resource, main_resource, config): if main_resource == None and resource.get_property("allow_trackbacks", "NO") != "NO": enclosures = resource.enclosures("trackback") html = """

    Trackbacks (%s)

    """ % len(enclosures) for trackback in enclosures: url = trackback.get_property("url") title = trackback.get_property("title") or url blog_name = trackback.get_property("blog_name") or "unknown" content = trackback.get_content() # @@@ escape characters from content lastmod = trackback.get_lastmod() if lastmod: import time lastmod_display = time.strftime("%A %d %B, %Y", time.gmtime(lastmod)) else: lastmod_display = "" citation = """%(title)s""" % locals() html += """

    %(citation)s from %(blog_name)s on %(lastmod_display)s:

    %(content)s
    """ % locals() else: html = "" return html def trackbacks_post_processing(request, resource, main_resource, config): allow_trackbacks = request.fields.getvalue("allow_trackbacks") if allow_trackbacks and request.logged_in(): resource.set_property("allow_trackbacks", allow_trackbacks) if resource.get_property("allow_trackback", "NO") != "YES": return None url = request.fields.getvalue("url", None) if url: return self.add_trackback(resource, request, main_resource) def add_trackback(self, resource, request, main_resource): title = request.fields.getvalue("title", None) excerpt = request.fields.getvalue("excerpt", None) url = request.fields.getvalue("url", None) blog_name = request.fields.getvalue("blog_name", None) inner_resource = resource # @@@ should this be wrapped by a trackback object existing_trackbacks = inner_resource.enclosures("trackback") next_index = 0 for trackback in existing_trackbacks: if int(trackback.index) > next_index: next_index = int(trackbaack.index) next_index += 1 new_trackback = inner_resource.enclosure("trackback", next_index) new_trackback.set_content(excerpt, "txt") if title: new_trackback.set_property("title", title) if url: new_trackback.set_property("url", url) if blog_name: new_trackback.set_property("blog_name", blog_name) return TrackbackResultResource(resource.get_id()) class TrackbackProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config def get(self, resource_id, request, main_resource): return None def post(self, resource_id, request, main_resource): pass class TrackbackResultResource(TopLevelResource): def __init__(self, resource_id): TopLevelResource.__init__(self, resource_id, "text/xml") def get_content(self): return """ 0 """leonardo-0.7b1/lib/leonardo/providers/trackback/trackback_templates.py0000644000076500007650000000375110265653326027440 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # toggle_trackbacks_confirm_title = "%(site_title)s : toggle trackbacks on %(resource_to_toggle)s?" toggle_trackbacks_confirm_form = """

    Toggle Trackbacks on %(resource_to_toggle)s?

    Cancel """ #toggle_trackbacks_menu = """ #

    toggle_trackbacks Page

    #""" toggle_trackbacks_missing_id_title = "Missing Resource ID" toggle_trackbacks_missing_id_content = "Missing Resource ID." toggle_trackbacks_page_not_exist_title = "%(site_title)s : %(resource_to_toggle)s does not exist" toggle_trackbacks_page_not_exist_content = "%(resource_to_toggle)s does not exist." toggle_trackbacks_read_only_title = "Can't toggle trackbacks on a read-only resource" toggle_trackbacks_read_only_content = "Can't toggle trackbacks on a read-only resource." toggle_trackbacks_done_title = "%(site_title)s: %(resource_to_toggle)s trackbacks toggled" toggle_trackbacks_done_content = """%(resource_to_toggle)s trackbacks toggled to %(new_value)s."""leonardo-0.7b1/lib/leonardo/providers/upload/0000755000076500007650000000000010344513402022400 5ustar jtauberjtauber00000000000000leonardo-0.7b1/lib/leonardo/providers/upload/__init__.py0000644000076500007650000000145610235315303024516 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ Upload provider plugin. """leonardo-0.7b1/lib/leonardo/providers/upload/upload.config0000644000076500007650000000020110241506056025047 0ustar jtauberjtauber00000000000000# Configuration file for upload provider [upload] upload_path : %(path_prefix)supload upload_href : %(cgi_root)s%(upload_path)s leonardo-0.7b1/lib/leonardo/providers/upload/upload_provider.py0000755000076500007650000000544310343023112026152 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # __provider__ = "UploadFormProvider" __contributions__ = {"menu": "upload_menu_part"} import urllib from leonardo.web_resource import PagePart from leonardo.providers.page.page_provider import HTMLPage, ForbiddenPage import upload_templates def upload_menu_part(request, resource, main_resource, config): if request.logged_in(): upload_href = config.get("upload", "upload_href") menu_part = upload_templates.upload_menu % locals() else: menu_part = "" return menu_part class UploadFormProvider: def __init__(self, resource_manager, lfs, config): self.resource_manager = resource_manager self.lfs = lfs self.config = config self.upload_path = config.get("upload", "upload_path") def get(self, resource_id, request, main_resource): if resource_id != self.upload_path: return None if not request.logged_in(): return ForbiddenPage(resource_id, self.resource_manager, request, self.config) page_part = UploadPagePart(resource_id, self.resource_manager, request, self.config) html_page = HTMLPage(resource_id, page_part, self.resource_manager, request, main_resource, self.config) return html_page def post(self, resource_id, request, main_resource): return None class UploadPagePart(PagePart): def __init__(self, resource_id, resource_manager, request, config): PagePart.__init__(self, resource_id) action = config.get("put", "put_href") file_content_name = config.get("put", "put_content_field") file_content_type_name = config.get("put", "put_content_type_field") path_name = config.get("put", "put_resource_field") site_title = config.get("page", "site_title") self.title = upload_templates.upload_title % locals() content = upload_templates.upload_form % locals() self.html = content leonardo-0.7b1/lib/leonardo/providers/upload/upload_templates.py0000644000076500007650000000264110265653326026333 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # upload_title = "Upload File" upload_form = """
    Path
    File
    Content Type
     
    """ upload_menu = """

    Upload File

    """ leonardo-0.7b1/lib/leonardo/request_response.py0000644000076500007650000001104310343017712023060 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ This module defines request and response objects for handling communication from and to the client (e.g. web browser). """ import os import cgi import time import auth from auth import OK from web_resource import TopLevelResource class Request: def __init__(self, config): self.env = os.environ self.fields = cgi.FieldStorage() self.path_info = self.env.get("PATH_INFO", "/") self.relative_path = self.path_info.strip("/") self.method = self.env.get("REQUEST_METHOD", "") self.query = self.env.get("QUERY_STRING", "") self.remote_address = self.env.get("REMOTE_ADDR", "") self.cookie = self.env.get("HTTP_COOKIE", None) self.if_modified_since = self.env.get("HTTP_IF_MODIFIED_SINCE", None) self.if_none_match = self.env.get("HTTP_IF_NONE_MATCH", None) self.session_mgr = auth.SessionManager(self, config.get("DEFAULT", "data_dir")) self.user_mgr = auth.UserManager(self, config.get("DEFAULT", "data_dir")) def logged_in(self): """Is the user logged in?""" return (self.session_mgr.check_session()[0] == OK) def get_user(self): """Get the current user""" return self.session_mgr.check_session()[1] def get_name(self, user_id): """Get the name of the user with the given id""" return self.user_mgr.get_name(user_id) def correct_password(self, user_id, passwd): """Is the given user id, password combination correct?""" return self.user_mgr.correct_password(user_id, passwd) def check_session(self): return self.session_mgr.check_session() def create_session(self, uid, ip_address): return self.session_mgr.create_session(uid, ip_address) def delete_session(self, sid): return self.session_mgr.delete_session(sid) def not_modified(self, resource): if hasattr(resource, "lastmod"): if self.if_modified_since: lastmod = time.strptime(self.if_modified_since, "%a, %d %b %Y %H:%M:%S %Z") if time.mktime(resource.lastmod) <= time.mktime(lastmod): return True return False class Response: def __init__(self, resource=None, status=None, header_only=False): self.status = status self.head_only = header_only self.cookie = None self.content = None self.content_type = None self.lastmod = None self.headers = {} if isinstance(resource, TopLevelResource): self.content_type = resource.get_mime_type() self.content = resource.get_content() if hasattr(resource, "status"): self.status = resource.status if hasattr(resource, "cookie"): self.cookie = resource.cookie if hasattr(resource, "expiry"): self.headers["Expires"] = 0 if hasattr(resource, "lastmod"): lastmod = time.strftime("%a, %d %b %Y %H:%M:%S GMT", resource.lastmod) self.headers["Last-Modified"] = lastmod elif self.status is None: self.status = "403 Forbidden" def send(self, out): if self.status: out.write("Status: %s\n" % self.status) for name, value in self.headers.items(): out.write("%s: %s\n" % (name, value)) if self.cookie: out.write("%s\n" % self.cookie) if self.content_type: out.write("Content-type: %s\n" % self.content_type) if self.content: out.write("\n") if not self.head_only: out.write(self.content) leonardo-0.7b1/lib/leonardo/version.py0000644000076500007650000000016310343017712021140 0ustar jtauberjtauber00000000000000 _rev = int("$Rev: 402 $".split()[1]) VERSION = (0, 7, 0, _rev) PROGRAM_STRING = "Leonardo %s.%s.%s.%s" % VERSIONleonardo-0.7b1/lib/leonardo/web_resource.py0000755000076500007650000000777510343017712022162 0ustar jtauberjtauber00000000000000# # Copyright (C) 2003-2005 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # class Resource: """ A resource is content object that can be returned to the client / browser in one or more forms either as the entire response or as a part of the response. Typically sublcassed, a resource object wraps a data source (such as a leonardo "file") and may provide format translation (such as from a wiki format to HTML). Examples of resources include wiki pages, blog entries, menus, etc. """ def __init__(self, resource_id): self.resource_id = resource_id def get_id(self): return self.resource_id def get_title(self): return self.resource_id def __repr__(self): return "<%s '%s'>" % (self.__class__, self.resource_id) class PagePart(Resource): """ A resource that is part of a page. The key method that PagePart adds is get_html(). If the page part is editable, then get_content() will return the underlying content that can be edited. """ def __init__(self, resource_id, title=None, html=None): Resource.__init__(self, resource_id) self.title = title self.html = html def get_title(self): return self.title def get_html(self): return self.html def get_lastmod(self): return 0 def get_property(self, name): return None # by default page parts don't have properties def exists(self): return True def is_read_only(self): return True # default class TopLevelResource(Resource): """ A resource that is returned directly to the client / browser rather than as part of an enclosing resource. """ def __init__(self, resource_id, mime_type): Resource.__init__(self, resource_id) self.mime_type = mime_type def get_mime_type(self): return self.mime_type class FileObjectResource: """ A mix-in for resources that wrap an LFS file object. """ def __init__(self, file_object): self.file_object = file_object def exists(self): return self.file_object.exists() def get_lastmod(self): return self.file_object.get_lastmod() def get_content(self): return self.file_object.get_content() def get_content_type(self): return self.file_object.get_content_type() def set_content(self, content, content_type, update_lastmod=True): self.file_object.set_content(content, content_type, update_lastmod) def delete(self): self.file_object.delete() def is_read_only(self): return True # default def get_creation_time(self): return self.file_object.get_creation_time() def get_property(self, name, default=None): return self.file_object.get_property(name, default) def set_property(self, name, value): return self.file_object.set_property(name, value) def enclosures(self, enctype): return self.file_object.enclosures(enctype) def enclosure(self, enctype, index): return self.file_object.enclosure(enctype, index) leonardo-0.7b1/README0000644000076500007650000000105410344474370015420 0ustar jtauberjtauber00000000000000Leonardo 0.7b1 http://jtauber.com/leonardo Leonardo is a personal wiki and blog server written in Python. QUICKSTART * Run python test_server.py * To create a user, cd into bin and run create_user.py (bin must be parallel to data otherwise edit USER_DB) * Further customize by editing cgi-bin/leonardo.config * If you install lib somewhere other than parallel to cgi-bin, you'll want to change leonardo_lib in cgi-bin/leonardo-cgi.py also see APACHE_HOWTO and EXTRAS. Feel free to ask the Leonardo mailing list for any help.leonardo-0.7b1/test_server.py0000755000076500007650000000506710343023112017451 0ustar jtauberjtauber00000000000000#!/usr/bin/env python # # Copyright (C) 2003-2004 James Tauber # # This program 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 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # # Simulates Apache with the following directive: # # ScriptAlias / /cgi-bin/leonardo-cgi.py/ # # Useful for getting going before you deploy on an actual webserver import CGIHTTPServer from BaseHTTPServer import HTTPServer import sys sys.path.append("cgi-bin") def usage(): print """ -h, --help : this help message -n, --no-browser : don't start browser -p , --port= : port to run on if not 8000 """ import getopt try: opts, args = getopt.getopt(sys.argv[1:], "hnp:", ["help", "no-browser", "port="]) except getopt.GetoptError: usage() sys.exit(2) if args: usage() sys.exit(2) start_browser = True port = 8000 for o, a in opts: if o in ("-n", "--no-browser"): start_browser = False if o in ("-h", "--help"): usage() sys.exit() if o in ("-p", "--port"): port = int(a) import os class Handler(CGIHTTPServer.CGIHTTPRequestHandler): def do_POST(self): # for some reason the query string from the last request hangs # around without this. os.environ['QUERY_STRING'] = "" self.cgi_info = "/cgi-bin", "leonardo-cgi.py/" + self.path self.run_cgi() def do_GET(self): # for some reason the query string from the last request hangs # around without this. os.environ['QUERY_STRING'] = "" ims = self.headers.getheader('If-Modified-Since') if ims: os.environ['HTTP_IF_MODIFIED_SINCE'] = ims self.cgi_info = "/cgi-bin", "leonardo-cgi.py/" + self.path self.run_cgi() server_address = ("", port) httpd = HTTPServer(server_address, Handler) if start_browser: import webbrowser webbrowser.open("http://localhost:%s/" % port, 1) print "Serving HTTP on port", port, "..." httpd.serve_forever() leonardo-0.7b1/UPGRADE0000644000076500007650000000645010344507703015554 0ustar jtauberjtauber00000000000000Upgrading from 0.6.x to 0.7.x ----------------------------- 1. back up your entire existing Leonardo installation, *especially* your data. 2. note that leonardo.py has changed to leonardo-cgi.py so you will need to change anything (like a ScriptAlias in Apache) that say "leonardo.py" to "leonardo-cgi.py". 3. either copy over the new leonardo-cgi.py and make any necessary changes to the paths in that file, or rename your existing file to leonardo-cgi.py (if you do the latter, you'll also need to change the import config and import core to 'from leonardo import core' and 'from leonardo import config') 3. edit leonardo.config and change 'copyright_holder' to 'rights' 4. if you plan to use Atom 1.0 also add a [atom10] section with a blog_author property. 5. if you have made changes to any templates, make sure they are backed up. 6. copy over the new lib/ directory, replacing your old one. 7. if you had changed templates other than the main page template, you can just go and put them back in place (but notice the name change from template.py to PROVIDER_template.py 8. double check that you backed up your data directory! 9. copy __page_template__.ldv from the 0.7.x LFS to your existing LFS 10. if you had made changes to your main page template, edit content.xhtml in this new __page_template__.ldv bundle 11. you really should have backed up your data directory by now. 12. cd to bin and run convert-db-to-pickle.py (give the path to the LFS as an argument) This will take a while if you have a lot of files in the LFS. 13. run regen_index.py (give the path to the LFS as an argument) This will take a while if you have a lot of files in the LFS. 14. you will need to create your users again with bin/create_user.py 15. DONE! Upgrading from 0.5.x to 0.6.x ----------------------------- 1. back up your entire existing Leonardo installation, *especially* your data. 2. update leonardo.py as per README QUICKSTART 3. update leonardo.config as per README QUICKSTART but with any additional configuration changes you have made. Because of the changes to the format, you will have to do this manually. 4. if you have made changes to the templates, you will need to upgrade those manually. 5. assuming no changes have been made to templates, you can just copy over new lib/ directory. 6. double check that you backed up your data directory! 7. if you previously used the static provider, you will need to run bin/ convert_static.py to convert the static files over to LFS. If you have a directory '/static/1996' that you'd like to convert to '/lfs/1996', run convert_static.py /static/1996 /lfs/1996 8. you really should have backed up your data directory by now. 9. convert your LFS to the new database-based properties using bin/convert_lfs.py 10. add something like the following to your css: div.comment, div.trackback { border-top: 1px solid #999; margin: 10px 5px 5px 5px; padding: 3px; } p.comment_citation, p.trackback_citation { font-size : 0.8em; font-style: italic; } .footnotes { font-size : smaller; border-top : 1px solid #DDD; padding-top : 10px; } 11. DONE!