Skip to content

Commit 8e0b53b

Browse files
author
Richard Jones
committed
added generic item editing
. much nicer layout of template rendering errors . added context/is_edit_ok and context/is_view_ok convenience methods and implemented use of them in the classic template
1 parent 8b70a33 commit 8e0b53b

File tree

14 files changed

+214
-96
lines changed

14 files changed

+214
-96
lines changed

CHANGES.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ Fixed:
88
. switched the default issue item display to only show issue summary
99
(add instructions to doc to make it display entire content)
1010

11+
Feature:
12+
. added generic item editing
13+
. much nicer layout of template rendering errors
14+
. added context/is_edit_ok and context/is_view_ok convenience methods and
15+
implemented use of them in the classic template
16+
1117
2002-09-11 0.5.0 beta1
1218
Fixed:
1319
. #576086 ] dumb copying mistake (frontends/ZRoundup.py)

doc/customizing.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Customising Roundup
33
===================
44

5-
:Version: $Revision: 1.32 $
5+
:Version: $Revision: 1.33 $
66

77
.. This document borrows from the ZopeBook section on ZPT. The original is at:
88
http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -877,6 +877,8 @@ classhelp display a link to a javascript popup containing this class'
877877
submit generate a submit button (and action hidden element)
878878
renderWith render this class with the given template.
879879
history returns 'New node - no history' :)
880+
is_edit_ok is the user allowed to Edit the current class?
881+
is_view_ok is the user allowed to View the current class?
880882
=========== =============================================================
881883

882884
Note that if you have a property of the same name as one of the above methods,
@@ -910,6 +912,8 @@ renderQueryForm specific to the "query" class - render the search form for
910912
the query
911913
hasPermission specific to the "user" class - determine whether the user
912914
has a Permission
915+
is_edit_ok is the user allowed to Edit the current item?
916+
is_view_ok is the user allowed to View the current item?
913917
=============== =============================================================
914918

915919

