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 """Views for Role profiles.
21 '"Sverre Rabbelier" <sverre@rabbelier.nl>',
22 '"Lennard de Rijk" <ljvderijk@gmail.com>',
26 from django
import forms
27 from django
import http
28 from django
.utils
.translation
import ugettext
30 from soc
.logic
import cleaning
31 from soc
.logic
import dicts
32 from soc
.logic
.models
import request
as request_logic
33 from soc
.logic
.models
import user
as user_logic
34 from soc
.logic
.helper
import request
as request_helper
35 from soc
.views
.helper
import decorators
36 from soc
.views
.helper
import redirects
37 from soc
.views
.helper
import responses
38 from soc
.views
.models
import base
39 from soc
.views
.models
import request
as request_view
41 import soc
.models
.request
42 import soc
.views
.helper
.lists
43 import soc
.views
.helper
.responses
44 import soc
.views
.helper
.widgets
51 """Adds the specified view to the known role views.
55 params
= view
.getParams()
56 name
= params
['url_name']
57 ROLE_VIEWS
[name
] = view
60 class View(base
.View
):
61 """Views for all entities that inherit from Role.
63 All views that only Role entities have are defined in this subclass.
66 DEF_INVITE_INSTRUCTION_MSG_FMT
= ugettext(
67 'Please use this form to invite someone to become a %(name)s.')
69 DEF_REQUEST_INSTRUCTION_MSG_FMT
= ugettext(
70 'Please use this form to request to become a %(name)s')
72 DEF_INVITE_ERROR_MSG_FMT
= ugettext(
73 'This user can not receive an invite to become a %(name)s. <br/>'
74 'Please make sure there is no outstanding invite or request and '
75 'be sure that this user is not a %(name)s.')
77 DEF_REQUEST_ERROR_MSG_FMT
= ugettext(
78 'You can not request to become a %(name)s. <br/>'
79 'Please make sure there is no outstanding invite or request and '
80 'be sure that you are not a %(name)s already.')
83 def __init__(self
, params
=None):
87 params: This dictionary should be filled with the parameters
92 patterns
= params
.get('extra_django_patterns')
97 if params
.get('allow_requests_and_invites'):
98 # add patterns concerning requests and invites
99 patterns
+= [(r
'^%(url_name)s/(?P<access_type>invite)/%(scope)s$',
100 'soc.views.models.%(module_name)s.invite',
101 'Create invite for %(name)s'),
102 (r
'^%(url_name)s/(?P<access_type>accept_invite)/%(scope)s/%(lnp)s$',
103 'soc.views.models.%(module_name)s.accept_invite',
104 'Accept invite for %(name)s'),
105 (r
'^%(url_name)s/(?P<access_type>process_request)/%(scope)s/%(lnp)s$',
106 'soc.views.models.%(module_name)s.process_request',
107 'Process request for %(name)s'),
108 (r
'^%(url_name)s/(?P<access_type>request)/%(scope)s$',
109 'soc.views.models.%(module_name)s.role_request',
110 'Create a Request to become %(name)s')]
111 elif params
.get('allow_invites'):
112 # add patterns concerning only invites
113 patterns
+= [(r
'^%(url_name)s/(?P<access_type>invite)/%(scope)s$',
114 'soc.views.models.%(module_name)s.invite',
115 'Create invite for %(name)s'),
116 (r
'^%(url_name)s/(?P<access_type>accept_invite)/%(scope)s/%(lnp)s$',
117 'soc.views.models.%(module_name)s.accept_invite',
118 'Accept invite for %(name)s'),
119 (r
'^%(url_name)s/(?P<access_type>process_request)/%(scope)s/%(lnp)s$',
120 'soc.views.models.%(module_name)s.process_request',
121 'Process request for %(name)s')]
124 patterns
+= [(r
'^%(url_name)s/(?P<access_type>manage)/%(scope)s/%(lnp)s$',
125 'soc.views.models.%(module_name)s.manage',
126 'Manage a %(name)s'),]
128 new_params
['extra_django_patterns'] = patterns
129 new_params
['scope_redirect'] = redirects
.getInviteRedirect
130 new_params
['manage_redirect'] = redirects
.getListRolesRedirect
132 new_params
['create_template'] = 'soc/role/edit.html'
133 new_params
['edit_template'] = 'soc/role/edit.html'
135 new_params
['create_extra_dynaproperties'] = {
136 'latitude':forms
.fields
.FloatField(widget
=forms
.HiddenInput
,
138 'longitude': forms
.fields
.FloatField(widget
=forms
.HiddenInput
,
140 'clean_link_id': cleaning
.clean_existing_user('link_id'),
141 'clean_phone': cleaning
.clean_phone_number('phone'),
142 'clean_res_street': cleaning
.clean_ascii_only('res_street'),
143 'clean_res_city': cleaning
.clean_ascii_only('res_city'),
144 'clean_res_state': cleaning
.clean_ascii_only('res_state'),
145 'clean_res_postalcode': cleaning
.clean_ascii_only('res_postalcode'),
146 'clean_ship_street': cleaning
.clean_ascii_only('ship_street'),
147 'clean_ship_city': cleaning
.clean_ascii_only('ship_city'),
148 'clean_ship_state': cleaning
.clean_ascii_only('ship_state'),
149 'clean_ship_postalcode': cleaning
.clean_ascii_only('ship_postalcode'),
150 'clean_home_page': cleaning
.clean_url('home_page'),
151 'clean_blog': cleaning
.clean_url('blog'),
152 'clean_photo_url': cleaning
.clean_url('photo_url'),
153 'scope_path': forms
.CharField(widget
=forms
.HiddenInput
,
157 new_params
['extra_dynaexclude'] = ['user', 'status', 'agreed_to_tos_on']
159 params
= dicts
.merge(params
, new_params
, sub_merge
=True)
161 super(View
, self
).__init
__(params
=params
)
163 # add manage template
164 params
['manage_template'] = 'soc/%(module_name)s/manage.html' % params
166 if params
.get('show_in_roles_overview'):
167 # add to roles overview
170 @decorators.merge_params
171 @decorators.check_access
172 def invite(self
, request
, access_type
,
173 page_name
=None, params
=None, **kwargs
):
174 """Creates the page on which an invite can be send out.
177 request: the standard Django HTTP request object
178 access_type : the name of the access type which should be checked
179 context: dictionary containing the context for this view
180 params: a dict with params for this View
181 kwargs: the Key Fields for the specified entity
184 # get the context for this webpage
185 context
= responses
.getUniversalContext(request
)
186 responses
.useJavaScript(context
, params
['js_uses_all'])
187 context
['page_name'] = page_name
188 context
['instruction_message'] = (self
.DEF_INVITE_INSTRUCTION_MSG_FMT
%
191 if request
.method
== 'POST':
192 return self
.invitePost(request
, context
, params
, **kwargs
)
194 # request.method == 'GET'
195 return self
.inviteGet(request
, context
, params
, **kwargs
)
197 def inviteGet(self
, request
, context
, params
, **kwargs
):
198 """Handles the GET request concerning the view that creates an invite
199 for attaining a certain Role.
202 request: the standard Django HTTP request object
203 context: dictionary containing the context for this view
204 params: a dict with params for this View
205 kwargs: the Key Fields for the specified entity
208 # set the role to the right name
209 fields
= {'role': '%(module_name)s' % (params
)}
211 # get the request view parameters and initialize the create form
212 request_params
= request_view
.view
.getParams()
213 form
= request_params
['invite_form'](initial
=fields
)
215 # construct the appropriate response
216 return super(View
, self
)._constructResponse
(request
, entity
=None,
217 context
=context
, form
=form
, params
=request_params
)
219 def invitePost(self
, request
, context
, params
, **kwargs
):
220 """Handles the POST request concerning the view that creates an invite
221 for attaining a certain Role.
224 request: the standard Django HTTP request object
225 page_name: the page name displayed in templates as page and header title
226 params: a dict with params for this View
227 kwargs: the Key Fields for the specified entity
230 # get the request view parameters and populate the form using POST data
231 request_params
= request_view
.view
.getParams()
232 form
= request_params
['invite_form'](request
.POST
)
234 if not form
.is_valid():
235 # return the invalid form response
236 return self
._constructResponse
(request
, entity
=None, context
=context
,
237 form
=form
, params
=request_params
)
239 # collect the cleaned data from the valid form
240 key_name
, form_fields
= soc
.views
.helper
.forms
.collectCleanedFields(form
)
242 # get the group entity for which this request is via the scope_path
243 group
= self
._logic
.getGroupEntityFromScopePath(params
['group_logic'],
244 kwargs
['scope_path'])
246 # get the request scope path
247 request_scope_path
= self
._getRequestScopePathFromGroup
(group
)
249 # create the fields for the new request entity
250 request_fields
= {'link_id': form_fields
['link_id'].link_id
,
252 'scope_path': request_scope_path
,
253 'role': params
['module_name'],
254 'role_verbose': params
['name'],
255 'status': 'group_accepted'}
257 if not self
._isValidNewRequest
(request_fields
, params
):
259 context
['error_message'] = self
.DEF_INVITE_ERROR_MSG_FMT
% (
261 return self
.inviteGet(request
, context
, params
, **kwargs
)
263 # extract the key_name for the new request entity
264 key_name
= request_logic
.logic
.getKeyNameFromFields(request_fields
)
266 # create the request entity
267 request_logic
.logic
.updateOrCreateFromKeyName(request_fields
, key_name
)
269 group_view
= params
.get('group_view')
271 return http
.HttpResponseRedirect('/')
273 # redirect to the requests list
274 return http
.HttpResponseRedirect(
275 redirects
.getListRequestsRedirect(group
, group_view
.getParams()))
277 def _getRequestScopePathFromGroup(self
, group_entity
):
278 """Returns the scope_path that should be put in a request for a given group.
281 group_entity: The group entity for which the request scope_path should
285 if group_entity
.scope_path
:
286 request_scope_path
= '%s/%s' % (
287 group_entity
.scope_path
, group_entity
.link_id
)
289 request_scope_path
= group_entity
.link_id
291 return request_scope_path
294 @decorators.merge_params
295 @decorators.check_access
296 def acceptInvite(self
, request
, access_type
,
297 page_name
=None, params
=None, **kwargs
):
298 """Creates the page process an invite into a Role.
301 request: the standard Django HTTP request object
302 access_type : the name of the access type which should be checked
303 page_name: the page name displayed in templates as page and header title
304 params: a dict with params for this View
305 kwargs: the Key Fields for the specified entity
308 # get the context for this webpage
309 context
= responses
.getUniversalContext(request
)
310 responses
.useJavaScript(context
, params
['js_uses_all'])
311 context
['page_name'] = page_name
313 if request
.method
== 'POST':
314 return self
.acceptInvitePost(request
, context
, params
, **kwargs
)
316 # request.method == 'GET'
317 return self
.acceptInviteGet(request
, context
, params
, **kwargs
)
319 def acceptInviteGet(self
, request
, context
, params
, **kwargs
):
320 """Handles the GET request concerning the creation of a Role via an
324 request: the standard Django HTTP request object
325 context: dictionary containing the context for this view
326 params: a dict with params for this View
327 kwargs: the Key Fields for the specified entity
330 # create the form using the scope_path and link_id from kwargs
332 fields
= {'link_id': kwargs
['link_id'],
333 'scope_path': kwargs
['scope_path']}
334 form
= params
['invited_create_form'](initial
=fields
)
336 # construct the appropriate response
337 return super(View
, self
)._constructResponse
(request
, entity
=None,
338 context
=context
, form
=form
, params
=params
)
340 def acceptInvitePost(self
, request
, context
, params
, **kwargs
):
341 """Handles the POST request concerning the creation of a Role via an
345 request: the standard Django HTTP request object
346 context: dictionary containing the context for this view
347 params: a dict with params for this View
348 kwargs: the Key Fields for the specified entity
351 # populate the form using the POST data
352 form
= params
['invited_create_form'](request
.POST
)
354 if not form
.is_valid():
355 # return the invalid form response
356 return self
._constructResponse
(request
, entity
=None, context
=context
,
357 form
=form
, params
=params
)
359 # collect the cleaned data from the valid form
360 key_name
, fields
= soc
.views
.helper
.forms
.collectCleanedFields(form
)
362 # call the post process method
363 self
._acceptInvitePost
(fields
, request
, context
, params
, **kwargs
)
365 group_logic
= params
['group_logic']
366 group_entity
= group_logic
.getFromKeyName(fields
['scope_path'])
367 fields
['scope'] = group_entity
369 # make sure that this role becomes active once more in case this user
371 fields
['status'] = 'active'
373 # get the key_name for the new entity
374 key_name
= self
._logic
.getKeyNameFromFields(fields
)
376 # create new Role entity
377 entity
= self
._logic
.updateOrCreateFromKeyName(fields
, key_name
)
379 # mark the request as completed
380 request_helper
.completeRequestForRole(entity
, params
['module_name'])
382 # redirect to the roles overview page
383 return http
.HttpResponseRedirect('/user/roles')
385 def _acceptInvitePost(self
, fields
, request
, context
, params
, **kwargs
):
386 """Used to post-process data after the fields have been cleaned.
389 fields : the cleaned fields from the role form
390 request: the standard Django HTTP request object
391 context: dictionary containing the context for this view
392 params: a dict with params for this View
393 kwargs: the Key Fields for the specified entity
398 @decorators.merge_params
399 @decorators.check_access
400 def manage(self
, request
, access_type
,
401 page_name
=None, params
=None, **kwargs
):
402 """Handles the request concerning the view that let's
403 you manage a role's status.
406 request: the standard Django HTTP request object
407 page_name: the page name displayed in templates as page and header title
408 params: a dict with params for this View
409 kwargs: the Key Fields for the specified entity
412 # get the context for this webpage
413 context
= responses
.getUniversalContext(request
)
414 responses
.useJavaScript(context
, params
['js_uses_all'])
415 context
['page_name'] = page_name
417 logic
= params
['logic']
419 # get the entity for the given fields in kwargs
420 fields
= {'scope_path': kwargs
['scope_path'],
421 'link_id': kwargs
['link_id']}
422 role_entity
= logic
.getForFields(kwargs
, unique
=True)
424 # get the redirect for the cancel button or when the resignation is done
425 redirect
= params
['manage_redirect'](role_entity
.scope
,
426 params
['group_view'].getParams())
428 # check to see if resign is true
429 get_dict
= request
.GET
430 resign
= get_dict
.get('resign')
434 resign_error
= params
['logic'].canResign(role_entity
)
437 # change the status of this role_entity to invalid
438 fields
= {'status': 'invalid'}
439 logic
.updateEntityProperties(role_entity
, fields
)
441 # redirect to the roles listing
442 return http
.HttpResponseRedirect(redirect
)
444 # show error to the user
445 context
['resign_error'] = ugettext(resign_error
%params
)
447 # set the appropriate context
448 context
['entity'] = role_entity
449 context
['url_name'] = params
['url_name']
450 context
['cancel_redirect'] = redirect
452 # get the manage template
453 template
= params
['manage_template']
455 # return a proper response
456 return responses
.respond(request
, template
, context
=context
)
458 @decorators.merge_params
459 @decorators.check_access
460 def request(self
, request
, access_type
,
461 page_name
=None, params
=None, **kwargs
):
462 """Handles the request concerning the view that creates a request
463 for attaining a certain Role.
466 request: the standard Django HTTP request object
467 page_name: the page name displayed in templates as page and header title
468 params: a dict with params for this View
469 kwargs: the Key Fields for the specified entity
472 # get the context for this webpage
473 context
= responses
.getUniversalContext(request
)
474 responses
.useJavaScript(context
, params
['js_uses_all'])
475 context
['page_name'] = page_name
476 context
['instruction_message'] = (self
.DEF_REQUEST_INSTRUCTION_MSG_FMT
%
479 if request
.method
== 'POST':
480 return self
.requestPost(request
, context
, params
, **kwargs
)
482 # request.method == 'GET'
483 return self
.requestGet(request
, context
, params
, **kwargs
)
485 def requestGet(self
, request
, context
, params
, **kwargs
):
486 """Handles the GET request concerning the creation of a request
490 request: the standard Django HTTP request object
491 context: dictionary containing the context for this view
492 params: a dict with params for this View
493 kwargs: the Key Fields for the specified entity
496 # set right fields for the request form
497 user_entity
= user_logic
.logic
.getForCurrentAccount()
498 fields
= {'link_id' : user_entity
.link_id
,
499 'role' : params
['module_name'],
500 'group_id' : kwargs
['scope_path']}
502 # get the request view parameters and initialize the create form
503 request_params
= request_view
.view
.getParams()
504 form
= request_params
['request_form'](initial
=fields
)
506 # construct the appropriate response
507 return super(View
, self
)._constructResponse
(request
, entity
=None,
508 context
=context
, form
=form
, params
=request_params
)
510 def requestPost(self
, request
, context
, params
, **kwargs
):
511 """Handles the POST request concerning the creation of a request
515 request: the standard Django HTTP request object
516 context: dictionary containing the context for this view
517 params: a dict with params for this View
518 kwargs: the Key Fields for the specified entity
521 # get the request view parameters and populate the form using POST data
522 request_params
= request_view
.view
.getParams()
523 form
= request_params
['invite_form'](request
.POST
)
525 if not form
.is_valid():
526 # return the invalid form response
527 return self
._constructResponse
(request
, entity
=None, context
=context
,
528 form
=form
, params
=request_params
)
530 # get the group entity for which this request is via the scope_path
531 group
= self
._logic
.getGroupEntityFromScopePath(params
['group_logic'],
532 kwargs
['scope_path'])
534 # get the request scope path
535 request_scope_path
= self
._getRequestScopePathFromGroup
(group
)
537 # defensively set the fields we need for this request and set status to new
538 user_entity
= user_logic
.logic
.getForCurrentAccount()
539 request_fields
= {'link_id' : user_entity
.link_id
,
541 'scope_path' : request_scope_path
,
542 'role' : params
['module_name'],
543 'role_verbose' : params
['name'],
546 if not self
._isValidNewRequest
(request_fields
, params
):
547 # not a valid request
548 context
['error_message'] = self
.DEF_REQUEST_ERROR_MSG_FMT
% (
550 return self
.requestGet(request
, context
, params
, **kwargs
)
552 # extract the key_name for the new request entity
553 key_name
= request_logic
.logic
.getKeyNameFromFields(request_fields
)
555 # create the request entity
556 request_logic
.logic
.updateOrCreateFromKeyName(request_fields
, key_name
)
558 # TODO(ljvderijk): send out a message to alert the users
559 # able to process this request
561 # redirect to requests overview
562 return http
.HttpResponseRedirect('/user/requests')
565 @decorators.merge_params
566 @decorators.check_access
567 def processRequest(self
, request
, access_type
,
568 page_name
=None, params
=None, **kwargs
):
569 """Creates the page upon which a request can be processed.
572 request: the standard Django HTTP request object
573 access_type : the name of the access type which should be checked
574 page_name: the page name displayed in templates as page and header title
575 params: a dict with params for this View
576 kwargs: the Key Fields for the specified entity
579 # get the context for this webpage
580 context
= responses
.getUniversalContext(request
)
581 responses
.useJavaScript(context
, params
['js_uses_all'])
582 context
['page_name'] = page_name
584 # get the request entity using the information from kwargs
585 fields
= {'link_id': kwargs
['link_id'],
586 'scope_path': kwargs
['scope_path'],
587 'role': params
['module_name']}
588 request_entity
= request_logic
.logic
.getForFields(fields
, unique
=True)
590 user_entity
= user_logic
.logic
.getFromKeyNameOr404(request_entity
.link_id
)
592 get_dict
= request
.GET
594 if 'status' in get_dict
.keys():
595 if get_dict
['status'] in ['group_accepted', 'rejected', 'ignored']:
596 # update the request_entity and redirect away from this page
597 request_status
= get_dict
['status']
599 # only update when the status is changing
600 if request_status
!= request_entity
.status
:
601 request_logic
.logic
.updateEntityProperties(request_entity
, {
602 'status': get_dict
['status']})
604 group_view
= params
.get('group_view')
606 return http
.HttpResponseRedirect('/')
608 # redirect to the requests list
609 return http
.HttpResponseRedirect(
610 redirects
.getListRequestsRedirect(request_entity
.scope
,
611 group_view
.getParams()))
613 # put the entity in the context
614 context
['entity'] = request_entity
615 context
['user_in_request'] = user_entity
616 context
['request_status'] = request_entity
.status
617 context
['module_name'] = params
['module_name']
619 #display the request processing page using the appropriate template
620 template
= request_view
.view
.getParams()['request_processing_template']
622 return responses
.respond(request
, template
, context
=context
)
624 def _isValidNewRequest(self
, request_fields
, params
):
625 """Checks if this is a valid Request object to make.
628 request_fields: dict containing the fields for the new request entity.
629 params: parameters for the current view
631 fields
= request_fields
.copy()
632 fields
['status'] = ['new', 'group_accepted', 'ignored']
634 request_entity
= request_logic
.logic
.getForFields(fields
, unique
=True)
637 # already outstanding request
640 # check if the role already exists
641 fields
= {'scope': request_fields
['scope'],
642 'link_id': request_fields
['link_id'],
643 'status': ['active','inactive'],
646 role_entity
= params
['logic'].getForFields(fields
, unique
=True)
649 # already has this role
652 # no oustanding request or a valid role