comparison roundup/cgi/templating.py @ 1204:b862bbf2067a

Replaced the content() callback ickiness with Page Template macro usage changed the default CSS style to be less offensive to some ;) better handling of Page Template compilation errors removed dependency on ComputedAttribute
author Richard Jones <richard@users.sourceforge.net>
date Wed, 25 Sep 2002 02:10:25 +0000
parents 01a143f9382e
children 3a5e05edcd87
comparison
equal deleted inserted replaced
1203:735adcbfc665 1204:b862bbf2067a
20 from roundup.cgi.PageTemplates import PageTemplate 20 from roundup.cgi.PageTemplates import PageTemplate
21 from roundup.cgi.PageTemplates.Expressions import getEngine 21 from roundup.cgi.PageTemplates.Expressions import getEngine
22 from roundup.cgi.TAL.TALInterpreter import TALInterpreter 22 from roundup.cgi.TAL.TALInterpreter import TALInterpreter
23 from roundup.cgi import ZTUtils 23 from roundup.cgi import ZTUtils
24 24
25 # XXX WAH pagetemplates aren't pickleable :(
26 #def getTemplate(dir, name, classname=None, request=None):
27 # ''' Interface to get a template, possibly loading a compiled template.
28 # '''
29 # # source
30 # src = os.path.join(dir, name)
31 #
32 # # see if we can get a compile from the template"c" directory (most
33 # # likely is "htmlc"
34 # split = list(os.path.split(dir))
35 # split[-1] = split[-1] + 'c'
36 # cdir = os.path.join(*split)
37 # split.append(name)
38 # cpl = os.path.join(*split)
39 #
40 # # ok, now see if the source is newer than the compiled (or if the
41 # # compiled even exists)
42 # MTIME = os.path.stat.ST_MTIME
43 # if (not os.path.exists(cpl) or os.stat(cpl)[MTIME] < os.stat(src)[MTIME]):
44 # # nope, we need to compile
45 # pt = RoundupPageTemplate()
46 # pt.write(open(src).read())
47 # pt.id = name
48 #
49 # # save off the compiled template
50 # if not os.path.exists(cdir):
51 # os.makedirs(cdir)
52 # f = open(cpl, 'wb')
53 # pickle.dump(pt, f)
54 # f.close()
55 # else:
56 # # yay, use the compiled template
57 # f = open(cpl, 'rb')
58 # pt = pickle.load(f)
59 # return pt
60
61 templates = {}
62
63 class NoTemplate(Exception): 25 class NoTemplate(Exception):
64 pass 26 pass
65 27
66 def precompileTemplates(dir): 28 class Templates:
67 ''' Go through a directory and precompile all the templates therein 29 templates = {}
68 ''' 30
69 for filename in os.listdir(dir): 31 def __init__(self, dir):
70 if os.path.isdir(filename): continue 32 self.dir = dir
71 if '.' in filename: 33
72 name, extension = filename.split('.') 34 def precompileTemplates(self):
73 getTemplate(dir, name, extension) 35 ''' Go through a directory and precompile all the templates therein
74 else: 36 '''
75 getTemplate(dir, filename, None) 37 for filename in os.listdir(self.dir):
76 38 if os.path.isdir(filename): continue
77 def getTemplate(dir, name, extension, classname=None, request=None): 39 if '.' in filename:
78 ''' Interface to get a template, possibly loading a compiled template. 40 name, extension = filename.split('.')
79 41 self.getTemplate(name, extension)
80 "name" and "extension" indicate the template we're after, which in 42 else:
81 most cases will be "name.extension". If "extension" is None, then 43 self.getTemplate(filename, None)
82 we look for a template just called "name" with no extension. 44
83 45 def get(self, name, extension):
84 If the file "name.extension" doesn't exist, we look for 46 ''' Interface to get a template, possibly loading a compiled template.
85 "_generic.extension" as a fallback. 47
86 ''' 48 "name" and "extension" indicate the template we're after, which in
87 # default the name to "home" 49 most cases will be "name.extension". If "extension" is None, then
88 if name is None: 50 we look for a template just called "name" with no extension.
89 name = 'home' 51
90 52 If the file "name.extension" doesn't exist, we look for
91 # find the source, figure the time it was last modified 53 "_generic.extension" as a fallback.
92 if extension: 54 '''
93 filename = '%s.%s'%(name, extension) 55 # default the name to "home"
94 else: 56 if name is None:
95 filename = name 57 name = 'home'
96 src = os.path.join(dir, filename) 58
97 try: 59 # find the source, figure the time it was last modified
98 stime = os.stat(src)[os.path.stat.ST_MTIME] 60 if extension:
99 except os.error, error: 61 filename = '%s.%s'%(name, extension)
100 if error.errno != errno.ENOENT: 62 else:
101 raise 63 filename = name
102 if not extension: 64 src = os.path.join(self.dir, filename)
103 raise NoTemplate, 'Template file "%s" doesn\'t exist'%name
104
105 # try for a generic template
106 generic = '_generic.%s'%extension
107 src = os.path.join(dir, generic)
108 try: 65 try:
109 stime = os.stat(src)[os.path.stat.ST_MTIME] 66 stime = os.stat(src)[os.path.stat.ST_MTIME]
110 except os.error, error: 67 except os.error, error:
111 if error.errno != errno.ENOENT: 68 if error.errno != errno.ENOENT:
112 raise 69 raise
113 # nicer error 70 if not extension:
114 raise NoTemplate, 'No template file exists for templating '\ 71 raise NoTemplate, 'Template file "%s" doesn\'t exist'%name
115 '"%s" with template "%s" (neither "%s" nor "%s")'%(name, 72
116 extension, filename, generic) 73 # try for a generic template
117 filename = generic 74 generic = '_generic.%s'%extension
118 75 src = os.path.join(self.dir, generic)
119 key = (dir, filename) 76 try:
120 if templates.has_key(key) and stime < templates[key].mtime: 77 stime = os.stat(src)[os.path.stat.ST_MTIME]
121 # compiled template is up to date 78 except os.error, error:
122 return templates[key] 79 if error.errno != errno.ENOENT:
123 80 raise
124 # compile the template 81 # nicer error
125 templates[key] = pt = RoundupPageTemplate() 82 raise NoTemplate, 'No template file exists for templating '\
126 pt.write(open(src).read()) 83 '"%s" with template "%s" (neither "%s" nor "%s")'%(name,
127 pt.id = filename 84 extension, filename, generic)
128 pt.mtime = time.time() 85 filename = generic
129 return pt 86
87 if self.templates.has_key(filename) and \
88 stime < self.templates[filename].mtime:
89 # compiled template is up to date
90 return self.templates[filename]
91
92 # compile the template
93 self.templates[filename] = pt = RoundupPageTemplate()
94 pt.write(open(src).read())
95 pt.id = filename
96 pt.mtime = time.time()
97 return pt
98
99 def __getitem__(self, name):
100 name, extension = os.path.splitext(name)
101 if extension:
102 extension = extension[1:]
103 try:
104 return self.get(name, extension)
105 except NoTemplate, message:
106 raise KeyError, message
130 107
131 class RoundupPageTemplate(PageTemplate.PageTemplate): 108 class RoundupPageTemplate(PageTemplate.PageTemplate):
132 ''' A Roundup-specific PageTemplate. 109 ''' A Roundup-specific PageTemplate.
133 110
134 Interrogate the client to set up the various template variables to 111 Interrogate the client to set up the various template variables to
147 - the current index information (``filterspec``, ``filter`` args, 124 - the current index information (``filterspec``, ``filter`` args,
148 ``properties``, etc) parsed out of the form. 125 ``properties``, etc) parsed out of the form.
149 - methods for easy filterspec link generation 126 - methods for easy filterspec link generation
150 - *user*, the current user node as an HTMLItem instance 127 - *user*, the current user node as an HTMLItem instance
151 - *form*, the current CGI form information as a FieldStorage 128 - *form*, the current CGI form information as a FieldStorage
152 *instance* 129 *tracker*
153 The current instance 130 The current tracker
154 *db* 131 *db*
155 The current database, through which db.config may be reached. 132 The current database, through which db.config may be reached.
156 ''' 133 '''
157 def getContext(self, client, classname, request): 134 def getContext(self, client, classname, request):
158 c = { 135 c = {
159 'options': {}, 136 'options': {},
160 'nothing': None, 137 'nothing': None,
161 'request': request, 138 'request': request,
162 'content': client.content,
163 'db': HTMLDatabase(client), 139 'db': HTMLDatabase(client),
164 'instance': client.instance, 140 'tracker': client.instance,
165 'utils': TemplatingUtils(client), 141 'utils': TemplatingUtils(client),
142 'templates': Templates(client.instance.config.TEMPLATES),
166 } 143 }
167 # add in the item if there is one 144 # add in the item if there is one
168 if client.nodeid: 145 if client.nodeid:
169 if classname == 'user': 146 if classname == 'user':
170 c['context'] = HTMLUser(client, classname, client.nodeid) 147 c['context'] = HTMLUser(client, classname, client.nodeid)
171 else: 148 else:
172 c['context'] = HTMLItem(client, classname, client.nodeid) 149 c['context'] = HTMLItem(client, classname, client.nodeid)
173 else: 150 elif client.db.classes.has_key(classname):
174 c['context'] = HTMLClass(client, classname) 151 c['context'] = HTMLClass(client, classname)
175 return c 152 return c
176 153
177 def render(self, client, classname, request, **options): 154 def render(self, client, classname, request, **options):
178 """Render this Page Template""" 155 """Render this Page Template"""
192 c = self.getContext(client, classname, request) 169 c = self.getContext(client, classname, request)
193 c.update({'options': options}) 170 c.update({'options': options})
194 171
195 # and go 172 # and go
196 output = StringIO.StringIO() 173 output = StringIO.StringIO()
197 TALInterpreter(self._v_program, self._v_macros, 174 TALInterpreter(self._v_program, self.macros,
198 getEngine().getContext(c), output, tal=1, strictinsert=0)() 175 getEngine().getContext(c), output, tal=1, strictinsert=0)()
199 return output.getvalue() 176 return output.getvalue()
200 177
201 class HTMLDatabase: 178 class HTMLDatabase:
202 ''' Return HTMLClasses for valid class fetches 179 ''' Return HTMLClasses for valid class fetches
258 self._db = client.db 235 self._db = client.db
259 236
260 # we want classname to be exposed, but _classname gives a 237 # we want classname to be exposed, but _classname gives a
261 # consistent API for extending Class/Item 238 # consistent API for extending Class/Item
262 self._classname = self.classname = classname 239 self._classname = self.classname = classname
263 if classname is not None: 240 self._klass = self._db.getclass(self.classname)
264 self._klass = self._db.getclass(self.classname) 241 self._props = self._klass.getprops()
265 self._props = self._klass.getprops()
266 242
267 def __repr__(self): 243 def __repr__(self):
268 return '<HTMLClass(0x%x) %s>'%(id(self), self.classname) 244 return '<HTMLClass(0x%x) %s>'%(id(self), self.classname)
269 245
270 def __getitem__(self, item): 246 def __getitem__(self, item):
424 if properties is None: 400 if properties is None:
425 properties = self._klass.getprops(protected=0).keys() 401 properties = self._klass.getprops(protected=0).keys()
426 properties.sort() 402 properties.sort()
427 properties = ','.join(properties) 403 properties = ','.join(properties)
428 return '<a href="javascript:help_window(\'%s?:template=help&' \ 404 return '<a href="javascript:help_window(\'%s?:template=help&' \
429 ':contentonly=1&properties=%s\', \'%s\', \'%s\')"><b>'\ 405 'properties=%s\', \'%s\', \'%s\')"><b>(%s)</b></a>'%(
430 '(%s)</b></a>'%(self.classname, properties, width, height, label) 406 self.classname, properties, width, height, label)
431 407
432 def submit(self, label="Submit New Entry"): 408 def submit(self, label="Submit New Entry"):
433 ''' Generate a submit button (and action hidden element) 409 ''' Generate a submit button (and action hidden element)
434 ''' 410 '''
435 return ' <input type="hidden" name=":action" value="new">\n'\ 411 return ' <input type="hidden" name=":action" value="new">\n'\
445 req = HTMLRequest(self._client) 421 req = HTMLRequest(self._client)
446 req.classname = self.classname 422 req.classname = self.classname
447 req.update(kwargs) 423 req.update(kwargs)
448 424
449 # new template, using the specified classname and request 425 # new template, using the specified classname and request
450 pt = getTemplate(self._db.config.TEMPLATES, self.classname, name) 426 pt = Templates(self._db.config.TEMPLATES).get(self.classname, name)
451 427
452 # use our fabricated request 428 # use our fabricated request
453 return pt.render(self._client, self.classname, req) 429 return pt.render(self._client, self.classname, req)
454 430
455 class HTMLItem(HTMLPermissions): 431 class HTMLItem(HTMLPermissions):
1438 submitted = true; 1414 submitted = true;
1439 return 1; 1415 return 1;
1440 } 1416 }
1441 1417
1442 function help_window(helpurl, width, height) { 1418 function help_window(helpurl, width, height) {
1443 HelpWin = window.open('%s/' + helpurl, 'RoundupHelpWindow', 'scrollbars=yes,resizable=yes,toolbar=no,height='+height+',width='+width); 1419 HelpWin = window.open('%s' + helpurl, 'RoundupHelpWindow', 'scrollbars=yes,resizable=yes,toolbar=no,height='+height+',width='+width);
1444 } 1420 }
1445 </script> 1421 </script>
1446 '''%self.base 1422 '''%self.base
1447 1423
1448 def batch(self): 1424 def batch(self):

Roundup Issue Tracker: http://roundup-tracker.org/