roundup/cgi/PageTemplates/Expressions.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
2626
"""
2727

28-
__version__='$Revision: 1.5 $'[11:-2]
28+
__version__='$Revision: 1.6 $'[11:-2]
2929

3030
import re, sys
3131
from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
@@ -121,7 +121,7 @@ def _eval(self, econtext,
121121
# of path names.
122122
path[i:i+1] = list(val)
123123
base = self._base
124-
__traceback_info__ = 'sub path expression "%s"'%base
124+
__traceback_info__ = 'path expression "%s"'%('/'.join(self._path))
125125
if base == 'CONTEXTS':
126126
ob = econtext.contexts
127127
else:
@@ -264,6 +264,10 @@ def __call__(self, econtext):
264264
def __repr__(self):
265265
return 'defer:%s' % `self._s`
266266

267+
class TraversalError:
268+
def __init__(self, path, name):
269+
self.path = path
270+
self.name = name
267271

268272
def restrictedTraverse(self, path, securityManager,
269273
get=getattr, has=hasattr, N=None, M=[],
@@ -281,8 +285,8 @@ def restrictedTraverse(self, path, securityManager,
281285
#print 'TRAVERSE', (object, path)
282286
done = []
283287
while path:
284-
__traceback_info__ = 'Traversed %r\n ... looking for %r'%(done, path)
285288
name = path.pop()
289+
__traceback_info__ = TraversalError(done, name)
286290

287291
if isinstance(name, TupleType):
288292
object = apply(object, name)
@@ -320,7 +324,7 @@ def restrictedTraverse(self, path, securityManager,
320324
raise
321325
#print '... object is now', `o`
322326
object = o
323-
done.append(o)
327+
done.append((name, o))
324328

325329
return object
326330

roundup/cgi/PageTemplates/MultiMapping.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@ def push(self, store):
2222
self.stores.append(store)
2323
def pop(self):
2424
return self.stores.pop()
25-
25+
def items(self):
26+
l = []
27+
for store in self.stores:
28+
l = l + store.items()
29+
return l

roundup/cgi/cgitb.py

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#
22
# This module was written by Ka-Ping Yee, <[email protected]>.
33
#
4-
# $Id: cgitb.py,v 1.5 2002-09-10 01:07:05 richard Exp $
4+
# $Id: cgitb.py,v 1.6 2002-09-13 03:31:18 richard Exp $
55

66
__doc__ = """
77
Extended CGI traceback handler by Ka-Ping Yee, <[email protected]>.
88
"""
99

10-
import sys, os, types, string, keyword, linecache, tokenize, inspect
10+
import sys, os, types, string, keyword, linecache, tokenize, inspect, cgi
1111
import pydoc, traceback
1212

1313
from roundup.i18n import _
@@ -20,43 +20,55 @@ def breaker():
2020
def niceDict(indent, dict):
2121
l = []
2222
for k,v in dict.items():
23-
l.append('%s%s: %r'%(indent,k,v))
23+
l.append('<tr><td><strong>%s</strong></td><td>%s</td></tr>'%(k,
24+
cgi.escape(repr(v))))
2425
return '\n'.join(l)
2526

2627
def pt_html(context=5):
27-
import cgi
28-
etype, evalue = sys.exc_type, sys.exc_value
29-
if type(etype) is types.ClassType:
30-
etype = etype.__name__
31-
pyver = 'Python ' + string.split(sys.version)[0] + '<br>' + sys.executable
32-
head = pydoc.html.heading(
33-
'<font size=+1><strong>%s</strong>: %s</font>'%(etype, evalue),
34-
'#ffffff', '#777777', pyver)
35-
36-
head = head + _('<p>A problem occurred in your template</p><pre>')
37-
38-
l = []
28+
l = ['<h1>Templating Error</h1>'
29+
'<p class="help">Debugging information follows</p>'
30+
'<ol>']
31+
from roundup.cgi.PageTemplates.Expressions import TraversalError
3932
for frame, file, lnum, func, lines, index in inspect.trace(context):
4033
args, varargs, varkw, locals = inspect.getargvalues(frame)
4134
if locals.has_key('__traceback_info__'):
4235
ti = locals['__traceback_info__']
43-
l.append(str(ti))
36+
if isinstance(ti, TraversalError):
37+
s = []
38+
for name, info in ti.path:
39+
s.append('<li>"%s" (%s)</li>'%(name,cgi.escape(repr(info))))
40+
s = '\n'.join(s)
41+
l.append('<li>Looking for "%s", current path:<ol>%s</ol></li>'%(
42+
ti.name, s))
43+
else:
44+
l.append('<li>In %s</li>'%cgi.escape(str(ti)))
4445
if locals.has_key('__traceback_supplement__'):
4546
ts = locals['__traceback_supplement__']
4647
if len(ts) == 2:
4748
supp, context = ts
48-
l.append('in template %r'%context.id)
49+
l.append('<li>A problem occurred in your template "%s"</li>'%
50+
str(context.id))
4951
elif len(ts) == 3:
5052
supp, context, info = ts
51-
l.append('in expression %r\n current variables:\n%s\n%s\n'%(info,
52-
niceDict(' ', context.global_vars),
53-
niceDict(' ', context.local_vars)))
54-
# context._scope_stack))
55-
56-
l.append('\n')
57-
l.append(''.join(traceback.format_exception(etype, evalue,
58-
sys.exc_traceback)))
59-
return head + cgi.escape('\n'.join(l)) + '</pre><p>&nbsp;</p>'
53+
l.append('''
54+
<li>While evaluating the %r expression on line %d
55+
<table class="otherinfo" style="font-size: 90%%">
56+
<tr><th colspan="2" class="header">Current variables:</th></tr>
57+
%s
58+
%s
59+
</table></li>
60+
'''%(info, context.position[0], niceDict(' ', context.global_vars),
61+
niceDict(' ', context.local_vars)))
62+
63+
l.append('''
64+
</ol>
65+
<table style="font-size: 80%%; color: gray">
66+
<tr><th class="header" align="left">Full traceback:</th></tr>
67+
<tr><td><pre>%s</pre></td></tr>
68+
</table>'''%cgi.escape(''.join(traceback.format_exception(sys.exc_type,
69+
sys.exc_value, sys.exc_traceback))))
70+
l.append('<p>&nbsp;</p>')
71+
return '\n'.join(l)
6072

6173
def html(context=5):
6274
etype, evalue = sys.exc_type, sys.exc_value

roundup/cgi/templating.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,10 @@ def getContext(self, client, classname, request):
155155
}
156156
# add in the item if there is one
157157
if client.nodeid:
158-
c['context'] = HTMLItem(client, classname, client.nodeid)
158+
if classname == 'user':
159+
c['context'] = HTMLUser(client, classname, client.nodeid)
160+
else:
161+
c['context'] = HTMLItem(client, classname, client.nodeid)
159162
else:
160163
c['context'] = HTMLClass(client, classname)
161164
return c
@@ -218,15 +221,34 @@ def lookupIds(db, prop, ids, num_re=re.compile('-?\d+')):
218221
l.append(cl.lookup(entry))
219222
return l
220223

221-
class HTMLClass:
224+
class HTMLPermissions:
225+
''' Helpers that provide answers to commonly asked Permission questions.
226+
'''
227+
def is_edit_ok(self):
228+
''' Is the user allowed to Edit the current class?
229+
'''
230+
return self._db.security.hasPermission('Edit', self._client.userid,
231+
self._classname)
232+
def is_view_ok(self):
233+
''' Is the user allowed to View the current class?
234+
'''
235+
return self._db.security.hasPermission('View', self._client.userid,
236+
self._classname)
237+
def is_only_view_ok(self):
238+
''' Is the user only allowed to View (ie. not Edit) the current class?
239+
'''
240+
return self.is_view_ok() and not self.is_edit_ok()
241+
242+
class HTMLClass(HTMLPermissions):
222243
''' Accesses through a class (either through *class* or *db.<classname>*)
223244
'''
224245
def __init__(self, client, classname):
225246
self._client = client
226247
self._db = client.db
227248

228-
# we want classname to be exposed
229-
self.classname = classname
249+
# we want classname to be exposed, but _classname gives a
250+
# consistent API for extending Class/Item
251+
self._classname = self.classname = classname
230252
if classname is not None:
231253
self._klass = self._db.getclass(self.classname)
232254
self._props = self._klass.getprops()
@@ -399,7 +421,7 @@ def renderWith(self, name, **kwargs):
399421
# use our fabricated request
400422
return pt.render(self._client, self.classname, req)
401423

402-
class HTMLItem:
424+
class HTMLItem(HTMLPermissions):
403425
''' Accesses through an *item*
404426
'''
405427
def __init__(self, client, classname, nodeid):
@@ -627,6 +649,7 @@ def __init__(self, client, classname, nodeid):
627649

628650
# used for security checks
629651
self._security = client.db.security
652+
630653
_marker = []
631654
def hasPermission(self, role, classname=_marker):
632655
''' Determine if the user has the Role.
@@ -638,6 +661,20 @@ def hasPermission(self, role, classname=_marker):
638661
classname = self._default_classname
639662
return self._security.hasPermission(role, self._nodeid, classname)
640663

664+
def is_edit_ok(self):
665+
''' Is the user allowed to Edit the current class?
666+
Also check whether this is the current user's info.
667+
'''
668+
return self._db.security.hasPermission('Edit', self._client.userid,
669+
self._classname) or self._nodeid == self._client.userid
670+
671+
def is_view_ok(self):
672+
''' Is the user allowed to View the current class?
673+
Also check whether this is the current user's info.
674+
'''
675+
return self._db.security.hasPermission('Edit', self._client.userid,
676+
self._classname) or self._nodeid == self._client.userid
677+
641678
class HTMLProperty:
642679
''' String, Number, Date, Interval HTMLProperty
643680

roundup/templates/classic/dbinit.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: dbinit.py,v 1.28 2002-09-11 02:49:56 richard Exp $
18+
# $Id: dbinit.py,v 1.29 2002-09-13 03:31:18 richard Exp $
1919

2020
import os
2121

@@ -93,15 +93,15 @@ def open(name=None):
9393
# SECURITY SETTINGS
9494
#
9595
# new permissions for this schema
96-
for cl in 'issue', 'file', 'msg', 'user', 'keyword':
96+
for cl in 'issue', 'file', 'msg', 'user', 'query', 'keyword':
9797
db.security.addPermission(name="Edit", klass=cl,
9898
description="User is allowed to edit "+cl)
9999
db.security.addPermission(name="View", klass=cl,
100100
description="User is allowed to access "+cl)
101101

102102
# Assign the access and edit permissions for issue, file and message
103103
# to regular users now
104-
for cl in 'issue', 'file', 'msg', 'keyword':
104+
for cl in 'issue', 'file', 'msg', 'query', 'keyword':
105105
p = db.security.getPermission('View', cl)
106106
db.security.addPermissionToRole('User', p)
107107
p = db.security.getPermission('Edit', cl)

roundup/templates/classic/html/_generic.index

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
<!-- dollarId: issue.index,v 1.2 2001/07/29 04:07:37 richard Exp dollar-->
2-
<tal:block tal:define="
3-
editok python:request.user.hasPermission('Edit') or
4-
context.id == request.user.id;
5-
viewok python:request.user.hasPermission('View')">
6-
7-
<span tal:condition="python:not (viewok or editok)">
2+
<span tal:condition="python:not (context.is_view_ok() or context.is_edit_ok())">
83
You are not allowed to view this page.
94
</span>
105

11-
<tal:block tal:condition="editok">
6+
<tal:block tal:condition="context/is_edit_ok">
127
<p class="form-help">
138
You may edit the contents of the <span tal:replace="request/classname" />
149
class using this form. Commas, newlines and double quotes (") must be
@@ -35,8 +30,7 @@ You are not allowed to view this page.
3530
</form>
3631
</tal:block>
3732

38-
<tal:block tal:condition="python:viewok and not editok">
33+
<tal:block tal:condition="context/is_only_view_ok">
3934
view ok
4035
</tal:block>
4136

42-
</tal:block>

roundup/templates/classic/html/_generic.item

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
<span tal:condition="python:not (context.is_view_ok() or context.is_edit_ok())">
2+
You are not allowed to view this page.
3+
</span>
4+
15
<form method="POST" onSubmit="return submit_once()"
2-
enctype="multipart/form-data">
6+
enctype="multipart/form-data" tal:condition="context/is_edit_ok">
37

48
<input type="hidden" name=":template" value="item">
59
<input type="hidden" name=":required" value="title">
@@ -22,8 +26,24 @@
2226
</table>
2327

2428

25-
<tal:block tal:condition="context/id">
29+
<table class="form" tal:condition="context/is_only_view_ok">
30+
31+
<tr tal:repeat="prop python:db[context._classname].properties()">
32+
<tal:block tal:condition="python:prop._name not in ('id', 'creator',
33+
'creation', 'activity')">
34+
<th tal:content="prop/_name"></th>
35+
<td tal:content="structure python:context[prop._name].field()"></td>
36+
</tal:block>
37+
</tr>
38+
<tr>
39+
<td>&nbsp;</td>
40+
<td colspan=3 tal:content="structure context/submit">
41+
submit button will go here
42+
</td>
43+
</tr>
44+
</table>
45+
46+
47+
<tal:block tal:condition="python:context.id and context.is_view_ok()">
2648
<tal:block tal:replace="structure context/history" />
2749
</tal:block>
28-
29-
</form>

roundup/templates/classic/html/issue.index

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
<!-- dollarId: issue.index,v 1.2 2001/07/29 04:07:37 richard Exp dollar-->
2-
<tal:block tal:define="batch request/batch">
2+
<tal:block tal:condition="not:context/is_view_ok">
3+
You are not allowed to view this page.
4+
</tal:block>
5+
6+
<tal:block tal:define="batch request/batch" tal:condition="context/is_view_ok">
37
<table class="list">
48
<tr>
59
<th tal:condition="request/show/priority">Priority</th>

0 commit comments

Comments
 (0)