diff --git a/roundup/cgi_client.py b/roundup/cgi_client.py
index c7a41dc317b418d5cca3f1aa1895f935dd135d0b..1f1c02dd35b21eb939bb0ee7e2f8267a61dcfa9d 100644 (file)
--- a/roundup/cgi_client.py
+++ b/roundup/cgi_client.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: cgi_client.py,v 1.149 2002-07-30 20:04:38 gmcm Exp $
+# $Id: cgi_client.py,v 1.162 2002-08-23 04:42:30 richard Exp $
__doc__ = """
WWW request handler (also used in the stand-alone server).
'''
security.addPermission(name="Web Registration",
description="User may register through the web")
+ p = security.addPermission(name="Web Access",
+ description="User may access the web interface")
+ security.addPermissionToRole('Admin', p)
# doing Role stuff through the web - make sure Admin can
p = security.addPermission(name="Web Roles",
# figure who the user is
user_name = self.user
userid = self.db.user.lookup(user_name)
+ default_queries = 1
+ links = []
+ if user_name != 'anonymous':
+ try:
+ default_queries = self.db.user.get(userid, 'defaultqueries')
+ except KeyError:
+ pass
# figure all the header links
- if hasattr(self.instance, 'HEADER_INDEX_LINKS'):
- links = []
- for name in self.instance.HEADER_INDEX_LINKS:
- spec = getattr(self.instance, name + '_INDEX')
- # skip if we need to fill in the logged-in user id and
- # we're anonymous
- if (spec['FILTERSPEC'].has_key('assignedto') and
- spec['FILTERSPEC']['assignedto'] in ('CURRENT USER',
- None) and user_name == 'anonymous'):
- continue
- links.append(self.make_index_link(name))
- else:
- # no config spec - hard-code
- links = [
- _('All <a href="issue?status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>'),
- _('Unassigned <a href="issue?assignedto=-1&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status,assignedto&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>')
- ]
+ if default_queries:
+ if hasattr(self.instance, 'HEADER_INDEX_LINKS'):
+ for name in self.instance.HEADER_INDEX_LINKS:
+ spec = getattr(self.instance, name + '_INDEX')
+ # skip if we need to fill in the logged-in user id and
+ # we're anonymous
+ if (spec['FILTERSPEC'].has_key('assignedto') and
+ spec['FILTERSPEC']['assignedto'] in ('CURRENT USER',
+ None) and user_name == 'anonymous'):
+ continue
+ links.append(self.make_index_link(name))
+ else:
+ # no config spec - hard-code
+ links = [
+ _('All <a href="issue?status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>'),
+ _('Unassigned <a href="issue?assignedto=-1&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status,assignedto&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>')
+ ]
user_info = _('<a href="login">Login</a>')
add_links = ''
return []
def index_sort(self):
- # first try query string
+ # first try query string / simple form
x = self.index_arg(':sort')
if x:
+ if self.index_arg(':descending'):
+ return ['-'+x[0]]
return x
# nope - get the specs out of the form
specs = []
all_columns = self.db.getclass(cn).getprops().keys()
all_columns.sort()
index.filter_section('', filter, columns, group, all_columns, sort,
- filterspec, pagesize, 0)
+ filterspec, pagesize, 0, 0)
self.pagefoot()
- index.db = index.cl = index.properties = None
- index.clear()
# XXX deviates from spec - loses the '+' (that's a reserved character
# in URLS
startwith = int(self.form[':startwith'].value)
else:
startwith = 0
+ simpleform = 1
+ if self.form.has_key(':advancedsearch'):
+ simpleform = 0
if self.form.has_key('Query') and self.form['Query'].value == 'Save':
# format a query string
self.pagehead(_('%(instancename)s: Index of %(classname)s')%{
'classname': cn, 'instancename': self.instance.INSTANCE_NAME})
-
+
index = htmltemplate.IndexTemplate(self, self.instance.TEMPLATES, cn)
try:
- index.render(filterspec, search_text, filter, columns, sort,
- group, show_customization=show_customization,
- show_nodes=show_nodes, pagesize=pagesize, startwith=startwith)
+ index.render(filterspec=filterspec, search_text=search_text,
+ filter=filter, columns=columns, sort=sort, group=group,
+ show_customization=show_customization,
+ show_nodes=show_nodes, pagesize=pagesize, startwith=startwith,
+ simple_search=simpleform)
except htmltemplate.MissingTemplateError:
self.basicClassEditPage()
self.pagefoot()
for name in props:
w('<th align=left>%s</th>'%name)
w('</tr>')
- for nodeid in cl.filter(None, {}, sort, []): #cl.list():
+ for nodeid in cl.filter(None, {}, sort, []):
w('<tr>')
for name in props:
value = cgi.escape(str(cl.get(nodeid, name)))
'''
cn = self.classname
cl = self.db.classes[cn]
+ keys = self.form.keys()
+ fromremove = 0
if self.form.has_key(':multilink'):
- link = self.form[':multilink'].value
- designator, linkprop = link.split(':')
- xtra = ' for <a href="%s">%s</a>' % (designator, designator)
+ # is the multilink there because we came from remove()?
+ if self.form.has_key(':target'):
+ xtra = ''
+ fromremove = 1
+ message = _('%s removed' % self.index_arg(":target")[0])
+ else:
+ link = self.form[':multilink'].value
+ designator, linkprop = link.split(':')
+ xtra = ' for <a href="%s">%s</a>' % (designator, designator)
else:
xtra = ''
-
+
# possibly perform an edit
- keys = self.form.keys()
# don't try to set properties if the user has just logged in
- if keys and not self.form.has_key('__login_name'):
+ if keys and not fromremove and not self.form.has_key('__login_name'):
try:
userid = self.db.user.lookup(self.user)
- if not self.db.security.hasPermission('Edit', userid):
+ if not self.db.security.hasPermission('Edit', userid, cn):
message = _('You do not have permission to edit %s' %cn)
else:
props = parsePropsFromForm(self.db, cl, self.form, self.nodeid)
# make changes to the node
- self._changenode(props)
+ props = self._changenode(props)
# handle linked nodes
self._post_editnode(self.nodeid)
# and some nice feedback for the user
filterspec = self.index_filterspec(filter, queries.get(self.nodeid, 'klass'))
if self.form.has_key('search_text'):
search_text = self.form['search_text'].value
+ search_text = urllib.quote(search_text)
else:
search_text = ''
if self.form.has_key(':pagesize'):
url += '&:pagesize=%s' % pagesize
if search_text:
url += '&search_text=%s' % search_text
- qname = self.form['name'].value
- chgd = []
- if qname != queries.get(self.nodeid, 'name'):
- chgd.append('name')
if url != queries.get(self.nodeid, 'url'):
- chgd.append('url')
- if chgd:
- queries.set(self.nodeid, name=qname, url=url)
- message = _('%(changes)s edited ok')%{'changes': ', '.join(chgd)}
+ queries.set(self.nodeid, url=url)
+ message = _('url edited ok')
else:
message = _('nothing changed')
else:
props['files'] = cl.get(self.nodeid, 'files') + files
# make the changes
- cl.set(self.nodeid, **props)
+ return cl.set(self.nodeid, **props)
def _createnode(self):
''' create a node based on the contents of the form
def showuser(self, message=None, num_re=re.compile('^\d+$')):
'''Display a user page for editing. Make sure the user is allowed
- to edit this node, and also check for password changes.
+ to edit this node, and also check for password changes.
+
+ Note: permission checks for this node are handled in the template.
'''
user = self.db.user
except IndexError:
raise NotFound, 'user%s'%self.nodeid
- # ok, so we need to be able to edit everything, or be this node's
- # user
- userid = self.db.user.lookup(self.user)
- if (not self.db.security.hasPermission('Edit', userid)
- and self.user != node_user):
- raise Unauthorised, _("You do not have permission to access"\
- " %(action)s.")%{'action': self.classname +
- str(self.nodeid)}
-
#
# perform any editing
#
def showfile(self):
''' display a file
'''
+ # nothing in xtrapath - edit the file's metadata
+ if self.xtrapath is None:
+ return self.shownode()
+
+ # something in xtrapath - download the file
nodeid = self.nodeid
cl = self.db.classes[self.classname]
try:
mime_type = 'text/plain'
self.header(headers={'Content-Type': mime_type})
self.write(cl.get(nodeid, 'content'))
-
+
def permission(self):
'''
'''
''' display a list of all the classes in the database
'''
userid = self.db.user.lookup(self.user)
- raise Unauthorised, _("You do not have permission to access"\
- " %(action)s.")%{'action': 'all classes'}
+ if not self.db.security.hasPermission('Edit', userid):
+ raise Unauthorised, _("You do not have permission to access"\
+ " %(action)s.")%{'action': 'all classes'}
self.pagehead(_('Table of classes'), message)
classnames = self.db.classes.keys()
# re-open the database for real, using the user
self.opendb(self.user)
password = cl.get(uid, 'password')
- self.set_cookie(self.user, self.form['password'].value)
+ self.set_cookie(self.user, password)
return 1
def set_cookie(self, user, password):
self.login()
def opendb(self, user):
- ''' Open the database - but include the definition of the sessions db.
+ ''' Open the database.
'''
- # open the db
- self.db = self.instance.open(user)
+ # open the db if the user has changed
+ if not hasattr(self, 'db') or user != self.db.journaltag:
+ self.db = self.instance.open(user)
def main(self):
''' Wrap the request and handle unauthorised requests
# now figure which function to call
path = self.split_path
+ self.xtrapath = None
# default action to index if the path has no information in it
if not path or path[0] in ('', 'index'):
action = 'index'
else:
action = path[0]
+ if len(path) > 1:
+ self.xtrapath = path[1:]
self.desired_action = action
- # Everthing ignores path[1:]
- # - The file download link generator actually relies on this - it
- # appends the name of the file to the URL so the download file name
- # is correct, but doesn't actually use it.
-
# everyone is allowed to try to log in
if action == 'login_action':
# try to login
return
# figure the resulting page
action = self.form['__destination_url'].value
- if not action:
- action = 'index'
- self.do_action(action)
- return
# allow anonymous people to register
- if action == 'newuser_action':
+ elif action == 'newuser_action':
# try to add the user
if not self.newuser_action():
return
# figure the resulting page
action = self.form['__destination_url'].value
- if not action:
- action = 'index'
+
+ # ok, now we have figured out who the user is, make sure the user
+ # has permission to use this interface
+ userid = self.db.user.lookup(self.user)
+ if not self.db.security.hasPermission('Web Access', userid):
+ raise Unauthorised, \
+ _("You do not have permission to access this interface.")
# re-open the database for real, using the user
self.opendb(self.user)
- # just a regular action
- self.do_action(action)
+ # make sure we have a sane action
+ if not action:
+ action = 'index'
- # commit all changes to the database
- self.db.commit()
+ # just a regular action
+ try:
+ self.do_action(action)
+ except Unauthorised, message:
+ # if unauth is raised here, then a page header will have
+ # been displayed
+ self.write('<p class="system-msg">%s</p>'%message)
+ else:
+ # commit all changes to the database
+ self.db.commit()
def do_action(self, action, dre=re.compile(r'([^\d]+)(\d+)'),
nre=re.compile(r'new(\w+)'), sre=re.compile(r'search(\w+)')):
if action == 'logout':
self.logout()
return
+ if action == 'remove':
+ self.remove()
+ return
# see if we're to display an existing node
m = dre.match(action)
raise NotFound, self.classname
self.list()
+ def remove(self, dre=re.compile(r'([^\d]+)(\d+)')):
+ target = self.index_arg(':target')[0]
+ m = dre.match(target)
+ if m:
+ classname = m.group(1)
+ nodeid = m.group(2)
+ cl = self.db.getclass(classname)
+ cl.retire(nodeid)
+ # now take care of the reference
+ parentref = self.index_arg(':multilink')[0]
+ parent, prop = parentref.split(':')
+ m = dre.match(parent)
+ if m:
+ self.classname = m.group(1)
+ self.nodeid = m.group(2)
+ cl = self.db.getclass(self.classname)
+ value = cl.get(self.nodeid, prop)
+ value.remove(nodeid)
+ cl.set(self.nodeid, **{prop:value})
+ func = getattr(self, 'show%s'%self.classname)
+ return func()
+ else:
+ raise NotFound, parent
+ else:
+ raise NotFound, target
class ExtendedClient(Client):
'''Includes pages and page heading information that relate to the
value = form[key].value.strip()
# see if it's the "no selection" choice
if value == '-1':
- # don't set this property
- continue
+ value = None
else:
# handle key values
link = cl.properties[key].classname
#
# $Log: not supported by cvs2svn $
+# Revision 1.161 2002/08/19 00:21:10 richard
+# removed debug prints
+#
+# Revision 1.160 2002/08/19 00:20:34 richard
+# grant web access to admin ;)
+#
+# Revision 1.159 2002/08/16 04:29:41 richard
+# bugfix
+#
+# Revision 1.158 2002/08/15 00:40:10 richard
+# cleanup
+#
+# Revision 1.157 2002/08/13 20:16:09 gmcm
+# Use a real parser for templates.
+# Rewrite htmltemplate to use the parser (hack, hack).
+# Move the "do_XXX" methods to template_funcs.py.
+# Redo the funcion tests (but not Template tests - they're hopeless).
+# Simplified query form in cgi_client.
+# Ability to delete msgs, files, queries.
+# Ability to edit the metadata on files.
+#
+# Revision 1.156 2002/08/01 15:06:06 gmcm
+# Use same regex to split search terms as used to index text.
+# Fix to back_metakit for not changing journaltag on reopen.
+# Fix htmltemplate's do_link so [No <whatever>] strings are href'd.
+# Fix bogus "nosy edited ok" msg - the **d syntax does NOT share d between caller and callee.
+#
+# Revision 1.155 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).
+# Also added "security" command to the roundup-admin interface to display the
+# Role/Permission config for an instance.
+#
+# Revision 1.154 2002/07/31 23:57:36 richard
+# . web forms may now unset Link values (like assignedto)
+#
+# Revision 1.153 2002/07/31 22:40:50 gmcm
+# Fixes to the search form and saving queries.
+# Fixes to sorting in back_metakit.py.
+#
+# Revision 1.152 2002/07/31 22:04:14 richard
+# cleanup
+#
+# Revision 1.151 2002/07/30 21:37:43 richard
+# oops, thanks Duncan Booth for spotting this one
+#
+# Revision 1.150 2002/07/30 20:43:18 gmcm
+# Oops, fix the permission check!
+#
+# Revision 1.149 2002/07/30 20:04:38 gmcm
+# Adapt metakit backend to new security scheme.
+# Put some more permission checks in cgi_client.
+#
# Revision 1.148 2002/07/30 16:09:11 gmcm
# Simple optimization.
#