1 """Classes and methods for TurboGears controllers."""
2
3 import logging
4 import re
5 import urllib
6 import types
7 from itertools import izip
8 from dispatch import generic, strategy, functions
9 from inspect import isclass
10 import cherrypy
11 from cherrypy import request, response
12 import turbogears.util as tg_util
13 from turbogears import view, database, errorhandling, config
14 from turbogears.decorator import weak_signature_decorator
15 from turbogears.validators import Invalid
16 from turbogears.errorhandling import error_handler, exception_handler
17
18
19 log = logging.getLogger("turbogears.controllers")
20
21 unicodechars = re.compile(r"([^\x00-\x7F])")
22
23 if config.get("session_filter.on", None) == True:
24 if config.get("session_filter.storage_type", None) == "PostgreSQL":
25 import psycopg2
26 config.update(
27 {'session_filter.get_db': psycopg2.connect(
28 psycopg2.get('sessions.postgres.dsn'))
29 })
30
31
32
33 -def _process_output(output, template, format, content_type,
34 mapping, fragment=False):
35 """Produce final output form from data returned from a controller method.
36
37 See the expose() arguments for more info since they are the same.
38
39 """
40 if isinstance(output, dict):
41 from turbogears.widgets import js_location
42
43 css = tg_util.setlike()
44 js = dict(izip(js_location, iter(tg_util.setlike, None)))
45 include_widgets = {}
46 include_widgets_lst = config.get("tg.include_widgets", [])
47
48 if config.get("tg.mochikit_all", False):
49 include_widgets_lst.insert(0, 'turbogears.mochikit')
50
51 for i in include_widgets_lst:
52 widget = tg_util.load_class(i)
53 if isclass(widget):
54 widget = widget()
55 include_widgets["tg_%s" % i.split(".")[-1]] = widget
56 for script in widget.retrieve_javascript():
57 if hasattr(script, "location"):
58 js[script.location].add(script)
59 else:
60 js[js_location.head].add(script)
61 css.add_all(widget.retrieve_css())
62
63 for value in output.itervalues():
64 if hasattr(value, "retrieve_css"):
65 retrieve = getattr(value, "retrieve_css")
66 if callable(retrieve):
67 css.add_all(value.retrieve_css())
68 if hasattr(value, "retrieve_javascript"):
69 retrieve = getattr(value, "retrieve_javascript")
70 if callable(retrieve):
71 for script in value.retrieve_javascript():
72 if hasattr(script, "location"):
73 js[script.location].add(script)
74 else:
75 js[js_location.head].add(script)
76 output.update(include_widgets)
77 output["tg_css"] = css
78
79 for l in iter(js_location):
80 output["tg_js_%s" % str(l)] = js[l]
81
82 tg_flash = _get_flash()
83 if not tg_flash == None:
84 output["tg_flash"] = tg_flash
85 output = view.render(output, template=template, format=format,
86 mapping=mapping, content_type=content_type,
87 fragment=fragment)
88 else:
89 if content_type:
90 response.headers["Content-Type"] = content_type
91
92
93 try:
94 contentType = response.headers["Content-Type"]
95 ua = request.headers["User-Agent"]
96 except KeyError:
97 return output
98 if not contentType.startswith("text/"):
99 return output
100 ua = view.UserAgent(ua)
101 enc = tg_util.get_template_encoding_default()
102 if ua.browser == "safari":
103 if isinstance(output, str):
104 output = output.decode(enc)
105 elif isinstance(output, types.GeneratorType):
106 output = "".join(output)
107
108 output = unicodechars.sub(
109 lambda m: "&#x%x;" % ord(m.group(1)), output).encode("ascii")
110
111 if isinstance(output, unicode):
112 output = output.encode(enc)
113 return output
114
115
118
119
123 """Validate input.
124
125 @param form: a form instance that must be passed throught the validation
126 process... you must give a the same form instance as the one that will
127 be used to post data on the controller you are putting the validate
128 decorator on.
129 @type form: a form instance
130
131 @param validators: individual validators to use for parameters.
132 If you use a schema for validation then the schema instance must
133 be the sole argument.
134 If you use simple validators, then you must pass a dictionary with
135 each value name to validate as a key of the dictionary and the validator
136 instance (eg: tg.validators.Int() for integer) as the value.
137 @type validators: dictionary or schema instance
138
139 @param failsafe_schema: a schema for handling failsafe values.
140 The default is 'none', but you can also use 'values', 'map_errors',
141 or 'defaults' to map erroneous inputs to values, corresponding exceptions
142 or method defaults.
143 @type failsafe_schema: errorhandling.FailsafeSchema
144
145 @param failsafe_values: replacements for erroneous inputs. You can either
146 define replacements for every parameter, or a single replacement value
147 for all parameters. This is only used when failsafe_schema is 'values'.
148 @type failsafe_values: a dictionary or a single value
149
150 @param state_factory: If this is None, the initial state for validation
151 is set to None, otherwise this must be a callable that returns the initial
152 state to be used for validation.
153 @type state_factory: callable or None
154
155 """
156 def entangle(func):
157 if callable(form) and not hasattr(form, "validate"):
158 init_form = lambda self: form(self)
159 else:
160 init_form = lambda self: form
161
162 def validate(func, *args, **kw):
163
164 if hasattr(request, 'validation_state'):
165 return func(*args, **kw)
166
167 form = init_form(args and args[0] or kw["self"])
168 args, kw = tg_util.to_kw(func, args, kw)
169
170 errors = {}
171 if state_factory is not None:
172 state = state_factory()
173 else:
174 state = None
175
176 if form:
177 value = kw.copy()
178 try:
179 kw.update(form.validate(value, state))
180 except Invalid, e:
181 errors = e.unpack_errors()
182 request.validation_exception = e
183 request.validated_form = form
184
185 if validators:
186 if isinstance(validators, dict):
187 for field, validator in validators.iteritems():
188 try:
189 kw[field] = validator.to_python(
190 kw.get(field, None), state)
191 except Invalid, error:
192 errors[field] = error
193 else:
194 try:
195 value = kw.copy()
196 kw.update(validators.to_python(value, state))
197 except Invalid, e:
198 errors = e.unpack_errors()
199 request.validation_exception = e
200 request.validation_errors = errors
201 request.input_values = kw.copy()
202 request.validation_state = state
203
204 if errors:
205 kw = errorhandling.dispatch_failsafe(failsafe_schema,
206 failsafe_values, errors, func, kw)
207 args, kw = tg_util.from_kw(func, args, kw)
208 return errorhandling.run_with_errors(errors, func, *args, **kw)
209
210 return validate
211 return weak_signature_decorator(entangle)
212
213
215
217 strict = [strategy.ordered_signatures, strategy.safe_methods]
218 cases = strategy.separate_qualifiers(
219 cases,
220 primary = strict,
221 )
222 primary = strategy.method_chain(cases.get('primary', []))
223 if type(primary) != types.FunctionType:
224 for i in primary:
225 for y in i:
226 return y[1]
227 return primary
228
229
230 -def _add_rule(_expose, found_default, as_format, accept_format, template,
231 rulefunc):
232 if as_format == "default":
233 if found_default:
234 colon = template.find(":")
235 if colon == -1:
236 as_format = template
237 else:
238 as_format = template[:colon]
239 else:
240 found_default = True
241 ruleparts = []
242 ruleparts.append('kw.get("tg_format", "default") == "%s"'
243 % as_format)
244 if accept_format:
245 ruleparts.append('(accept == "%s" and kw.get("tg_format", '
246 '"default") == "default")' % accept_format)
247 rule = " or ".join(ruleparts)
248 log.debug("Generated rule %s", rule)
249 _expose.when(rule)(rulefunc)
250
251 return found_default
252
253
255 [generic(CustomDispatch)]
256 def _expose(func, accept, allow_json, *args, **kw):
257 pass
258
259 if func._allow_json:
260 log.debug("Adding allow_json rule: "
261 'allow_json and '
262 '(kw.get("tg_format", None) == "json" or accept '
263 '=="text/javascript"