Total Complexity | 53 |
Total Lines | 289 |
Duplicated Lines | 0 % |
Complex classes like src.registration.admin.RegistrationAdmin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | # -*- coding: utf-8 -*- |
||
161 | class RegistrationAdmin(admin.ModelAdmin): |
||
162 | """Admin class of RegistrationProfile |
||
163 | |||
164 | Admin users can accept/reject registration and activate user in Django |
||
165 | Admin page. |
||
166 | |||
167 | If ``REGISTRATION_SUPPLEMENT_CLASS`` is specified, admin users can see the |
||
168 | summary of the supplemental information in list view and detail of it in |
||
169 | change view. |
||
170 | |||
171 | ``RegistrationProfile`` is not assumed to handle by hand thus |
||
172 | adding/changing/deleting is not accepted even in Admin page. |
||
173 | ``RegistrationProfile`` only can be accepted/rejected or activated. |
||
174 | To prevent these disallowed functions, the special AdminForm called |
||
175 | ``RegistrationAdminForm`` is used. |
||
176 | Its ``save`` method is overridden and it actually does not save the |
||
177 | instance. |
||
178 | It just call ``accept``, ``reject`` or ``activate`` method of current |
||
179 | registration backend. So you don't want to override the ``save`` method of |
||
180 | the form. |
||
181 | |||
182 | """ |
||
183 | list_display = ('user', 'get_status_display', |
||
184 | 'activation_key_expired', 'display_supplement_summary') |
||
185 | raw_id_fields = ['user'] |
||
186 | search_fields = ('user__username', 'user__first_name', 'user__last_name') |
||
187 | list_filter = ('_status', ) |
||
188 | |||
189 | form = RegistrationAdminForm |
||
190 | backend = get_backend() |
||
191 | |||
192 | readonly_fields = ('user', '_status') |
||
193 | |||
194 | actions = ( |
||
195 | 'accept_users', |
||
196 | 'reject_users', |
||
197 | 'force_activate_users', |
||
198 | 'resend_acceptance_email' |
||
199 | ) |
||
200 | |||
201 | def __init__(self, model, admin_site): |
||
202 | super(RegistrationAdmin, self).__init__(model, admin_site) |
||
203 | if not hasattr(super(RegistrationAdmin, self), 'get_inline_instances'): |
||
204 | # Django 1.3 doesn't have ``get_inline_instances`` method but |
||
205 | # ``inline_instances`` was generated in ``__init__`` thus |
||
206 | # update the attribute |
||
207 | self.inline_instances = self.get_inline_instances(None) |
||
208 | |||
209 | def has_add_permission(self, request): |
||
210 | """registration profile should not be created by hand""" |
||
211 | return False |
||
212 | |||
213 | def has_delete_permission(self, request, obj=None): |
||
214 | """registration profile should not be created by hand""" |
||
215 | return False |
||
216 | |||
217 | def has_accept_permission(self, request, obj): |
||
218 | """whether the user has accept permission""" |
||
219 | if not settings.REGISTRATION_USE_OBJECT_PERMISSION: |
||
220 | obj = None |
||
221 | return request.user.has_perm('registration.accept_registration', obj) |
||
222 | |||
223 | def has_reject_permission(self, request, obj): |
||
224 | """whether the user has reject permission""" |
||
225 | if not settings.REGISTRATION_USE_OBJECT_PERMISSION: |
||
226 | obj = None |
||
227 | return request.user.has_perm('registration.reject_registration', obj) |
||
228 | |||
229 | def has_activate_permission(self, request, obj): |
||
230 | """whether the user has activate permission""" |
||
231 | if not settings.REGISTRATION_USE_OBJECT_PERMISSION: |
||
232 | obj = None |
||
233 | return request.user.has_perm('registration.activate_user', obj) |
||
234 | |||
235 | def get_actions(self, request): |
||
236 | """get actions displaied in admin site |
||
237 | |||
238 | RegistrationProfile should not be deleted in admin site thus |
||
239 | 'delete_selected' is disabled in default. |
||
240 | |||
241 | Each actions has permissions thus delete the action if the accessed |
||
242 | user doesn't have appropriate permission. |
||
243 | |||
244 | """ |
||
245 | actions = super(RegistrationAdmin, self).get_actions(request) |
||
246 | if 'delete_selected' in actions: |
||
247 | del actions['delete_selected'] |
||
248 | if not request.user.has_perm('registration.accept_registration'): |
||
249 | del actions['accept_users'] |
||
250 | if not request.user.has_perm('registration.reject_registration'): |
||
251 | del actions['reject_users'] |
||
252 | if not request.user.has_perm('registration.accept_registration') or \ |
||
253 | not request.user.has_perm('registration.activate_user'): |
||
254 | del actions['force_activate_users'] |
||
255 | return actions |
||
256 | |||
257 | def accept_users(self, request, queryset): |
||
258 | """Accept the selected users, if they are not already accepted""" |
||
259 | for profile in queryset: |
||
260 | self.backend.accept(profile, request=request, force=True) |
||
261 | accept_users.short_description = _( |
||
262 | "(Re)Accept registrations of selected users" |
||
263 | ) |
||
264 | |||
265 | def reject_users(self, request, queryset): |
||
266 | """Reject the selected users, if they are not already accepted""" |
||
267 | for profile in queryset: |
||
268 | self.backend.reject(profile, request=request) |
||
269 | reject_users.short_description = _( |
||
270 | "Reject registrations of selected users" |
||
271 | ) |
||
272 | |||
273 | def force_activate_users(self, request, queryset): |
||
274 | """Activates the selected users, if they are not already activated""" |
||
275 | for profile in queryset: |
||
276 | self.backend.accept(profile, request=request, send_email=False) |
||
277 | self.backend.activate(profile.activation_key, request=request) |
||
278 | force_activate_users.short_description = _( |
||
279 | "Activate selected users forcibly" |
||
280 | ) |
||
281 | |||
282 | def resend_acceptance_email(self, request, queryset): |
||
283 | """Re-sends acceptance emails for the selected users |
||
284 | |||
285 | Note that this will *only* send acceptance emails for users |
||
286 | who are eligible to activate; emails will not be sent to users |
||
287 | whose activation keys have expired or who have already |
||
288 | activated or rejected. |
||
289 | |||
290 | """ |
||
291 | site = get_site(request) |
||
292 | for profile in queryset: |
||
293 | if not profile.activation_key_expired(): |
||
294 | if profile.status != 'rejected': |
||
295 | profile.send_acceptance_email(site=site) |
||
296 | resend_acceptance_email.short_description = _( |
||
297 | "Re-send acceptance emails to selected users" |
||
298 | ) |
||
299 | |||
300 | def display_supplement_summary(self, obj): |
||
301 | """Display supplement summary |
||
302 | |||
303 | Display ``__unicode__`` method result of |
||
304 | ``REGISTRATION_SUPPLEMENT_CLASS`` |
||
305 | ``Not available`` when ``REGISTRATION_SUPPLEMENT_CLASS`` is not |
||
306 | specified |
||
307 | |||
308 | """ |
||
309 | if obj.supplement: |
||
310 | return force_text(obj.supplement) |
||
311 | return _('Not available') |
||
312 | display_supplement_summary.short_description = _( |
||
313 | 'A summary of supplemental information' |
||
314 | ) |
||
315 | |||
316 | def display_activation_key(self, obj): |
||
317 | """Display activation key with link |
||
318 | |||
319 | Note that displaying activation key is not recommended in security |
||
320 | reason. |
||
321 | If you really want to use this method, create your own subclass and |
||
322 | re-register to admin.site |
||
323 | |||
324 | Even this is a little bit risky, it is really useful for developping |
||
325 | (without checking email, you can activate any user you want) thus |
||
326 | I created but turned off in default :-p |
||
327 | |||
328 | """ |
||
329 | if obj.status == 'accepted': |
||
330 | activation_url = reverse('registration_activate', |
||
331 | kwargs={ |
||
332 | 'activation_key': obj.activation_key}) |
||
333 | return mark_safe('<a href="%s">%s</a>' % ( |
||
334 | activation_url, obj.activation_key)) |
||
335 | return _('Not available') |
||
336 | display_activation_key.short_description = _('Activation key') |
||
337 | display_activation_key.allow_tags = True |
||
338 | |||
339 | def get_inline_instances(self, request, obj=None): |
||
340 | """ |
||
341 | return inline instances with registration supplement inline instance |
||
342 | """ |
||
343 | inline_instances = [] |
||
344 | supplement_class = self.backend.get_supplement_class() |
||
345 | if supplement_class: |
||
346 | kwargs = { |
||
347 | 'extra': 1, |
||
348 | 'max_num': 1, |
||
349 | 'can_delete': False, |
||
350 | 'model': supplement_class, |
||
351 | } |
||
352 | inline_base = get_supplement_admin_inline_base_class() |
||
353 | inline_form = type( |
||
354 | str("RegistrationSupplementInlineAdmin"), |
||
355 | (inline_base,), kwargs |
||
356 | ) |
||
357 | inline_instances = [inline_form(self.model, self.admin_site)] |
||
358 | supercls = super(RegistrationAdmin, self) |
||
359 | if hasattr(supercls, 'get_inline_instances'): |
||
360 | if django.VERSION >= (1, 5): |
||
361 | inline_instances.extend(supercls.get_inline_instances( |
||
362 | request, obj |
||
363 | )) |
||
364 | else: |
||
365 | # Django 1.4 cannot handle obj |
||
366 | inline_instances.extend(supercls.get_inline_instances( |
||
367 | request, |
||
368 | )) |
||
369 | else: |
||
370 | # Django 1.3 |
||
371 | for inline_class in self.inlines: |
||
372 | inline_instance = inline_class(self.model, self.admin_site) |
||
373 | inline_instances.append(inline_instance) |
||
374 | return inline_instances |
||
375 | |||
376 | def get_object(self, request, object_id, from_field=None): |
||
377 | """add ``request`` instance to model instance and return |
||
378 | |||
379 | To get ``request`` instance in form, ``request`` instance is stored |
||
380 | in the model instance. |
||
381 | |||
382 | """ |
||
383 | if django.VERSION < (1, 8, 0): |
||
384 | obj = super(RegistrationAdmin, self).get_object(request, object_id) |
||
385 | else: |
||
386 | # Note: |
||
387 | # from_field was introduced from django 1.8 |
||
388 | obj = super(RegistrationAdmin, self).get_object(request, |
||
389 | object_id, |
||
390 | from_field) |
||
391 | if obj: |
||
392 | attr_name = settings._REGISTRATION_ADMIN_REQ_ATTR_NAME_IN_MODEL_INS |
||
393 | setattr(obj, attr_name, request) |
||
394 | return obj |
||
395 | |||
396 | @csrf_protect_m |
||
397 | @transaction_atomic |
||
398 | def change_view(self, request, object_id, form_url='', extra_context=None): |
||
399 | """called for change view |
||
400 | |||
401 | Check permissions of the admin user for ``POST`` request depends on |
||
402 | what action is requested and raise PermissionDenied if the action is |
||
403 | not accepted for the admin user. |
||
404 | |||
405 | """ |
||
406 | obj = self.get_object(request, unquote(object_id)) |
||
407 | |||
408 | # Permissin check |
||
409 | if request.method == 'POST': |
||
410 | # |
||
411 | # Note: |
||
412 | # actions will be treated in form.save() method. |
||
413 | # in general, activate action will remove the profile because |
||
414 | # the profile is no longer required after the activation |
||
415 | # but if I remove the profile in form.save() method, django admin |
||
416 | # will raise IndexError thus I passed `no_profile_delete = True` |
||
417 | # to activate with backend in form.save() method. |
||
418 | # |
||
419 | action_name = request.POST.get('action_name') |
||
420 | if (action_name == 'accept' and |
||
421 | not self.has_accept_permission(request, obj)): |
||
422 | raise PermissionDenied |
||
423 | elif (action_name == 'reject' and |
||
424 | not self.has_reject_permission(request, obj)): |
||
425 | raise PermissionDenied |
||
426 | elif (action_name == 'activate' and |
||
427 | not self.has_activate_permission(request, obj)): |
||
428 | raise PermissionDenied |
||
429 | elif action_name == 'force_activate' and ( |
||
430 | not self.has_accept_permission(request, obj) or |
||
431 | not self.has_activate_permission(request, obj)): |
||
432 | raise PermissionDenied |
||
433 | |||
434 | if django.VERSION < (1, 4,): |
||
435 | response = super(RegistrationAdmin, self).change_view( |
||
436 | request, object_id, extra_context |
||
437 | ) |
||
438 | else: |
||
439 | response = super(RegistrationAdmin, self).change_view( |
||
440 | request, object_id, form_url, extra_context |
||
441 | ) |
||
442 | |||
443 | if (request.method == 'POST' and |
||
444 | action_name in ('activate', 'force_activate')): |
||
445 | # if the requested data is valid then response will be an instance |
||
446 | # of ``HttpResponseRedirect`` otherwise ``TemplateResponse`` |
||
447 | if isinstance(response, HttpResponseRedirect): |
||
448 | obj.delete() |
||
449 | return response |
||
450 | admin.site.register(RegistrationProfile, RegistrationAdmin) |
||
451 |