index 8f63d3a8576c24897cb600d89e3cd194f82144f8..a9757fa35bba8716ded4b9cc4b0f9420b3151911 100644 (file)
from roundup.cgi.TAL.TALInterpreter import TALInterpreter
from roundup.cgi import ZTUtils
-# XXX WAH pagetemplates aren't pickleable :(
-#def getTemplate(dir, name, classname=None, request=None):
-# ''' Interface to get a template, possibly loading a compiled template.
-# '''
-# # source
-# src = os.path.join(dir, name)
-#
-# # see if we can get a compile from the template"c" directory (most
-# # likely is "htmlc"
-# split = list(os.path.split(dir))
-# split[-1] = split[-1] + 'c'
-# cdir = os.path.join(*split)
-# split.append(name)
-# cpl = os.path.join(*split)
-#
-# # ok, now see if the source is newer than the compiled (or if the
-# # compiled even exists)
-# MTIME = os.path.stat.ST_MTIME
-# if (not os.path.exists(cpl) or os.stat(cpl)[MTIME] < os.stat(src)[MTIME]):
-# # nope, we need to compile
-# pt = RoundupPageTemplate()
-# pt.write(open(src).read())
-# pt.id = name
-#
-# # save off the compiled template
-# if not os.path.exists(cdir):
-# os.makedirs(cdir)
-# f = open(cpl, 'wb')
-# pickle.dump(pt, f)
-# f.close()
-# else:
-# # yay, use the compiled template
-# f = open(cpl, 'rb')
-# pt = pickle.load(f)
-# return pt
-
-templates = {}
-
class NoTemplate(Exception):
pass
-def getTemplate(dir, name, extension, classname=None, request=None):
- ''' Interface to get a template, possibly loading a compiled template.
+class Templates:
+ templates = {}
- "name" and "extension" indicate the template we're after, which in
- most cases will be "name.extension". If "extension" is None, then
- we look for a template just called "name" with no extension.
+ def __init__(self, dir):
+ self.dir = dir
- If the file "name.extension" doesn't exist, we look for
- "_generic.extension" as a fallback.
- '''
- # default the name to "home"
- if name is None:
- name = 'home'
+ def precompileTemplates(self):
+ ''' Go through a directory and precompile all the templates therein
+ '''
+ for filename in os.listdir(self.dir):
+ if os.path.isdir(filename): continue
+ if '.' in filename:
+ name, extension = filename.split('.')
+ self.getTemplate(name, extension)
+ else:
+ self.getTemplate(filename, None)
- # find the source, figure the time it was last modified
- if extension:
- filename = '%s.%s'%(name, extension)
- else:
- filename = name
- src = os.path.join(dir, filename)
- try:
- stime = os.stat(src)[os.path.stat.ST_MTIME]
- except os.error, error:
- if error.errno != errno.ENOENT:
- raise
- if not extension:
- raise NoTemplate, 'Template file "%s" doesn\'t exist'%name
-
- # try for a generic template
- generic = '_generic.%s'%extension
- src = os.path.join(dir, generic)
+ def get(self, name, extension):
+ ''' Interface to get a template, possibly loading a compiled template.
+
+ "name" and "extension" indicate the template we're after, which in
+ most cases will be "name.extension". If "extension" is None, then
+ we look for a template just called "name" with no extension.
+
+ If the file "name.extension" doesn't exist, we look for
+ "_generic.extension" as a fallback.
+ '''
+ # default the name to "home"
+ if name is None:
+ name = 'home'
+
+ # find the source, figure the time it was last modified
+ if extension:
+ filename = '%s.%s'%(name, extension)
+ else:
+ filename = name
+ src = os.path.join(self.dir, filename)
try:
stime = os.stat(src)[os.path.stat.ST_MTIME]
except os.error, error:
if error.errno != errno.ENOENT:
raise
- # nicer error
- raise NoTemplate, 'No template file exists for templating '\
- '"%s" with template "%s" (neither "%s" nor "%s")'%(name,
- extension, filename, generic)
- filename = generic
-
- key = (dir, filename)
- if templates.has_key(key) and stime < templates[key].mtime:
- # compiled template is up to date
- return templates[key]
-
- # compile the template
- templates[key] = pt = RoundupPageTemplate()
- pt.write(open(src).read())
- pt.id = filename
- pt.mtime = time.time()
- return pt
+ if not extension:
+ raise NoTemplate, 'Template file "%s" doesn\'t exist'%name
+
+ # try for a generic template
+ generic = '_generic.%s'%extension
+ src = os.path.join(self.dir, generic)
+ try:
+ stime = os.stat(src)[os.path.stat.ST_MTIME]
+ except os.error, error:
+ if error.errno != errno.ENOENT:
+ raise
+ # nicer error
+ raise NoTemplate, 'No template file exists for templating '\
+ '"%s" with template "%s" (neither "%s" nor "%s")'%(name,
+ extension, filename, generic)
+ filename = generic
+
+ if self.templates.has_key(filename) and \
+ stime < self.templates[filename].mtime:
+ # compiled template is up to date
+ return self.templates[filename]
+
+ # compile the template
+ self.templates[filename] = pt = RoundupPageTemplate()
+ pt.write(open(src).read())
+ pt.id = filename
+ pt.mtime = time.time()
+ return pt
+
+ def __getitem__(self, name):
+ name, extension = os.path.splitext(name)
+ if extension:
+ extension = extension[1:]
+ try:
+ return self.get(name, extension)
+ except NoTemplate, message:
+ raise KeyError, message
class RoundupPageTemplate(PageTemplate.PageTemplate):
''' A Roundup-specific PageTemplate.
- methods for easy filterspec link generation
- *user*, the current user node as an HTMLItem instance
- *form*, the current CGI form information as a FieldStorage
- *instance*
- The current instance
+ *tracker*
+ The current tracker
*db*
The current database, through which db.config may be reached.
'''
'options': {},
'nothing': None,
'request': request,
- 'content': client.content,
'db': HTMLDatabase(client),
- 'instance': client.instance,
+ 'tracker': client.instance,
'utils': TemplatingUtils(client),
+ 'templates': Templates(client.instance.config.TEMPLATES),
}
# add in the item if there is one
if client.nodeid:
- c['context'] = HTMLItem(client, classname, client.nodeid)
- else:
+ if classname == 'user':
+ c['context'] = HTMLUser(client, classname, client.nodeid)
+ else:
+ c['context'] = HTMLItem(client, classname, client.nodeid)
+ elif client.db.classes.has_key(classname):
c['context'] = HTMLClass(client, classname)
return c
if self._v_errors:
raise PageTemplate.PTRuntimeError, \
- 'Page Template %s has errors.' % self.id
+ 'Page Template %s has errors.'%self.id
# figure the context
classname = classname or client.classname
# and go
output = StringIO.StringIO()
- TALInterpreter(self._v_program, self._v_macros,
+ TALInterpreter(self._v_program, self.macros,
getEngine().getContext(c), output, tal=1, strictinsert=0)()
return output.getvalue()
l.append(cl.lookup(entry))
return l
-class HTMLClass:
+class HTMLPermissions:
+ ''' Helpers that provide answers to commonly asked Permission questions.
+ '''
+ def is_edit_ok(self):
+ ''' Is the user allowed to Edit the current class?
+ '''
+ return self._db.security.hasPermission('Edit', self._client.userid,
+ self._classname)
+ def is_view_ok(self):
+ ''' Is the user allowed to View the current class?
+ '''
+ return self._db.security.hasPermission('View', self._client.userid,
+ self._classname)
+ def is_only_view_ok(self):
+ ''' Is the user only allowed to View (ie. not Edit) the current class?
+ '''
+ return self.is_view_ok() and not self.is_edit_ok()
+
+class HTMLClass(HTMLPermissions):
''' Accesses through a class (either through *class* or *db.<classname>*)
'''
def __init__(self, client, classname):
self._client = client
self._db = client.db
- # we want classname to be exposed
- self.classname = classname
- if classname is not None:
- self._klass = self._db.getclass(self.classname)
- self._props = self._klass.getprops()
+ # we want classname to be exposed, but _classname gives a
+ # consistent API for extending Class/Item
+ self._classname = self.classname = classname
+ self._klass = self._db.getclass(self.classname)
+ self._props = self._klass.getprops()
def __repr__(self):
return '<HTMLClass(0x%x) %s>'%(id(self), self.classname)
except KeyError:
raise AttributeError, attr
+ def getItem(self, itemid, num_re=re.compile('\d+')):
+ ''' Get an item of this class by its item id.
+ '''
+ # make sure we're looking at an itemid
+ if not num_re.match(itemid):
+ itemid = self._klass.lookup(itemid)
+
+ if self.classname == 'user':
+ klass = HTMLUser
+ else:
+ klass = HTMLItem
+
+ return klass(self._client, self.classname, itemid)
+
def properties(self):
''' Return HTMLProperty for all of this class' properties.
'''
klass = HTMLUser
else:
klass = HTMLItem
- l = [klass(self._client, self.classname, x) for x in self._klass.list()]
+
+ # get the list and sort it nicely
+ l = self._klass.list()
+ sortfunc = make_sort_function(self._db, self.classname)
+ l.sort(sortfunc)
+
+ l = [klass(self._client, self.classname, x) for x in l]
return l
def csv(self):
properties.sort()
properties = ','.join(properties)
return '<a href="javascript:help_window(\'%s?:template=help&' \
- ':contentonly=1&properties=%s\', \'%s\', \'%s\')"><b>'\
- '(%s)</b></a>'%(self.classname, properties, width, height, label)
+ 'properties=%s\', \'%s\', \'%s\')"><b>(%s)</b></a>'%(
+ self.classname, properties, width, height, label)
def submit(self, label="Submit New Entry"):
''' Generate a submit button (and action hidden element)
req.update(kwargs)
# new template, using the specified classname and request
- pt = getTemplate(self._db.config.TEMPLATES, self.classname, name)
+ pt = Templates(self._db.config.TEMPLATES).get(self.classname, name)
# use our fabricated request
return pt.render(self._client, self.classname, req)
-class HTMLItem:
+class HTMLItem(HTMLPermissions):
''' Accesses through an *item*
'''
def __init__(self, client, classname, nodeid):
def __getitem__(self, item):
''' return an HTMLProperty instance
'''
- #print 'HTMLItem.getitem', (self, item)
+ #print 'HTMLItem.getitem', (self, item)
if item == 'id':
return self._nodeid
# used for security checks
self._security = client.db.security
+
_marker = []
def hasPermission(self, role, classname=_marker):
''' Determine if the user has the Role.
classname = self._default_classname
return self._security.hasPermission(role, self._nodeid, classname)
+ def is_edit_ok(self):
+ ''' Is the user allowed to Edit the current class?
+ Also check whether this is the current user's info.
+ '''
+ return self._db.security.hasPermission('Edit', self._client.userid,
+ self._classname) or self._nodeid == self._client.userid
+
+ def is_view_ok(self):
+ ''' Is the user allowed to View the current class?
+ Also check whether this is the current user's info.
+ '''
+ return self._db.security.hasPermission('Edit', self._client.userid,
+ self._classname) or self._nodeid == self._client.userid
+
class HTMLProperty:
''' String, Number, Date, Interval HTMLProperty
return _('*encrypted*')
def field(self, size = 30):
- ''' Render a form edit field for the property
+ ''' Render a form edit field for the property.
'''
return '<input type="password" name="%s" size="%s">'%(self._name, size)
+ def confirm(self, size = 30):
+ ''' Render a second form edit field for the property, used for
+ confirmation that the user typed the password correctly. Generates
+ a field with name "name:confirm".
+ '''
+ return '<input type="password" name="%s:confirm" size="%s">'%(
+ self._name, size)
+
class NumberHTMLProperty(HTMLProperty):
def plain(self):
''' Render a "plain" representation of the property
s = ''
l.append(_('<option %svalue="-1">- no selection -</option>')%s)
for optionid in options:
- option = linkcl.get(optionid, k)
+ # get the option value, and if it's None use an empty string
+ option = linkcl.get(optionid, k) or ''
+
+ # figure if this option is selected
s = ''
if optionid == self._value:
s = 'selected '
+
+ # figure the label
if showid:
lab = '%s%s: %s'%(self._prop.classname, optionid, option)
else:
lab = option
+
+ # truncate if it's too long
if size is not None and len(lab) > size:
lab = lab[:size-3] + '...'
+
+ # and generate
lab = cgi.escape(lab)
l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
l.append('</select>')
# sort function
sortfunc = make_sort_function(self._db, self._prop.classname)
- # force the value to be a single choice
- if isinstance(value, type('')):
- value = value[0]
linkcl = self._db.getclass(self._prop.classname)
l = ['<select name="%s">'%self._name]
k = linkcl.labelprop(1)
sort_on = ('+', linkcl.labelprop())
options = linkcl.filter(None, conditions, sort_on, (None, None))
for optionid in options:
- option = linkcl.get(optionid, k)
+ # get the option value, and if it's None use an empty string
+ option = linkcl.get(optionid, k) or ''
+
+ # figure if this option is selected
s = ''
if value in [optionid, option]:
s = 'selected '
+
+ # figure the label
if showid:
lab = '%s%s: %s'%(self._prop.classname, optionid, option)
else:
lab = option
+
+ # truncate if it's too long
if size is not None and len(lab) > size:
lab = lab[:size-3] + '...'
if additional:
for propname in additional:
m.append(linkcl.get(optionid, propname))
lab = lab + ' (%s)'%', '.join(map(str, m))
+
+ # and generate
lab = cgi.escape(lab)
l.append('<option %svalue="%s">%s</option>'%(s, optionid, lab))
l.append('</select>')
l = ['<select multiple name="%s" size="%s">'%(self._name, height)]
k = linkcl.labelprop(1)
for optionid in options:
- option = linkcl.get(optionid, k)
+ # get the option value, and if it's None use an empty string
+ option = linkcl.get(optionid, k) or ''
+
+ # figure if this option is selected
s = ''
if optionid in value or option in value:
s = 'selected '
+
+ # figure the label
if showid:
lab = '%s%s: %s'%(self._prop.classname, optionid, option)
else:
lab = option
+ # truncate if it's too long
if size is not None and len(lab) > size:
lab = lab[:size-3] + '...'
if additional:
for propname in additional:
m.append(linkcl.get(optionid, propname))
lab = lab + ' (%s)'%', '.join(m)
+
+ # and generate
lab = cgi.escape(lab)
l.append('<option %svalue="%s">%s</option>'%(s, optionid,
lab))
"form" the CGI form as a cgi.FieldStorage
"env" the CGI environment variables
- "url" the current URL path for this request
"base" the base URL for this instance
"user" a HTMLUser instance for this user
"classname" the current classname (possibly None)
self.form = client.form
self.env = client.env
self.base = client.base
- self.url = client.url
self.user = HTMLUser(client, 'user', client.userid)
# store the current class name and action
if self.form.has_key(':filter'):
self.filter = handleListCGIValue(self.form[':filter'])
self.filterspec = {}
+ db = self.client.db
if self.classname is not None:
- props = self.client.db.getclass(self.classname).getprops()
+ props = db.getclass(self.classname).getprops()
for name in self.filter:
if self.form.has_key(name):
prop = props[name]
fv = self.form[name]
if (isinstance(prop, hyperdb.Link) or
isinstance(prop, hyperdb.Multilink)):
- self.filterspec[name] = handleListCGIValue(fv)
+ self.filterspec[name] = lookupIds(db, prop,
+ handleListCGIValue(fv))
else:
self.filterspec[name] = fv.value
d['env'] = e
return '''
form: %(form)s
-url: %(url)r
base: %(base)r
classname: %(classname)r
template: %(template)r
}
function help_window(helpurl, width, height) {
- HelpWin = window.open('%s/' + helpurl, 'RoundupHelpWindow', 'scrollbars=yes,resizable=yes,toolbar=no,height='+height+',width='+width);
+ HelpWin = window.open('%s' + helpurl, 'RoundupHelpWindow', 'scrollbars=yes,resizable=yes,toolbar=no,height='+height+',width='+width);
}
</script>
'''%self.base
matches = None
l = klass.filter(matches, filterspec, sort, group)
- # map the item ids to instances
- if self.classname == 'user':
- klass = HTMLUser
- else:
- klass = HTMLItem
- l = [klass(self.client, self.classname, item) for item in l]
-
- # return the batch object
- return Batch(self.client, l, self.pagesize, self.startwith)
+ # return the batch object, using IDs only
+ return Batch(self.client, l, self.pagesize, self.startwith,
+ classname=self.classname)
# extend the standard ZTUtils Batch object to remove dependency on
# Acquisition and add a couple of useful methods
========= ========================================================
Parameter Usage
========= ========================================================
- sequence a list of HTMLItems
+ sequence a list of HTMLItems or item ids
+ classname if sequence is a list of ids, this is the class of item
size how big to make the sequence.
start where to start (0-indexed) in the sequence.
end where to end (0-indexed) in the sequence.
"sequence_length" is the length of the original, unbatched, sequence.
'''
def __init__(self, client, sequence, size, start, end=0, orphan=0,
- overlap=0):
+ overlap=0, classname=None):
self.client = client
self.last_index = self.last_item = None
self.current_item = None
+ self.classname = classname
self.sequence_length = len(sequence)
ZTUtils.Batch.__init__(self, sequence, size, start, end, orphan,
overlap)
self.last_item = self.current_item
self.last_index = index
- self.current_item = self._sequence[index + self.first]
- return self.current_item
+ item = self._sequence[index + self.first]
+ if self.classname:
+ # map the item ids to instances
+ if self.classname == 'user':
+ item = HTMLUser(self.client, self.classname, item)
+ else:
+ item = HTMLItem(self.client, self.classname, item)
+ self.current_item = item
+ return item
def propchanged(self, property):
''' Detect if the property marked as being the group property