comparison roundup/cgi/templating.py @ 1016:d6c13142e7b9

Keep a cache of compiled PageTemplates. Reinstated query saving.
author Richard Jones <richard@users.sourceforge.net>
date Tue, 03 Sep 2002 02:58:11 +0000
parents 10ed4791f969
children 16498e77e3ff
comparison
equal deleted inserted replaced
1014:8816534e6a1a 1016:d6c13142e7b9
1 import sys, cgi, urllib, os, re, os.path 1 import sys, cgi, urllib, os, re, os.path, time
2 2
3 from roundup import hyperdb, date 3 from roundup import hyperdb, date
4 from roundup.i18n import _ 4 from roundup.i18n import _
5 5
6 6 try:
7 import cPickle as pickle
8 except ImportError:
9 import pickle
10 try:
11 import cStringIO as StringIO
12 except ImportError:
13 import StringIO
7 try: 14 try:
8 import StructuredText 15 import StructuredText
9 except ImportError: 16 except ImportError:
10 StructuredText = None 17 StructuredText = None
11 18
27 sys.modules['ExtensionClass'] = ExtensionClass 34 sys.modules['ExtensionClass'] = ExtensionClass
28 if not sys.modules.has_key('Acquisition'): 35 if not sys.modules.has_key('Acquisition'):
29 import Acquisition 36 import Acquisition
30 sys.modules['Acquisition'] = Acquisition 37 sys.modules['Acquisition'] = Acquisition
31 38
32 # now it's safe to import PageTemplates and ZTUtils 39 # now it's safe to import PageTemplates, TAL and ZTUtils
33 from PageTemplates import PageTemplate 40 from PageTemplates import PageTemplate
41 from PageTemplates.Expressions import getEngine
42 from TAL.TALInterpreter import TALInterpreter
34 import ZTUtils 43 import ZTUtils
44
45 # XXX WAH pagetemplates aren't pickleable :(
46 #def getTemplate(dir, name, classname=None, request=None):
47 # ''' Interface to get a template, possibly loading a compiled template.
48 # '''
49 # # source
50 # src = os.path.join(dir, name)
51 #
52 # # see if we can get a compile from the template"c" directory (most
53 # # likely is "htmlc"
54 # split = list(os.path.split(dir))
55 # split[-1] = split[-1] + 'c'
56 # cdir = os.path.join(*split)
57 # split.append(name)
58 # cpl = os.path.join(*split)
59 #
60 # # ok, now see if the source is newer than the compiled (or if the
61 # # compiled even exists)
62 # MTIME = os.path.stat.ST_MTIME
63 # if (not os.path.exists(cpl) or os.stat(cpl)[MTIME] < os.stat(src)[MTIME]):
64 # # nope, we need to compile
65 # pt = RoundupPageTemplate()
66 # pt.write(open(src).read())
67 # pt.id = name
68 #
69 # # save off the compiled template
70 # if not os.path.exists(cdir):
71 # os.makedirs(cdir)
72 # f = open(cpl, 'wb')
73 # pickle.dump(pt, f)
74 # f.close()
75 # else:
76 # # yay, use the compiled template
77 # f = open(cpl, 'rb')
78 # pt = pickle.load(f)
79 # return pt
80
81 templates = {}
82
83 def getTemplate(dir, name, classname=None, request=None):
84 ''' Interface to get a template, possibly loading a compiled template.
85 '''
86 # find the source, figure the time it was last modified
87 src = os.path.join(dir, name)
88 stime = os.stat(src)[os.path.stat.ST_MTIME]
89
90 key = (dir, name)
91 if templates.has_key(key) and stime < templates[key].mtime:
92 # compiled template is up to date
93 return templates[key]
94
95 # compile the template
96 templates[key] = pt = RoundupPageTemplate()
97 pt.write(open(src).read())
98 pt.id = name
99 pt.mtime = time.time()
100 return pt
35 101
36 class RoundupPageTemplate(PageTemplate.PageTemplate): 102 class RoundupPageTemplate(PageTemplate.PageTemplate):
37 ''' A Roundup-specific PageTemplate. 103 ''' A Roundup-specific PageTemplate.
38 104
39 Interrogate the client to set up the various template variables to 105 Interrogate the client to set up the various template variables to
75 141
76 *modules* 142 *modules*
77 python modules made available (XXX: not sure what's actually in 143 python modules made available (XXX: not sure what's actually in
78 there tho) 144 there tho)
79 ''' 145 '''
80 def __init__(self, client, classname=None, request=None): 146 def getContext(self, client, classname, request):
81 ''' Extract the vars from the client and install in the context.
82 '''
83 self.client = client
84 self.classname = classname or self.client.classname
85 self.request = request or HTMLRequest(self.client)
86
87 def pt_getContext(self):
88 c = { 147 c = {
89 'klass': HTMLClass(self.client, self.classname), 148 'klass': HTMLClass(client, classname),
90 'options': {}, 149 'options': {},
91 'nothing': None, 150 'nothing': None,
92 'request': self.request, 151 'request': request,
93 'content': self.client.content, 152 'content': client.content,
94 'db': HTMLDatabase(self.client), 153 'db': HTMLDatabase(client),
95 'instance': self.client.instance 154 'instance': client.instance
96 } 155 }
97 # add in the item if there is one 156 # add in the item if there is one
98 if self.client.nodeid: 157 if client.nodeid:
99 c['item'] = HTMLItem(self.client.db, self.classname, 158 c['item'] = HTMLItem(client.db, classname, client.nodeid)
100 self.client.nodeid) 159 c[classname] = c['item']
101 c[self.classname] = c['item']
102 else: 160 else:
103 c[self.classname] = c['klass'] 161 c[classname] = c['klass']
104 return c 162 return c
105 163
106 def render(self, *args, **kwargs): 164 def render(self, client, classname, request, **options):
107 if not kwargs.has_key('args'): 165 """Render this Page Template"""
108 kwargs['args'] = args 166
109 return self.pt_render(extra_context={'options': kwargs}) 167 if not self._v_cooked:
168 self._cook()
169
170 __traceback_supplement__ = (PageTemplate.PageTemplateTracebackSupplement, self)
171
172 if self._v_errors:
173 raise PTRuntimeError, 'Page Template %s has errors.' % self.id
174
175 # figure the context
176 classname = classname or client.classname
177 request = request or HTMLRequest(client)
178 c = self.getContext(client, classname, request)
179 c.update({'options': options})
180
181 # and go
182 output = StringIO.StringIO()
183 TALInterpreter(self._v_program, self._v_macros,
184 getEngine().getContext(c), output, tal=1, strictinsert=0)()
185 return output.getvalue()
110 186
111 class HTMLDatabase: 187 class HTMLDatabase:
112 ''' Return HTMLClasses for valid class fetches 188 ''' Return HTMLClasses for valid class fetches
113 ''' 189 '''
114 def __init__(self, client): 190 def __init__(self, client):
223 ''' Render this class with the given template. 299 ''' Render this class with the given template.
224 ''' 300 '''
225 # create a new request and override the specified args 301 # create a new request and override the specified args
226 req = HTMLRequest(self.client) 302 req = HTMLRequest(self.client)
227 req.classname = self.classname 303 req.classname = self.classname
228 req.__dict__.update(kwargs) 304 req.update(kwargs)
229 305
230 # new template, using the specified classname and request 306 # new template, using the specified classname and request
231 pt = RoundupPageTemplate(self.client, self.classname, req)
232
233 # use the specified template
234 name = self.classname + '.' + name 307 name = self.classname + '.' + name
235 pt.write(open(os.path.join(self.db.config.TEMPLATES, name)).read()) 308 pt = getTemplate(self.db.config.TEMPLATES, name)
236 pt.id = name
237 309
238 # XXX handle PT rendering errors here nicely 310 # XXX handle PT rendering errors here nicely
239 try: 311 try:
240 return pt.render() 312 # use our fabricated request
313 return pt.render(self.client, self.classname, req)
241 except PageTemplate.PTRuntimeError, message: 314 except PageTemplate.PTRuntimeError, message:
242 return '<strong>%s</strong><ol>%s</ol>'%(message, 315 return '<strong>%s</strong><ol>%s</ol>'%(message,
243 cgi.escape('<li>'.join(pt._v_errors))) 316 cgi.escape('<li>'.join(pt._v_errors)))
244 317
245 class HTMLItem: 318 class HTMLItem:
849 if isinstance(value, type([])): 922 if isinstance(value, type([])):
850 return [value.value for value in value] 923 return [value.value for value in value]
851 else: 924 else:
852 return value.value.split(',') 925 return value.value.split(',')
853 926
927 class ShowDict:
928 ''' A convenience access to the :columns index parameters
929 '''
930 def __init__(self, columns):
931 self.columns = {}
932 for col in columns:
933 self.columns[col] = 1
934 def __getitem__(self, name):
935 return self.columns.has_key(name)
936
854 class HTMLRequest: 937 class HTMLRequest:
855 ''' The *request*, holding the CGI form and environment. 938 ''' The *request*, holding the CGI form and environment.
856 939
857 "form" the CGI form as a cgi.FieldStorage 940 "form" the CGI form as a cgi.FieldStorage
858 "env" the CGI environment variables 941 "env" the CGI environment variables
862 "classname" the current classname (possibly None) 945 "classname" the current classname (possibly None)
863 "template_type" the current template type (suffix, also possibly None) 946 "template_type" the current template type (suffix, also possibly None)
864 947
865 Index args: 948 Index args:
866 "columns" dictionary of the columns to display in an index page 949 "columns" dictionary of the columns to display in an index page
950 "show" a convenience access to columns - request/show/colname will
951 be true if the columns should be displayed, false otherwise
867 "sort" index sort column (direction, column name) 952 "sort" index sort column (direction, column name)
868 "group" index grouping property (direction, column name) 953 "group" index grouping property (direction, column name)
869 "filter" properties to filter the index on 954 "filter" properties to filter the index on
870 "filterspec" values to filter the index on 955 "filterspec" values to filter the index on
871 "search_text" text to perform a full-text search on for an index 956 "search_text" text to perform a full-text search on for an index
884 # store the current class name and action 969 # store the current class name and action
885 self.classname = client.classname 970 self.classname = client.classname
886 self.template_type = client.template_type 971 self.template_type = client.template_type
887 972
888 # extract the index display information from the form 973 # extract the index display information from the form
889 self.columns = {} 974 self.columns = []
890 if self.form.has_key(':columns'): 975 if self.form.has_key(':columns'):
891 for entry in handleListCGIValue(self.form[':columns']): 976 self.columns = handleListCGIValue(self.form[':columns'])
892 self.columns[entry] = 1 977 self.show = ShowDict(self.columns)
893 978
894 # sorting 979 # sorting
895 self.sort = (None, None) 980 self.sort = (None, None)
896 if self.form.has_key(':sort'): 981 if self.form.has_key(':sort'):
897 sort = self.form[':sort'].value 982 sort = self.form[':sort'].value
932 1017
933 # full-text search argument 1018 # full-text search argument
934 self.search_text = None 1019 self.search_text = None
935 if self.form.has_key(':search_text'): 1020 if self.form.has_key(':search_text'):
936 self.search_text = self.form[':search_text'].value 1021 self.search_text = self.form[':search_text'].value
1022
1023 # pagination - size and start index
1024 # figure batch args
1025 if self.form.has_key(':pagesize'):
1026 self.pagesize = int(self.form[':pagesize'].value)
1027 else:
1028 self.pagesize = 50
1029 if self.form.has_key(':startwith'):
1030 self.startwith = int(self.form[':startwith'].value)
1031 else:
1032 self.startwith = 0
1033
1034 def update(self, kwargs):
1035 self.__dict__.update(kwargs)
1036 if kwargs.has_key('columns'):
1037 self.show = ShowDict(self.columns)
937 1038
938 def __str__(self): 1039 def __str__(self):
939 d = {} 1040 d = {}
940 d.update(self.__dict__) 1041 d.update(self.__dict__)
941 f = '' 1042 f = ''
954 template_type: %(template_type)r 1055 template_type: %(template_type)r
955 columns: %(columns)r 1056 columns: %(columns)r
956 sort: %(sort)r 1057 sort: %(sort)r
957 group: %(group)r 1058 group: %(group)r
958 filter: %(filter)r 1059 filter: %(filter)r
959 filterspec: %(filterspec)r 1060 search_text: %(search_text)r
1061 pagesize: %(pagesize)r
1062 startwith: %(startwith)r
960 env: %(env)s 1063 env: %(env)s
961 '''%d 1064 '''%d
962 1065
963 def indexargs_form(self, columns=1, sort=1, group=1, filter=1, 1066 def indexargs_form(self, columns=1, sort=1, group=1, filter=1,
964 filterspec=1): 1067 filterspec=1):
965 ''' return the current index args as form elements ''' 1068 ''' return the current index args as form elements '''
966 l = [] 1069 l = []
967 s = '<input type="hidden" name="%s" value="%s">' 1070 s = '<input type="hidden" name="%s" value="%s">'
968 if columns and self.columns: 1071 if columns and self.columns:
969 l.append(s%(':columns', ','.join(self.columns.keys()))) 1072 l.append(s%(':columns', ','.join(self.columns)))
970 if sort and self.sort is not None: 1073 if sort and self.sort[1] is not None:
971 if self.sort[0] == '-': 1074 if self.sort[0] == '-':
972 val = '-'+self.sort[1] 1075 val = '-'+self.sort[1]
973 else: 1076 else:
974 val = self.sort[1] 1077 val = self.sort[1]
975 l.append(s%(':sort', val)) 1078 l.append(s%(':sort', val))
976 if group and self.group is not None: 1079 if group and self.group[1] is not None:
977 if self.group[0] == '-': 1080 if self.group[0] == '-':
978 val = '-'+self.group[1] 1081 val = '-'+self.group[1]
979 else: 1082 else:
980 val = self.group[1] 1083 val = self.group[1]
981 l.append(s%(':group', val)) 1084 l.append(s%(':group', val))
982 if filter and self.filter: 1085 if filter and self.filter:
983 l.append(s%(':filter', ','.join(self.filter))) 1086 l.append(s%(':filter', ','.join(self.filter)))
984 if filterspec: 1087 if filterspec:
985 for k,v in self.filterspec.items(): 1088 for k,v in self.filterspec.items():
986 l.append(s%(k, ','.join(v))) 1089 l.append(s%(k, ','.join(v)))
1090 if self.search_text:
1091 l.append(s%(':search_text', self.search_text))
1092 l.append(s%(':pagesize', self.pagesize))
1093 l.append(s%(':startwith', self.startwith))
987 return '\n'.join(l) 1094 return '\n'.join(l)
988 1095
989 def indexargs_href(self, url, args): 1096 def indexargs_href(self, url, args):
990 ''' embed the current index args in a URL ''' 1097 ''' embed the current index args in a URL '''
991 l = ['%s=%s'%(k,v) for k,v in args.items()] 1098 l = ['%s=%s'%(k,v) for k,v in args.items()]
992 if self.columns: 1099 if self.columns:
993 l.append(':columns=%s'%(','.join(self.columns.keys()))) 1100 l.append(':columns=%s'%(','.join(self.columns)))
994 if self.sort is not None: 1101 if self.sort[1] is not None:
995 if self.sort[0] == '-': 1102 if self.sort[0] == '-':
996 val = '-'+self.sort[1] 1103 val = '-'+self.sort[1]
997 else: 1104 else:
998 val = self.sort[1] 1105 val = self.sort[1]
999 l.append(':sort=%s'%val) 1106 l.append(':sort=%s'%val)
1000 if self.group is not None: 1107 if self.group[1] is not None:
1001 if self.group[0] == '-': 1108 if self.group[0] == '-':
1002 val = '-'+self.group[1] 1109 val = '-'+self.group[1]
1003 else: 1110 else:
1004 val = self.group[1] 1111 val = self.group[1]
1005 l.append(':group=%s'%val) 1112 l.append(':group=%s'%val)
1006 if self.filter: 1113 if self.filter:
1007 l.append(':filter=%s'%(','.join(self.filter))) 1114 l.append(':filter=%s'%(','.join(self.filter)))
1008 for k,v in self.filterspec.items(): 1115 for k,v in self.filterspec.items():
1009 l.append('%s=%s'%(k, ','.join(v))) 1116 l.append('%s=%s'%(k, ','.join(v)))
1117 if self.search_text:
1118 l.append(':search_text=%s'%self.search_text)
1119 l.append(':pagesize=%s'%self.pagesize)
1120 l.append(':startwith=%s'%self.startwith)
1010 return '%s?%s'%(url, '&'.join(l)) 1121 return '%s?%s'%(url, '&'.join(l))
1011 1122
1012 def base_javascript(self): 1123 def base_javascript(self):
1013 return ''' 1124 return '''
1014 <script language="javascript"> 1125 <script language="javascript">
1042 re.findall(r'\b\w{2,25}\b', self.search_text), klass) 1153 re.findall(r'\b\w{2,25}\b', self.search_text), klass)
1043 else: 1154 else:
1044 matches = None 1155 matches = None
1045 l = klass.filter(matches, filterspec, sort, group) 1156 l = klass.filter(matches, filterspec, sort, group)
1046 1157
1047 # figure batch args
1048 if self.form.has_key(':pagesize'):
1049 size = int(self.form[':pagesize'].value)
1050 else:
1051 size = 50
1052 if self.form.has_key(':startwith'):
1053 start = int(self.form[':startwith'].value)
1054 else:
1055 start = 0
1056
1057 # return the batch object 1158 # return the batch object
1058 return Batch(self.client, self.classname, l, size, start) 1159 return Batch(self.client, self.classname, l, self.pagesize,
1160 self.startwith)
1059 1161
1060 1162
1061 # extend the standard ZTUtils Batch object to remove dependency on 1163 # extend the standard ZTUtils Batch object to remove dependency on
1062 # Acquisition and add a couple of useful methods 1164 # Acquisition and add a couple of useful methods
1063 class Batch(ZTUtils.Batch): 1165 class Batch(ZTUtils.Batch):

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