|
1 #!/usr/bin/python2.5 |
|
2 # |
|
3 # Copyright 2008 the Melange authors. |
|
4 # |
|
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 |
|
8 # |
|
9 # http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
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. |
|
16 |
|
17 """Helpers functions for displaying views. |
|
18 """ |
|
19 |
|
20 __authors__ = [ |
|
21 '"Todd Larsen" <tlarsen@google.com>', |
|
22 '"Sverre Rabbelier" <sverer@rabbelier.nl>', |
|
23 '"Pawel Solyga" <pawel.solyga@gmail.com>', |
|
24 ] |
|
25 |
|
26 |
|
27 from django import http |
|
28 from django.utils.translation import ugettext_lazy |
|
29 |
|
30 import soc.logic |
|
31 import soc.logic.out_of_band |
|
32 import soc.views.helper.lists |
|
33 import soc.views.helper.responses |
|
34 import soc.views.out_of_band |
|
35 |
|
36 from soc.logic import models |
|
37 from soc.logic import validate |
|
38 from soc.views import simple |
|
39 from soc.views import helper |
|
40 from soc.views.helper import access |
|
41 |
|
42 |
|
43 class View: |
|
44 """Views for entity classes. |
|
45 |
|
46 The View class functions specific to Entity classes by relying |
|
47 on the the child-classes to define the following fields: |
|
48 |
|
49 self._logic: the logic singleton for this entity |
|
50 |
|
51 Args: |
|
52 rights: This dictionary should be filled with the access check |
|
53 functions that should be called |
|
54 |
|
55 params: This dictionary should be filled with the parameters |
|
56 specific to this entity. |
|
57 """ |
|
58 |
|
59 def __init__(self, params=None, rights=None): |
|
60 """ |
|
61 """ |
|
62 |
|
63 new_rights = {} |
|
64 new_rights['base'] = [access.checkIsLoggedIn] |
|
65 |
|
66 self._rights = soc.logic.dicts.mergeDicts(rights, new_rights) |
|
67 self._params = params |
|
68 |
|
69 self.DEF_SUBMIT_MSG_PARAM_NAME = 's' |
|
70 |
|
71 self.DEF_CREATE_NEW_ENTITY_MSG = ugettext_lazy( |
|
72 ' You can create a new %(model_type)s by visiting' |
|
73 ' <a href="%(create)s">Create ' |
|
74 'a New %(Type)s</a> page.') |
|
75 |
|
76 def public(self, request, **kwargs): |
|
77 """Displays the public page for the entity specified by **kwargs |
|
78 |
|
79 Args: |
|
80 request: the Django request object |
|
81 kwargs: the Key Fields for the specified entity |
|
82 """ |
|
83 |
|
84 try: |
|
85 self.checkAccess('edit', request) |
|
86 except soc.views.out_of_band.AccessViolationResponse, alt_response: |
|
87 return alt_response.response() |
|
88 |
|
89 # create default template context for use with any templates |
|
90 context = helper.responses.getUniversalContext(request) |
|
91 entity = None |
|
92 |
|
93 try: |
|
94 entity = self._logic.getIfFields(**kwargs) |
|
95 except soc.logic.out_of_band.ErrorResponse, error: |
|
96 template = soc._params['public_template'] |
|
97 return simple.errorResponse(request, error, template, context) |
|
98 |
|
99 if not entity: |
|
100 #TODO: Change this into a proper redirect |
|
101 return http.HttpResponseRedirect('/') |
|
102 |
|
103 self._makePublic(entity) |
|
104 |
|
105 context['entity'] = entity |
|
106 context['entity_type'] = self._params['name'] |
|
107 |
|
108 template = self._params['public_template'] |
|
109 |
|
110 return helper.responses.respond(request, template, context) |
|
111 |
|
112 def create(self, request, **kwargs): |
|
113 """Displays the create page for this entity type |
|
114 |
|
115 request: the django request object |
|
116 kwargs: not used |
|
117 """ |
|
118 |
|
119 # Create page is an edit page with no key fields |
|
120 kwargs = {} |
|
121 return self.edit(request, **kwargs) |
|
122 |
|
123 def edit(self, request, **kwargs): |
|
124 """Displays the public page for the entity specified by **kwargs |
|
125 |
|
126 Args: |
|
127 request: The Django request object |
|
128 kwargs: The Key Fields for the specified entity |
|
129 """ |
|
130 |
|
131 try: |
|
132 self.checkAccess('edit', request) |
|
133 except soc.views.out_of_band.AccessViolationResponse, alt_response: |
|
134 return alt_response.response() |
|
135 |
|
136 entity = None |
|
137 |
|
138 try: |
|
139 entity = self._logic.getIfFields(**kwargs) |
|
140 except soc.logic.out_of_band.ErrorResponse, error: |
|
141 template = soc._params['public_template'] |
|
142 error.message = error.message + self.DEF_CREATE_NEW_ENTITY_MSG % { |
|
143 'entity_type_lower' : self._params['name_lower'], |
|
144 'entity_type_upper' : self._parmas['name_upper'], |
|
145 'create' : self._redirects['create'] |
|
146 } |
|
147 return simple.errorResponse(request, error, template, context) |
|
148 |
|
149 if request.method == 'POST': |
|
150 return self.editPost(request, entity) |
|
151 else: |
|
152 return self.editGet(request, entity) |
|
153 |
|
154 def editPost(self, request, entity): |
|
155 """Same as edit, but on POST |
|
156 """ |
|
157 |
|
158 context = helper.responses.getUniversalContext(request) |
|
159 |
|
160 if entity: |
|
161 form = self._params['edit_form'](request.POST) |
|
162 else: |
|
163 form = self._params['create_form'](request.POST) |
|
164 |
|
165 if not form.is_valid(): |
|
166 return |
|
167 |
|
168 fields = self.collectCleanedFields(form) |
|
169 |
|
170 self._editPost(request, entity, fields) |
|
171 |
|
172 keys = self._logic.extractKeyFields(fields) |
|
173 entity = self._logic.updateOrCreateFromFields(fields, **keys) |
|
174 |
|
175 if not entity: |
|
176 return http.HttpResponseRedirect('/') |
|
177 |
|
178 params=self._params['edit_params'] |
|
179 #TODO(SRabbelier) Construct a suffix |
|
180 suffix = None |
|
181 |
|
182 # redirect to (possibly new) location of the entity |
|
183 # (causes 'Profile saved' message to be displayed) |
|
184 return helper.responses.redirectToChangedSuffix( |
|
185 request, None, suffix, |
|
186 params=params) |
|
187 |
|
188 def editGet(self, request, entity): |
|
189 """Same as edit, but on GET |
|
190 """ |
|
191 |
|
192 context = helper.responses.getUniversalContext(request) |
|
193 #TODO(SRabbelier) Construct a suffix |
|
194 suffix = None |
|
195 is_self_referrer = helper.requests.isReferrerSelf(request, suffix=suffix) |
|
196 |
|
197 # Remove the params from the request, this is relevant only if |
|
198 # someone bookmarked a POST page. |
|
199 if request.GET.get(self.DEF_SUBMIT_MSG_PARAM_NAME): |
|
200 if (not entity) or (not is_self_referrer): |
|
201 return http.HttpResponseRedirect(request.path) |
|
202 |
|
203 if entity: |
|
204 # Note: no message will be displayed if parameter is not present |
|
205 context['notice'] = helper.requests.getSingleIndexedParamValue( |
|
206 request, |
|
207 self.DEF_SUBMIT_MSG_PARAM_NAME, |
|
208 values=self._params['save_message']) |
|
209 |
|
210 # populate form with the existing entity |
|
211 form = self._params['edit_form'](instance=entity) |
|
212 else: |
|
213 form = self._params['create_form']() |
|
214 |
|
215 context['form'] = form |
|
216 context['entity'] = entity |
|
217 context['entity_type'] = self._params['name'] |
|
218 context['entity_type_plural'] = self._params['name_plural'] |
|
219 |
|
220 template = self._params['create_template'] |
|
221 |
|
222 return helper.responses.respond(request, template, context) |
|
223 |
|
224 def list(self, request): |
|
225 """Displays the list page for the entity type |
|
226 """ |
|
227 |
|
228 try: |
|
229 self.checkAccess('list', request) |
|
230 except soc.views.out_of_band.AccessViolationResponse, alt_response: |
|
231 return alt_response.response() |
|
232 |
|
233 context = helper.responses.getUniversalContext(request) |
|
234 |
|
235 offset, limit = helper.lists.cleanListParameters( |
|
236 offset=request.GET.get('offset'), limit=request.GET.get('limit')) |
|
237 |
|
238 # Fetch one more to see if there should be a 'next' link |
|
239 entities = self._logic.getForLimitAndOffset(limit + 1, offset=offset) |
|
240 |
|
241 context['pagination_form'] = helper.lists.makePaginationForm(request, limit) |
|
242 |
|
243 templates = self._params['lists_template'] |
|
244 |
|
245 context = helper.lists.setList(request, context, entities, |
|
246 offset, limit, templates) |
|
247 |
|
248 context['entity_type'] = self._params['name'] |
|
249 context['entity_type_plural'] = self._params['name_plural'] |
|
250 |
|
251 template = self._params['list_template'] |
|
252 |
|
253 return helper.responses.respond(request, template, context) |
|
254 |
|
255 def delete(self, request, **kwargs): |
|
256 """Shows the delete page for the entity specified by kwargs |
|
257 |
|
258 Args: |
|
259 request: The Django request object |
|
260 kwargs: The Key Fields for the specified entity |
|
261 """ |
|
262 |
|
263 try: |
|
264 self.checkAccess('delete', request) |
|
265 except soc.views.out_of_band.AccessViolationResponse, alt_response: |
|
266 return alt_response.response() |
|
267 |
|
268 # create default template context for use with any templates |
|
269 context = helper.responses.getUniversalContext(request) |
|
270 entity = None |
|
271 |
|
272 try: |
|
273 entity = models.sponsor.logic.getIfFields(**kwargs) |
|
274 except soc.logic.out_of_band.ErrorResponse, error: |
|
275 template = self._templates['create'] |
|
276 error.message = error.message + DEF_CREATE_NEW_ENTITY_MSG % { |
|
277 'entity_type_lower' : self._name, |
|
278 'entity_type_upper' : self._Name, |
|
279 'create' : self._redirects['create'] |
|
280 } |
|
281 return simple.errorResponse(request, error, template, context) |
|
282 |
|
283 if not entity: |
|
284 #TODO: Create a proper error page for this |
|
285 return http.HttpResponseRedirect('/') |
|
286 |
|
287 if not self._logic.isDeletable(entity): |
|
288 # TODO: Direct user to page telling them they can't delete that entity, and why |
|
289 pass |
|
290 |
|
291 self._logic.delete(entity) |
|
292 redirect = self._params['delete_redirect'] |
|
293 |
|
294 return http.HttpResponseRedirect(redirect) |
|
295 |
|
296 def _editPost(self, request, entity, fields): |
|
297 """Performs any required processing on the entity to post its edit page |
|
298 |
|
299 Args: |
|
300 request: The django request object |
|
301 entity: the entity to make public |
|
302 fields: The new field values |
|
303 """ |
|
304 |
|
305 pass |
|
306 |
|
307 def checkUnspecified(self, access_type, request): |
|
308 """Checks whether an unspecified access_type should be allowed |
|
309 |
|
310 Args: |
|
311 access_type: the access type (such as 'list' or 'edit') that was |
|
312 not present in the _rights dictionary when checking. |
|
313 """ |
|
314 |
|
315 pass |
|
316 |
|
317 def checkAccess(self, access_type, request): |
|
318 """Runs all the defined checks for the specified type |
|
319 |
|
320 Args: |
|
321 access_type: the type of request (such as 'list' or 'edit') |
|
322 request: the Django request object |
|
323 |
|
324 Returns: |
|
325 True: If all the required access checks have been made successfully |
|
326 False: If a check failed, in this case self._response will contain |
|
327 the response provided by the failed access check. |
|
328 """ |
|
329 |
|
330 if access_type not in self._rights: |
|
331 self.checkUnspecified(access_type, request) |
|
332 return |
|
333 |
|
334 # Call each access checker |
|
335 for check in self._rights['base']: |
|
336 check(request) |
|
337 |
|
338 for check in self._rights[access_type]: |
|
339 check(request) |
|
340 |
|
341 # All checks were successfull |
|
342 return |
|
343 |
|
344 def collectCleanedFields(self, form): |
|
345 """Collects all cleaned fields from form and returns them |
|
346 |
|
347 Args: |
|
348 form: The form from which the cleaned fields should be collected |
|
349 """ |
|
350 |
|
351 fields = {} |
|
352 |
|
353 for field, value in form.cleaned_data.iteritems(): |
|
354 fields[field] = value |
|
355 |
|
356 return fields |