Skip to content

Commit

Permalink
Nicer admin tabs (many thanks to @FinalAngel for his help with this)
Browse files Browse the repository at this point in the history
Ability to delete translations form admin (not the whole instance)
  • Loading branch information
Jonas Obrist committed May 18, 2011
1 parent b275ea9 commit e551ab1
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 19 deletions.
141 changes: 128 additions & 13 deletions nani/admin.py
@@ -1,16 +1,29 @@
from django import forms
from distutils.version import LooseVersion
from django.conf import settings
from django.contrib.admin.options import ModelAdmin
from django.contrib.admin.util import flatten_fieldsets
from django.core.exceptions import ValidationError
from django.contrib.admin.options import ModelAdmin, csrf_protect_m
from django.contrib.admin.util import (flatten_fieldsets, unquote,
get_deleted_objects)
from django.core.exceptions import ValidationError, PermissionDenied
from django.core.urlresolvers import reverse
from django.db import router, transaction
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import TemplateDoesNotExist
from django.template.context import RequestContext
from django.template.loader import find_template
from django.utils.encoding import iri_to_uri
from django.utils.encoding import iri_to_uri, force_unicode
from django.utils.functional import curry
from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _, get_language
from functools import update_wrapper
from nani.forms import TranslatableModelForm
import django
import urllib


NEW_GET_DELETE_OBJECTS = LooseVersion(django.get_version()) >= LooseVersion('1.3')


class CleanMixin(object):
def clean(self):
data = super(CleanMixin, self).clean()
Expand Down Expand Up @@ -63,6 +76,25 @@ class TranslatableAdmin(ModelAdmin):

change_form_template = 'admin/nani/change_form.html'

def get_urls(self):
from django.conf.urls.defaults import patterns, url

urlpatterns = super(TranslatableAdmin, self).get_urls()

def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)

info = self.model._meta.app_label, self.model._meta.module_name

urlpatterns = patterns('',
url(r'^(.+)/delete-translation/(.+)/$',
wrap(self.delete_translation),
name='%s_%s_delete_translation' % info),
) + urlpatterns
return urlpatterns

