Page Source
from utils.controls import *
from utils.output import html
from utils import output
from mod_python import apache
from utils.display import Display, Database, FileCopy, Arguments
from utils.display import Modal
from utils.web_exc import WebError, SendLiteral
from utils import bounce
from config import docroot
import string, os
import debugging
class NoCommentsControl(SingleControl):
"""If there are no bodies (display.db_rows() == 0) then display a
'nothing found' header."""
def output_disp(self, display, container):
if display.db_rows() == 0:
container.append("<i>This path is empty. Sorry.</i>")
class AddBodyFormControl(MultipleControl):
"""Makes an action form for adding a new body segment, with the
specified PRIORITY, or one more than the current row's priority if
no PRIORITY is specified."""
def __init__(self, priority=None):
self.priority = priority
def output_disp(self, display, container):
priority = self.priority
if priority is None:
priority = display.fields['priority'] + 1
f = html.FORM(action="%(action:)s&add_priority=%(!prio)s"
% display.req.urls({'prio' : priority}))
f.append("<center>Add a new body segment with identifier ")
f.append(html.TEXT_INPUT(attrs={'size' : 10,
'name' : 'new_body_name'}))
f.append(" here. ")
f.append(html.SUBMIT_INPUT(attrs={'value' : 'Add'}))
f.append("</center>")
container.append(f)
class DeleteBodyControl(SingleControl):
def __init__(self, url=''):
self.url = url
def output_form(self, display, container):
tr = output.TR()
tr.append("""<b>Delete:</b>""")
url = self.url % display.req.urls(display.fields)
tr.append("""[ <a href="%s">Delete This Body</a> ]""" % (url,))
tr.append("""Deleting is not reversible.""")
container.append(tr)
###################
# The display class itself
class PathDisplay(Display, FileCopy, Database, Arguments, Modal):
def __init__(self, req):
Display.__init__(self, req)
def process_arguments(self):
if not hasattr(self, 'args'):
self.args = self.parse_arguments(['path_name', 'body_name'])
if self.args['path_name'] and \
not self.check_name_characters(self.args['path_name']):
self.args['path_name'] = None
if self.args['body_name'] and \
not self.check_name_characters(self.args['body_name']):
self.args['body_name'] = None
def perform_action(self):
self.process_arguments()
self.prep_database()
if self.permit_mode('edit'):
if self.is_multiple():
if self.req.url.internal.has_key('add_priority'):
# Add a new body
new_prio = int(self.req.url.internal['add_priority'][0])
new_body_name = self.req.form_data['new_body_name']
if new_body_name:
self.db_add_body(new_prio, new_body_name)
return "%(docpath!path_name)s/%(!body_name)s" \
% self.req.urls( { 'path_name' : self.args['path_name'],
'body_name' : new_body_name } )
else:
if self.req.url.internal.has_key('delete_body'):
# Delete a body
path_name = self.args['path_name']
body_name = self.req.url.internal['delete_body'][0]
self.db_delete_body(path_name, body_name)
return "%(docpath!path_name)s" \
% self.req.urls( { 'path_name' : self.args['path_name'] } )
else:
# Edit a body
self.perform_database_update()
return "%(docpath!path_name)s" \
% self.req.urls( { 'path_name' : self.args['path_name'] } )
def make_page(self, page):
self.process_arguments()
# bounce back to the top of the docs if we have no arguments
if not self.args['path_name']:
bounce.bounce(self.req, "%(documentation)s" % self.req.urls)
self.prep_database()
page.set_type("info")
self.add_modes_to_page(page)
# Check to see if we need to give the user *just* the body
# in text format
if not self.is_multiple() and self.req.url.internal.has_key('body'):
raise SendLiteral('text/plain', self.fields['body'])
if self.is_multiple():
page.set_title(self.get_multiple_title())
page.append("<h1>%s</h1>\n" % self.get_multiple_title())
page.add_navigation("%(documentation)s" % self.req.urls(self.args),
"Site Documentation")
page.append(self.multiple_database())
else:
page.set_title(self.get_single_title())
page.add_navigation("%(docpath!path_name)s" % self.req.urls(self.args),
"View Path")
if self.get_mode() == 'edit':
self.shape = shapes.FORM
page.append(self.single_database())
def permit_mode(self, mode):
return ((mode == 'display') or
(mode == 'edit') and perms.may(self.req.login, 'edit', 'documentation'))
_name_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-."
def check_name_characters(self, name):
"""Check a name to be sure it's URL-OK."""
if len(name) > 20:
return None # too long
for char in name:
if char not in self._name_characters:
return None # not OK
return 1 # OK
multiple_container_control = CoalesceContainerControl([
NoCommentsControl(),
IfModeControl("edit", AddBodyFormControl(0)),
SimpleContainerControl(),
])
multiple_controls = [
LinkControl("path_name",
"%(docpath!path_name)s/%(!body_name)s",
[ "<h3>", FieldControl("title"), "</h3>"] ),
"\n",
DropCapControl(HTMLEditorControl("body",
"%(docpath!path_name)s/%(!body_name)s?body=txt")),
IfModeControl("edit", AddBodyFormControl()),
]
def get_multiple_title(self):
if not hasattr(self, '_multiple_title'):
s = queries.Select(tables="doc_paths",
where="path_name = %s"
% queries.represent_value(self.args['path_name']),
columns=['title'])
s.execute()
row = s.fetch()
if row:
self._multiple_title = row['title']
else:
self._multiple_title = 'Path Display'
return self._multiple_title
single_controls = [
IfShapeControl(formcontrols=[ NoEditControl("path_name", title="Path Identifier"),
NoEditControl("body_name", title="Body Identifier"),
BooleanControl("private", title="Private",
comment="""Private data can only be read by those with permission to <tt>read_private</tt> in <tt>documentation</tt>.""") ] ),
"<h1>", TextFieldControl("title", title="Title"), "</h1>",
DropCapControl(HTMLEditorControl("body",
"%(docpath!path_name)s/%(!body_name)s?body=txt",
title="Body", rows=25, cols=80)),
DeleteBodyControl(url="%(action:)s&delete_body=%(!body_name)s")
]
def get_single_title(self):
return self.fields['title']
def db_query(self):
wc = [ "p.path_name = b.path_name",
"b.path_name = %s" % queries.represent_value(self.args['path_name']) ]
# if not logged in, don't show private paths
if not perms.may(self.req.login, 'read_private', 'documentation'):
wc.append("not p.private")
wc.append("not b.private")
if self.args['body_name']:
wc.append("b.body_name = %s" % queries.represent_value(self.args['body_name']))
s = queries.Select(tables=["doc_paths as p", "doc_bodies as b"],
order="b.priority",
where=string.join(wc, ' AND '),
columns = { 'title' : 'b.title',
'path_name' : 'b.path_name',
'body_name' : 'b.body_name',
'body' : 'b.body',
'priority' : 'b.priority',
'private' : 'b.private'} )
s.execute()
if not self.is_multiple() and s.count() == 0:
raise WebError("Not Found",
"""The specified piece of documentation was not found. Please
navigate to the documentation you would like to see starting
at the <a href="%(documentation)s">documentation</a> page.""" %
self.req.urls)
self.query = s
def is_multiple(self):
return not self.args['body_name']
def db_fetch(self):
self.fields = self.query.fetch()
return self.fields
def db_update(self):
u = queries.Update(table="doc_bodies",
where="path_name = %s and body_name = %s"
% (queries.represent_value(self.args['path_name']),
queries.represent_value(self.args['body_name'])))
u['title'] = self.form_fields['title']
u['body'] = self.form_fields['body']
u['private'] = self.form_fields['private']
u.execute()
def db_add_body(self, priority, body_name):
path_name = self.args['path_name']
if not self.check_name_characters(body_name):
raise WebError("Illegal Characters in identifier",
"""Identifiers must be short and safe to include in a URL.
They cannot contain spaces.""")
# Lock down the table while we do all of this
lock = queries.Lock(tables="doc_bodies")
lock.lock()
try:
# check to see if the identifier already exists
s = queries.Select(tables="doc_bodies",
where="path_name = %s and body_name = %s"
% (queries.represent_value(path_name),
queries.represent_value(body_name)))
s.execute()
if s.count() > 0:
raise WebError("Identifier Already In Use",
"""The identifier you selected, '%s', is already in use within
this path. Please go back and try another.""" % body_name)
# now move all of the other bodies' priorities up by one to
# make room for this one.
u = queries.Update(table="doc_bodies",
where="priority >= %s" % queries.represent_value(priority),
sets={'priority' : queries.SQL('priority + 1')})
u.execute()
# finally, insert the new one
i = queries.Insert(table="doc_bodies",
rows=[{'title' : 'Untitled',
'path_name' : path_name,
'body_name' : body_name,
'priority' : priority,
'private' : 0,
'body' : ''}])
i.execute()
finally:
lock.unlock()
def db_delete_body(self, path_name, body_name):
d = queries.Delete(table="doc_bodies",
where="path_name = %s and body_name = %s"
% (queries.represent_value(path_name),
queries.represent_value(body_name)))
d.execute()
def db_rows(self):
return self.query.count()
def new(req):
return PathDisplay(req)