index b458c6c7e2a932fb8bf3cebe9f6c54b76c1f9a49..2f2a478f14e570dbe49dc0cae1c0b21f67dfb6a5 100644 (file)
'request': request,
'content': client.content,
'db': HTMLDatabase(client),
- 'instance': client.instance
+ 'instance': client.instance,
+ 'utils': TemplatingUtils(client),
}
# add in the item if there is one
if client.nodeid:
- c['context'] = HTMLItem(client, classname, client.nodeid)
+ if classname == 'user':
+ c['context'] = HTMLUser(client, classname, client.nodeid)
+ else:
+ c['context'] = HTMLItem(client, classname, client.nodeid)
else:
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
# we want config to be exposed
self.config = client.db.config
+ def __getitem__(self, item):
+ self._client.db.getclass(item)
+ return HTMLClass(self._client, item)
+
def __getattr__(self, attr):
try:
- self._client.db.getclass(attr)
+ return self[attr]
except KeyError:
raise AttributeError, attr
- return HTMLClass(self._client, attr)
+
def classes(self):
l = self._client.db.classes.keys()
l.sort()
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
+ # we want classname to be exposed, but _classname gives a
+ # consistent API for extending Class/Item
+ self._classname = self.classname = classname
if classname is not None:
self._klass = self._db.getclass(self.classname)
self._props = self._klass.getprops()
value = []
else:
value = None
- print (prop, value)
return htmlklass(self._client, '', prop, item, value)
# no good
raise AttributeError, attr
def properties(self):
- ''' Return HTMLProperty for all props
+ ''' Return HTMLProperty for all of this class' properties.
'''
l = []
for name, prop in self._props.items():
return l
def list(self):
+ ''' List all items in this class.
+ '''
if self.classname == 'user':
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._prop.classname)
+ l.sort(sortfunc)
+
+ l = [klass(self._client, self.classname, x) for x in l]
return l
def csv(self):
for x in self._klass.filter(None, filterspec, sort, group)]
return l
- def classhelp(self, properties, label='?', width='400', height='400'):
- '''pop up a javascript window with class help
+ def classhelp(self, properties=None, label='list', width='500',
+ height='400'):
+ ''' Pop up a javascript window with class help
- This generates a link to a popup window which displays the
- properties indicated by "properties" of the class named by
- "classname". The "properties" should be a comma-separated list
- (eg. 'id,name,description').
+ This generates a link to a popup window which displays the
+ properties indicated by "properties" of the class named by
+ "classname". The "properties" should be a comma-separated list
+ (eg. 'id,name,description'). Properties defaults to all the
+ properties of a class (excluding id, creator, created and
+ activity).
- You may optionally override the label displayed, the width and
- height. The popup window will be resizable and scrollable.
+ You may optionally override the label displayed, the width and
+ height. The popup window will be resizable and scrollable.
'''
+ if properties is None:
+ properties = self._klass.getprops(protected=0).keys()
+ 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)
# 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):
# 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
- Hase useful attributes:
+ Has useful attributes:
_name the name of the property
_value the value of the property if any
class StringHTMLProperty(HTMLProperty):
def plain(self, escape=0):
+ ''' Render a "plain" representation of the property
+ '''
if self._value is None:
return ''
if escape:
return str(self._value)
def stext(self, escape=0):
+ ''' Render the value of the property as StructuredText.
+
+ This requires the StructureText module to be installed separately.
+ '''
s = self.plain(escape=escape)
if not StructuredText:
return s
return StructuredText(s,level=1,header=0)
def field(self, size = 30):
+ ''' Render a form edit field for the property
+ '''
if self._value is None:
value = ''
else:
return '<input name="%s" value="%s" size="%s">'%(self._name, value, size)
def multiline(self, escape=0, rows=5, cols=40):
+ ''' Render a multiline form edit field for the property
+ '''
if self._value is None:
value = ''
else:
self._name, rows, cols, value)
def email(self, escape=1):
- ''' fudge email '''
+ ''' Render the value of the property as an obscured email address
+ '''
if self._value is None: value = ''
else: value = str(self._value)
- value = value.replace('@', ' at ')
- value = value.replace('.', ' ')
+ if value.find('@') != -1:
+ name, domain = value.split('@')
+ domain = ' '.join(domain.split('.')[:-1])
+ name = name.replace('.', ' ')
+ value = '%s at %s ...'%(name, domain)
+ else:
+ value = value.replace('.', ' ')
if escape:
value = cgi.escape(value)
return value
class PasswordHTMLProperty(HTMLProperty):
def plain(self):
+ ''' Render a "plain" representation of the property
+ '''
if self._value is None:
return ''
return _('*encrypted*')
def field(self, size = 30):
+ ''' 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
+ '''
return str(self._value)
def field(self, size = 30):
+ ''' Render a form edit field for the property
+ '''
if self._value is None:
value = ''
else:
class BooleanHTMLProperty(HTMLProperty):
def plain(self):
+ ''' Render a "plain" representation of the property
+ '''
if self.value is None:
return ''
return self._value and "Yes" or "No"
def field(self):
+ ''' Render a form edit field for the property
+ '''
checked = self._value and "checked" or ""
s = '<input type="radio" name="%s" value="yes" %s>Yes'%(self._name,
checked)
class DateHTMLProperty(HTMLProperty):
def plain(self):
+ ''' Render a "plain" representation of the property
+ '''
if self._value is None:
return ''
return str(self._value)
def field(self, size = 30):
+ ''' Render a form edit field for the property
+ '''
if self._value is None:
value = ''
else:
return '<input name="%s" value="%s" size="%s">'%(self._name, value, size)
def reldate(self, pretty=1):
+ ''' Render the interval between the date and now.
+
+ If the "pretty" flag is true, then make the display pretty.
+ '''
if not self._value:
return ''
class IntervalHTMLProperty(HTMLProperty):
def plain(self):
+ ''' Render a "plain" representation of the property
+ '''
if self._value is None:
return ''
return str(self._value)
def pretty(self):
+ ''' Render the interval in a pretty format (eg. "yesterday")
+ '''
return self._value.pretty()
def field(self, size = 30):
+ ''' Render a form edit field for the property
+ '''
if self._value is None:
value = ''
else:
return getattr(i, attr)
def plain(self, escape=0):
+ ''' Render a "plain" representation of the property
+ '''
if self._value is None:
return ''
linkcl = self._db.classes[self._prop.classname]
value = cgi.escape(value)
return value
- def field(self):
+ def field(self, showid=0, size=None):
+ ''' Render a form edit field for the property
+ '''
linkcl = self._db.getclass(self._prop.classname)
if linkcl.getprops().has_key('order'):
sort_on = 'order'
else:
sort_on = linkcl.labelprop()
- options = linkcl.filter(None, {}, [sort_on], [])
+ options = linkcl.filter(None, {}, ('+', sort_on), (None, None))
# TODO: make this a field display, not a menu one!
- l = ['<select name="%s">'%property]
+ l = ['<select name="%s">'%self._name]
k = linkcl.labelprop(1)
- if value is None:
+ if self._value is None:
s = 'selected '
else:
s = ''
for optionid in options:
option = linkcl.get(optionid, k)
s = ''
- if optionid == value:
+ if optionid == self._value:
s = 'selected '
if showid:
lab = '%s%s: %s'%(self._prop.classname, optionid, option)
def menu(self, size=None, height=None, showid=0, additional=[],
**conditions):
+ ''' Render a form select list for this property
+ '''
value = self._value
# 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)
return [klass(self._client, self._prop.classname, value) for value in l]
def plain(self, escape=0):
+ ''' Render a "plain" representation of the property
+ '''
linkcl = self._db.classes[self._prop.classname]
k = linkcl.labelprop(1)
labels = []
return value
def field(self, size=30, showid=0):
+ ''' Render a form edit field for the property
+ '''
sortfunc = make_sort_function(self._db, self._prop.classname)
linkcl = self._db.getclass(self._prop.classname)
value = self._value[:]
if value:
value.sort(sortfunc)
# map the id to the label property
+ if not linkcl.getkey():
+ showid=1
if not showid:
k = linkcl.labelprop(1)
value = [linkcl.get(v, k) for v in value]
def menu(self, size=None, height=None, showid=0, additional=[],
**conditions):
+ ''' Render a form select list for this property
+ '''
value = self._value
# sort function
"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.client.nodeid:
s.append('- %s%s'%(self.classname, self.client.nodeid))
else:
- s.append('- index of '+self.classname)
+ if self.template == 'item':
+ s.append('- new %s'%self.classname)
+ elif self.template == 'index':
+ s.append('- %s index'%self.classname)
+ else:
+ s.append('- %s %s'%(self.classname, self.template))
else:
s.append('- home')
return ' '.join(s)
l.append(s%(':startwith', self.startwith))
return '\n'.join(l)
- def indexargs_href(self, url, args):
+ def indexargs_url(self, url, args):
''' embed the current index args in a URL '''
l = ['%s=%s'%(k,v) for k,v in args.items()]
if self.columns and not args.has_key(':columns'):
if not args.has_key(':startwith'):
l.append(':startwith=%s'%self.startwith)
return '%s?%s'%(url, '&'.join(l))
+ indexargs_href = indexargs_url
def base_javascript(self):
return '''
matches = None
l = klass.filter(matches, filterspec, sort, group)
- # return the batch object
- return Batch(self.client, self.classname, l, self.pagesize,
- self.startwith)
+ # 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)
# extend the standard ZTUtils Batch object to remove dependency on
# Acquisition and add a couple of useful methods
class Batch(ZTUtils.Batch):
- def __init__(self, client, classname, l, size, start, end=0, orphan=0, overlap=0):
+ ''' Use me to turn a list of items, or item ids of a given class, into a
+ series of batches.
+
+ ========= ========================================================
+ Parameter Usage
+ ========= ========================================================
+ sequence a list of HTMLItems
+ 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.
+ orphan if the next batch would contain less items than this
+ value, then it is combined with this batch
+ overlap the number of items shared between adjacent batches
+ ========= ========================================================
+
+ Attributes: Note that the "start" attribute, unlike the
+ argument, is a 1-based index (I know, lame). "first" is the
+ 0-based index. "length" is the actual number of elements in
+ the batch.
+
+ "sequence_length" is the length of the original, unbatched, sequence.
+ '''
+ def __init__(self, client, sequence, size, start, end=0, orphan=0,
+ overlap=0):
self.client = client
- self.classname = classname
self.last_index = self.last_item = None
self.current_item = None
- ZTUtils.Batch.__init__(self, l, size, start, end, orphan, overlap)
+ self.sequence_length = len(sequence)
+ ZTUtils.Batch.__init__(self, sequence, size, start, end, orphan,
+ overlap)
# overwrite so we can late-instantiate the HTMLItem instance
def __getitem__(self, index):
if index + self.end < self.first: raise IndexError, index
return self._sequence[index + self.end]
- if index >= self.length: raise IndexError, index
+ if index >= self.length:
+ raise IndexError, index
# move the last_item along - but only if the fetched index changes
# (for some reason, index 0 is fetched twice)
self.last_item = self.current_item
self.last_index = index
- # wrap the return in an HTMLItem
- if self.classname == 'user':
- klass = HTMLUser
- else:
- klass = HTMLItem
- self.current_item = klass(self.client, self.classname,
- self._sequence[index+self.first])
+ self.current_item = self._sequence[index + self.first]
return self.current_item
def propchanged(self, property):
def previous(self):
if self.start == 1:
return None
- return Batch(self.client, self.classname, self._sequence, self._size,
+ return Batch(self.client, self._sequence, self._size,
self.first - self._size + self.overlap, 0, self.orphan,
self.overlap)
self._sequence[self.end]
except IndexError:
return None
- return Batch(self.client, self.classname, self._sequence, self._size,
+ return Batch(self.client, self._sequence, self._size,
self.end - self.overlap, 0, self.orphan, self.overlap)
- def length(self):
- self.sequence_length = l = len(self._sequence)
- return l
+class TemplatingUtils:
+ ''' Utilities for templating
+ '''
+ def __init__(self, client):
+ self.client = client
+ def Batch(self, sequence, size, start, end=0, orphan=0, overlap=0):
+ return Batch(self.client, sequence, size, start, end, orphan,
+ overlap)