Browse Source

Add sibling buttons to change form

Denis K 8 years ago
parent
commit
80f6e8430a

+ 3 - 0
jet/settings.py

@@ -7,3 +7,6 @@ JET_THEMES = getattr(settings, 'JET_THEMES', [])
 # Side menu
 JET_SIDE_MENU_COMPACT = getattr(settings, 'JET_SIDE_MENU_COMPACT', False)
 JET_SIDE_MENU_CUSTOM_APPS = getattr(settings, 'JET_SIDE_MENU_CUSTOM_APPS', None)
+
+# Improved usability
+JET_CHANGE_FORM_SIBLING_LINKS = getattr(settings, 'JET_CHANGE_FORM_SIBLING_LINKS', True)

+ 4 - 0
jet/static/jet/css/_changeform.scss

@@ -138,6 +138,10 @@
   border-radius: 4px;
   min-width: 800px;
 
+  &-navigation {
+    float: left;
+  }
+
   &-tabs {
     @extend .clear-list;
     border-bottom: 2px solid $background-color;

+ 39 - 1
jet/static/jet/css/_forms.scss

@@ -83,7 +83,45 @@
   }
 }
 
-a.button {
+.segmented-button {
+  &, &:visited, &:hover {
+    @extend .base_input;
+    font-size: 12px;
+    text-align: center;
+    background-color: $button-background-color;
+    color: $button-text-color;
+    padding: 0 10px;
+    display: inline-block;
+    text-transform: none;
+    border-radius: 0;
+  }
+
+  &:hover {
+    background-color: $button-hover-background-color;
+    color: $button-hover-text-color;
+  }
+
+  &:active {
+    background-color: $button-active-background-color;
+    color: $button-active-text-color;
+  }
+
+  &.disabled {
+    background-color: $button-background-color !important;
+    color: $button-text-color;
+    opacity: 0.5;
+  }
+
+  &.left {
+    border-radius: 4px 0 0 4px;
+  }
+
+  &.right {
+    border-radius: 0 4px 4px 0;
+  }
+}
+
+a.button, a.segmented-button {
   line-height: 32px;
 }
 

+ 16 - 0
jet/templates/admin/change_form.html

@@ -23,6 +23,22 @@
 
 {% block content %}
     <div id="content-main">
+        {% jet_change_form_sibling_links_enabled as show_siblings %}
+        {% if show_siblings %}
+            <div class="changeform-navigation">
+                {% spaceless %}
+                    {% jet_previous_object_url as url %}
+                    <a{% if url %} href="{{ url }}"{% endif %} class="segmented-button left{% if not url %} disabled{% endif %}">
+                        {% trans "←" %}
+                    </a>
+
+                    {% jet_next_object_url as url %}
+                    <a{% if url %} href="{{ url }}"{% endif %} class="segmented-button right{% if not url %} disabled{% endif %}">
+                        {% trans "→" %}
+                    </a>
+                {% endspaceless %}
+            </div>
+        {% endif %}
         <div class="changeform-object-tools">
             {% block object-tools %}
                 {% if change %}

+ 58 - 1
jet/templatetags/jet_tags.py

@@ -9,7 +9,12 @@ from django.template import loader, Context
 from jet import settings, VERSION
 from jet.models import Bookmark, PinnedApplication
 import re
-from jet.utils import get_app_list, get_model_instance_label
+from jet.utils import get_app_list, get_model_instance_label, get_model_queryset
+try:
+    from urllib.parse import parse_qsl
+except ImportError:
+    from urlparse import parse_qsl
+
 
 register = template.Library()
 
@@ -304,3 +309,55 @@ def get_current_jet_version():
 @register.assignment_tag
 def get_side_menu_compact():
     return settings.JET_SIDE_MENU_COMPACT
+
+
+@register.assignment_tag
+def jet_change_form_sibling_links_enabled():
+    return settings.JET_CHANGE_FORM_SIBLING_LINKS
+
+
+@register.simple_tag(takes_context=True)
+def jet_sibling_object_url(context, next):
+    original = context.get('original')
+
+    if not original:
+        return
+
+    model = type(original)
+    preserved_filters_plain = context.get('preserved_filters', '')
+    preserved_filters = dict(parse_qsl(preserved_filters_plain))
+    queryset = get_model_queryset(model, preserved_filters=preserved_filters)
+
+    sibling_object = None
+    object_pks = list(queryset.values_list('pk', flat=True))
+
+    try:
+        index = object_pks.index(original.pk)
+        sibling_index = index + 1 if next else index - 1
+        exists = sibling_index < len(object_pks) if next else sibling_index >= 0
+        sibling_object = queryset.get(pk=object_pks[sibling_index]) if exists else None
+    except ValueError:
+        pass
+
+    if sibling_object is None:
+        return
+
+    url = reverse('admin:%s_%s_change' % (
+        model._meta.app_label,
+        model._meta.model_name
+    ), args=(sibling_object.pk,))
+
+    if preserved_filters_plain != '':
+        url += '?' + preserved_filters_plain
+
+    return url
+
+
+@register.simple_tag(takes_context=True)
+def jet_previous_object_url(context):
+    return jet_sibling_object_url(context, False)
+
+
+@register.simple_tag(takes_context=True)
+def jet_next_object_url(context):
+    return jet_sibling_object_url(context, True)

+ 51 - 1
jet/utils.py

@@ -10,13 +10,16 @@ except ImportError:
 from django.core.serializers.json import DjangoJSONEncoder
 from django.http import HttpResponse
 from django.core.urlresolvers import reverse, resolve, NoReverseMatch
-from django.contrib import admin
 from django.contrib.admin import AdminSite
 from django.utils.encoding import smart_text
 from django.utils.text import capfirst
 from django.contrib import messages
 from django.utils.encoding import force_text
 from django.utils.functional import Promise
+from django.contrib.admin.options import IncorrectLookupParameters
+from django.core import urlresolvers
+from django.contrib import admin
+from django.test.client import RequestFactory
 
 
 class JsonResponse(HttpResponse):
@@ -152,3 +155,50 @@ class SuccessMessageMixin(object):
 
     def get_success_message(self, cleaned_data):
         return self.success_message % cleaned_data
+
+
+def get_model_queryset(model, preserved_filters=None):
+    model_admin = admin.site._registry.get(model)
+
+    changelist_url = urlresolvers.reverse('admin:%s_%s_changelist' % (
+        model._meta.app_label,
+        model._meta.model_name
+    ))
+    changelist_filters = None
+
+    if preserved_filters:
+        changelist_filters = preserved_filters.get('_changelist_filters')
+
+    if changelist_filters:
+        changelist_url += '?' + changelist_filters
+
+    request = RequestFactory().get(changelist_url)
+
+    if model_admin:
+        queryset = model_admin.get_queryset(request)
+    else:
+        queryset = model.objects
+
+    list_display = model_admin.get_list_display(request)
+    list_display_links = model_admin.get_list_display_links(request, list_display)
+    list_filter = model_admin.get_list_filter(request)
+    search_fields = model_admin.get_search_fields(request)
+    list_select_related = model_admin.get_list_select_related(request)
+
+    actions = model_admin.get_actions(request)
+    if actions:
+        list_display = ['action_checkbox'] + list(list_display)
+
+    ChangeList = model_admin.get_changelist(request)
+
+    try:
+        cl = ChangeList(
+            request, model, list_display, list_display_links, list_filter, model_admin.date_hierarchy, search_fields,
+            list_select_related, model_admin.list_per_page, model_admin.list_max_show_all, model_admin.list_editable,
+            model_admin)
+
+        queryset = cl.get_queryset(request)
+    except IncorrectLookupParameters:
+        pass
+
+    return queryset