+from django.core.exceptions import PermissionDenied
+from django.core.urlresolvers import reverse, NoReverseMatch
+
+import django_evolution
+import threading
+
+# thread locals necessary to work around a django-suit issue
+_thread_locals = threading.local()
+
+def backend_icon(obj): # backend_status, enacted, updated):
+ #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
+ if (obj.enacted is not None) and obj.enacted >= obj.updated:
+ return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
+ else:
+ if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
+ return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
+ else:
+ return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % obj.backend_status
+
+def backend_text(obj):
+ icon = backend_icon(obj)
+ if (obj.enacted is not None) and obj.enacted >= obj.updated:
+ return "%s %s" % (icon, "successfully enacted") # enacted on %s" % str(obj.enacted))
+ else:
+ return "%s %s" % (icon, obj.backend_status)
+
+class PlainTextWidget(forms.HiddenInput):
+ input_type = 'hidden'
+
+ def render(self, name, value, attrs=None):
+ if value is None:
+ value = ''
+ return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
+
+class PermissionCheckingAdminMixin(object):
+ # call save_by_user and delete_by_user instead of save and delete
+
+ def has_add_permission(self, request, obj=None):
+ return (not self.__user_is_readonly(request))
+
+ def has_delete_permission(self, request, obj=None):
+ return (not self.__user_is_readonly(request))
+
+ def save_model(self, request, obj, form, change):
+ if self.__user_is_readonly(request):
+ # this 'if' might be redundant if save_by_user is implemented right
+ raise PermissionDenied
+
+ obj.caller = request.user
+ # update openstack connection to use this site/tenant
+ obj.save_by_user(request.user)
+
+ def delete_model(self, request, obj):
+ obj.delete_by_user(request.user)
+
+ def save_formset(self, request, form, formset, change):
+ instances = formset.save(commit=False)
+ for instance in instances:
+ instance.save_by_user(request.user)
+
+ # BUG in django 1.7? Objects are not deleted by formset.save if
+ # commit is False. So let's delete them ourselves.
+ #
+ # code from forms/models.py save_existing_objects()
+ try:
+ forms_to_delete = formset.deleted_forms\r
+ except AttributeError:\r
+ forms_to_delete = []
+ if formset.initial_forms:
+ for form in formset.initial_forms:
+ obj = form.instance
+ if form in forms_to_delete:
+ if obj.pk is None:
+ continue
+ formset.deleted_objects.append(obj)
+ obj.delete()
+
+ formset.save_m2m()
+
+ def get_actions(self,request):
+ actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
+
+ if self.__user_is_readonly(request):
+ if 'delete_selected' in actions:
+ del actions['delete_selected']
+
+ return actions
+
+ def change_view(self,request,object_id, extra_context=None):
+ if self.__user_is_readonly(request):
+ if not hasattr(self, "readonly_save"):\r
+ # save the original readonly fields\r
+ self.readonly_save = self.readonly_fields\r
+ self.inlines_save = self.inlines\r
+ if hasattr(self, "user_readonly_fields"):\r
+ self.readonly_fields=self.user_readonly_fields\r
+ if hasattr(self, "user_readonly_inlines"):\r
+ self.inlines = self.user_readonly_inlines\r
+ else:\r
+ if hasattr(self, "readonly_save"):\r
+ # restore the original readonly fields\r
+ self.readonly_fields = self.readonly_save\r
+ if hasattr(self, "inlines_save"):\r
+ self.inlines = self.inlines_save
+
+ try:
+ return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
+ except PermissionDenied:
+ pass
+ if request.method == 'POST':
+ raise PermissionDenied
+ request.readonly = True
+ return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
+
+ def __user_is_readonly(self, request):
+ return request.user.isReadOnlyUser()
+
+ def backend_status_text(self, obj):
+ return mark_safe(backend_text(obj))
+
+ def backend_status_icon(self, obj):
+ return mark_safe(backend_icon(obj))
+ backend_status_icon.short_description = ""
+
+class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
+ # Note: Make sure PermissionCheckingAdminMixin is listed before
+ # admin.ModelAdmin in the class declaration.
+
+ pass
+
+class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
+ save_on_top = False
+
+class SingletonAdmin (ReadOnlyAwareAdmin):
+ def has_add_permission(self, request):
+ if not super(SingletonAdmin, self).has_add_permission(request):
+ return False