Now clicking cancel button returns user to previous page. Fixes issue 569.
[Melange.git] / app / soc / views / helper / params.py
blobe1b4f62c3aa9033a5b0193ebdc1dc0e7cb9d7441
1 #!/usr/bin/python2.5
3 # Copyright 2008 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Params related methods.
18 """
20 __authors__ = [
21 '"Sverre Rabbelier" <sverre@rabbelier.nl>',
25 import copy
27 from django import forms
28 from django.utils.translation import ugettext
30 from soc.logic import cleaning
31 from soc.logic import dicts
32 from soc.models import linkable
33 from soc.views import helper
34 from soc.views.helper import access
35 from soc.views.helper import dynaform
36 from soc.views.helper import redirects
37 from soc.views.helper import widgets
40 DEF_LIST_DESCRIPTION_FMT = ugettext(
41 'List of %(name_plural)s.')
43 DEF_CREATE_INSTRUCTION_MSG_FMT = ugettext(
44 'Please use this form to select a %(name).')
46 DEF_SUBMIT_MSG_PARAM_NAME = 's'
47 DEF_SUBMIT_MSG_PROFILE_SAVED = 0
48 DEF_SUBMIT_MSG_CANNOT_DELETE_ENTITY = 1
51 # list with all js scripts used for documentary purposes
52 DEF_JS_USES_LIST = [
53 'jq',
54 'jq_ajaqQueue',
55 'jq_autocomplete',
56 'jq_bgiframe',
57 'jq_purr',
58 'jq_spin',
59 'jq_datetimepicker',
60 'jq_progressbar',
61 'jq_thickbox',
62 'jq_ui_core',
63 'menu',
64 'bulk_review',
65 'tinymce',
68 DEF_FIELD_INIT_PARAMS = ['required', 'widget', 'label', 'initial', 'help_text',
69 'error_messages', 'show_hidden_initial']
72 def constructParams(params):
73 """Constructs a new params dictionary based on params.
75 Params usage:
76 The params dictionary is passed to getCreateForm and getEditForm,
77 see their docstring on how they use it.
79 rights: The rights value is merged with a default rights
80 dictionary and then used as rights value.
81 url_name: The url_name value is used in constructing several
82 redirects as the first part of the url.
83 module_name: The module_name value is used in constructing the
84 location of several templates. It is expected that it matches
85 the part after "/templates/soc/" for this View.
86 name_plural: The name_plural argument is provided to the
87 LIST_DESCRIPTION when constructing the list_description field.
88 extra_dynainclude: The extra_dynainclude value is used when
89 constructing the create_dynainclude value.
90 extra_dynaexclude: The extra_dynaexclude value is used when
91 constructing the create_dynaexclude value.
92 logic: The logic value is used as argument to save the scope_logic
93 and create a create form.
94 """
96 logic = params['logic']
98 rights = access.Checker(params)
99 rights['unspecified'] = []
100 rights['any_access'] = ['checkIsLoggedIn']
101 rights['show'] = ['checkIsUser']
102 rights['create'] = ['checkIsDeveloper']
103 rights['edit'] = ['checkIsDeveloper']
104 rights['delete'] = ['checkIsDeveloper']
105 rights['list'] = ['checkIsDeveloper']
106 rights['pick'] = ['checkIsUser'] # TODO(SRabbelier): proper check
108 new_params = {}
109 new_params['scope_logic'] = logic.getScopeLogic()
111 if 'name_short' not in params:
112 params['name_short'] = params['name']
114 if 'name_plural' not in params:
115 params['name_plural'] = params['name'] + 's'
117 if 'module_name' not in params:
118 params['module_name'] = params['name_short'].replace(' ', '_').lower()
120 if 'url_name' not in params:
121 params['url_name'] = params['module_name']
123 if 'document_prefix' not in params:
124 params['document_prefix'] = params['url_name']
126 # Do not expand edit_redirect to allow it to be overwritten without suffix
127 new_params['edit_redirect'] = '/%(url_name)s/edit/%(suffix)s'
128 new_params['missing_redirect'] = '/%(url_name)s/create' % params
129 new_params['delete_redirect'] = '/%(url_name)s/list' % params
130 new_params['invite_redirect'] = '/request/list'
131 # new_params['cancel_redirect'] = '/%(url_name)s/list' % params
132 new_params['public_redirect'] = None
134 new_params['sidebar'] = None
135 new_params['sidebar_grouping'] = 'main'
136 new_params['sidebar_defaults'] = [
137 ('/%s/create', 'New %(name)s', 'create'),
138 ('/%s/list', 'List %(name_plural)s', 'list'),
140 new_params['sidebar_additional'] = []
142 names_sans_link_id = [i for i in logic.getKeyFieldNames() if i != 'link_id']
143 sans_link_id_pattern = getPattern(names_sans_link_id,
144 linkable.SCOPE_PATH_ARG_PATTERN)
146 new_params['link_id_arg_pattern'] = linkable.LINK_ID_ARG_PATTERN
147 new_params['link_id_pattern_core'] = linkable.LINK_ID_PATTERN_CORE
148 new_params['scope_path_pattern'] = getScopePattern(params)
149 new_params['sans_link_id_pattern'] = sans_link_id_pattern
151 new_params['django_patterns'] = None
152 new_params['extra_django_patterns'] = []
153 new_params['django_patterns_defaults'] = []
155 if not params.get('no_edit'):
156 new_params['django_patterns_defaults'] += [
157 (r'^%(url_name)s/(?P<access_type>edit)/%(key_fields)s$',
158 'soc.views.models.%(module_name)s.edit', 'Edit %(name_short)s')]
160 if not params.get('no_delete'):
161 new_params['django_patterns_defaults'] += [
162 (r'^%(url_name)s/(?P<access_type>delete)/%(key_fields)s$',
163 'soc.views.models.%(module_name)s.delete', 'Delete %(name_short)s')]
165 if not params.get('no_show'):
166 new_params['django_patterns_defaults'] += [
167 (r'^%(url_name)s/(?P<access_type>show)/%(key_fields)s$',
168 'soc.views.models.%(module_name)s.public', 'Show %(name_short)s')]
170 if not params.get('no_admin'):
171 new_params['django_patterns_defaults'] += [
172 (r'^%(url_name)s/(?P<access_type>admin)/%(key_fields)s$',
173 'soc.views.models.%(module_name)s.admin',
174 'Show %(name_short)s (admin)')]
176 if not params.get('no_create_raw'):
177 new_params['django_patterns_defaults'] += [
178 (r'^%(url_name)s/(?P<access_type>create)$',
179 'soc.views.models.%(module_name)s.create', 'Create %(name_short)s')]
181 if not params.get('no_create_with_scope'):
182 new_params['django_patterns_defaults'] += [
183 (r'^%(url_name)s/(?P<access_type>create)/%(scope)s$',
184 'soc.views.models.%(module_name)s.create', 'Create %(name_short)s')]
186 if not params.get('no_create_with_key_fields'):
187 new_params['django_patterns_defaults'] += [
188 (r'^%(url_name)s/(?P<access_type>create)/%(key_fields)s$',
189 'soc.views.models.%(module_name)s.create', 'Create %(name_short)s')]
191 if not params.get('no_list_raw'):
192 new_params['django_patterns_defaults'] += [
193 (r'^%(url_name)s/(?P<access_type>list)$',
194 'soc.views.models.%(module_name)s.list', 'List %(name_plural)s')]
196 if params.get('pickable'):
197 new_params['django_patterns_defaults'] += [
198 (r'^%(url_name)s/(?P<access_type>pick)$',
199 'soc.views.models.%(module_name)s.pick', 'Pick %(name_short)s')]
201 if params.get('export_content_type'):
202 new_params['django_patterns_defaults'] += [
203 (r'^%(url_name)s/(?P<access_type>export)/%(key_fields)s$',
204 'soc.views.models.%(module_name)s.export', 'Export %(name_short)s')]
206 if params.get('sans_link_id_create'):
207 new_params['django_patterns_defaults'] += [
208 (r'^%(url_name)s/(?P<access_type>create)/%(sans_link_id)s$',
209 'soc.views.models.%(module_name)s.create', 'Create %(name_short)s')]
211 if params.get('sans_link_id_list'):
212 new_params['django_patterns_defaults'] += [
213 (r'^%(url_name)s/(?P<access_type>list)/%(sans_link_id)s$',
214 'soc.views.models.%(module_name)s.list', 'List %(name_plural)s')]
216 if params.get('sans_link_id_public_list'):
217 new_params['django_patterns_defaults'] += [
218 (r'^%(url_name)s/(?P<access_type>list_public)/%(sans_link_id)s$',
219 'soc.views.models.%(module_name)s.list_public',
220 'List %(name_plural)s')]
222 new_params['public_template'] = 'soc/%(module_name)s/public.html' % params
223 new_params['export_template'] = 'soc/export.html' % params
224 new_params['create_template'] = 'soc/models/edit.html'
225 new_params['edit_template'] = 'soc/models/edit.html'
226 new_params['admin_template'] = 'soc/models/admin.html'
227 new_params['list_template'] = 'soc/models/list.html'
228 new_params['invite_template'] = 'soc/models/invite.html'
230 new_params['context'] = None
232 new_params['cache_pick'] = False
234 new_params['export_content_type'] = None
235 new_params['export_extension'] = '.txt'
236 new_params['csv_fieldnames'] = []
238 # TODO: Use only the js modules needed instead of js_uses_all
239 new_params['js_uses_all'] = DEF_JS_USES_LIST
240 new_params['js_uses_list'] = ['jq', 'menu']
241 new_params['js_uses_show'] = ['jq', 'menu']
242 new_params['js_uses_edit'] = ['jq', 'menu', 'tinymce', 'jq_purr',
243 'jq_spin', 'jq_autocomplete']
245 new_params['error_public'] = 'soc/%(module_name)s/error.html' % params
246 new_params['error_export'] = new_params['error_public']
247 new_params['error_edit'] = new_params['error_public']
249 new_params['list_main'] = 'soc/list/main.html'
250 new_params['list_pagination'] = 'soc/list/pagination.html'
251 new_params['list_row'] = 'soc/%(module_name)s/list/row.html' % params
252 new_params['list_heading'] = 'soc/%(module_name)s/list/heading.html' % params
254 new_params['list_action'] = (redirects.getEditRedirect, params)
255 new_params['list_params'] = {
256 'list_action': 'action',
257 'list_description': 'description',
258 'list_info': 'info',
259 'list_key_order': 'key_order',
260 'list_main': 'main',
261 'list_pagination': 'pagination',
262 'list_row': 'row',
263 'list_heading': 'heading',
266 new_params['list_description'] = DEF_LIST_DESCRIPTION_FMT % params
267 new_params['no_lists_msg'] = ""
268 new_params['save_message'] = [ugettext('%(name)s saved.' % params),
269 ugettext('Cannot delete %(name)s.' % params)]
270 new_params['submit_msg_param_name'] = DEF_SUBMIT_MSG_PARAM_NAME
271 new_params['edit_params'] = {
272 DEF_SUBMIT_MSG_PARAM_NAME: DEF_SUBMIT_MSG_PROFILE_SAVED,
275 new_params['cannot_delete_params'] = {
276 DEF_SUBMIT_MSG_PARAM_NAME: DEF_SUBMIT_MSG_CANNOT_DELETE_ENTITY,
279 new_params['dynabase'] = helper.forms.BaseForm
281 create_dynaproperties = {
282 'clean_link_id': cleaning.clean_link_id('link_id'),
283 'clean_feed_url': cleaning.clean_feed_url,
285 create_dynaproperties.update(params.get('create_extra_dynaproperties', {}))
287 # dynafields override any dynaproperties
288 create_dynafields = getDynaFields(params.get('create_dynafields', {}))
289 create_dynaproperties = dicts.merge(create_dynafields, create_dynaproperties)
291 new_params['references'] = []
292 new_params['create_dynainclude'] = [] + params.get('extra_dynainclude', [])
293 new_params['create_dynaexclude'] = ['scope', 'scope_path'] + \
294 params.get('extra_dynaexclude', [])
295 new_params['create_dynaproperties'] = create_dynaproperties
297 edit_dynaproperties = {
298 'clean_link_id': cleaning.clean_link_id('link_id'),
299 'link_id': forms.CharField(widget=helper.widgets.ReadOnlyInput()),
301 edit_dynaproperties.update(params.get('edit_extra_dynaproperties', {}))
303 # dynafields override any dynaproperties
304 edit_dynafields = getDynaFields(params.get('edit_dynafields', {}))
305 edit_dynaproperties = dicts.merge(edit_dynafields, edit_dynaproperties)
307 new_params['edit_dynainclude'] = None
308 new_params['edit_dynaexclude'] = None
309 new_params['edit_dynaproperties'] = edit_dynaproperties
310 new_params['list_msg'] = None
312 params = dicts.merge(params, new_params)
314 # These need to be constructed separately, because they require
315 # parameters that can be defined either in params, or new_params.
316 if not 'create_form' in params:
317 params['create_form'] = getCreateForm(params, logic.getModel())
319 if not 'edit_form' in params:
320 params['edit_form'] = getEditForm(params, params['create_form'])
322 if not 'admin_form' in params:
323 params['admin_form'] = getAdminForm(params['edit_form'])
325 if not 'key_fields_pattern' in params:
326 params['key_fields_pattern'] = getKeyFieldsPattern(params)
328 # merge already done by access.Checker
329 params['rights'] = rights
331 return params
334 def getDynaFields(fields):
335 """Constructs a new DynaField using params.
337 Args:
338 params: the params dictionary used to extract the dyanfields
339 param_name: the name of the parameter to use
342 dynafields = {}
344 # generate the dynafields
345 for field in fields:
346 base = field.pop('base')
347 name = field.pop('name')
348 passthrough = field.pop('passthrough', DEF_FIELD_INIT_PARAMS)
350 dynafield = dynaform.newDynaField(field, base, passthrough)
351 dynafields[name] = dynafield()
353 return dynafields
356 def getCreateForm(params, model):
357 """Constructs a new CreateForm using params.
359 Params usage:
360 dynabase: The dynabase value is used as the base argument to
361 dynaform.newDynaForm.
362 logic: The logic value is used to get the model argument to newDynaForm.
363 create_dynainclude: same as dynabase, but as dynainclude argument
364 create_dynaexclude: same as dynabase, but as dynaexclude argument
365 create_dynaproperties: same as dynabase, but as dynaproperties argument
368 create_form = dynaform.newDynaForm(
369 dynabase = params['dynabase'],
370 dynamodel = model,
371 dynainclude = params['create_dynainclude'],
372 dynaexclude = params['create_dynaexclude'],
373 dynaproperties = params['create_dynaproperties'],
376 if 'extra_key_order' in params:
377 for field in params['extra_key_order']:
378 if field in create_form.base_fields.keyOrder:
379 create_form.base_fields.keyOrder.remove(field)
380 create_form.base_fields.keyOrder.extend(params['extra_key_order'])
382 return create_form
385 def getEditForm(params, base_form):
386 """Constructs a new EditForm using params.
388 Params usage:
389 create_form: The dynabase value is used as the dynaform argument
390 to dyanform.extendDynaForm.
391 edit_dynainclude: same as create_form, but as dynainclude argument
392 edit_dynaexclude: same as create_form, but as dynaexclude argument
393 edit_dynaproperties: same as create_form, but as dynaproperties argument
396 edit_form = dynaform.extendDynaForm(
397 dynaform = base_form,
398 dynainclude = params['edit_dynainclude'],
399 dynaexclude = params['edit_dynaexclude'],
400 dynaproperties = params['edit_dynaproperties'],
403 return edit_form
406 def getAdminForm(base_form):
407 """Constructs a new AdminForm from base_form.
410 # extend _and_ deepcopy the base_fields to do a proper copy
411 admin_form = dynaform.extendDynaForm(dynaform = base_form)
412 admin_form.base_fields = copy.deepcopy(admin_form.base_fields)
414 # replace all widgets with PTW's
415 for _, value in admin_form.base_fields.iteritems():
416 if not isinstance(value, forms.fields.Field):
417 continue
419 value.widget = widgets.PlainTextWidget()
421 return admin_form
424 def getKeyFieldsPattern(params):
425 """Returns the Django pattern for this View's entity.
428 names = params['logic'].getKeyFieldNames()
429 scope_path_pattern = params['scope_path_pattern']
431 return getPattern(names, scope_path_pattern)
434 def getPattern(names, scope_path_pattern):
435 """Returns the Django patterns for the specified names.
437 Args:
438 names: the field names that should be included in the pattern
439 scope_path_pattern: the pattern to use if the name is 'scope_path'
442 patterns = []
444 for name in names:
445 if name == 'scope_path':
446 pattern = scope_path_pattern
447 else:
448 pattern = r'(?P<%s>%s)' % (name, linkable.LINK_ID_PATTERN_CORE)
449 patterns.append(pattern)
451 result = '/'.join(patterns)
452 return result
455 def getScopePattern(params):
456 """Returns the Scope pattern for this entity.
459 logic = params['logic']
460 depth = logic.getScopeDepth()
461 if depth is None:
462 return linkable.SCOPE_PATH_ARG_PATTERN
464 regexps = [linkable.LINK_ID_PATTERN_CORE for _ in range(depth)]
465 regexp = '/'.join(regexps)
466 return r'(?P<scope_path>%s)' % regexp