def get_form(self, request, obj=None, **kwargs):
"""
Returns a Form class for use in the admin add view. This is used by
Expand Down Expand Up @@ -118,8 +150,14 @@ class MyAdmin(admin.ModelAdmin):

def render_change_form(self, request, context, add=False, change=False,
form_url='', obj=None):
context['title'] = '%s (%s)' % (context['title'], self._language(request))
context['language_tabs'] = self.get_language_tabs(request, obj)
lang_code = self._language(request)
lang = dict(settings.LANGUAGES).get(lang_code, lang_code)
available_languages = []
if obj:
available_languages = obj.get_available_languages()
context['title'] = '%s (%s)' % (context['title'], lang)
context['current_is_translated'] = lang_code in available_languages
context['language_tabs'] = self.get_language_tabs(request, available_languages)
context['base_template'] = self.get_change_form_base_template()
return super(TranslatableAdmin, self).render_change_form(request,
context,
Expand All @@ -135,6 +173,86 @@ def response_change(self, request, obj):
self.query_language_key, request.GET[self.query_language_key])
return redirect

@csrf_protect_m
@transaction.commit_on_success
def delete_translation(self, request, object_id, language_code):
"The 'delete translation' admin view for this model."
opts = self.model._meta
app_label = opts.app_label
translations_model = opts.translations_model

try:
obj = translations_model.objects.get(master__pk=unquote(object_id),
language_code=language_code)
except translations_model.DoesNotExist:
raise Http404

if not self.has_delete_permission(request, obj):
raise PermissionDenied

using = router.db_for_write(translations_model)

# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.

protected = False
if NEW_GET_DELETE_OBJECTS:
(deleted_objects, perms_needed, protected) = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site, using)
else:
(deleted_objects, perms_needed) = get_deleted_objects(
[obj], translations_model._meta, request.user, self.admin_site)


lang = dict(settings.LANGUAGES).get(language_code, language_code)


if request.POST: # The user has already confirmed the deletion.
if perms_needed:
raise PermissionDenied
obj_display = '%s translation of %s' % (lang, force_unicode(obj.master))
self.log_deletion(request, obj, obj_display)
self.delete_model_translation(request, obj)

self.message_user(request,
_('The %(name)s "%(obj)s" was deleted successfully.') % {
'name': force_unicode(opts.verbose_name),
'obj': force_unicode(obj_display)
}
)

if not self.has_change_permission(request, None):
return HttpResponseRedirect(reverse('admin:index'))
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (opts.app_label, opts.module_name)))

object_name = '%s Translations' % force_unicode(opts.verbose_name)

if perms_needed or protected:
title = _("Cannot delete %(name)s") % {"name": object_name}
else:
title = _("Are you sure?")

context = {
"title": title,
"object_name": object_name,
"object": obj,
"deleted_objects": deleted_objects,
"perms_lacking": perms_needed,
"protected": protected,
"opts": opts,
"root_path": self.admin_site.root_path,
"app_label": app_label,
}

return render_to_response(self.delete_confirmation_template or [
"admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()),
"admin/%s/delete_confirmation.html" % app_label,
"admin/delete_confirmation.html"
], context, RequestContext(request))

def delete_model_translation(self, request, obj):
obj.delete()

def get_object(self, request, object_id):
obj = super(TranslatableAdmin, self).get_object(request, object_id)
if obj:
Expand All @@ -160,13 +278,10 @@ def queryset(self, request):
def _language(self, request):
return request.GET.get(self.query_language_key, get_language())

def get_language_tabs(self, request, obj):
def get_language_tabs(self, request, available_languages):
tabs = []
get = dict(request.GET)
language = self._language(request)
available_languages = []
if obj:
available_languages = obj.get_available_languages()
language = self._language(request)
for key, name in settings.LANGUAGES:
get.update({'language': key})
url = '%s://%s%s?%s' % (request.is_secure() and 'https' or 'http',
Expand All @@ -178,7 +293,7 @@ def get_language_tabs(self, request, obj):
status = 'available'
else:
status = 'empty'
tabs.append((url, name, status))
tabs.append((url, name, key, status))
return tabs

def get_change_form_base_template(self):
Expand Down
34 changes: 31 additions & 3 deletions nani/templates/admin/nani/change_form.html
@@ -1,14 +1,42 @@
{% extends base_template %}
{% load i18n admin_modify %}

{% block extrahead %}
{{ block.super }}
<style type="text/css">
.nani-language-tabs span {
display: inline-block;
padding: 5px 15px;
border: 1px solid #ccc;
border-bottom: none;
position: relative;
left: 0px;
top: 1px;
font-weight: bold;
}
.nani-language-tabs span.current {
border-bottom: 1px solid #fff;
}
.nani-language-tabs span.empty {
opacity: 0.7;
font-weight: normal;
}
.nani-language-tabs a.deletelink {
right: -17px;
bottom: 4px;
position: relative;
}
</style>
{% endblock %}

{% block object-tools %}
{{ block.super }}
<div class="nani-language-tabs">
{% for url,name,status in language_tabs %}
{% for url,name,code,status in language_tabs %}
{% if status == 'current' %}
<span class="current">{{ name }}</span>
<span class="current">{{ name }}{% if current_is_translated %}<a class="deletelink" href="./delete-translation/{{ code }}/" title="{% trans 'Delete Translation' %}">&nbsp;</a>{% endif %}</span>
{% else %}
<a href="{{ url }}" class="{{ status }}">{{ name }}</a>
<span class="{{ status }}"><a href="{{ url }}">{{ name }}</a> {% if status == 'available' %}<a class="deletelink" href="./delete-translation/{{ code }}/" title="{% trans 'Delete Translation' %}">&nbsp;</a>{% endif %}</span>
{% endif %}
{% endfor %}
</div>
Expand Down
2 changes: 0 additions & 2 deletions testproject/runtests.sh

This file was deleted.

2 changes: 1 addition & 1 deletion testproject/settings.py
Expand Up @@ -57,4 +57,4 @@
ROOT_URLCONF = 'testproject.urls'

DEBUG = True
TEMPLATE_DEBUG = True
TEMPLATE_DEBUG = True

0 comments on commit e551ab1

Please sign in to comment.