Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Ludvig Wadenstein 2013-05-18 18:28:03 +02:00
commit bbb8207217
7 changed files with 188 additions and 42 deletions

View file

@ -14,4 +14,4 @@ Developers
* Raphael Kimmig (@RaphaelKimmig)
* Andrew Ingram (@AndrewIngram)
* Gregor Müllegger (@gregmuellegger)
* Rivo Laks (@rivol)

View file

@ -5,6 +5,8 @@ synonymous with the django.contrib.admin.sites model.
"""
from django.conf.urls import patterns, include, url
from django.contrib.auth import models as auth_app
from django.db.models import get_models, signals
from djadmin2 import views
@ -44,24 +46,42 @@ class BaseAdmin2(object):
readonly_fields = ()
ordering = None
def has_view_permission(self, request):
"""
Returns True if the given HttpRequest has permission to view
*at least one* page in the mongonaut site.
"""
return request.user.is_authenticated() and request.user.is_active
def has_edit_permission(self, request):
def __init__(self, model):
super(BaseAdmin2, self).__init__()
self.model = model
def _user_has_permission(self, user, permission_type, obj=None):
""" Generic method for checking whether the user has permission of specified type for the model.
Type can be one of view, add, change, delete.
You can also specify instance of the model for object-specific permission check.
"""
if not user.is_authenticated() or not user.is_staff:
return False
opts = self.model._meta
full_permission_name = '%s.%s_%s' % (opts.app_label, permission_type, opts.object_name.lower())
return user.has_perm(full_permission_name, obj)
def has_permission(self, request, permission_type, obj=None):
return self._user_has_permission(request.user, permission_type, obj)
def has_view_permission(self, request, obj=None):
""" Can view this object """
return self.has_permission(request, 'view', obj)
def has_edit_permission(self, request, obj=None):
""" Can edit this object """
return request.user.is_authenticated() and request.user.is_active and request.user.is_staff
return self.has_permission(request, 'change', obj)
def has_add_permission(self, request):
def has_add_permission(self, request, obj=None):
""" Can add this object """
return request.user.is_authenticated() and request.user.is_active and request.user.is_staff
return self.has_permission(request, 'add', obj)
def has_delete_permission(self, request):
def has_delete_permission(self, request, obj=None):
""" Can delete this object """
return request.user.is_authenticated() and request.user.is_active and request.user.is_superuser
return self.has_permission(request, 'delete', obj)
class ModelAdmin2(BaseAdmin2):
@ -134,12 +154,12 @@ class ModelAdmin2(BaseAdmin2):
),
url(
regex=r'^create/$',
view=self.create_view.as_view(**self.get_create_kwargs()),
view=self.create_view.as_view(**self.get_create_kwargs()),
name='create'
),
url(
regex=r'^(?P<pk>[0-9]+)/$',
view=self.detail_view.as_view(**self.get_detail_kwargs()),
view=self.detail_view.as_view(**self.get_detail_kwargs()),
name='detail'
),
url(
@ -158,3 +178,55 @@ class ModelAdmin2(BaseAdmin2):
def urls(self):
# We set the application and instance namespace here
return self.get_urls(), None, None
def create_permissions(app, created_models, verbosity, **kwargs):
"""
Creates 'view' permissions for all models.
django.contrib.auth only creates add, change and delete permissions. Since we also support read-only views, we need
to add our own extra permission.
Copied from django.contrib.auth.management.create_permissions
"""
from django.contrib.contenttypes.models import ContentType
def _get_permission_codename(action, opts):
return u'%s_%s' % (action, opts.object_name.lower())
app_models = get_models(app)
# This will hold the permissions we're looking for as
# (content_type, (codename, name))
searched_perms = list()
# The codenames and ctypes that should exist.
ctypes = set()
for klass in app_models:
ctype = ContentType.objects.get_for_model(klass)
ctypes.add(ctype)
opts = klass._meta
perm = (_get_permission_codename('view', opts), u'Can view %s' % opts.verbose_name_raw)
searched_perms.append((ctype, perm))
# Find all the Permissions that have a context_type for a model we're
# looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create.
all_perms = set(auth_app.Permission.objects.filter(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
objs = [
auth_app.Permission(codename=codename, name=name, content_type=ctype)
for ctype, (codename, name) in searched_perms
if (ctype.pk, codename) not in all_perms
]
auth_app.Permission.objects.bulk_create(objs)
if verbosity >= 2:
for obj in objs:
print "Adding permission '%s'" % obj
signals.post_syncdb.connect(create_permissions,
dispatch_uid = "django-admin2.djadmin2.models.create_permissions")

View file

@ -1,11 +1,42 @@
{% extends "admin2/bootstrap/base.html" %}
{% block content %}
<a href="./create/">add</a>
<hr/>
{% for obj in object_list %}
{{ obj }} <a href="./{{ obj.pk }}/">detail</a> <a href="./{{ obj.pk }}/update/">edit</a> <a href="./{{ obj.pk }}/delete/">delete</a><br/>
{% endfor %}
<div class="row">
<div class="span10">
<h3>Select {{ model }} to change</h3>
</div>
<div class="span2">
{% if has_add_permission %}
<a class="btn" href="./create/">Add {{ model|title }} <i class=" icon-plus-sign"></i></a>
{% endif %}
</div>
</div>
<hr/>
<div class="row">
<div class="span12">
<table class="table table-bordered table-striped">
<thead>
<th><input type="checkbox"></th>
<th>{{ model|title}}</th>
</thead>
<tbody>
{% for obj in object_list %}
<td><input type="checkbox"></td>
<td>
{{ obj }} <a href="./{{ obj.pk }}/">detail</a>
{% if has_edit_permission %}
<a href="./{{ obj.pk }}/update/">edit</a>
{% endif %}
{% if has_delete_permission %}
<a href="./{{ obj.pk }}/delete/">delete</a>
{% endif %}
</td>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock content %}

View file

@ -1,21 +1,52 @@
import os
from django.conf import settings
from django.contrib.auth.views import redirect_to_login
from django.core.exceptions import PermissionDenied
from django.forms.models import modelform_factory
from django.views import generic
from django.db import models
from braces.views import LoginRequiredMixin, StaffuserRequiredMixin
from braces.views import LoginRequiredMixin, StaffuserRequiredMixin, AccessMixin
ADMIN2_THEME_DIRECTORY = getattr(settings, "ADMIN2_THEME_DIRECTORY", "admin2/bootstrap")
class Admin2Mixin(object):
modeladmin = None
class Admin2Mixin(object):
def get_template_names(self):
return [os.path.join(ADMIN2_THEME_DIRECTORY, self.default_template_name)]
class AdminModel2Mixin(Admin2Mixin, AccessMixin):
modeladmin = None
# Permission type to check for when a request is sent to this view.
permission_type = None
def dispatch(self, request, *args, **kwargs):
# Check if user has necessary permissions. If the permission_type isn't specified then check for staff status.
print "distpatch perm check:", self.permission_type
has_permission = self.modeladmin.has_permission(request, self.permission_type) \
if self.permission_type else request.user.is_staff
# Raise exception or redirect to login if user doesn't have permissions.
if not has_permission:
if self.raise_exception:
raise PermissionDenied # return a forbidden response
else:
return redirect_to_login(request.get_full_path(),
self.get_login_url(), self.get_redirect_field_name())
return super(AdminModel2Mixin, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(AdminModel2Mixin, self).get_context_data(**kwargs)
context.update({
'has_add_permission': self.modeladmin.has_add_permission(self.request),
'has_edit_permission': self.modeladmin.has_edit_permission(self.request),
'has_delete_permission': self.modeladmin.has_delete_permission(self.request),
})
return context
def get_model(self):
return self.model
@ -39,26 +70,37 @@ class IndexView(Admin2Mixin, generic.TemplateView):
})
return data
class ModelListView(Admin2Mixin, generic.ListView):
class ModelListView(AdminModel2Mixin, generic.ListView):
default_template_name = "model_list.html"
permission_type = 'view'
def get_context_data(self, **kwargs):
context = super(ModelListView, self).get_context_data(**kwargs)
context['model'] = self.get_model()._meta.verbose_name
context['model_pluralized'] = self.get_model()._meta.verbose_name_plural
return context
class ModelDetailView(Admin2Mixin, generic.DetailView):
class ModelDetailView(AdminModel2Mixin, generic.DetailView):
default_template_name = "model_detail.html"
permission_type = 'view'
class ModelEditFormView(Admin2Mixin, generic.UpdateView):
class ModelEditFormView(AdminModel2Mixin, generic.UpdateView):
form_class = None
success_url = "../../"
default_template_name = "model_edit_form.html"
permission_type = 'change'
class ModelAddFormView(Admin2Mixin, generic.CreateView):
class ModelAddFormView(AdminModel2Mixin, generic.CreateView):
form_class = None
success_url = "../"
default_template_name = "model_add_form.html"
permission_type = 'add'
class ModelDeleteView(Admin2Mixin, generic.DeleteView):
class ModelDeleteView(AdminModel2Mixin, generic.DeleteView):
success_url = "../../"
default_template_name = "model_delete.html"
permission_type = 'delete'

View file

View file

@ -12,17 +12,24 @@ Our goal is to make this API work:
.. code-block:: python
# myapp/admin2.py
# Import your custom models
from .models import Post, Comment
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.contrib.auth.models import User
# Import the Admin2 base class
from djadmin2.models import Admin2
import djadmin2
from djadmin2.models import ModelAdmin2
# Import your custom models
from blog.models import Post
# Instantiate the Admin2 class
# Then attach the admin2 object to your model
Post.admin2 = Admin2()
class UserAdmin2(ModelAdmin2):
create_form_class = UserCreationForm
update_form_class = UserChangeForm
# Register each model with the admin
djadmin2.default.register(Post)
djadmin2.default.register(Comment)
djadmin2.default.register(User, UserAdmin2)
.. _GitHub: https://github.com/pydanny/django-admin2

View file

@ -8,12 +8,6 @@ import djadmin2
djadmin2.default.autodiscover()
urlpatterns = patterns('',
# Examples:
url(r'^admin2/', include(djadmin2.default.urls)),
# url(r'^example/', include('example.foo.urls')),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
)