Fix user_self calling editGet with a wrong parameter
[Melange.git] / app / soc / views / helper / params.py
blob2851c03202f9ab87c919dab5e8903efe58867e72
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_bt',
58 'jq_purr',
59 'jq_spin',
60 'jq_datetimepicker',
61 'jq_progressbar',
62 'jq_thickbox',
63 'jq_ui_core',
64 'menu',
65 'bulk_review',
66 'tinymce',
69 DEF_FIELD_INIT_PARAMS = ['required', 'widget', 'label', 'initial', 'help_text',
70 'error_messages', 'show_hidden_initial']
73 def constructParams(params):
74 """Constructs a new params dictionary based on params.
76 Params usage:
77 The params dictionary is passed to getCreateForm and getEditForm,
78 see their docstring on how they use it.
80 rights: The rights value is merged with a default rights
81 dictionary and then used as rights value.
82 url_name: The url_name value is used in constructing several
83 redirects as the first part of the url.
84 module_name: The module_name value is used in constructing the
85 location of several templates. It is expected that it matches
86 the part after "/templates/soc/" for this View.
87 name_plural: The name_plural argument is provided to the
88 LIST_DESCRIPTION when constructing the list_description field.
89 extra_dynainclude: The extra_dynainclude value is used when
90 constructing the create_dynainclude value.
91 extra_dynaexclude: The extra_dynaexclude value is used when
92 constructing the create_dynaexclude value.
93 logic: The logic value is used as argument to save the scope_logic
94 and create a create form.
95 """
97 logic = params['logic']
99 rights = access.Checker(params)
100 rights['unspecified'] = []
101 rights['any_access'] = ['checkIsLoggedIn']
102 rights['show'] = ['checkIsUser']
103 rights['create'] = ['checkIsDeveloper']
104 rights['edit'] = ['checkIsDeveloper']
105 rights['delete'] = ['checkIsDeveloper']
106 rights['list'] = ['checkIsDeveloper']
107 rights['pick'] = ['checkIsUser'] # TODO(SRabbelier): proper check
109 new_params = {}
110 new_params['scope_logic'] = logic.getScopeLogic()
112 if 'name_short' not in params:
113 params['name_short'] = params['name']
115 if 'name_plural' not in params:
116 params['name_plural'] = params['name'] + 's'
118 if 'module_name' not in params:
119 params['module_name'] = params['name_short'].replace(' ', '_').lower()
121 if 'url_name' not in params:
122 params['url_name'] = params['module_name']
124 if 'document_prefix' not in params:
125 params['document_prefix'] = params['url_name']
127 # Do not expand edit_redirect to allow it to be overwritten without suffix
128 new_params['edit_redirect'] = '/%(url_name)s/edit/%(suffix)s'
129 new_params['missing_redirect'] = '/%(url_name)s/create' % params
130 new_params['delete_redirect'] = '/%(url_name)s/list' % params
131 new_params['invite_redirect'] = '/request/list'
132 new_params['edit_cancel_redirect'] = '/%(url_name)s/list' % params
133 new_params['public_redirect'] = None
135 new_params['sidebar'] = None
136 new_params['sidebar_grouping'] = 'main'
137 new_params['sidebar_defaults'] = [
138 ('/%s/create', 'New %(name)s', 'create'),
139 ('/%s/list', 'List %(name_plural)s', 'list'),
141 new_params['sidebar_additional'] = []
143 names_sans_link_id = [i for i in logic.getKeyFieldNames() if i != 'link_id']
144 sans_link_id_pattern = getPattern(names_sans_link_id,
145 linkable.SCOPE_PATH_ARG_PATTERN)
147 new_params['link_id_arg_pattern'] = linkable.LINK_ID_ARG_PATTERN
148 new_params['link_id_pattern_core'] = linkable.LINK_ID_PATTERN_CORE
149 new_params['scope_path_pattern'] = getScopePattern(params)
150 new_params['sans_link_id_pattern'] = sans_link_id_pattern
152 new_params['django_patterns'] = None
153 new_params['extra_django_patterns'] = []
154 new_params['django_patterns_defaults'] = []
156 if not params.get('no_edit'):
157 new_params['django_patterns_defaults'] += [
158 (r'^%(url_name)s/(?P<access_type>edit)/%(key_fields)s$',
159 'soc.views.models.%(module_name)s.edit', 'Edit %(name_short)s')]
161 if not params.get('no_delete'):
162 new_params['django_patterns_defaults'] += [
163 (r'^%(url_name)s/(?P<access_type>delete)/%(key_fields)s$',
164 'soc.views.models.%(module_name)s.delete', 'Delete %(name_short)s')]
166 if not params.get('no_show'):
167 new_params['django_patterns_defaults'] += [
168 (r'^%(url_name)s/(?P<access_type>show)/%(key_fields)s$',
169 'soc.views.models.%(module_name)s.public', 'Show %(name_short)s')]
171 if not params.get('no_admin'):
172 new_params['django_patterns_defaults'] += [
173 (r'^%(url_name)s/(?P<access_type>admin)/%(key_fields)s$',
174 'soc.views.models.%(module_name)s.admin',
175 'Show %(name_short)s (admin)')]
177 if not params.get('no_create_raw'):
178 new_params['django_patterns_defaults'] += [
179 (r'^%(url_name)s/(?P<access_type>create)$',
180 'soc.views.models.%(module_name)s.create', 'Create %(name_short)s')]
182 if not params.get('no_create_with_scope'):
183 new_params['django_patterns_defaults'] += [
184 (r'^%(url_name)s/(?P<access_type>create)/%(scope)s$',
185 'soc.views.models.%(module_name)s.create', 'Create %(name_short)s')]
187 if not params.get('no_create_with_key_fields'):
188 new_params['django_patterns_defaults'] += [
189 (r'^%(url_name)s/(?P<access_type>create)/%(key_fields)s$',
190 'soc.views.models.%(module_name)s.create', 'Create %(name_short)s')]
192 if not params.get('no_list_raw'):
193 new_params['django_patterns_defaults'] += [
194 (r'^%(url_name)s/(?P<access_type>list)$',
195 'soc.views.models.%(module_name)s.list', 'List %(name_plural)s')]
197 if params.get('pickable'):
198 new_params['django_patterns_defaults'] += [
199 (r'^%(url_name)s/(?P<access_type>pick)$',
200 'soc.views.models.%(module_name)s.pick', 'Pick %(name_short)s')]
202 if params.get('export_content_type'):
203 new_params['django_patterns_defaults'] += [
204 (r'^%(url_name)s/(?P<access_type>export)/%(key_fields)s$',
205 'soc.views.models.%(module_name)s.export', 'Export %(name_short)s')]
207 if params.get('sans_link_id_create'):
208 new_params['django_patterns_defaults'] += [
209 (r'^%(url_name)s/(?P<access_type>create)/%(sans_link_id)s$',
210 'soc.views.models.%(module_name)s.create', 'Create %(name_short)s')]
212 if params.get('sans_link_id_list'):
213 new_params['django_patterns_defaults'] += [
214 (r'^%(url_name)s/(?P<access_type>list)/%(sans_link_id)s$',
215 'soc.views.models.%(module_name)s.list', 'List %(name_plural)s')]
217 if params.get('sans_link_id_public_list'):
218 new_params['django_patterns_defaults'] += [
219 (r'^%(url_name)s/(?P<access_type>list_public)/%(sans_link_id)s$',
220 'soc.views.models.%(module_name)s.list_public',
221 'List %(name_plural)s')]
223 new_params['public_template'] = 'soc/%(module_name)s/public.html' % params
224 new_params['export_template'] = 'soc/export.html' % params
225 new_params['create_template'] = 'soc/models/edit.html'
226 new_params['edit_template'] = 'soc/models/edit.html'
227 new_params['admin_template'] = 'soc/models/admin.html'
228 new_params['list_template'] = 'soc/models/list.html'
229 new_params['invite_template'] = 'soc/models/invite.html'
231 new_params['context'] = None
233 new_params['cache_pick'] = False
235 new_params['export_content_type'] = None
236 new_params['export_extension'] = '.txt'
237 new_params['csv_fieldnames'] = []
239 # TODO: Use only the js modules needed instead of js_uses_all
240 new_params['js_uses_all'] = DEF_JS_USES_LIST
241 new_params['js_uses_list'] = ['jq', 'menu']
242 new_params['js_uses_show'] = ['jq', 'menu']
243 new_params['js_uses_edit'] = ['jq', 'menu', 'tinymce', 'jq_bt',
244 'jq_purr','jq_spin','jq_autocomplete']
246 new_params['error_public'] = 'soc/%(module_name)s/error.html' % params
247 new_params['error_export'] = new_params['error_public']
248 new_params['error_edit'] = new_params['error_public']
250 new_params['list_main'] = 'soc/list/main.html'
251 new_params['list_pagination'] = 'soc/list/pagination.html'
252 new_params['list_row'] = 'soc/%(module_name)s/list/row.html' % params
253 new_params['list_heading'] = 'soc/%(module_name)s/list/heading.html' % params
255 new_params['list_action'] = (redirects.getEditRedirect, params)
256 new_params['list_params'] = {
257 'list_action': 'action',
258 'list_description': 'description',
259 'list_info': 'info',
260 'list_key_order': 'key_order',
261 'list_main': 'main',
262 'list_pagination': 'pagination',
263 'list_row': 'row',
264 'list_heading': 'heading',
267 new_params['list_description'] = DEF_LIST_DESCRIPTION_FMT % params
268 new_params['no_lists_msg'] = ""
269 new_params['save_message'] = [ugettext('%(name)s saved.' % params),
270 ugettext('Cannot delete %(name)s.' % params)]
271 new_params['submit_msg_param_name'] = DEF_SUBMIT_MSG_PARAM_NAME
272 new_params['edit_params'] = {
273 DEF_SUBMIT_MSG_PARAM_NAME: DEF_SUBMIT_MSG_PROFILE_SAVED,
276 new_params['cannot_delete_params'] = {
277 DEF_SUBMIT_MSG_PARAM_NAME: DEF_SUBMIT_MSG_CANNOT_DELETE_ENTITY,
280 new_params['dynabase'] = helper.forms.BaseForm
282 create_dynaproperties = {
283 'clean_link_id': cleaning.clean_link_id('link_id'),
284 'clean_feed_url': cleaning.clean_feed_url,
286 create_dynaproperties.update(params.get('create_extra_dynaproperties', {}))
288 # dynafields override any dynaproperties
289 create_dynafields = getDynaFields(params.get('create_dynafields', {}))
290 create_dynaproperties = dicts.merge(create_dynafields, create_dynaproperties)
292 new_params['references'] = []
293 new_params['create_dynainclude'] = [] + params.get('extra_dynainclude', [])
294 new_params['create_dynaexclude'] = ['scope', 'scope_path'] + \
295 params.get('extra_dynaexclude', [])
296 new_params['create_dynaproperties'] = create_dynaproperties
298 edit_dynaproperties = {
299 'clean_link_id': cleaning.clean_link_id('link_id'),
300 'link_id': forms.CharField(widget=helper.widgets.ReadOnlyInput()),
302 edit_dynaproperties.update(params.get('edit_extra_dynaproperties', {}))
304 # dynafields override any dynaproperties
305 edit_dynafields = getDynaFields(params.get('edit_dynafields', {}))
306 edit_dynaproperties = dicts.merge(edit_dynafields, edit_dynaproperties)
308 new_params['edit_dynainclude'] = None
309 new_params['edit_dynaexclude'] = None
310 new_params['edit_dynaproperties'] = edit_dynaproperties
311 new_params['list_msg'] = None
313 params = dicts.merge(params, new_params)
315 # These need to be constructed separately, because they require
316 # parameters that can be defined either in params, or new_params.
317 if not 'create_form' in params:
318 params['create_form'] = getCreateForm(params, logic.getModel())
320 if not 'edit_form' in params:
321 params['edit_form'] = getEditForm(params, params['create_form'])
323 if not 'admin_form' in params:
324 params['admin_form'] = getAdminForm(params['edit_form'])
326 if not 'key_fields_pattern' in params:
327 params['key_fields_pattern'] = getKeyFieldsPattern(params)
329 # merge already done by access.Checker
330 params['rights'] = rights
332 return params
335 def getDynaFields(fields):
336 """Constructs a new DynaField using params.
338 Args:
339 params: the params dictionary used to extract the dyanfields
340 param_name: the name of the parameter to use
343 dynafields = {}
345 # generate the dynafields
346 for field in fields:
347 base = field.pop('base')
348 name = field.pop('name')
349 passthrough = field.pop('passthrough', DEF_FIELD_INIT_PARAMS)
351 dynafield = dynaform.newDynaField(field, base, passthrough)
352 dynafields[name] = dynafield()
354 return dynafields
357 def getCreateForm(params, model):
358 """Constructs a new CreateForm using params.
360 Params usage:
361 dynabase: The dynabase value is used as the base argument to
362 dynaform.newDynaForm.
363 logic: The logic value is used to get the model argument to newDynaForm.
364 create_dynainclude: same as dynabase, but as dynainclude argument
365 create_dynaexclude: same as dynabase, but as dynaexclude argument
366 create_dynaproperties: same as dynabase, but as dynaproperties argument
369 create_form = dynaform.newDynaForm(
370 dynabase = params['dynabase'],
371 dynamodel = model,
372 dynainclude = params['create_dynainclude'],
373 dynaexclude = params['create_dynaexclude'],
374 dynaproperties = params['create_dynaproperties'],
377 if 'extra_key_order' in params:
378 for field in params['extra_key_order']:
379 if field in create_form.base_fields.keyOrder:
380 create_form.base_fields.keyOrder.remove(field)
381 create_form.base_fields.keyOrder.extend(params['extra_key_order'])
383 return create_form
386 def getEditForm(params, base_form):
387 """Constructs a new EditForm using params.
389 Params usage:
390 create_form: The dynabase value is used as the dynaform argument
391 to dyanform.extendDynaForm.
392 edit_dynainclude: same as create_form, but as dynainclude argument
393 edit_dynaexclude: same as create_form, but as dynaexclude argument
394 edit_dynaproperties: same as create_form, but as dynaproperties argument
397 edit_form = dynaform.extendDynaForm(
398 dynaform = base_form,
399 dynainclude = params['edit_dynainclude'],
400 dynaexclude = params['edit_dynaexclude'],
401 dynaproperties = params['edit_dynaproperties'],
404 return edit_form
407 def getAdminForm(base_form):
408 """Constructs a new AdminForm from base_form.
411 # extend _and_ deepcopy the base_fields to do a proper copy
412 admin_form = dynaform.extendDynaForm(dynaform = base_form)
413 admin_form.base_fields = copy.deepcopy(admin_form.base_fields)
415 # replace all widgets with PTW's
416 for _, value in admin_form.base_fields.iteritems():
417 if not isinstance(value, forms.fields.Field):
418 continue
420 value.widget = widgets.PlainTextWidget()
422 return admin_form
425 def getKeyFieldsPattern(params):
426 """Returns the Django pattern for this View's entity.
429 names = params['logic'].getKeyFieldNames()
430 scope_path_pattern = params['scope_path_pattern']
432 return getPattern(names, scope_path_pattern)
435 def getPattern(names, scope_path_pattern):
436 """Returns the Django patterns for the specified names.
438 Args:
439 names: the field names that should be included in the pattern
440 scope_path_pattern: the pattern to use if the name is 'scope_path'
443 patterns = []
445 for name in names:
446 if name == 'scope_path':
447 pattern = scope_path_pattern
448 else:
449 pattern = r'(?P<%s>%s)' % (name, linkable.LINK_ID_PATTERN_CORE)
450 patterns.append(pattern)
452 result = '/'.join(patterns)
453 return result
456 def getScopePattern(params):
457 """Returns the Scope pattern for this entity.
460 logic = params['logic']
461 depth = logic.getScopeDepth()
462 if depth is None:
463 return linkable.SCOPE_PATH_ARG_PATTERN
465 regexps = [linkable.LINK_ID_PATTERN_CORE for _ in range(depth)]
466 regexp = '/'.join(regexps)
467 return r'(?P<scope_path>%s)' % regexp