leonardo-0.4.1/ 0040755 0000765 0000765 00000000000 10167570055 013215 5 ustar jtauber jtauber leonardo-0.4.1/bin/ 0040755 0000765 0000765 00000000000 10167570055 013765 5 ustar jtauber jtauber leonardo-0.4.1/bin/create_user.py 0100755 0000765 0000765 00000002710 10167570124 016635 0 ustar jtauber jtauber #!/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
#
"""
This script is used for creating or modifying users in the user database.
"""
USER_DB = "../data/user_db"
def create_user(name, email, password):
"""
Creates (or overwrites) a new user entry in the user database.
"""
import anydbm, sha
user_db = anydbm.open(USER_DB, "c")
user_db[email] = "%s|%s|%s" % (email, name, sha.sha(password).hexdigest())
if __name__ == "__main__":
name = raw_input("User's Full Name: ")
email = raw_input("User's Email Address (used as user id): ")
password = raw_input("User's Password (will be echoed): ")
print "Creating account...",
create_user(name, email, password)
print "done."
leonardo-0.4.1/bin/leonconv-04.py 0100644 0000765 0000765 00000002161 10167570124 016375 0 ustar jtauber jtauber #! /usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Contributed by Min Sik Kim
#
import codecs
import os
import stat
import sys
def usage():
print 'Usage:', sys.argv[0], ''
sys.exit(0)
if len(sys.argv) != 3:
usage()
src_root, dst_root = sys.argv[1], sys.argv[2]
if not os.path.exists(src_root):
sys.stderr.write('Error: cannot find %s\n' % srcdir)
sys.exit(1)
for root, dirs, files in os.walk(src_root):
for name in files:
src = os.path.join(root, name)
dst = os.path.join(root.replace(src_root, dst_root),
name.replace('.txt', '.ldv'),
'__content__.wiki04')
dstdir = os.path.dirname(dst)
if not os.path.exists(dstdir):
os.makedirs(dstdir)
srcfile = codecs.getreader('UTF-8')(file(src, 'r'))
dstfile = codecs.getwriter('UTF-8')(file(dst, 'w'))
for line in srcfile.readlines():
dstfile.write(line)
dstfile.close()
srcfile.close()
s = os.stat(src)
times = (s[stat.ST_ATIME], s[stat.ST_MTIME])
os.utime(dst, times)
leonardo-0.4.1/cgi-bin/ 0040755 0000765 0000765 00000000000 10167570055 014525 5 ustar jtauber jtauber leonardo-0.4.1/cgi-bin/leonardo.py 0100755 0000765 0000765 00000002046 10167570124 016701 0 ustar jtauber jtauber #!/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
#
## Leonardo main CGI script
import cgitb; cgitb.enable()
import sys
# location of leonardo modules (full path to leonardo lib directory)
sys.path.append("/Users/jtauber/leonardo/lib")
import leonardo_config
import core
core.dispatcher(leonardo_config)
leonardo-0.4.1/cgi-bin/leonardo_config.py 0100644 0000765 0000765 00000002476 10167570124 020232 0 ustar jtauber jtauber # Configuration file for Leonardo
data_dir = "/Users/jtauber/leonardo/data/"
lfs_root = data_dir + "lfs/"
# static specific
static_dir = data_dir + "static/"
# css specific
css_id = "css"
css_href = "/" + css_id
# page specific
site_title = "Leonardo"
site_sub_title = "sample site"
home_href = "/"
home_key = "__home__"
copyright_holder = "James Tauber"
# menu specific
menu_key = "__MENU__"
main_menu_key = "__menu__"
# login_logout specific
login_path = "login"
logout_path = "logout"
login_href = "/" + login_path
logout_href = "/" + logout_path
# editform specific
edit_path = "edit"
edit_href = "/" + edit_path
edit_prefix = "/"
edit_resource_field = "resource"
# put specific
put_path = "put"
put_href = "/" + put_path
put_resource_field = "resource"
put_content_field = "page_content"
# blog specific
blog_prefix = "blog"
blog_title = "Leonardo Sample Blog"
site_url = "http://localhost:8000/"
# atom specific
atom_path = "atom"
full_atom_path = "atom/full"
blog_author = "James Tauber"
# draft specific
draft_prefix = "draft"
draft_href = "/" + draft_prefix leonardo-0.4.1/CREDITS 0100644 0000765 0000765 00000000146 10167570124 014230 0 ustar jtauber jtauber Principal Developer
James Tauber
Contributors
Bryan Lawrence
Peter Sefton
Min Sik Kim leonardo-0.4.1/data/ 0040755 0000765 0000765 00000000000 10167570055 014126 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/ 0040755 0000765 0000765 00000000000 10167570055 014712 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/__home__.ldv/ 0040755 0000765 0000765 00000000000 10167570055 017222 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/__home__.ldv/__content__.wiki04 0100644 0000765 0000765 00000004274 10167570124 022522 0 ustar jtauber jtauber ==Welcome to Leonardo!==
It is recommended you read this page to get started.
===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, either at the top-level or 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 easily changed in cgi-bin/leonardo-config.py
===Static Files===
Static files may be served up by placing them under the static directory of your leonardo data directory. By default, static files have paths beginning with a year. This is to encourage the best practice of giving permanent date-based URIs to resources that do not change. An example is accessible at [:/2004/sample.txt]
===Customization===
Customisation can be done in a number of places:
* the leonardo_config.py file in cgi-bin
* the menu (see below)
* the css stylesheet (see below)
* individual templates under the lib/templates in the Leonardo directory
===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.4.1/data/lfs/__menu__.ldv/ 0040755 0000765 0000765 00000000000 10167570055 017236 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/__menu__.ldv/__content__.wiki04 0100644 0000765 0000765 00000000462 10167570124 022531 0 ustar jtauber jtauber ===General===
[http://jtauber.com/leonardo Leonardo Home Page]
[Wiki Formatting Guide]
[:/2004/sample.txt sample static file]
===Blog===
[:/blog blog]
[:/blog/2004/ just 2004 blog]
[:/blog/2004/12/ december 2004]
[:/atom/ title-only atom feed]
[:/atom/full/ full-entry atom feed] leonardo-0.4.1/data/lfs/blog/ 0040755 0000765 0000765 00000000000 10167570055 015635 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/blog/2004/ 0040755 0000765 0000765 00000000000 10167570055 016222 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/blog/2004/12/ 0040755 0000765 0000765 00000000000 10167570055 016444 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/blog/2004/12/16/ 0040755 0000765 0000765 00000000000 10167570055 016672 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/blog/2004/12/16/my_first_blog_entry.ldv/ 0040755 0000765 0000765 00000000000 10167570055 023536 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/blog/2004/12/16/my_first_blog_entry.ldv/__content__.wiki04 0100644 0000765 0000765 00000000067 10167570124 027032 0 ustar jtauber jtauber ==My First Blog Entry==
This is my first blog entry. leonardo-0.4.1/data/lfs/css.ldv/ 0040755 0000765 0000765 00000000000 10167570055 016266 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/css.ldv/__content__.css 0100644 0000765 0000765 00000005521 10167570124 021243 0 ustar jtauber jtauber body {
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;
}
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;
}
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;
}
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;
} leonardo-0.4.1/data/lfs/draft/ 0040755 0000765 0000765 00000000000 10167570055 016012 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/draft/my_first_blog_entry.ldv/ 0040755 0000765 0000765 00000000000 10167570055 022656 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/draft/my_first_blog_entry.ldv/__content__.wiki04 0100644 0000765 0000765 00000000102 10167570124 026140 0 ustar jtauber jtauber ==My First Blog Entry==
This is a draft of my first blog entry. leonardo-0.4.1/data/lfs/wiki_formatting_guide.ldv/ 0040755 0000765 0000765 00000000000 10167570055 022050 5 ustar jtauber jtauber leonardo-0.4.1/data/lfs/wiki_formatting_guide.ldv/__content__.wiki04 0100644 0000765 0000765 00000002443 10167570124 025344 0 ustar jtauber jtauber ==Wiki Formatting Guide==
===Headings===
==Second Level Heading==
===Third Level Heading===
====Fourth Level Heading====
===Lists===
asterix at the start of a line give you
* this
===Blocks===
Five spaces at start of line to blockquote
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.4.1/data/static/ 0040755 0000765 0000765 00000000000 10167570055 015415 5 ustar jtauber jtauber leonardo-0.4.1/data/static/2004/ 0040755 0000765 0000765 00000000000 10167570055 016002 5 ustar jtauber jtauber leonardo-0.4.1/data/static/2004/sample.txt 0100644 0000765 0000765 00000000060 10167570124 020012 0 ustar jtauber jtauber This is an example of a statically served file.
leonardo-0.4.1/GPL 0100644 0000765 0000765 00000043131 10167570124 013556 0 ustar jtauber jtauber GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
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.4.1/lib/ 0040755 0000765 0000765 00000000000 10167570055 013763 5 ustar jtauber jtauber leonardo-0.4.1/lib/auth.py 0100755 0000765 0000765 00000011420 10167570124 015271 0 ustar jtauber jtauber #
# 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
#
"""
This module deals with session and user management.
"""
# 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.user_db = None
self.data_dir = data_dir
def get_users(self):
"""Get the user database, loading from disk if necessary"""
if not self.user_db:
import anydbm
self.user_db = anydbm.open(self.data_dir + "user_db", "c")
return self.user_db
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_userinfo(self, user_id):
"""Get info for the given user. Returned as triple:
user_id, name, (hashed) password"""
return self.get_users()[user_id].split("|")
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.4.1/lib/core.py 0100755 0000765 0000765 00000006203 10167570124 015263 0 ustar jtauber jtauber #
# 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
#
import request_response
import filesystem
import providers.manager
import providers.static
import providers.css
import providers.login_logout
import providers.editform
import providers.put
import providers.blog
import providers.atom
import providers.draft
import providers.menu
import providers.page
def dispatcher(config):
request = request_response.Request(config)
lfs = filesystem.LeonardoFileSystem(config.lfs_root)
# RESOURCE PROVIDERS
resource_manager = providers.manager.ProviderManager()
# menu provider
menu_provider = providers.menu.MenuProvider(resource_manager, lfs, config)
# register resource providers
resource_manager.register_provider(providers.static.StaticFileProvider(config))
resource_manager.register_provider(providers.css.CSSProvider(lfs, config))
resource_manager.register_provider(providers.login_logout.LoginLogoutProvider(resource_manager, lfs, config))
resource_manager.register_provider(providers.editform.EditFormProvider(resource_manager, lfs, config))
resource_manager.register_provider(providers.put.PutProvider(resource_manager, lfs, config))
resource_manager.register_provider(providers.blog.BlogProvider(resource_manager, lfs, config))
resource_manager.register_provider(providers.atom.AtomProvider(resource_manager, lfs, config))
resource_manager.register_provider(providers.draft.DraftProvider(resource_manager, lfs, config))
resource_manager.register_provider(menu_provider)
resource_manager.register_provider(providers.page.PageProvider(resource_manager, lfs, config))
menu_provider.register_provider(providers.login_logout.menu_part)
menu_provider.register_provider(providers.editform.edit_menu_part)
menu_provider.register_provider(providers.draft.draft_menu_part)
resource_id = request.relative_path
resource = resource_manager.get(resource_id, request, None)
# @@@ we could just pass the resource into the constructor
response = request_response.Response()
response.set_content_type(resource.get_mime_type())
response.set_content(resource.get_content())
# @@@ bit of a hack
if hasattr(resource, "status"):
response.set_status(resource.status)
if hasattr(resource, "cookie"):
response.set_cookie(resource.cookie)
if hasattr(resource, "expiry"):
response.set_header("Expires", 0)
response.send()
leonardo-0.4.1/lib/filesystem.py 0100644 0000765 0000765 00000010261 10167570124 016513 0 ustar jtauber jtauber #
# 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
#
"""
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.
"""
import os
class LeonardoFileSystem:
def __init__(self, root):
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"
# @@@ at present you are required to specify the content type to
# @@@ determine the extension to use for the content file.
# @@@ it would be nice if this were optional but it's not clear
# @@@ how ambiguities should be handled.
def get(self, key, content_type):
return LeonardoFile(self, key, content_type)
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
"""
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"):
files.append(f[:-4])
else:
directories.append(f)
directories.sort()
directories.reverse()
files.sort()
files.reverse()
return directories, files
class LeonardoFile:
def __init__(self, filesystem, key, content_type):
self.filesystem = filesystem
self.key = key
self.content_type = content_type
self.content = None
# @@@ refactor to filesystem?
def __get_directory(self):
return self.filesystem.root + self.key.strip("/") + ".ldv/"
# @@@ refactor to filesystem?
def __get_content_filename(self):
return self.__get_directory() + "__content__." + self.content_type
def exists(self):
try:
f = file(self.__get_content_filename())
f.close()
return True
except IOError:
return False
# @@@ make private?
def load_content(self):
f = file(self.__get_content_filename())
s = f.read()
f.close()
self.content = s
def get_content(self):
if not self.content:
self.load_content()
return self.content
def get_lastmod(self):
import os
ST_MTIME = 8
return os.stat(self.__get_content_filename())[ST_MTIME]
def set_content(self, content):
path = self.__get_content_filename()
directory = path[:path.rfind("/")]
try:
d = file(directory)
d.close()
except:
import os
try:
os.makedirs(directory)
except:
pass
f = file(path, "w")
f.write(content)
f.close()
leonardo-0.4.1/lib/providers/ 0040755 0000765 0000765 00000000000 10167570055 016000 5 ustar jtauber jtauber leonardo-0.4.1/lib/providers/__init__.py 0100644 0000765 0000765 00000001477 10167570124 020114 0 ustar jtauber jtauber #
# 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.4.1/lib/providers/atom.py 0100755 0000765 0000765 00000010651 10167570124 017312 0 ustar jtauber jtauber #
# 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
#
import time
import templates.atom as template
from blog import Walker
from web_resource import TopLevelResource
class AtomProvider:
def __init__(self, resource_manager, lfs, config):
self.resource_manager = resource_manager
self.lfs = lfs
self.config = config
self.atom_path = config.atom_path
self.full_atom_path = config.full_atom_path
def get(self, resource_id, request, main_resource):
if resource_id == self.atom_path:
return AtomResource(resource_id, self.resource_manager, self.lfs, request, self.config, full=False)
elif resource_id == self.full_atom_path:
return AtomResource(resource_id, self.resource_manager, self.lfs, request, self.config, full=True)
else:
return None
class AtomResource(TopLevelResource):
def __init__(self, resource_id, resource_manager, lfs, request, config, full):
TopLevelResource.__init__(self, resource_id, 'text/xml; charset="utf-8"')
if full:
formatter = FullAtomFormatter(resource_manager, config)
else:
formatter = AtomFormatter(resource_manager, config)
walker = Walker(formatter, config.blog_prefix, lfs)
self.content = walker.all(request, self)
def get_content(self):
return self.content
class AtomFormatter:
def __init__(self, resource_manager, config):
self.resource_manager = resource_manager
self.atom_path = config.atom_path
self.site_url = config.site_url
self.blog_title = config.blog_title
self.blog_prefix = config.blog_prefix
self.blog_author = config.blog_author
self.shown_modified = False
def header(self):
blog_title = self.blog_title
blog_url = self.site_url + self.blog_prefix
blog_author = self.blog_author
return template.atom_header % locals()
def footer(self):
return template.atom_footer % locals()
def no_entries(self):
return template.atom_no_entries % locals()
def entry(self, year, month, day, entry, request, main_resource):
page_key = "%s/%s/%s/%s/%s" % (self.blog_prefix, 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 + page_key
if not self.shown_modified:
self.shown_modified = True
s = template.atom_modified % locals()
else:
s = ""
modified = iso_time
issued = iso_time
s += template.atom_entry % locals()
return s
class FullAtomFormatter(AtomFormatter):
def entry(self, year, month, day, entry, request, main_resource):
page_key = "%s/%s/%s/%s/%s" % (self.blog_prefix, 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 + page_key
if not self.shown_modified:
self.shown_modified = True
s = template.atom_modified % locals()
else:
s = ""
modified = iso_time
issued = iso_time
content = page.get_html()
s += template.atom_full_entry % locals()
return s
leonardo-0.4.1/lib/providers/blog.py 0100755 0000765 0000765 00000014444 10167570124 017301 0 ustar jtauber jtauber #
# 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
#
import templates.blog as template
import wiki
from web_resource import PagePart
from page import HTMLPage, HTMLPage404
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_prefix = config.blog_prefix
self.site_url = config.site_url
def get(self, resource_id, request, main_resource):
if not resource_id.startswith(self.blog_prefix):
return None
blog_path = resource_id[len(self.blog_prefix):]
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, self.config)
if year and year not in YEARS:
return HTMLPage404(resource_id, self.resource_manager, request, self.config)
if month and month not in MONTHS:
return HTMLPage404(resource_id, self.resource_manager, request, self.config)
if day and day not in DAYS:
return HTMLPage404(resource_id, self.resource_manager, request, self.config)
if entry:
return None # we don't need to handle it @@@ although we could to add date/permalink
else:
page_part = BlogPagePart(resource_id, self.resource_manager, request, self.lfs, self.config, year, month, day)
return HTMLPage(resource_id, page_part, self.resource_manager, request, self.config)
class BlogPagePart(PagePart):
def __init__(self, resource_id, resource_manager, request, lfs, config, year, month, day):
PagePart.__init__(self, resource_id)
formatter = HTMLFormatter(resource_manager, config)
walker = Walker(formatter, config.blog_prefix, lfs)
blog_title = config.blog_title
if day:
title = template.blog_title_day % locals()
content = walker.day(year, month, day, request, self)
elif month:
title = template.blog_title_month % locals()
content = walker.month(year, month, request, self)
elif year:
title = template.blog_title_year % locals()
content = walker.year(year, request, self)
else:
title = template.blog_title_all % locals()
content = walker.all(request, self)
self.title = title
self.html = content
class HTMLFormatter:
def __init__(self, resource_manager, config):
self.resource_manager = resource_manager
self.site_url = config.site_url
self.blog_prefix = config.blog_prefix
def header(self):
return ""
def footer(self):
return ""
def no_entries(self):
return template.blog_no_entries % locals()
def entry(self, year, month, day, entry, request, main_resource):
page_key = "%s/%s/%s/%s/%s" % (self.blog_prefix, year, month, day, entry)
page = self.resource_manager.get(page_key, request, main_resource)
body = page.get_html()
date = "%s/%s/%s" % (year, month, day)
html_link = self.site_url + page_key
return template.blog_entry % locals()
class Walker:
def __init__(self, formatter, blog_prefix, lfs):
self.formatter = formatter
self.blog_prefix = blog_prefix
self.lfs = lfs
def all(self, request, main_resource):
return \
self.formatter.header() + \
self.descend(self.blog_prefix, lambda year: self.year(year, request, main_resource)) + \
self.formatter.footer()
def year(self, year, request, main_resource):
return self.descend(self.blog_prefix + "/" + 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_prefix, 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_prefix, 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.4.1/lib/providers/css.py 0100755 0000765 0000765 00000003006 10167570124 017136 0 ustar jtauber jtauber #
# 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, FileObjectResource
class CSSProvider:
def __init__(self, lfs, config):
self.lfs = lfs
self.config = config
self.css_id = config.css_id
def get(self, resource_id, request, main_resource):
if resource_id == self.css_id:
return CSSResource(self.lfs, self.config)
else:
return None
class CSSResource(FileObjectResource, TopLevelResource):
def __init__(self, lfs, config):
css_id = config.css_id
TopLevelResource.__init__(self, css_id, "text/css")
FileObjectResource.__init__(self, lfs.get(css_id, "css"))
def is_read_only(self):
return False # allow editing
leonardo-0.4.1/lib/providers/draft.py 0100755 0000765 0000765 00000013050 10167570124 017446 0 ustar jtauber jtauber #
# 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
#
import re
import time
import templates.draft as template
from providers.page import HTMLPage, ForbiddenPage
from web_resource import PagePart
def draft_menu_part(request, resource, config):
if request.logged_in():
draft_href = config.draft_href
menu_part = template.draft_menu % locals()
else:
menu_part = ""
return menu_part
# form field names
page_title_name = "page_title"
page_content_name = "page_content"
# requires 'blog' and 'put' modules
class DraftProvider:
def __init__(self, resource_manager, lfs, config):
self.resource_manager = resource_manager
self.lfs = lfs
self.config = config
self.draft_prefix = config.draft_prefix
def get(self, resource_id, request, main_resource):
if not resource_id.startswith(self.draft_prefix):
return None
if not request.logged_in():
return ForbiddenPage(resource_id, self.resource_manager, request, 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)
return HTMLPage(resource_id, page_part, self.resource_manager, request, 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, self.config)
class DraftListPagePart(PagePart):
def __init__(self, resource_id, resource_manager, request, lfs, config):
status = ""
# @@@ how can this be avoided?
page_title_name = globals()["page_title_name"]
page_content_name = globals()["page_content_name"]
form_content = template.draft_initial_form_content % locals()
# @@@ posting should really be done as a separate method
if request.method == "POST":
page_title = request.fields.getvalue(page_title_name, "")
page_content = request.fields.getvalue(page_content_name, "")
if page_title:
draft_resource_id = config.draft_prefix + "/" + re.sub(' ', '_', page_title.lower())
page = resource_manager.get(draft_resource_id, request, self)
if page.exists():
status = template.draft_existing_title % locals()
form_content = page_content
else:
page.set_content(page_content)
status = template.draft_success % locals()
else:
status = template.draft_no_title % locals()
form_content = page_content
title = template.draft_title
# get list of drafts
drafts = lfs.get_children(config.draft_prefix)[1]
if drafts:
draft_items = ""
for draft_title in drafts:
draft_link = config.draft_href + "/" + draft_title
draft_items += template.draft_item % locals()
draft_list = template.draft_list % locals()
else:
draft_list = template.empty_draft_list % locals()
action = config.draft_href
content = template.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)
title = page_part.get_title()
content = page_part.get_html()
page_content = page_part.get_content()
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.draft_prefix):].strip("/")
post_action = config.put_href
resource_name = config.put_resource_field
page_content_name = config.put_content_field
resource_id_to_put = draft_path
content = template.draft_post_to_wiki_form % locals() + content
resource_id_to_put = config.blog_prefix + "/%04d/%02d/%02d/" % tuple(time.localtime()[:3]) + draft_path
content = template.draft_post_to_blog_form % locals() + content
self.title = title
self.html = content
def is_read_only(self):
return False
leonardo-0.4.1/lib/providers/editform.py 0100755 0000765 0000765 00000007113 10167570124 020162 0 ustar jtauber jtauber #
# 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
#
import templates.editform as template
from providers.page import HTMLPage, ForbiddenPage
from web_resource import PagePart
def edit_menu_part(request, resource, config):
if request.logged_in() and not resource.is_read_only():
edit_page_href = config.edit_href + "?" + config.edit_resource_field + "=" + resource.get_id()
menu_part = template.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.edit_path
self.resource_name = config.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, 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, self.config)
html_page.expiry = 0
return html_page
class EditFormPagePart(PagePart):
def __init__(self, resource_id, resource_to_edit, resource_manager, request, config):
post_action = config.put_href
page_content_name = config.put_content_field
resource_name = config.put_resource_field
cancel_action = resource_to_edit
if resource_to_edit:
page = resource_manager.get(resource_to_edit, request, self)
if page.is_read_only():
self.title = template.edit_read_only_title % locals()
self.html = template.edit_read_only_title % locals()
else:
time = None
site_title = config.site_title
if page.exists():
page_title = page.get_title()
title = template.page_title % locals()
page_content = page.get_content()
time = page.get_lastmod()
else: # new wiki word
title = template.new_page_title % locals()
page_content = template.new_page_content % locals()
content = template.edit_form % locals()
self.title = title
self.html = content
else:
self.title = template.edit_missing_id_title % locals()
self.html = template.edit_missing_id_content % locals()
leonardo-0.4.1/lib/providers/login_logout.py 0100755 0000765 0000765 00000011501 10167570124 021046 0 ustar jtauber jtauber #
# 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 auth import OK
from providers.page import HTMLPage
import templates.login_logout as template
from web_resource import PagePart
def menu_part(request, resource, config):
if request.logged_in():
user_id = request.get_user()
logout_href = config.logout_href
menu_part = template.logged_in_menu % locals()
else:
login_href = config.login_href
menu_part = template.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.login_path
self.logout_path = config.logout_path
def get(self, resource_id, request, main_resource):
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, 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"
session_status = request.check_session()
action = config.login_href
if request.method == "GET":
status_code = session_status[0]
if session_status[0] == OK:
user_id = session_status[1]
content = template.already_logged_in % locals()
else:
content = template.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, "")
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 = template.successful_login % locals()
else:
content = template.incorrect_password % locals()
self.title = template.login_title
self.html = content
class LogoutPagePart(PagePart):
def __init__(self, resource_id, request, config):
PagePart.__init__(self, resource_id)
action = config.logout_href
if request.method == "GET":
content = template.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 = template.successful_logout % locals()
else:
status_code = session_status[0]
content = template.not_logged_in % locals()
self.title = template.logout_title
self.html = content
leonardo-0.4.1/lib/providers/manager.py 0100755 0000765 0000765 00000003362 10167570124 017765 0 ustar jtauber jtauber #
# 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
#
class ProviderManager:
def __init__(self):
self.providers = []
def register_provider(self, provider):
self.providers.append(provider)
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 leonardo-0.4.1/lib/providers/menu.py 0100644 0000765 0000765 00000005050 10167570124 017310 0 ustar jtauber jtauber #
# 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
#
import templates.page as template
from web_resource import PagePart
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.menu_key
self.menu_providers = []
def register_provider(self, part_provider):
self.menu_providers.append(part_provider)
def get(self, resource_id, request, main_resource):
if resource_id == self.menu_key:
return MenuPagePart(self.resource_manager, self.menu_providers, request, main_resource, self.config)
else:
return None
class MenuPagePart(PagePart):
"""
The page part representing the full menu, including the content provided by
menu providers.
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, menu_providers, request, main_resource, config):
self.main_menu = resource_manager.get(config.main_menu_key, request, self)
self.menu_providers = menu_providers
self.request = request
self.main_resource = main_resource
self.config = config
def get_html(self):
html = self.main_menu.get_html()
html += template.menu_separator % locals()
for provider in self.menu_providers:
html += provider(self.request, self.main_resource, self.config)
return html
leonardo-0.4.1/lib/providers/page.py 0100755 0000765 0000765 00000015501 10167570124 017265 0 ustar jtauber jtauber #
# 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 wiki import WikiFormatter
import templates.page as template
from web_resource import PagePart, TopLevelResource, FileObjectResource
class PageProvider:
def __init__(self, resource_manager, lfs, config):
self.resource_manager = resource_manager
self.lfs = lfs
self.config = config
self.home_key = config.home_key
self.formatter = WikiFormatter(config.home_href, lambda x: self.wiki_page(x).get_html())
def get(self, resource_id, request, main_resource):
if resource_id == "":
resource_id = self.home_key
page_part = self.wiki_page(resource_id)
if main_resource: # if there's a parent resource, we're done
return page_part
if page_part.exists():
return HTMLPage(resource_id, page_part, self.resource_manager, request, self.config)
else: # new page
if request.logged_in():
return PageDoesNotExist(resource_id, self.resource_manager, request, self.config)
else:
return HTMLPage404(resource_id, self.resource_manager, request, self.config)
def wiki_page(self, key):
# note that the content type "wiki04" is used to allow concurrent use
# of alternative wiki formats. other engines may look for other
# content types.
return WikiPagePart(key, self.lfs.get(key, "wiki04"), self.formatter)
class HTMLPage(TopLevelResource):
def __init__(self, resource_id, page_part, resource_manager, request, config):
TopLevelResource.__init__(self, resource_id, "text/html")
self.resource_manager = resource_manager
self.site_title = config.site_title
self.site_sub_title = config.site_sub_title
self.css_href = config.css_href
self.home_href = config.home_href
self.copyright_holder = config.copyright_holder
self.home_key = config.home_key
self.menu_key = config.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):
title = page_part.get_title()
if title:
title = "%s : %s" % (self.site_title, title)
else:
title = self.site_title
content = page_part.get_html()
time = page_part.get_lastmod()
menu = self.resource_manager.get(self.menu_key, request, page_part).get_html()
self.html = self.page_template(title, menu, content, time)
def page_template(self, title, menu, content, lastmod):
if lastmod:
import time
lastmod = time.strftime(template.time_format, time.gmtime(lastmod))
lastmod_display = template.lastmod_display % locals()
else:
lastmod_display = ""
if self.site_sub_title:
site_sub_title = self.site_sub_title
sub_title = template.sub_title % locals()
else:
sub_title = ""
site_title = self.site_title
home_page = self.home_href
copyright_holder = self.copyright_holder
# @@@ this makes page module dependent on css module
css_href = self.css_href
return template.page_template % locals()
class ForbiddenPage(HTMLPage):
def __init__(self, resource_id, resource_manager, request, config):
HTMLPage.__init__(self, resource_id, None, resource_manager, request, config)
self.status = "403 Forbidden"
def make_html(self, page_part, request):
site_title = self.site_title
title = template.operation_not_allowed_title % locals()
content = template.operation_not_allowed % locals()
time = None
menu = self.resource_manager.get(self.menu_key, request, self).get_html()
self.html = self.page_template(title, menu, content, time)
class PageDoesNotExist(HTMLPage):
def __init__(self, resource_id, resource_manager, request, config):
HTMLPage.__init__(self, resource_id, None, resource_manager, request, config)
self.status = "404 Not Found"
def make_html(self, page_part, request):
site_title = self.site_title
title = template.page_does_not_exist_title % locals()
content = template.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, menu, content, time)
def is_read_only(self):
return False
class HTMLPage404(HTMLPage):
def __init__(self, resource_id, resource_manager, request, config):
HTMLPage.__init__(self, resource_id, None, resource_manager, request, config)
self.status = "404 Not Found"
def make_html(self, page_part, request):
site_title = self.site_title
title = template.title_404 % locals()
content = template.content_404 % locals()
time = None
menu = self.resource_manager.get(self.menu_key, request, self).get_html()
self.html = self.page_template(title, menu, content, time)
def is_read_only(self):
return True
class WikiPagePart(FileObjectResource, PagePart):
def __init__(self, resource_id, file_object, formatter):
PagePart.__init__(self, resource_id)
FileObjectResource.__init__(self, file_object)
self.formatter = formatter
self.title = None
def get_html(self):
return self.formatter.format(self.get_content())
def get_title(self):
if not self.title:
self.__update_title()
return self.title
def is_read_only(self):
return False
def __update_title(self):
self.title = self.formatter.extract_title(self.file_object.get_content())
leonardo-0.4.1/lib/providers/put.py 0100755 0000765 0000765 00000006563 10167570124 017171 0 ustar jtauber jtauber #
# 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
#
"""
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.
"""
import templates.put as template
from providers.page import HTMLPage, ForbiddenPage
from web_resource import PagePart
class PutProvider:
def __init__(self, resource_manager, lfs, config):
self.resource_manager = resource_manager
self.lfs = lfs
self.config = config
self.put_path = config.put_path
def get(self, resource_id, request, main_resource):
if resource_id != self.put_path:
return None
if request.method == "GET":
return ForbiddenPage(resource_id, self.resource_manager, request, self.config)
if not request.logged_in():
return ForbiddenPage(resource_id, self.resource_manager, request, self.config)
page_part = PutPagePart(resource_id, self.resource_manager, request, self.config)
return HTMLPage(resource_id, page_part, self.resource_manager, request, 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.put_resource_field)
if resource_to_put:
page = resource_manager.get(resource_to_put, request, self)
if page.is_read_only():
title = template.put_read_only_title % locals()
content = template.put_read_only_content % locals()
else:
new_content = request.fields.getvalue(config.put_content_field)
if page.exists():
replace = True
else:
replace = False
page.set_content(new_content)
resource_link = config.home_href + resource_to_put
resource_label = resource_to_put
if replace:
title = template.put_replace_title % locals()
content = template.put_replace_content % locals()
else:
title = template.put_create_title % locals()
content = template.put_create_content % locals()
else: # no resource_id provided
title = template.put_missing_id_title % locals()
content = template.put_missing_id_content % locals()
self.title = title
self.html = content
leonardo-0.4.1/lib/providers/static.py 0100755 0000765 0000765 00000005113 10167570124 017636 0 ustar jtauber jtauber #
# 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
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)
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.4.1/lib/request_response.py 0100644 0000765 0000765 00000006554 10167570124 017747 0 ustar jtauber jtauber #
# 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
#
"""
This module defines request and response objects for handling communication
from and to the client (e.g. web browser).
"""
import os
import cgi
import sys
import auth
from auth import OK
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.session_mgr = auth.SessionManager(self, config.data_dir)
self.user_mgr = auth.UserManager(self, config.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 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)
class Response:
def __init__(self):
self.status = None
self.cookie = None
self.content_type = None
self.content = None
self.headers = {}
def set_content_type(self, content_type):
self.content_type = content_type
def set_status(self, status):
self.status = status
def set_content(self, content):
self.content = content
def set_header(self, name, value):
self.headers[name] = value
def set_cookie(self, cookie):
self.cookie = cookie
def send(self):
if self.status:
sys.stdout.write("Status: %s\n" % self.status)
for name, value in self.headers.items():
sys.stdout.write("%s: %s\n" % (name, value))
if self.cookie:
sys.stdout.write("%s\n" % self.cookie)
if self.content_type:
sys.stdout.write("Content-type: %s\n" % self.content_type)
if self.content:
sys.stdout.write("\n")
sys.stdout.write(self.content)
leonardo-0.4.1/lib/templates/ 0040755 0000765 0000765 00000000000 10167570055 015761 5 ustar jtauber jtauber leonardo-0.4.1/lib/templates/__init__.py 0100644 0000765 0000765 00000001466 10167570124 020073 0 ustar jtauber jtauber #
# 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 templates.
""" leonardo-0.4.1/lib/templates/atom.py 0100644 0000765 0000765 00000003200 10167570124 017260 0 ustar jtauber jtauber #
# 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_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.4.1/lib/templates/blog.py 0100644 0000765 0000765 00000002153 10167570124 017251 0 ustar jtauber jtauber #
# 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_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_all = "%(blog_title)s"
blog_no_entries = """
"""
leonardo-0.4.1/lib/templates/draft.py 0100644 0000765 0000765 00000004505 10167570124 017431 0 ustar jtauber jtauber #
# 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_initial_form_content = "Enter content of new page 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_post_to_blog_form = """
""" leonardo-0.4.1/lib/templates/editform.py 0100644 0000765 0000765 00000003037 10167570124 020141 0 ustar jtauber jtauber #
# 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_title = "%(site_title)s : EDIT %(page_title)s"
new_page_title = "%(site_title)s: NEW PAGE"
new_page_content = "==%(resource_to_edit)s==\n\nType content here."
edit_form = """
Cancel
"""
edit_menu = """
"""
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.4.1/lib/templates/login_logout.py 0100644 0000765 0000765 00000004070 10167570124 021027 0 ustar jtauber jtauber #
# 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 = """
"""
leonardo-0.4.1/lib/templates/page.py 0100644 0000765 0000765 00000003736 10167570124 017252 0 ustar jtauber jtauber #
# 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_does_not_exist = """
Page does not exist. Create it with Edit Page if you like.
"""
page_does_not_exist_title = "%(site_title)s: PAGE DOES NOT EXIST"
operation_not_allowed = """
Operation not allowed.
"""
operation_not_allowed_title = "%(site_title)s: OPERATION NOT ALLOWED"
title_404 = "%(site_title)s: FILE NOT FOUND"
content_404 = """
"""
leonardo-0.4.1/lib/templates/put.py 0100644 0000765 0000765 00000002343 10167570124 017137 0 ustar jtauber jtauber #
# 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.'
put_create_title = "Resource created"
put_create_content = 'Resource %(resource_label)s created.'
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.4.1/lib/web_resource.py 0100755 0000765 0000765 00000006334 10167570124 017024 0 ustar jtauber jtauber #
# 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
#
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
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 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.
"""
# @@@ this should potentially abstract multiple forms of the same resource.
# @@@ at the moment, this assumes, as does the LeonardoFileSystem, that
# @@@ a file object (although not necessarily a "file") is only of one type.
def __init__(self, file_object):
self.file_object = file_object
def exists(self):
return self.file_object.exists()
def get_title(self):
return self.resource_id
def get_lastmod(self):
return self.file_object.get_lastmod()
def get_content(self):
return self.file_object.get_content()
def set_content(self, content):
self.file_object.set_content(content)
def is_read_only(self):
return True # default
leonardo-0.4.1/lib/wiki.py 0100755 0000765 0000765 00000011075 10167570124 015301 0 ustar jtauber jtauber #
# 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
#
import re
class WikiFormatter:
def __init__(self, link_prefix, content_getter):
self.link_prefix = link_prefix
self.content_getter = content_getter
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 insert(self, matchobj):
wiki_word = matchobj.group(1)
link = re.sub(' ', '_', wiki_word).lower()
return self.content_getter(link)
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):
s = page_content
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)
# five spaces 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)
# [..] to wiki link
s = re.sub(r'\[([ \.A-Za-z0-9]+)\]', self.sub_link, s)
URL = r'((?:ftp|http):[^ \]]+)'
# [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', s)
# [image:... ...] to local image
s = re.sub('\[image:/([^ \]]+) ([^\]]+)\]', '' % self.link_prefix, s)
s = re.sub('\[image:([^ \]]+) ([^\]]+)\]', '', s)
# [insert:...] to insert the contents of a wiki page
s = re.sub(r'\[insert:([^\]]+)\]', self.insert, s)
# [amazon:... ...] to link to Amazon using ISBN and link title
s = re.sub(r'\[amazon:(\d+) ([^\]]+)\]',
'\\2', s)
return s
leonardo-0.4.1/README 0100644 0000765 0000765 00000001024 10167570124 014064 0 ustar jtauber jtauber Leonardo 0.4.1
http://jtauber.com/leonardo
Leonardo is a personal wiki and blog server written in Python.
QUICKSTART
* Edit cgi-bin/leonardo_config.py and change data_dir to the correct path
* Edit cgi-bin/leonardo.py and change the sys.append to include the
correct path to the Leonardo lib directory
* 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.py
leonardo-0.4.1/test_server.py 0100755 0000765 0000765 00000003605 10167570124 016135 0 ustar jtauber jtauber #!/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.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")
if sys.argv[1:]:
port = int(sys.argv[1])
else:
port = 8000
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.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'] = ""
self.cgi_info = "/cgi-bin", "leonardo.py/" + self.path
self.run_cgi()
server_address = ("", port)
httpd = HTTPServer(server_address, Handler)
import webbrowser
webbrowser.open("http://localhost:%s/" % port, 1)
print "Serving HTTP on port", port, "..."
httpd.serve_forever()