summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 6ebcd80)
raw | patch | inline | side by side (parent: 6ebcd80)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Sun, 1 Sep 2002 04:32:30 +0000 (04:32 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Sun, 1 Sep 2002 04:32:30 +0000 (04:32 +0000) |
. Reinstated searching, but not query saving yet
. Filtering only allows sorting and grouping by one property - all backends
now implement this behaviour.
. Nosy list journalling turned off by default, everything else is on.
. Added some convenience methods (reverse, propchanged, [item] accesses, ...)
. Did I mention the stylesheet is much cleaner now? :)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1019 57a73879-2fb5-44c3-a270-3262357dd7e2
. Filtering only allows sorting and grouping by one property - all backends
now implement this behaviour.
. Nosy list journalling turned off by default, everything else is on.
. Added some convenience methods (reverse, propchanged, [item] accesses, ...)
. Did I mention the stylesheet is much cleaner now? :)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1019 57a73879-2fb5-44c3-a270-3262357dd7e2
14 files changed:
diff --git a/TODO.txt b/TODO.txt
index e87933019fcfe00865cc7303ae14be1bc4c4f7cf..4e48e0ec77167889d3d62c072a104bfe666e10a4 100644 (file)
--- a/TODO.txt
+++ b/TODO.txt
[value, value, ...] implies "in"
pending hyperdb: make creator, creation and activity available pre-commit
pending hyperdb: migrate "id" property to be Number type
-pending hyperdb: allow classes to define their ordering (properties, asc/desc)
pending instance: including much simpler upgrade path and the use of
non-Python configuration files (ConfigParser)
pending instance: split instance.open() into open() and login()
pending security: at least an LDAP user database implementation
pending security: authenticate over a secure connection
pending security: use digital signatures in mailgw
+pending security: submission protection
pending web: I18N
pending web: Better message summary display (feature request #520244)
pending web: Navigating around the issues (feature request #559149)
-pending web: Re-enable link backrefs from messages (feature request #568714)
pending web: Quick help links next to the property labels giving a
description of the property. Combine with help for the actual
form element too, eg. how to use the nosy list edit box.
pending web: clicking on a group header should filter for that type of entry
pending web: re-enable auth by basic http auth
-New templating TODO
+New templating TODO:
+. generic class editing
. classhelp
. query saving
-. search page is a shambles
-. view customisation is a shambles
-. style is a shambles
+. search "refinement" (pre-fill the search page with the current search
+ parameters)
+. security on actions (only allows/enforces generic Edit perm on the class :()
ongoing: any bugs
+done web: Re-enable link backrefs from messages (feature request #568714) (RJ)
done web: have the page layout (header/footer) be templatable (RJ)
done web: fixing the templating so it works (RJ)
done web: re-work cgi interface to abstract out the explicit "issue"
index fee6d95dd6d9f967886ba3938a5cbb919cb2e7a8..0f0faa017f947a0af214fa4fcab2f4d5cb7b366e 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-#$Id: back_anydbm.py,v 1.65 2002-08-30 08:35:45 richard Exp $
+#$Id: back_anydbm.py,v 1.66 2002-09-01 04:32:30 richard Exp $
'''
This module defines a backend that saves the hyperdatabase in a database
chosen by anydbm. It is guaranteed to always be available in python
sort spec.
"filterspec" is {propname: value(s)}
- "sort" is ['+propname', '-propname', 'propname', ...]
- "group is ['+propname', '-propname', 'propname', ...]
+ "sort" and "group" are (dir, prop) where dir is '+', '-' or None
+ and prop is a prop name or None
"search_matches" is {nodeid: marker}
'''
cn = self.classname
k.append(v)
l = k
- # optimise sort
- m = []
- for entry in sort:
- if entry[0] != '-':
- m.append(('+', entry))
- else:
- m.append((entry[0], entry[1:]))
- sort = m
-
- # optimise group
- m = []
- for entry in group:
- if entry[0] != '-':
- m.append(('+', entry))
- else:
- m.append((entry[0], entry[1:]))
- group = m
# now, sort the result
def sortfun(a, b, sort=sort, group=group, properties=self.getprops(),
db = self.db, cl=self):
a_id, an = a
b_id, bn = b
# sort by group and then sort
- for list in group, sort:
- for dir, prop in list:
- # sorting is class-specific
- propclass = properties[prop]
+ for dir, prop in group, sort:
+ if dir is None: continue
- # handle the properties that might be "faked"
- # also, handle possible missing properties
- try:
- if not an.has_key(prop):
- an[prop] = cl.get(a_id, prop)
- av = an[prop]
- except KeyError:
- # the node doesn't have a value for this property
- if isinstance(propclass, Multilink): av = []
- else: av = ''
+ # sorting is class-specific
+ propclass = properties[prop]
+
+ # handle the properties that might be "faked"
+ # also, handle possible missing properties
+ try:
+ if not an.has_key(prop):
+ an[prop] = cl.get(a_id, prop)
+ av = an[prop]
+ except KeyError:
+ # the node doesn't have a value for this property
+ if isinstance(propclass, Multilink): av = []
+ else: av = ''
+ try:
+ if not bn.has_key(prop):
+ bn[prop] = cl.get(b_id, prop)
+ bv = bn[prop]
+ except KeyError:
+ # the node doesn't have a value for this property
+ if isinstance(propclass, Multilink): bv = []
+ else: bv = ''
+
+ # String and Date values are sorted in the natural way
+ if isinstance(propclass, String):
+ # clean up the strings
+ if av and av[0] in string.uppercase:
+ av = an[prop] = av.lower()
+ if bv and bv[0] in string.uppercase:
+ bv = bn[prop] = bv.lower()
+ if (isinstance(propclass, String) or
+ isinstance(propclass, Date)):
+ # it might be a string that's really an integer
try:
- if not bn.has_key(prop):
- bn[prop] = cl.get(b_id, prop)
- bv = bn[prop]
- except KeyError:
- # the node doesn't have a value for this property
- if isinstance(propclass, Multilink): bv = []
- else: bv = ''
-
- # String and Date values are sorted in the natural way
- if isinstance(propclass, String):
- # clean up the strings
- if av and av[0] in string.uppercase:
- av = an[prop] = av.lower()
- if bv and bv[0] in string.uppercase:
- bv = bn[prop] = bv.lower()
- if (isinstance(propclass, String) or
- isinstance(propclass, Date)):
- # it might be a string that's really an integer
- try:
- av = int(av)
- bv = int(bv)
- except:
- pass
+ av = int(av)
+ bv = int(bv)
+ except:
+ pass
+ if dir == '+':
+ r = cmp(av, bv)
+ if r != 0: return r
+ elif dir == '-':
+ r = cmp(bv, av)
+ if r != 0: return r
+
+ # Link properties are sorted according to the value of
+ # the "order" property on the linked nodes if it is
+ # present; or otherwise on the key string of the linked
+ # nodes; or finally on the node ids.
+ elif isinstance(propclass, Link):
+ link = db.classes[propclass.classname]
+ if av is None and bv is not None: return -1
+ if av is not None and bv is None: return 1
+ if av is None and bv is None: continue
+ if link.getprops().has_key('order'):
if dir == '+':
- r = cmp(av, bv)
+ r = cmp(link.get(av, 'order'),
+ link.get(bv, 'order'))
if r != 0: return r
elif dir == '-':
- r = cmp(bv, av)
+ r = cmp(link.get(bv, 'order'),
+ link.get(av, 'order'))
if r != 0: return r
-
- # Link properties are sorted according to the value of
- # the "order" property on the linked nodes if it is
- # present; or otherwise on the key string of the linked
- # nodes; or finally on the node ids.
- elif isinstance(propclass, Link):
- link = db.classes[propclass.classname]
- if av is None and bv is not None: return -1
- if av is not None and bv is None: return 1
- if av is None and bv is None: continue
- if link.getprops().has_key('order'):
- if dir == '+':
- r = cmp(link.get(av, 'order'),
- link.get(bv, 'order'))
- if r != 0: return r
- elif dir == '-':
- r = cmp(link.get(bv, 'order'),
- link.get(av, 'order'))
- if r != 0: return r
- elif link.getkey():
- key = link.getkey()
- if dir == '+':
- r = cmp(link.get(av, key), link.get(bv, key))
- if r != 0: return r
- elif dir == '-':
- r = cmp(link.get(bv, key), link.get(av, key))
- if r != 0: return r
- else:
- if dir == '+':
- r = cmp(av, bv)
- if r != 0: return r
- elif dir == '-':
- r = cmp(bv, av)
- if r != 0: return r
-
- # Multilink properties are sorted according to how many
- # links are present.
- elif isinstance(propclass, Multilink):
+ elif link.getkey():
+ key = link.getkey()
if dir == '+':
- r = cmp(len(av), len(bv))
+ r = cmp(link.get(av, key), link.get(bv, key))
if r != 0: return r
elif dir == '-':
- r = cmp(len(bv), len(av))
+ r = cmp(link.get(bv, key), link.get(av, key))
if r != 0: return r
- elif isinstance(propclass, Number) or isinstance(propclass, Boolean):
+ else:
if dir == '+':
r = cmp(av, bv)
+ if r != 0: return r
elif dir == '-':
r = cmp(bv, av)
-
- # end for dir, prop in list:
- # end for list in sort, group:
+ if r != 0: return r
+
+ # Multilink properties are sorted according to how many
+ # links are present.
+ elif isinstance(propclass, Multilink):
+ if dir == '+':
+ r = cmp(len(av), len(bv))
+ if r != 0: return r
+ elif dir == '-':
+ r = cmp(len(bv), len(av))
+ if r != 0: return r
+ elif isinstance(propclass, Number) or isinstance(propclass, Boolean):
+ if dir == '+':
+ r = cmp(av, bv)
+ elif dir == '-':
+ r = cmp(bv, av)
+
+ # end for dir, prop in sort, group:
# if all else fails, compare the ids
return cmp(a[0], b[0])
if not properties.has_key('files'):
properties['files'] = hyperdb.Multilink("file")
if not properties.has_key('nosy'):
- properties['nosy'] = hyperdb.Multilink("user")
+ # note: journalling is turned off as it really just wastes
+ # space. this behaviour may be overridden in an instance
+ properties['nosy'] = hyperdb.Multilink("user", do_journal="no")
if not properties.has_key('superseder'):
properties['superseder'] = hyperdb.Multilink(classname)
Class.__init__(self, db, classname, **properties)
#
#$Log: not supported by cvs2svn $
+#Revision 1.65 2002/08/30 08:35:45 richard
+#minor edits
+#
#Revision 1.64 2002/08/22 07:57:11 richard
#Consistent quoting
#
index 7ac724bb4fac5bd3644811d981cef3fc1f51cb9b..95b5befbd676548c9bad8542e2b5308a900babd2 100644 (file)
-# $Id: back_gadfly.py,v 1.6 2002-08-30 08:35:16 richard Exp $
+# $Id: back_gadfly.py,v 1.7 2002-09-01 04:32:30 richard Exp $
__doc__ = '''
About Gadfly
============
sort spec
"filterspec" is {propname: value(s)}
- "sort" is ['+propname', '-propname', 'propname', ...]
- "group is ['+propname', '-propname', 'propname', ...]
+ "sort" and "group" are (dir, prop) where dir is '+', '-' or None
+ and prop is a prop name or None
"search_matches" is {nodeid: marker}
'''
cn = self.classname
# figure the order by clause
orderby = []
ordercols = []
- if sort:
- for entry in sort:
- if entry[0] != '-':
- orderby.append('_'+entry)
- ordercols.append(entry)
- else:
- orderby.append('_'+entry[1:]+' desc')
- ordercols.append(entry)
+ if sort is not None:
+ if sort[0] != '-':
+ orderby.append('_'+sort[1])
+ ordercols.append(sort[1])
+ else:
+ orderby.append('_'+sort[1]+' desc')
+ ordercols.append(sort[1])
# figure the group by clause
groupby = []
groupcols = []
- if group:
- for entry in group:
- if entry[0] != '-':
- groupby.append('_'+entry)
- groupcols.append(entry)
- else:
- groupby.append('_'+entry[1:]+' desc')
- groupcols.append(entry[1:])
+ if group is not None:
+ if group[0] != '-':
+ groupby.append('_'+group[1])
+ groupcols.append(group[1])
+ else:
+ groupby.append('_'+group[1]+' desc')
+ groupcols.append(group[1])
# construct the SQL
frum = ','.join(frum)
if not properties.has_key('files'):
properties['files'] = hyperdb.Multilink("file")
if not properties.has_key('nosy'):
- properties['nosy'] = hyperdb.Multilink("user")
+ # note: journalling is turned off as it really just wastes
+ # space. this behaviour may be overridden in an instance
+ properties['nosy'] = hyperdb.Multilink("user", do_journal="no")
if not properties.has_key('superseder'):
properties['superseder'] = hyperdb.Multilink(classname)
Class.__init__(self, db, classname, **properties)
#
# $Log: not supported by cvs2svn $
+# Revision 1.6 2002/08/30 08:35:16 richard
+# very basic filter support
+#
# Revision 1.5 2002/08/23 05:33:32 richard
# implemented multilink changes (and a unit test)
#
index d78b27f9f8034f176cf7c29718d4c42938faad02..89193822f499a5be15aacfbdd4082f98087052f7 100755 (executable)
# search_matches is None or a set (dict of {nodeid: {propname:[nodeid,...]}})
# filterspec is a dict {propname:value}
# sort and group are lists of propnames
-
+ # sort and group are (dir, prop) where dir is '+', '-' or None
+ # and prop is a prop name or None
+
where = {'_isdel':0}
mlcriteria = {}
regexes = {}
if sort or group:
sortspec = []
rev = []
- for propname in group + sort:
+ for dir, propname in group, sort:
+ if propname is None: continue
isreversed = 0
- if propname[0] == '-':
- propname = propname[1:]
+ if dir == '-':
isreversed = 1
try:
prop = getattr(v, propname)
if not properties.has_key('files'):
properties['files'] = hyperdb.Multilink("file")
if not properties.has_key('nosy'):
- properties['nosy'] = hyperdb.Multilink("user")
+ # note: journalling is turned off as it really just wastes
+ # space. this behaviour may be overridden in an instance
+ properties['nosy'] = hyperdb.Multilink("user", do_journal="no")
if not properties.has_key('superseder'):
properties['superseder'] = hyperdb.Multilink(classname)
Class.__init__(self, db, classname, **properties)
diff --git a/roundup/cgi/client.py b/roundup/cgi/client.py
index bcc0a16157a5dcbbf2ddb29d142c120b7da7d0ef..81695009a719c6a80a4aa59eb6077e4a4d51882d 100644 (file)
--- a/roundup/cgi/client.py
+++ b/roundup/cgi/client.py
-# $Id: client.py,v 1.1 2002-08-30 08:28:44 richard Exp $
+# $Id: client.py,v 1.2 2002-09-01 04:32:30 richard Exp $
__doc__ = """
WWW request handler (also used in the stand-alone server).
'login': 'login_action',
'logout': 'logout_action',
'register': 'register_action',
+ 'search': 'search_action',
}
def handle_action(self):
''' Determine whether there should be an _action called.
"login" -> self.login_action
"logout" -> self.logout_action
"register" -> self.register_action
+ "search" -> self.search_action
'''
if not self.form.has_key(':action'):
"files" property. Attach the file to the message created from
the __note if it's supplied.
'''
- cn = self.classname
- cl = self.db.classes[cn]
+ cl = self.db.classes[self.classname]
# check permission
userid = self.db.user.lookup(self.user)
- if not self.db.security.hasPermission('Edit', userid, cn):
+ if not self.db.security.hasPermission('Edit', userid, self.classname):
self.error_message.append(
- _('You do not have permission to edit %s' %cn))
+ _('You do not have permission to edit %(classname)s' %
+ self.__dict__))
+ return
# perform the edit
- props = parsePropsFromForm(self.db, cl, self.form, self.nodeid)
+ try:
+ props = parsePropsFromForm(self.db, cl, self.form, self.nodeid)
- # make changes to the node
- props = self._changenode(props)
+ # make changes to the node
+ props = self._changenode(props)
- # handle linked nodes
- self._post_editnode(self.nodeid)
+ # handle linked nodes
+ self._post_editnode(self.nodeid)
+
+ except (ValueError, KeyError), message:
+ self.error_message.append(_('Error: ') + str(message))
+ return
# commit now that all the tricky stuff is done
self.db.commit()
message = _('nothing changed')
# redirect to the item's edit page
- raise Redirect, '%s/%s%s?:ok_message=%s'%(self.base, cn, self.nodeid,
- urllib.quote(message))
+ raise Redirect, '%s/%s%s?:ok_message=%s'%(self.base, self.classname,
+ self.nodeid, urllib.quote(message))
def newitem_action(self):
''' Add a new item to the database.
This follows the same form as the edititem_action
'''
# check permission
- cn = self.classname
userid = self.db.user.lookup(self.user)
- if not self.db.security.hasPermission('Edit', userid, cn):
+ if not self.db.security.hasPermission('Edit', userid, self.classname):
self.error_message.append(
- _('You do not have permission to create %s' %cn))
+ _('You do not have permission to create %s' %self.classname))
# XXX
# cl = self.db.classes[cn]
self.nodeid = nid
# and some nice feedback for the user
- message = _('%(classname)s created ok')%{'classname': cn}
+ message = _('%(classname)s created ok')%self.__dict__
+ except (ValueError, KeyError), message:
+ self.error_message.append(_('Error: ') + str(message))
+ return
except:
# oops
self.db.rollback()
s = StringIO.StringIO()
traceback.print_exc(None, s)
self.error_message.append('<pre>%s</pre>'%cgi.escape(s.getvalue()))
+ return
# redirect to the new item's page
- raise Redirect, '%s/%s%s?:ok_message=%s'%(self.base, cn, nid,
- urllib.quote(message))
+ raise Redirect, '%s/%s%s?:ok_message=%s'%(self.base, self.classname,
+ nid, urllib.quote(message))
def genericedit_action(self):
''' Performs an edit of all of a class' items in one go.
non-existent ID) and removed lines are retired.
'''
userid = self.db.user.lookup(self.user)
- if not self.db.security.hasPermission('Edit', userid):
+ if not self.db.security.hasPermission('Edit', userid, self.classname):
raise Unauthorised, _("You do not have permission to access"\
" %(action)s.")%{'action': self.classname}
- w = self.write
- cn = self.classname
- cl = self.db.classes[cn]
+ cl = self.db.classes[self.classname]
idlessprops = cl.getprops(protected=0).keys()
props = ['id'] + idlessprops
# confirm correct weight
if len(idlessprops) != len(values):
- w(_('Not enough values on line %(line)s'%{'line':line}))
+ message=(_('Not enough values on line %(line)s'%{'line':line}))
return
# extract the new values
message = _('items edited OK')
# redirect to the class' edit page
- raise Redirect, '%s/%s?:ok_message=%s'%(self.base, cn,
+ raise Redirect, '%s/%s?:ok_message=%s'%(self.base, self.classname,
urllib.quote(message))
def _changenode(self, props):
link = self.db.classes[link]
link.set(nodeid, **{property: nid})
+ def search_action(self):
+ ''' Mangle some of the form variables.
+
+ Set the form ":filter" variable based on the values of the
+ filter variables - if they're set to anything other than
+ "dontcare" then add them to :filter.
+ '''
+ # add a faked :filter form variable for each filtering prop
+ props = self.db.classes[self.classname].getprops()
+ for key in self.form.keys():
+ if not props.has_key(key): continue
+ if not self.form[key].value: continue
+ self.form.value.append(cgi.MiniFieldStorage(':filter', key))
def remove_action(self, dre=re.compile(r'([^\d]+)(\d+)')):
# XXX handle this !
if isinstance(proptype, hyperdb.String):
value = form[key].value.strip()
elif isinstance(proptype, hyperdb.Password):
- value = password.Password(form[key].value.strip())
+ value = form[key].value.strip()
+ if not value:
+ # ignore empty password values
+ continue
+ value = password.Password(value)
elif isinstance(proptype, hyperdb.Date):
value = form[key].value.strip()
if value:
index a01fa7c768b2740d13e176a2e2eb12b0e22989f2..2a04f5ab44ea1eba69522dccd351f10bd4587ae7 100644 (file)
-import sys, cgi, urllib, os
+import sys, cgi, urllib, os, re
from roundup import hyperdb, date
from roundup.i18n import _
def __repr__(self):
return '<HTMLClass(0x%x) %s>'%(id(self), self.classname)
- def __getattr__(self, attr):
+ def __getitem__(self, item):
''' return an HTMLItem instance'''
- #print 'getattr', (self, attr)
- if attr == 'creator':
+ #print 'getitem', (self, attr)
+ if item == 'creator':
return HTMLUser(self.client)
- if not self.props.has_key(attr):
- raise AttributeError, attr
- prop = self.props[attr]
+ if not self.props.has_key(item):
+ raise KeyError, item
+ prop = self.props[item]
# look up the correct HTMLProperty class
for klass, htmlklass in propclasses:
else:
value = None
if isinstance(prop, klass):
- return htmlklass(self.db, '', prop, attr, value)
+ return htmlklass(self.db, '', prop, item, value)
# no good
- raise AttributeError, attr
+ raise KeyError, item
+
+ def __getattr__(self, attr):
+ ''' convenience access '''
+ try:
+ return self[attr]
+ except KeyError:
+ raise AttributeError, attr
def properties(self):
''' Return HTMLProperty for all props
def __repr__(self):
return '<HTMLItem(0x%x) %s %s>'%(id(self), self.classname, self.nodeid)
- def __getattr__(self, attr):
+ def __getitem__(self, item):
''' return an HTMLItem instance'''
- #print 'getattr', (self, attr)
- if attr == 'id':
+ if item == 'id':
return self.nodeid
-
- if not self.props.has_key(attr):
- raise AttributeError, attr
- prop = self.props[attr]
+ if not self.props.has_key(item):
+ raise KeyError, item
+ prop = self.props[item]
# get the value, handling missing values
- value = self.klass.get(self.nodeid, attr, None)
+ value = self.klass.get(self.nodeid, item, None)
if value is None:
- if isinstance(self.props[attr], hyperdb.Multilink):
+ if isinstance(self.props[item], hyperdb.Multilink):
value = []
# look up the correct HTMLProperty class
for klass, htmlklass in propclasses:
if isinstance(prop, klass):
- return htmlklass(self.db, self.nodeid, prop, attr, value)
+ return htmlklass(self.db, self.nodeid, prop, item, value)
- # no good
- raise AttributeError, attr
+ raise KeyErorr, item
+
+ def __getattr__(self, attr):
+ ''' convenience access to properties '''
+ try:
+ return self[attr]
+ except KeyError:
+ raise AttributeError, attr
def submit(self, label="Submit Changes"):
''' Generate a submit button (and action hidden element)
def plain(self, escape=0):
if self.value is None:
return _('[unselected]')
- linkcl = self.db.classes[self.klass.classname]
+ linkcl = self.db.classes[self.prop.classname]
k = linkcl.labelprop(1)
value = str(linkcl.get(self.value, k))
if escape:
s = 'selected '
l.append(_('<option %svalue="-1">- no selection -</option>')%s)
if linkcl.getprops().has_key('order'):
- sort_on = 'order'
+ sort_on = ('+', 'order')
else:
- sort_on = linkcl.labelprop()
- options = linkcl.filter(None, conditions, [sort_on], [])
+ sort_on = ('+', linkcl.labelprop())
+ options = linkcl.filter(None, conditions, sort_on, (None, None))
for optionid in options:
option = linkcl.get(optionid, k)
s = ''
value = self.value[num]
return HTMLItem(self.db, self.prop.classname, value)
+ def reverse(self):
+ ''' return the list in reverse order '''
+ l = self.value[:]
+ l.reverse()
+ return [HTMLItem(self.db, self.prop.classname, value) for value in l]
+
def plain(self, escape=0):
linkcl = self.db.classes[self.prop.classname]
k = linkcl.labelprop(1)
linkcl = self.db.getclass(self.prop.classname)
if linkcl.getprops().has_key('order'):
- sort_on = 'order'
+ sort_on = ('+', 'order')
else:
- sort_on = linkcl.labelprop()
- options = linkcl.filter(None, conditions, [sort_on], [])
+ sort_on = ('+', linkcl.labelprop())
+ options = linkcl.filter(None, conditions, sort_on, (None,None))
height = height or min(len(options), 7)
l = ['<select multiple name="%s" size="%s">'%(self.name, height)]
k = linkcl.labelprop(1)
if self.form.has_key(':columns'):
for entry in handleListCGIValue(self.form[':columns']):
self.columns[entry] = 1
- self.sort = []
+
+ # sorting
+ self.sort = (None, None)
if self.form.has_key(':sort'):
- self.sort = handleListCGIValue(self.form[':sort'])
- self.group = []
+ sort = self.form[':sort'].value
+ if sort.startswith('-'):
+ self.sort = ('-', sort[1:])
+ else:
+ self.sort = ('+', sort)
+ if self.form.has_key(':sortdir'):
+ self.sort = ('-', self.sort[1])
+
+ # grouping
+ self.group = (None, None)
if self.form.has_key(':group'):
- self.group = handleListCGIValue(self.form[':group'])
+ group = self.form[':group'].value
+ if group.startswith('-'):
+ self.group = ('-', group[1:])
+ else:
+ self.group = ('+', group)
+ if self.form.has_key(':groupdir'):
+ self.group = ('-', self.group[1])
+
+ # filtering
self.filter = []
if self.form.has_key(':filter'):
self.filter = handleListCGIValue(self.form[':filter'])
self.filterspec = {}
+ props = self.client.db.getclass(self.classname).getprops()
for name in self.filter:
if self.form.has_key(name):
- self.filterspec[name]=handleListCGIValue(self.form[name])
+ prop = props[name]
+ if (isinstance(prop, hyperdb.Link) or
+ isinstance(prop, hyperdb.Multilink)):
+ self.filterspec[name] = handleListCGIValue(self.form[name])
+ else:
+ self.filterspec[name] = self.form[name].value
+
+ # full-text search argument
+ self.search_text = None
+ if self.form.has_key(':search_text'):
+ self.search_text = self.form[':search_text'].value
def __str__(self):
d = {}
env: %(env)s
'''%d
- def indexargs_form(self):
+ def indexargs_form(self, columns=1, sort=1, group=1, filter=1,
+ filterspec=1):
''' return the current index args as form elements '''
l = []
s = '<input type="hidden" name="%s" value="%s">'
- if self.columns:
+ if columns and self.columns:
l.append(s%(':columns', ','.join(self.columns.keys())))
- if self.sort:
- l.append(s%(':sort', ','.join(self.sort)))
- if self.group:
- l.append(s%(':group', ','.join(self.group)))
- if self.filter:
+ if sort and self.sort is not None:
+ l.append(s%(':sort', self.sort))
+ if group and self.group is not None:
+ l.append(s%(':group', self.group))
+ if filter and self.filter:
l.append(s%(':filter', ','.join(self.filter)))
- for k,v in self.filterspec.items():
- l.append(s%(k, ','.join(v)))
+ if filterspec:
+ for k,v in self.filterspec.items():
+ l.append(s%(k, ','.join(v)))
return '\n'.join(l)
def indexargs_href(self, url, args):
l = ['%s=%s'%(k,v) for k,v in args.items()]
if self.columns:
l.append(':columns=%s'%(','.join(self.columns.keys())))
- if self.sort:
- l.append(':sort=%s'%(','.join(self.sort)))
- if self.group:
- l.append(':group=%s'%(','.join(self.group)))
+ if self.sort is not None:
+ l.append(':sort=%s'%self.sort)
+ if self.group is not None:
+ l.append(':group=%s'%self.group)
if self.filter:
l.append(':filter=%s'%(','.join(self.filter)))
for k,v in self.filterspec.items():
# get the list of ids we're batching over
klass = self.client.db.getclass(self.classname)
- l = klass.filter(None, filterspec, sort, group)
+ if self.search_text:
+ matches = self.client.db.indexer.search(
+ re.findall(r'\b\w{2,25}\b', self.search_text), klass)
+ else:
+ matches = None
+ l = klass.filter(matches, filterspec, sort, group)
# figure batch args
if self.form.has_key(':pagesize'):
def __init__(self, client, classname, l, 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)
# overwrite so we can late-instantiate the HTMLItem instance
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)
+ if index != self.last_index:
+ self.last_item = self.current_item
+ self.last_index = index
+
# wrap the return in an HTMLItem
- return HTMLItem(self.client.db, self.classname,
+ self.current_item = HTMLItem(self.client.db, self.classname,
self._sequence[index+self.first])
+ return self.current_item
+
+ def propchanged(self, property):
+ ''' Detect if the property marked as being the group property
+ changed in the last iteration fetch
+ '''
+ if (self.last_item is None or
+ self.last_item[property] != self.current_item[property]):
+ return 1
+ return 0
# override these 'cos we don't have access to acquisition
def previous(self):
- print self.start
if self.start == 1:
return None
return Batch(self.client, self.classname, self._sequence, self._size,
index 29242df27c0ede9e4125657637e0e049af8f1928..c5a8ccf3354296e87b30b1a9d470bd66da0f6c87 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: dbinit.py,v 1.23 2002-08-30 08:30:45 richard Exp $
+# $Id: dbinit.py,v 1.24 2002-09-01 04:32:30 richard Exp $
import os
p = db.security.getPermission('Email Access')
db.security.addPermissionToRole('User', p)
+ # May users view other user information? Comment these lines out
+ # if you don't want them to
+ p = db.security.getPermission('View', 'user')
+ db.security.addPermissionToRole('User', p)
+
# Assign the appropriate permissions to the anonymous user's Anonymous
# Role. Choices here are:
# - Allow anonymous users to register through the web
#
# $Log: not supported by cvs2svn $
+# Revision 1.23 2002/08/30 08:30:45 richard
+# allow perms on user class
+#
# Revision 1.22 2002/08/01 00:56:22 richard
# Added the web access and email access permissions, so people can restrict
# access to users who register through the email interface (for example).
index b8e7ab0c14d9cfaf08058a6ef7a052925c4ff241..1d5d38c61136bb260013667a696644f2762e7f0f 100644 (file)
whatever. It's a good idea to have the issues on the front page though
-->
<span tal:replace="structure python:db.issue.renderWith('index',
- sort=['-activity'], group=['priority'], filter=['status'],
- columns={'id':1,'activity':1,'title':1,'creator':1,'assignedto':1},
+ sort=('-', 'activity'), group=('+', 'priority'), filter=['status'],
+ columns={'id':1,'activity':1,'title':1,'creator':1,'assignedto':1,
+ 'priority':1},
filterspec={'status':['-1','1','2','3','4','5','6','7']})" />
diff --git a/roundup/templates/classic/html/issue.index b/roundup/templates/classic/html/issue.index
index f726492161f143f5db59bcec15b2ba15d944f602..13b316fbb6377ae919e563dad4fb1a398bfeae06 100644 (file)
<!-- dollarId: issue.index,v 1.2 2001/07/29 04:07:37 richard Exp dollar-->
<tal:block tal:define="batch request/batch">
- <table width="100%" border=0 cellspacing=0 cellpadding=2 class="list">
- <tr class="list-header">
- <th align="left" tal:condition="exists:request/columns/id">ID</th>
- <th align="left" tal:condition="exists:request/columns/activity">Activity</th>
- <th align="left" tal:condition="exists:request/columns/priority">Priority</th>
- <th align="left" tal:condition="exists:request/columns/title">Title</th>
- <th align="left" tal:condition="exists:request/columns/status">Status</th>
- <th align="left" tal:condition="exists:request/columns/assignedto">Assigned To</th>
- </tr>
- <tr tal:repeat="i batch"
- tal:attributes="class python:['row-normal', 'row-alt'][repeat['i'].even()]">
- <td valign="top" tal:condition="exists:request/columns/id"
- tal:content="i/id"></td>
- <td valign="top" tal:condition="exists:request/columns/activity"
- tal:content="i/activity/reldate"></td>
- <td valign="top" tal:condition="exists:request/columns/priority"
- tal:content="i/priority"></td>
- <td valign="top" tal:condition="exists:request/columns/title">
- <a tal:attributes="href string:issue${i/id}"
- tal:content="python:i.title.value or '[no title]'">title</a>
+ <table class="list">
+ <tr>
+ <th tal:condition="exists:request/columns/priority">Priority</th>
+ <th tal:condition="exists:request/columns/id">ID</th>
+ <th tal:condition="exists:request/columns/activity">Activity</th>
+ <th tal:condition="exists:request/columns/title">Title</th>
+ <th tal:condition="exists:request/columns/status">Status</th>
+ <th tal:condition="exists:request/columns/creator">Created By</th>
+ <th tal:condition="exists:request/columns/assignedto">Assigned To</th>
+ </tr>
+ <tal:block tal:repeat="i batch">
+ <tr tal:condition="python:batch.propchanged(request.group[1])">
+ <th tal:attributes="colspan python:len(request.columns)"
+ tal:content="python:i[request.group[1]]" class="group">
+ </th>
+ </tr>
+ <tr tal:attributes="class python:['normal', 'alt'][repeat['i'].even()]">
+ <td tal:condition="exists:request/columns/priority"
+ tal:content="i/priority"></td>
+ <td tal:condition="exists:request/columns/id"
+ tal:content="i/id"></td>
+ <td tal:condition="exists:request/columns/activity"
+ tal:content="i/activity/reldate"></td>
+ <td tal:condition="exists:request/columns/title">
+ <a tal:attributes="href string:issue${i/id}"
+ tal:content="python:i.title.value or '[no title]'">title</a>
+ </td>
+ <td tal:condition="exists:request/columns/status"
+ tal:content="i/status"></td>
+ <td tal:condition="exists:request/columns/creator"
+ tal:content="i/creator"></td>
+ <td tal:condition="exists:request/columns/assignedto"
+ tal:content="i/assignedto"></td>
+ </tr>
+ </tal:block>
+ <tr>
+ <td style="padding: 0" tal:attributes="colspan python:len(request.columns)">
+ <table class="list">
+ <tr><th style="text-align: left">
+ <a tal:define="prev batch/previous" tal:condition="prev"
+ tal:attributes="href python:request.indexargs_href(request.classname,
+ {':startwith':prev.start, ':pagesize':prev.size})"><< previous</a>
+
+ </th>
+ <th style="text-align: right">
+ <a tal:define="next batch/next" tal:condition="next"
+ tal:attributes="href python:request.indexargs_href(request.classname,
+ {':startwith':next.start, ':pagesize':next.size})">next >></a>
+
+ </th></tr>
+ </table>
</td>
- <td valign="top" tal:condition="exists:request/columns/status"
- tal:content="i/status"></td>
- <td valign="top" tal:condition="exists:request/columns/assignedto"
- tal:content="i/assignedto/username | default"></td>
</tr>
- </table>
- <table width="100%" border=0 cellspacing=0 cellpadding=2>
- <tr class="list-header">
- <td width="50%" align="left">
-<a tal:define="prev batch/previous" tal:condition="prev"
- tal:attributes="href python:request.indexargs_href(request.classname,
- {':startwith':prev.start, ':pagesize':prev.size})"><< previous</a>
- </td><td width="50%" align="right">
-<a tal:define="next batch/next" tal:condition="next"
- tal:attributes="href python:request.indexargs_href(request.classname,
- {':startwith':next.start, ':pagesize':next.size})">next >></a>
- </tr>
- <tr class="list-header" tal:condition="batch">
- <td colspan="2">
- <form method="GET" tal:attributes="action request/classname">
- Pagesize: <input type="text" name=":pagesize" size="3" value="50">
- Starting at: <input type="text" name=":startwith" size="3" value="0">
- <br>
- <xml:block tal:condition="python:'status' in request.filter">
- Status:
- <input type="hidden" name=":filter" value="status">
- <select name="status">
- <option value="-1,1,2,3,4,5,6,7"
- tal:attributes="selected python:'-1,1,2,3,4,5,6,7'.split(',')==request.filterspec['status']">not resolved</option>
- <option value="-1"
- tal:attributes="selected python:['-1']==request.filterspec['status']">not selected</option>
- <option tal:repeat="s db/status/list"
- tal:attributes="value s/id;
- selected python:[s]==request.filterspec['status']"
- tal:content="s/name">status to filter on</option>
- </select>
- </xml:block>
+</table>
- <xml:block tal:condition="python:'assignedto' in request.filter">
- Assigned To:
- <input type="hidden" name=":filter" value="assignedto">
- <select name="assignedto">
- <option value="-1"
- tal:attributes="selected python:['-1']==request.filterspec['assignedto']">not selected</option>
- <option tal:repeat="s db/user/list"
- tal:attributes="value s/id;
- selected python:[s]==request.filterspec['assignedto']"
- tal:content="s/username">person to filter on</option>
- </select>
- </xml:block>
-
- <br>
- Show:
- <tal:block tal:repeat="col python:'id activity priority title status assignedto'.split()">
- <span tal:replace="col" /><input type="checkbox" name=":columns" tal:attributes="value col;
- checked python:request.columns.has_key(col)">
- </tal:block>
- <br>
- Sort on:
- <select name=":sort">
- <option tal:repeat="col python:request.columns.keys()"
- tal:attributes="value col; selected python:col in request.sort"
+<form method="GET" tal:attributes="action request/classname">
+ <tal:block tal:replace="structure python:request.indexargs_form(sort=0, group=0)" />
+ <table class="form">
+ <tr tal:condition="batch">
+ <th>Sort on:</th>
+ <td>
+ <select name=":sort">
+ <option value="">- nothing -</option>
+ <option tal:repeat="col python:request.columns.keys()"
+ tal:attributes="value col; selected python:col == request.sort[1]"
tal:content="col">column</option>
</select>
- Group on:
+ </td>
+ <th>Descending:</th>
+ <td><input type="checkbox" name=":sortdir"
+ tal:attributes="checked python:request.sort[0] == '-'">
+ </td>
+ </tr>
+ <tr>
+ <th>Group on:</th>
+ <td>
<select name=":group">
+ <option value="">- nothing -</option>
<option tal:repeat="col python:request.columns.keys()"
- tal:attributes="value col; selected python:col in request.group"
+ tal:attributes="value col; selected python:col == request.group[1]"
tal:content="col">column</option>
</select>
- <input type="submit" value="Redisplay">
- </form>
+ </td>
+ <th>Descending:</th>
+ <td><input type="checkbox" name=":groupdir"
+ tal:attributes="checked python:request.group[0] == '-'">
</td>
</tr>
+ <tr><td colspan="4"><input type="submit" value="Redisplay"></td></tr>
</table>
+</form>
+
</tal:block>
index d07fbec972987567b7d27b302ccba443f0dd6241..750f35cd44eb4bcef153e54c0d0cc2ddbaf49a62 100644 (file)
<!-- dollarId: issue.item,v 1.4 2001/08/03 01:19:43 richard Exp dollar-->
<form method="POST" onSubmit="return submit_once()"
enctype="multipart/form-data">
-<table border=0 cellspacing=0 cellpadding=2>
-
-<tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Title</span></td>
+<table border=0 cellspacing=0 cellpadding=2 class="form">
+<tr>
+ <th nowrap>Title</th>
<td colspan=3 class="form-text" tal:content="structure python:issue.title.field(size=60)">title</td>
</tr>
<tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Created</span></td>
+ <th nowrap>Created</th>
<td class="form-text" tal:content="string:${issue/creation} (${issue/creator/username})">creation (creator)</td>
- <td width=1% nowrap align=right><span class="form-label">Last activity</span></td>
+ <th nowrap>Last activity</th>
<td class="form-text" tal:content="issue/activity">activity</td>
</tr>
<tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Priority</span></td>
+ <th nowrap>Priority</th>
<td class="form-text" tal:content="structure issue/priority/menu">priority</td>
- <td width=1% nowrap align=right><span class="form-label">Status</span></td>
+ <th nowrap>Status</th>
<td class="form-text" tal:content="structure issue/status/menu">status</td>
</tr>
<tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Superseder</span></td>
- <td class="form-text">
+ <th nowrap>Superseder</th>
+ <td>
<span tal:replace="structure python:issue.superseder.field(showid=1)" />
<span tal:replace="structure python:db.issue.classhelp('id,title', label='list', width=500)" />
<span tal:condition="issue/superseder">
<br>View: <span tal:replace="structure python:issue.superseder.link(showid=1)" />
</span>
</td>
- <td width=1% nowrap align=right><span class="form-label">Nosy List</span></td>
- <td class="form-text">
+ <th nowrap>Nosy List</th>
+ <td>
<span tal:replace="structure issue/nosy/field" />
<span tal:replace="structure python:db.user.classhelp('username,realname,address,phone', label='list', width=500)" />
</td>
</tr>
<tr class="form">
- <td width=1% nowrap align=right>
- <span class="form-label">Assigned To</span>
- </td>
+ <th nowrap>Assigned To</th>
<td class="form-text" tal:content="structure issue/assignedto/menu">
assignedto menu
</td>
</tr>
<tr class="form">
- <td width=1% nowrap align=right>
- <span class="form-label">Change Note</span>
- </td>
+ <th nowrap>Change Note</th>
<td colspan=3 class="form-text">
<textarea name="__note" wrap="hard" rows="5" cols="60"></textarea>
</td>
</tr>
<tr class="form">
- <td width=1% nowrap align=right>
- <span class="form-label">File</span>
- </td>
+ <th nowrap>File</th>
<td colspan=3 class="form-text">
<input type="file" name="__file" size="60">
</td>
submit button will go here
</td>
</tr>
-
</table>
-<table width="100%" cellspacing=0 cellpadding=0 border=0 tal:condition="exists:item">
- <tr class="msg-header">
- <td colspan=2><b>Messages</b></td>
- </tr>
- <tal:block tal:condition="issue/messages" tal:repeat="msg issue/messages">
- <tr>
- <td class="msg-header" tal:content="msg/date">date</td>
- <td class="msg-header" tal:content="msg/author">author</td>
- </tr>
- <tr>
- <td class="msg-content" colspan="2" tal:content="msg/content">content</td>
- </tr>
- </tal:block>
+<tal:block tal:condition="exists:item">
+ <table class="messages" tal:condition="issue/messages">
+ <tr><th colspan=2 class="header">Messages</th></tr>
+ <tal:block tal:repeat="msg issue/messages/reverse">
+ <tr>
+ <th tal:content="string:Author: ${msg/author}">author</th>
+ <th tal:content="string:Date: ${msg/date}">date</th>
+ </tr>
+ <tr>
+ <td colspan="2"><pre tal:content="msg/content">content</pre></td>
+ </tr>
+ </tal:block>
+ </table>
- <tr class="file-header">
- <td colspan="2"><b>Files</b></td>
- </tr>
- <tal:block tal:condition="issue/files">
- <tr>
- <td class="file-header">File name</td>
- <td class="file-header">Uploaded</td>
- </tr>
+ <table class="files" tal:condition="issue/files">
+ <tr><th colspan="2" class="header">Files</th></tr>
+ <tr><th>File name</th><th>Uploaded</th></tr>
<tr tal:repeat="file issue/files">
<td>
<a tal:attributes="href string:file${file/id}/${file/name}"
<span tal:content="file/creation">creation date</span>
</td>
</tr>
- </tal:block>
+ </table>
- <tr class="history-header">
- <td colspan="2"><b>History</b></td>
- </tr>
- <tr>
- <td colspan="2" tal:content="structure issue/history">history</td>
- </tr>
-</table>
+ <table class="history">
+ <tr><th colspan="2" class="header">History</th></tr>
+ <tr><td colspan="2" tal:content="structure issue/history">history</td></tr>
+ </table>
+</tal:block>
</form>
diff --git a/roundup/templates/classic/html/issue.search b/roundup/templates/classic/html/issue.search
index accf7aed62b41b5035f4aa799de3a28437c3216d..30ed9884978fd2d8314a4a3343251a843ec66a63 100644 (file)
<form method="GET" tal:attributes="action request/classname">
<input type="hidden" name=":action" value="search">
-Columns you may display:
-<tal:block
- tal:repeat="n python:'id activity priority title status assignedto'.split()">
- <span tal:content="n">name</span>
- <input type="checkbox" name=":columns"
- tal:attributes="value n;
- checked python:request.columns.has_key(n)">
-</tal:block>
-<br>
-Sort on: <input type="radio" name=":sort" tal:repeat="n request/sort"
- tal:attributes="value n;
- checked python:n in request.sort">
-<br>
-Group on:
-<input type="radio" name=":group" tal:repeat="n request/group"
- tal:attributes="value n;
- checked python:n in request.group">
-
-<br>
-Priority:
-<span tal:replace="structure issue/priority/menu" />
-
-<br>
-Status:
-<select name="status">
- <option value="dontcare">don't care</option>
- <option value="-1,1,2,3,4,5,6,7">not resolved</option>
- <option value="-1">not selected</option>
- <option value="dontcare">------------</option>
- <option tal:repeat="s db/status/list" tal:attributes="value s/id"
- tal:content="s/name">status to filter on</option>
-</select>
-
-<br>
-Assigned To:
-<select name="assignedto">
- <option value="dontcare">don't care</option>
- <option tal:attributes="value request/user/id">assigned to me</option>
- <option value="-1">unassigned</option>
- <option value="-1">------------</option>
- <option tal:repeat="s db/user/list" tal:attributes="value s/id"
- tal:content="s/username">user to filter on</option>
-</select>
-
-<tal:block tal:repeat="n python:request.filterspec.keys()">
- Filter: <input tal:repeat="v python:request.filterspec[n]"
- tal:attributes="name n; value v">
-</tal:block>
-
-<br>
-Pagesize:
- <input type="text" name=":pagesize" size="3" value="50">
-
-<br>
-Start With:
-<input type="text" name=":startwith" size="3" value="0">
-
-<br>
-<input type="submit" value="Search">
+<table class="form" tal:define="
+ cols python:'id activity priority title status assignedto'.split();
+ defsort python:['activity'];
+ defgroup python:['priority'];
+ defdisp python:'id activity title status assignedto'.split()">
+
+<tr class="form-header">
+ <th> </th>
+ <th>Filter on</th><th>Display</th><th>Sort on</th><th>Group on</th>
+</tr>
+
+<tr>
+ <th>All text*:</th>
+ <td><input name=":search_text"></td>
+ <td> </td>
+ <td> </td>
+ <td> </td>
+</tr>
+
+<tr>
+ <th>Title:</th>
+ <td><input name="title"></td>
+ <td><input type="checkbox" name=":columns" value="title" checked></td>
+ <td><input type="radio" name=":sort" value="title"></td>
+ <td> </td>
+</tr>
+
+<tr>
+ <th>Created:</th>
+ <td><input name="activity"></td>
+ <td><input type="checkbox" name=":columns" value="created"></td>
+ <td><input type="radio" name=":sort" value="created"></td>
+ <td><input type="radio" name=":group" value="created"></td>
+</tr>
+
+<tr>
+ <th>Creator:</th>
+ <td>
+ <select name="creator">
+ <option value="">don't care</option>
+ <option tal:attributes="value request/user/id">created by me</option>
+ <option value="-1">------------</option>
+ <option tal:repeat="s db/user/list" tal:attributes="value s/id"
+ tal:content="s/username">user to filter on</option>
+ </select>
+ </td>
+ <td><input type="checkbox" name=":columns" value="creator" checked></td>
+ <td><input type="radio" name=":sort" value="creator"></td>
+ <td><input type="radio" name=":group" value="creator"></td>
+</tr>
+
+<tr>
+ <th>Activity:</th>
+ <td><input name="activity"></td>
+ <td><input type="checkbox" name=":columns" value="activity" checked></td>
+ <td><input type="radio" name=":sort" value="activity" checked></td>
+ <td> </td>
+</tr>
+
+<tr>
+ <th>Priority:</th>
+ <td>
+ <select name="priority">
+ <option value="">don't care</option>
+ <option value="-1">not selected</option>
+ <option value="">------------</option>
+ <option tal:repeat="s db/priority/list" tal:attributes="value s/id"
+ tal:content="s/name">priority to filter on</option>
+ </select>
+ </td>
+ <td><input type="checkbox" name=":columns" value="priority"></td>
+ <td><input type="radio" name=":sort" value="priority"></td>
+ <td><input type="radio" name=":group" value="priority" checked></td>
+</tr>
+
+<tr>
+ <th>Status:</th>
+ <td>
+ <select name="status">
+ <option value="">don't care</option>
+ <option value="-1,1,2,3,4,5,6,7">not resolved</option>
+ <option value="-1">not selected</option>
+ <option value="">------------</option>
+ <option tal:repeat="s db/status/list" tal:attributes="value s/id"
+ tal:content="s/name">status to filter on</option>
+ </select>
+ </td>
+ <td><input type="checkbox" name=":columns" value="status" checked></td>
+ <td><input type="radio" name=":sort" value="status"></td>
+ <td><input type="radio" name=":group" value="status"></td>
+</tr>
+
+<tr>
+ <th>Assigned To:</th>
+ <td>
+ <select name="assignedto">
+ <option value="">don't care</option>
+ <option tal:attributes="value request/user/id">assigned to me</option>
+ <option value="-1">unassigned</option>
+ <option value="">------------</option>
+ <option tal:repeat="s db/user/list" tal:attributes="value s/id"
+ tal:content="s/username">user to filter on</option>
+ </select>
+ </td>
+ <td><input type="checkbox" name=":columns" value="assignedto" checked></td>
+ <td><input type="radio" name=":sort" value="assignedto"></td>
+ <td><input type="radio" name=":group" value="assignedto"></td>
+</tr>
+
+<tr>
+<th>Pagesize:</th>
+<td><input type="text" name=":pagesize" size="3" value="50"></td>
+</tr>
+
+<tr>
+<th>Start With:</th>
+<td><input type="text" name=":startwith" size="3" value="0"></td>
+</tr>
+
+<tr>
+<th>Sort Descending:</th>
+<td><input type="checkbox" name=":sortdir" checked>
+</td>
+
+<tr>
+<th>Group Descending:</th>
+<td><input type="checkbox" name=":groupdir">
+</td>
+</tr>
+
+<tr><td> </td>
+<td><input type="submit" value="Search"></td>
+</tr>
+
+<tr><td> </td>
+ <td colspan="4" class="help">*: The "all text" field will look in message
+ bodies and issue titles</td>
+</tr>
+</table>
</form>
index a1b0da853a280e2809fd0e281c504133781a01f2..c6d6f98fb5e45786ea7a2cdaa4e8f1196c296c74 100644 (file)
<table border=0 cellspacing=0 cellpadding=0>
<tr>
- <td colspan="2" class="page-header">
+ <td class="page-header-left"> </td>
+ <td class="page-header-top">
<h2 tal:content="title">name</h2>
</td>
</tr>
<tr>
<td rowspan="2" valign="top" nowrap class="sidebar">
- <div class="classblock"
+ <p class="classblock"
tal:condition="python:request.user.hasPermission('Edit', 'issue')">
- <a href="issue?:sort=-activity&:group=priority&:filter=status,assignedto&:columns=id,activity,title,creator,assignedto&status=-1,1,2,3,4,5,6,7&assignedto=-1">Unassigned Issues</a><br>
- <a href="issue?:sort=-activity&:group=priority&:filter=status&:columns=id,activity,title,creator,assignedto&status=-1,1,2,3,4,5,6,7">All Issues</a><br>
+ <b>Issues</b><br>
+ <a href="issue?:sort=-activity&:group=priority&:filter=status,assignedto&:columns=id,activity,title,creator,priority&status=-1,1,2,3,4,5,6,7&assignedto=-1">Unassigned Issues</a><br>
+ <a href="issue?:sort=-activity&:group=priority&:filter=status&:columns=id,activity,title,creator,assignedto,priority&status=-1,1,2,3,4,5,6,7">All Issues</a><br>
<a href="issue?:template=search">Search Issues</a><br>
- <a href="issue?:template=item">New Issue</a><br>
- </div>
- <div class="classblock"
+ <a href="issue?:template=item">New Issue</a>
+ </p>
+
+ <p class="classblock"
tal:condition="python:request.user.hasPermission('Edit', None)">
+ <b>Admin</b><br>
<a href="home?:template=classlist">Class List</a><br>
<a href="user">User List</a><br>
- <a href="user?:template=item">Add User</a><br>
- </div>
- <hr>
- <b tal:content="request/user/username">username</b><br>
- <div tal:condition="python:request.user.username=='anonymous'">
- <form method="POST" action=''>
+ <a href="user?:template=item">Add User</a>
+ </p>
+
+ <p class="userblock">
+ <b>Logged in as</b><br><b tal:content="request/user/username">username</b><br>
+ <form method="POST" action=''
+ tal:condition="python:request.user.username=='anonymous'">
<input size="10" name="__login_name"><br>
<input size="10" type="password" name="__login_password"><br>
<input type="submit" name=":action" value="login">
<span tal:replace="structure request/indexargs_form" />
</form>
- </div>
- <div tal:condition="python:request.user.username!='anonymous'">
- <a tal:attributes="href string:issue?:sort=-activity&:group=priority&:filter=status,assignedto&:columns=id,activity,title,creator,assignedto&status=-1,1,2,3,4,5,6,7&assignedto=${request/user/id}">My Issues</a><br>
- <a tal:attributes="href string:user${request/user/id}">My Details</a><br>
- <a href="?:action=logout">Logout</a>
- </div>
+ <tal:block tal:condition="python:request.user.username != 'anonymous'">
+ <a tal:attributes="href string:issue?:sort=-activity&:group=priority&:filter=status,assignedto&:columns=id,activity,title,creator,priority&status=-1,1,2,3,4,5,6,7&assignedto=${request/user/id}">My Issues</a><br>
+ <a tal:attributes="href string:user${request/user/id}">My Details</a><br>
+ <a href="?:action=logout">Logout</a>
+ </tal:block>
+ </p>
</td>
<td>
<p tal:condition="options/error_message | nothing" class="error-message"
index da9d3eac88570e4528121790503ab844cc588e9f..c8bccc06b59eca6babcd7628b3682f80f94c201a 100644 (file)
+/* main page styles */
.body {
font-family: sans-serif, Arial, Helvetica;
+ color: #333333;
}
a:hover { text-decoration: underline; }
a:link { text-decoration: none; }
a { text-decoration: none; }
-p {
- color: #333333;
+.page-header-left {
+ background-color: #ffffee;
+ padding: 5px;
}
-td.sidebar {
- font-family: sans-serif, Arial, Helvetica;
- background-color: #efefef;
- border: none;
+.page-header-top {
+ background-color: #ffffee;
+ border-bottom: 1px solid #ffffbb;
padding: 5px;
}
-.page-header {
- font-family: sans-serif, Arial, Helvetica;
- background-color: #efefef;
- border: none;
+td.sidebar {
+ background-color: #ffffee;
+ border-right: 1px solid #ffffbb;
+ border-bottom: 1px solid #ffffbb;
padding: 5px;
}
-.strong-header {
- font-family: sans-serif, Arial, Helvetica;
- font-weight: bold;
- background-color: #000000;
- color: #ffffff;
+td.sidebar p.classblock {
+ border-top: 1px solid #ffffbb;
+ border-bottom: 1px solid #ffffbb;
+}
+
+td.sidebar p.userblock {
+ background-color: #eeffff;
+ border-top: 1px solid #bbffff;
+ border-bottom: 1px solid #bbffff;
}
td.content {
padding: 1px;
-/*
- border: 1px solid black;
-*/
}
-table.list td {
- border: 0 2 0 2;
- border-left: 1px solid #404070;
- border-right: 1px solid #404070;
+p.ok-message {
+ background-color: #22bb22;
+ padding: 5 5 5 5;
+ color: white;
+ font-weight: bold;
}
-/*
-WAAAAAAH nothing seems to support last-child :(
-table.list td:first-child {
- border-left: 2px solid #404070;
+p.error-message {
+ background-color: #bb2222;
+ padding: 5 5 5 5;
+ color: white;
+ font-weight: bold;
+}
+
+
+/* style for forms */
+table.form {
+ border-spacing: 0px;
+ border-collapse: separate;
+ /* width: 100%; */
+}
+
+.form th {
+ font-weight: bold;
+ color: #333388;
+ text-align: right;
+}
+
+.form-header th {
+ font-weight: bold;
+ color: #333388;
+ background-color: #eeeeff;
+ text-align: left;
+}
+
+.form td.optional {
+ font-weight: bold;
+ font-style: italic;
+ color: #333333;
+}
+
+.form td {
+ color: #333333;
}
-table.list td:last-child {
- border-right: 2px solid #404070;
+
+.form td.html {
+ color: #777777;
+}
+
+/* style for lists */
+table.list {
+ border-spacing: 0px;
+ border-collapse: separate;
+ width: 100%;
}
+
+table.list th {
+ padding: 0 4 0 4;
+ color: #404070;
+ background-color: #eeeeff;
+/*
+ border-right: 1px solid #404070;
*/
-table.list tr:last-child td {
- border-bottom: 2px solid #404070;
+ vertical-align: top;
}
-.list-header {
- background-color: #404070;
- color: white;
- border: none;
+table.list th a:hover { color: white }
+table.list th a:link { color: white }
+table.list th a { color: white }
+table.list th.group {
+ text-align: center;
}
-.section-bar {
- background-color: #e0e0e0;
+table.list td {
+ padding: 0 4 0 4;
+ border: 0 2 0 2;
+ border-right: 1px solid #404070;
+ color: #404070;
+ background-color: white;
+ vertical-align: top;
}
-.row-normal {
- background-color: #ffffff;
- border: none;
+table.list td.normal {
}
-.row-alt {
+table.list td.alt {
background-color: #efefef;
- border: none;
}
-.file-header {
- font-family: sans-serif, Arial, Helvetica;
- font-weight: bold;
- background-color: #41BE62;
- color: #ffffff;
-}
+table.list td:first-child {
+ border-left: 1px solid #404070;
+ border-right: 1px solid #404070;
+}
+/*
+table.list th:first-child {
+ border-left: 1px solid #404070;
+ border-right: 1px solid #404070;
+}
+*/
-.history-header {
- font-family: sans-serif, Arial, Helvetica;
+/* style for message displays */
+table.messages {
+ border-spacing: 0px;
+ border-collapse: separate;
+ width: 100%;
+}
+
+table.messages th.header{
+ padding-top: 10px;
+ border-bottom: 1px solid gray;
font-weight: bold;
- background-color: #739DEE;
- color: #ffffff;
+ background-color: white;
+ color: #707040;
}
-.msg-header {
- font-family: Verdana, Helvetica, sans-serif;
+table.messages th {
font-weight: bold;
- background-color: #EE71AC;
- color: #ffffff;
+ color: black;
+ text-align: left;
}
-.msg-content {
+table.messages td {
font-family: monospace;
- background-color: #ffeaff;
- color: #000000;
+ background-color: #efefef;
+ border-top: 1px solid #afafaf;
+ border-bottom: 1px solid #afafaf;
+ color: black;
}
-p.ok-message {
- background-color: #22bb22;
- padding: 5 5 5 5;
- color: white;
- font-weight: bold;
-}
-p.error-message {
- background-color: #bb2222;
- padding: 5 5 5 5;
- color: white;
- font-weight: bold;
+/* style for file displays */
+table.files {
+ border-spacing: 0px;
+ border-collapse: separate;
+ width: 100%;
}
-tr.form td {
- background-color: #f0f0f0;
-}
-.form-title {
+table.files th.header{
+ padding-top: 10px;
+ border-bottom: 1px solid gray;
font-weight: bold;
- color: #333333;
+ background-color: white;
+ color: #707040;
}
-.form-label {
+table.files th {
+ border-bottom: 1px solid #afafaf;
font-weight: bold;
- color: #333333;
+ text-align: left;
}
-.form-optional {
- font-weight: bold;
- font-style: italic;
- color: #333333;
+table.files td {
+ font-family: monospace;
}
-.form-element {
- color: #000000;
+
+/* style for file displays */
+table.otherinfo {
+ border-spacing: 0px;
+ border-collapse: separate;
+ width: 100%;
}
-.form-text {
- color: #333333;
+table.otherinfo th.header{
+ padding-top: 10px;
+ border-bottom: 1px solid gray;
+ font-weight: bold;
+ background-color: white;
+ color: #707040;
}
-.form-mono {
- font-family: monospace;
+table.otherinfo th {
+ border-bottom: 1px solid #afafaf;
+ font-weight: bold;
+ text-align: left;
}
index d9e84deb9a882f11c074853afdaeba646c7822d8..f44c23392cce2763a32e48be9096df3ee7196771 100644 (file)
You are not allowed to view this page.
</span>
-<form method="POST" onSubmit="return submit_once()"
- enctype="multipart/form-data" tal:condition="editok">
+<tal:block tal:condition="editok">
+<form method="POST" onSubmit="return submit_once()" enctype="multipart/form-data">
-<table border=0 cellspacing=0 cellpadding=2>
- <tr class="strong-header">
- <td colspan=2>Your Details</td>
- </tr>
- <tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Name</span></td>
- <td class="form-text" tal:content="structure user/realname/field">realname</td>
+<table class="form">
+ <tr>
+ <th>Name</th>
+ <td tal:content="structure user/realname/field">realname</td>
</tr>
- <tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Login Name</span></td>
- <td class="form-text" tal:content="structure user/username/field">username</td>
+ <tr>
+ <th>Login Name</th>
+ <td tal:content="structure user/username/field">username</td>
</tr>
- <tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Login Password</span></td>
- <td class="form-text" tal:content="structure user/password/field">password</td>
+ <tr>
+ <th>Login Password</th>
+ <td tal:content="structure user/password/field">password</td>
</tr>
- <tr tal:condition="python:request.user.hasPermission('Web Roles')" class="form">
- <td width=1% nowrap align=right><span class="form-label">Roles</span></td>
- <td class="form-text" tal:content="structure user/roles/field">roles</td>
+ <tr tal:condition="python:request.user.hasPermission('Web Roles')">
+ <th>Roles</th>
+ <td tal:content="structure user/roles/field">roles</td>
</tr>
- <tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Phone</span></td>
- <td class="form-text" tal:content="structure user/phone/field">phone</td>
+ <tr>
+ <th>Phone</th>
+ <td tal:content="structure user/phone/field">phone</td>
</tr>
- <tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Organisation</span></td>
- <td class="form-text" tal:content="structure user/organisation/field">organisation</td>
+ <tr>
+ <th>Organisation</th>
+ <td tal:content="structure user/organisation/field">organisation</td>
</tr>
- <tr class="form">
- <td width=1% nowrap align=right><span class="form-label">E-mail address</span></td>
- <td class="form-text" tal:content="structure user/address/field">address</td>
+ <tr>
+ <th>E-mail address</th>
+ <td tal:content="structure user/address/field">address</td>
</tr>
- <tr class="form">
- <td width=1% nowrap align=right><span class="form-label">Alternate
- E-mail addresses</span><br>
- <span class="form-help">One address per line</span></td>
- <td class="form-text" tal:content="structure user/alternate_addresses/multiline">alternate_addresses</td>
+ <tr>
+ <th>Alternate E-mail addresses<br>One address per line</th>
+ <td tal:content="structure user/alternate_addresses/multiline">alternate_addresses</td>
</tr>
- <tr class="form">
+ <tr>
<td> </td>
- <td colspan=3 class="form-text" tal:content="structure user/submit">
- submit button will go here
- </td>
+ <td colspan=3 tal:content="structure user/submit">submit button here</td>
</tr>
+</table>
+</form>
- <tr class="strong-header"><td colspan=2><b>Queries</b></td></tr>
- <tr class="form" tal:repeat="query user/queries">
- <td colspan=2 tal:content="query">query</td>
+<table class="otherinfo" tal:condition="user/queries">
+ <tr><th class="header">Queries</th></tr>
+ <tr tal:repeat="query user/queries">
+ <td tal:content="query">query</td>
</tr>
+</table>
- <tr class="strong-header"><td colspan=2><b>History</b></td></tr>
+<table class="otherinfo">
+ <tr><th class="header">History</th></tr>
<tr>
- <td colspan="2" tal:content="structure user/history">history</td>
+ <td tal:content="structure user/history">history</td>
</tr>
</table>
+</tal:block>
-</form>
-
-<table border=0 cellspacing=0 cellpadding=2
- tal:condition="python:viewok and not editok">
- <tr class="strong-header">
- <td colspan=2 tal:content="user/realname">realname</td>
+<table class="form" tal:condition="python:viewok and not editok">
+ <tr>
+ <th colspan=2 class="header" tal:content="user/realname">realname</th>
</tr>
<tr>
- <td width=1% nowrap align=right><span class="form-label">Login Name</span></td>
- <td class="form-text" tal:content="user/username">username</td>
+ <th>Login Name</th>
+ <td tal:content="user/username">username</td>
</tr>
<tr>
- <td width=1% nowrap align=right><span class="form-label">Phone</span></td>
- <td class="form-text" tal:content="user/phone">phone</td>
+ <th>Phone</th>
+ <td tal:content="user/phone">phone</td>
</tr>
<tr>
- <td width=1% nowrap align=right><span class="form-label">Organisation</span></td>
- <td class="form-text" tal:content="user/organisation">organisation</td>
+ <th>Organisation</th>
+ <td tal:content="user/organisation">organisation</td>
</tr>
<tr>
- <td width=1% nowrap align=right><span class="form-label">E-mail address</span></td>
- <td class="form-text" tal:content="user/address/email">address</td>
+ <th>E-mail address</th>
+ <td tal:content="user/address/email">address</td>
</tr>
</table>