Skip to content
Browse files

- forced standard MIDDLEWARE_CLASSES into tests

- fixed columns labels into show/hide column panel
- autoregister/autodiscover
  • Loading branch information...
1 parent 899c80e commit e79d157eeb7388e9ce4b7715a9c4e4ebdd0a39f6 @saxix committed Apr 25, 2012
Showing with 1,341 additions and 244 deletions.
  1. +10 −5 docs/source/index.rst
  2. +2 −2 iadmin/actions/__init__.py
  3. +77 −0 iadmin/actions/delete.py
  4. +1 −1 iadmin/actions/export.py
  5. +1 −1 iadmin/actions/mass_update.py
  6. +8 −8 iadmin/models.py
  7. +54 −31 iadmin/options.py
  8. +4 −7 iadmin/proxy.py
  9. +59 −29 iadmin/sites.py
  10. +840 −0 iadmin/static/iadmin/css/base.css
  11. +30 −0 iadmin/static/iadmin/css/dashboard.css
  12. +7 −3 iadmin/static/iadmin/css/ichangelists.css
  13. BIN iadmin/static/iadmin/css/images/ui-anim_basic_16x16.gif
  14. BIN iadmin/static/iadmin/css/images/ui-bg_flat_0_aaaaaa_40x100.png
  15. BIN iadmin/static/iadmin/css/images/ui-bg_flat_55_fbec88_40x100.png
  16. BIN iadmin/static/iadmin/css/images/ui-bg_glass_75_d0e5f5_1x400.png
  17. BIN iadmin/static/iadmin/css/images/ui-bg_glass_85_dfeffc_1x400.png
  18. BIN iadmin/static/iadmin/css/images/ui-bg_glass_95_fef1ec_1x400.png
  19. BIN iadmin/static/iadmin/css/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png
  20. BIN iadmin/static/iadmin/css/images/ui-bg_inset-hard_100_f5f8f9_1x100.png
  21. BIN iadmin/static/iadmin/css/images/ui-bg_inset-hard_100_fcfdfd_1x100.png
  22. BIN iadmin/static/iadmin/css/images/ui-icons_217bc0_256x240.png
  23. BIN iadmin/static/iadmin/css/images/ui-icons_2e83ff_256x240.png
  24. BIN iadmin/static/iadmin/css/images/ui-icons_469bdd_256x240.png
  25. BIN iadmin/static/iadmin/css/images/ui-icons_6da8d5_256x240.png
  26. BIN iadmin/static/iadmin/css/images/ui-icons_cd0a0a_256x240.png
  27. BIN iadmin/static/iadmin/css/images/ui-icons_d8e7f3_256x240.png
  28. BIN iadmin/static/iadmin/css/images/ui-icons_f9bd01_256x240.png
  29. BIN iadmin/static/iadmin/img/gis/move_vertex_off.png
  30. BIN iadmin/static/iadmin/img/gis/move_vertex_on.png
  31. BIN iadmin/static/iadmin/img/icon_alert.gif
  32. BIN iadmin/static/iadmin/img/icon_error.gif
  33. BIN iadmin/static/iadmin/img/icon_success.gif
  34. +1 −1 iadmin/templates/iadmin/base.html
  35. +24 −21 iadmin/templates/iadmin/change_list_results.html
  36. +49 −0 iadmin/templates/iadmin/delete_selected_confirmation.html
  37. +6 −12 iadmin/templates/iadmin/includes/columns_panel.html
  38. +2 −2 iadmin/templates/iadmin/index.html
  39. +109 −16 iadmin/templatetags/iadmin_list.py
  40. +17 −16 iadmin/tests/admin.py
  41. +2 −0 iadmin/tests/changelist.py
  42. +20 −3 iadmin/tests/common.py
  43. +1 −0 iadmin/tests/export_csv.py
  44. +3 −2 iadmin/tests/templates.py
  45. +0 −77 iadmin/tests/templates/test/base.html
  46. +1 −1 iadmin/tests/templates/test/index.html
  47. +4 −1 iadmin/tests/urls.py
  48. +2 −1 iadmin/views.py
  49. +3 −3 project/geo/admin.py
  50. +4 −1 project/project/settings.py
View
15 docs/source/index.rst
@@ -1,7 +1,6 @@
.. include globals.rst
.. _index:
-====================
iAdmin documentation
====================
@@ -10,9 +9,8 @@ iAdmin documentation
iAdmin is an alternative Django Admin application that offer some useful extra `features`; Can works both as replacement of standard admin application or parallel to it on an alternate url.
-========
Features
-========
+--------
- show/hide columns into change_list
- tabbed view of inlines
@@ -26,16 +24,23 @@ Features
- info page for packages and application version
- easy creation of multiple instances that use different template
-=================
Table Of Contents
-=================
+-----------------
+
.. toctree::
:maxdepth: 1
install
api
screenshots
+Links
+~~~~~
+
+ * Project home page: https://github.com/saxix/django-iadmin
+ * Issue tracker: https://github.com/saxix/django-iadmin/issues?sort
+ * Download: http://pypi.python.org/pypi/django-iadmin/
+ * Docs: http://readthedocs.org/docs/django-iadmin/en/latest/
Indices and tables
==================
View
4 iadmin/actions/__init__.py
@@ -3,5 +3,5 @@
from .export import export_to_csv,export_as_json
from .mass_update import mass_update
from .graph import graph_queryset
-
-#__all__ = ('export_to_csv', 'mass_update')
+from .delete import delete_selected
+#__all__ = ('export_to_csv', 'mass_update')
View
77 iadmin/actions/delete.py
@@ -0,0 +1,77 @@
+from django.core.exceptions import PermissionDenied
+from django.contrib.admin import helpers
+from django.contrib.admin.util import get_deleted_objects, model_ngettext
+from django.db import router
+from django.template.response import TemplateResponse
+from django.utils.encoding import force_unicode
+from django.utils.translation import ugettext_lazy, ugettext as _
+
+def delete_selected(modeladmin, request, queryset):
+ """
+ Default action which deletes the selected objects.
+
+ This action first displays a confirmation page whichs shows all the
+ deleteable objects, or, if the user has no permission one of the related
+ childs (foreignkeys), a "permission denied" message.
+
+ Next, it delets all selected objects and redirects back to the change list.
+ """
+ opts = modeladmin.model._meta
+ app_label = opts.app_label
+
+ # Check that the user has delete permission for the actual model
+ if not modeladmin.has_delete_permission(request):
+ raise PermissionDenied
+
+ using = router.db_for_write(modeladmin.model)
+
+ # Populate deletable_objects, a data structure of all related objects that
+ # will also be deleted.
+ deletable_objects, perms_needed, protected = get_deleted_objects(
+ queryset, opts, request.user, modeladmin.admin_site, using)
+
+ # The user has already confirmed the deletion.
+ # Do the deletion and return a None to display the change list view again.
+ if request.POST.get('post'):
+ if perms_needed:
+ raise PermissionDenied
+ n = queryset.count()
+ if n:
+ for obj in queryset:
+ obj_display = force_unicode(obj)
+ modeladmin.log_deletion(request, obj, obj_display)
+ queryset.delete()
+ modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % {
+ "count": n, "items": model_ngettext(modeladmin.opts, n)
+ })
+ # Return None to display the change list page again.
+ return None
+
+ if len(queryset) == 1:
+ objects_name = force_unicode(opts.verbose_name)
+ else:
+ objects_name = force_unicode(opts.verbose_name_plural)
+
+ if perms_needed or protected:
+ title = _("Cannot delete %(name)s") % {"name": objects_name}
+ else:
+ title = _("Are you sure?")
+
+ context = modeladmin.get_context_data(**{
+ "title": title,
+ "objects_name": objects_name,
+ "deletable_objects": [deletable_objects],
+ 'queryset': queryset,
+ "perms_lacking": perms_needed,
+ "protected": protected,
+ "opts": opts,
+ "app_label": app_label,
+ 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
+ })
+# tpls = modeladmin.get_template(request, 'delete_selected_confirmation.html')
+ # Display the confirmation page
+ return TemplateResponse(request,
+ modeladmin.delete_selected_confirmation_template or modeladmin.get_template(request,'delete_selected_confirmation.html')
+ , context, current_app=modeladmin.admin_site.name)
+
+delete_selected.short_description = ugettext_lazy("Delete selected %(verbose_name_plural)s")
View
2 iadmin/actions/export.py
@@ -85,7 +85,7 @@ def export_to_csv(modeladmin, request, queryset):
adminForm = helpers.AdminForm(form, modeladmin.get_fieldsets(request), {}, [], model_admin=modeladmin)
media = modeladmin.media + adminForm.media
tpl = modeladmin.get_template(request, 'export_csv.html' )
- ctx = modeladmin.get_context(**{'adminform': adminForm,
+ ctx = modeladmin.get_context_data(**{'adminform': adminForm,
'form': form,
'change': True,
'title': _('Export to CSV'),
View
2 iadmin/actions/mass_update.py
@@ -119,7 +119,7 @@ def mass_update(modeladmin, request, queryset):
media = modeladmin.media + adminForm.media
dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.date) else str(obj)
tpl = modeladmin.get_template(request, 'mass_update.html' )
- ctx = modeladmin.get_context(**{'adminform': adminForm,
+ ctx = modeladmin.get_context_data(**{'adminform': adminForm,
'form': form,
'title': u"Mass update %s" % force_unicode(modeladmin.opts.verbose_name_plural),
'grouped': grouped,
View
16 iadmin/models.py
@@ -14,19 +14,19 @@ def create_extra_permission(sender, **kwargs):
from django.contrib.contenttypes.models import ContentType
for model in get_models(sender):
- for action in ('view', 'export', 'massupdate', 'import'):
+ for action in ('read', 'export', 'massupdate', 'import'):
opts = model._meta
codename = _get_permission_codename(action, opts)
label = u'Can %s %s' % (action, opts.verbose_name_raw)
ct = ContentType.objects.get_for_model(model)
Permission.objects.get_or_create(codename=codename, content_type=ct, defaults={'name': label})
- # restrictions
- for model in get_models(sender):
- opts = model._meta
- codename = u'read_only_%s' % opts.object_name.lower()
- label = u'Can only read %s' % opts.verbose_name_raw
- ct = ContentType.objects.get_for_model(model)
- Permission.objects.get_or_create(codename=codename, content_type=ct, defaults={'name': label})
+# # restrictions
+# for model in get_models(sender):
+# opts = model._meta
+# codename = u'read_only_%s' % opts.object_name.lower()
+# label = u'Can only read %s' % opts.verbose_name_raw
+# ct = ContentType.objects.get_for_model(model)
+# Permission.objects.get_or_create(codename=codename, content_type=ct, defaults={'name': label})
signals.post_syncdb.connect(create_extra_permission)
View
85 iadmin/options.py
@@ -58,7 +58,7 @@ class Media:
def __init__(self, model, admin_site):
self.extra_allowed_filter = []
- self._original_list_display = self.list_display
+ self.full_list_display = self.list_display
DjangoModelAdmin.__init__(self, model, admin_site)
self._process_cell_filter()
@@ -94,7 +94,7 @@ def get_visible(opts):
ret = self.list_display
return ret
- def get_context(self, **kwargs):
+ def get_context_data(self, **kwargs):
opts = self.model._meta
app_label = opts.app_label
@@ -239,14 +239,13 @@ def changelist_view(self, request, extra_context=None):
selection_note_all = ungettext('%(total_count)s selected',
'All %(total_count)s selected', cl.result_count)
- context = self.get_context(**{
+ context = self.get_context_data(**{
'module_name': force_unicode(opts.verbose_name_plural),
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
'selection_note_all': selection_note_all % {'total_count': cl.result_count},
'title': cl.title,
'is_popup': cl.is_popup,
'cl': cl,
- 'original_list_display': self._original_list_display,
'media': media,
'has_add_permission': self.has_add_permission(request),
'app_label': app_label,
@@ -286,11 +285,11 @@ def get_template(self, request, template):
"iadmin/%s/%s/%s" % (app_label, template, opts.object_name.lower()),
"iadmin/%s/%s" % (app_label, template ),
"iadmin/%s" % template,
-# template
+ template
]
def change_view(self, request, object_id, form_url='', extra_context=None):
- context = self.get_context(**(extra_context or {} ))
+ context = self.get_context_data(**(extra_context or {} ))
ret = DjangoModelAdmin.change_view(self, request, object_id, form_url, context)
if request.method == 'POST' and "_saveasnew" in request.POST:
ret = self.add_view(request, form_url=reverse('%s:%s_%s_add' %
@@ -330,6 +329,30 @@ def lookup_allowed(self, lookup, value):
flat_filter.extend([isinstance(v, tuple) and v[0] or v for v in self.cell_filter])
return clean_lookup in self.extra_allowed_filter or clean_lookup in flat_filter
+ def has_view_permission(self, request, obj=None):
+ """
+ Returns True if the given request has permission to change or view
+ the given Django model instance.
+
+ If `obj` is None, this should return True if the given request has
+ permission to change *any* object of the given type.
+ """
+ opts = self.opts
+ return self.has_change_permission(request, obj) or\
+ request.user.has_perm(opts.app_label + '.' + opts.get_view_permission())
+
+ def get_model_perms(self, request):
+ """
+ Returns a dict of all perms for this model. This dict has the keys
+ ``add``, ``change``, and ``delete`` mapping to the True/False for each
+ of those actions.
+ """
+ return {
+ 'add': self.has_add_permission(request),
+ 'change': self.has_change_permission(request),
+ 'delete': self.has_delete_permission(request),
+ 'view': self.has_view_permission(request),
+ }
class IModelAdmin(IModelAdminMixin, DjangoModelAdmin):
@@ -392,18 +415,18 @@ def wrapper(*args, **kwargs):
# 'iadmin/js/jquery.url.js',
# )
- def get_model_perms(self, request):
- """
- Returns a dict of all perms for this model. This dict has the keys
- ``add``, ``change``, and ``delete`` mapping to the True/False for each
- of those actions.
- """
- return {
- 'add': self.has_add_permission(request),
- 'change': self.has_change_permission(request),
- 'delete': self.has_delete_permission(request),
- 'view': self.has_view_permission(request),
- }
+# def get_model_perms(self, request):
+# """
+# Returns a dict of all perms for this model. This dict has the keys
+# ``add``, ``change``, and ``delete`` mapping to the True/False for each
+# of those actions.
+# """
+# return {
+# 'add': self.has_add_permission(request),
+# 'change': self.has_change_permission(request),
+# 'delete': self.has_delete_permission(request),
+# 'view': self.has_view_permission(request),
+# }
# def get_actions(self, request):
# acts = super(IModelAdmin, self).get_actions(request)
@@ -454,17 +477,17 @@ def get_buttons(self):
# return ctx
- def has_view_permission(self, request, obj=None):
- """
- Returns True if the given request has permission to change or view
- the given Django model instance.
-
- If `obj` is None, this should return True if the given request has
- permission to change *any* object of the given type.
- """
- opts = self.opts
- return self.has_change_permission(request, obj) or\
- request.user.has_perm(opts.app_label + '.' + opts.get_view_permission())
+# def has_view_permission(self, request, obj=None):
+# """
+# Returns True if the given request has permission to change or view
+# the given Django model instance.
+#
+# If `obj` is None, this should return True if the given request has
+# permission to change *any* object of the given type.
+# """
+# opts = self.opts
+# return self.has_change_permission(request, obj) or\
+# request.user.has_perm(opts.app_label + '.' + opts.get_view_permission())
def response_add(self, request, obj, post_url_continue='../%s/'):
opts = obj._meta
@@ -524,7 +547,7 @@ def response_add(self, request, obj, post_url_continue='../%s/'):
# return super(IModelAdmin, self).change_view(request, object_id, form_url, context)
def add_view(self, request, form_url='', extra_context=None):
- context = self.get_context(**(extra_context or {} ))
+ context = self.get_context_data(**(extra_context or {} ))
return super(IModelAdmin, self).add_view(request, form_url, context)
@@ -737,7 +760,7 @@ def display_view(self, request, object_id, extra_context=None):
inline_admin_formsets.append(inline_admin_formset)
media = media + inline_admin_formset.media
- context = self.get_context(**{
+ context = self.get_context_data(**{
'title': _('Change %s') % force_unicode(opts.verbose_name),
'adminform': adminForm,
'has_view_permission': self.has_view_permission(request),
View
11 iadmin/proxy.py
@@ -1,10 +1,7 @@
-import django.contrib.admin
-import iadmin.api
-django.contrib.admin.site = iadmin.api.site
-django.contrib.admin.ModelAdmin = iadmin.api.IModelAdmin
-django.contrib.admin.TabularInline = iadmin.api.ITabularInline
+from iadmin.api import ITabularInline as TabularInline
+from iadmin.api import IModelAdmin as ModelAdmin
+from iadmin.api import IAdminSite as AdminSite
-from django.contrib.admin import site, ModelAdmin, TabularInline
+site = AdminSite( 'admin', 'admin')
from iadmin.api import *
-
View
88 iadmin/sites.py
@@ -19,52 +19,64 @@
from django.utils import dateformat
from django.utils.functional import update_wrapper
from django.utils.translation import gettext as _
+import iadmin.actions
from iadmin.options import IModelAdmin, IModelAdminMixin
from iadmin.tools import CSVImporter
+
try:
from getpass import getuser
except ImportError:
getuser = lambda: 'info not available'
-INDEX_CACHE_KEY = 'admin:admin_index'
-
-def cache_admin(method, key=None):
- entry = key or method.__name__
-
- def __inner(*args, **kwargs):
- cached = cache.get(entry, None)
- if not cached:
- cached = method(*args, **kwargs)
- cache.set(entry, cached, 3600)
- return cached
-
- return __inner
-
-
-def cache_app_index(func):
- def __inner(self, request, app_label, extra_context=None):
- entry = "%s_index" % app_label
- cached = cache.get(entry, None)
- if not cached:
- cached = func(self, request, app_label, extra_context)
- cache.set(entry, cached, -1)
- return cached
-
- return __inner
+#INDEX_CACHE_KEY = 'admin:admin_index'
+
+#def cache_admin(method, key=None):
+# entry = key or method.__name__
+#
+# def __inner(*args, **kwargs):
+# cached = cache.get(entry, None)
+# if not cached:
+# cached = method(*args, **kwargs)
+# cache.set(entry, cached, 3600)
+# return cached
+#
+# return __inner
+#
+#
+#def cache_app_index(func):
+# def __inner(self, request, app_label, extra_context=None):
+# entry = "%s_index" % app_label
+# cached = cache.get(entry, None)
+# if not cached:
+# cached = func(self, request, app_label, extra_context)
+# cache.set(entry, cached, -1)
+# return cached
+#
+# return __inner
__all__ = ['site', 'IAdminSite']
class IAdminService(object):
def __init__(self, adminsite):
self.admin_site = adminsite
+class IProxy(object):
+ def __enter__(self):
+ import iadmin.proxy
+ self.backup, django.contrib.admin.site = django.contrib.admin.site, iadmin.proxy.site
+
+ def __exit__( self, type, value, tb ):
+ django.contrib.admin.site = self.backup
class IAdminSite(AdminSite):
def __init__(self, name='iadmin', app_name='iadmin', template_prefix='iadmin'):
self.template_prefix = template_prefix or app_name
- return super(IAdminSite, self).__init__(name, app_name)
+ ret = super(IAdminSite, self).__init__(name, app_name)
+ self._actions = {'delete_selected': iadmin.actions.delete.delete_selected}
+ self._global_actions = self._actions.copy()
+ return ret
@property
def password_change_template(self):
@@ -84,10 +96,8 @@ def index(self, request, extra_context=None):
for model, model_admin in self._registry.items():
app_label = model._meta.app_label
has_module_perms = user.has_module_perms(app_label)
-
if has_module_perms:
perms = model_admin.get_model_perms(request)
-
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
@@ -96,7 +106,7 @@ def index(self, request, extra_context=None):
'name': capfirst(model._meta.verbose_name_plural),
'perms': perms,
}
- if perms.get('change', False):
+ if perms.get('view', False):
try:
model_dict['admin_url'] = reverse('%s:%s_%s_changelist' % info, current_app=self.name)
except NoReverseMatch:
@@ -283,6 +293,24 @@ def autodiscover(self):
from django.utils.importlib import import_module
from django.utils.module_loading import module_has_submodule
+ with IProxy():
+ for app in settings.INSTALLED_APPS:
+ mod = import_module(app)
+ try:
+ before_import_registry = copy.copy(self._registry)
+ mod = import_module('%s.admin' % app)
+ except BaseException:
+ self._registry = before_import_registry
+ if module_has_submodule(mod, 'admin'):
+ raise
+
+ def autoregister(self):
+ """
+ register models defined into __iadmin__ attribute
+ """
+ from django.utils.importlib import import_module
+ from django.utils.module_loading import module_has_submodule
+
for app in settings.INSTALLED_APPS:
mod = import_module(app)
try:
@@ -310,6 +338,8 @@ def register(self, model_or_iterable, admin_class=None, **options):
"""
if not admin_class:
admin_class = IModelAdmin
+ else:
+ admin_class = self.get_imodeladmin(admin_class)
if isinstance(model_or_iterable, ModelBase):
model_or_iterable = [model_or_iterable]
View
840 iadmin/static/iadmin/css/base.css
@@ -0,0 +1,840 @@
+/*
+ DJANGO Admin styles
+*/
+
+body {
+ margin: 0;
+ padding: 0;
+ font-size: 12px;
+ font-family: "Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif;
+ color: #333;
+ background: #fff;
+}
+
+/* LINKS */
+
+a:link, a:visited {
+ color: #5b80b2;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #036;
+}
+
+a img {
+ border: none;
+}
+
+a.section:link, a.section:visited {
+ color: white;
+ text-decoration: none;
+}
+
+/* GLOBAL DEFAULTS */
+
+p, ol, ul, dl {
+ margin: .2em 0 .8em 0;
+}
+
+p {
+ padding: 0;
+ line-height: 140%;
+}
+
+h1,h2,h3,h4,h5 {
+ font-weight: bold;
+}
+
+h1 {
+ font-size: 18px;
+ color: #666;
+ padding: 0 6px 0 0;
+ margin: 0 0 .2em 0;
+}
+
+h2 {
+ font-size: 16px;
+ margin: 1em 0 .5em 0;
+}
+
+h2.subhead {
+ font-weight: normal;
+ margin-top: 0;
+}
+
+h3 {
+ font-size: 14px;
+ margin: .8em 0 .3em 0;
+ color: #666;
+ font-weight: bold;
+}
+
+h4 {
+ font-size: 12px;
+ margin: 1em 0 .8em 0;
+ padding-bottom: 3px;
+}
+
+h5 {
+ font-size: 10px;
+ margin: 1.5em 0 .5em 0;
+ color: #666;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+
+ul li {
+ list-style-type: square;
+ padding: 1px 0;
+}
+
+ul.plainlist {
+ margin-left: 0 !important;
+}
+
+ul.plainlist li {
+ list-style-type: none;
+}
+
+li ul {
+ margin-bottom: 0;
+}
+
+li, dt, dd {
+ font-size: 11px;
+ line-height: 14px;
+}
+
+dt {
+ font-weight: bold;
+ margin-top: 4px;
+}
+
+dd {
+ margin-left: 0;
+}
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+fieldset {
+ margin: 0;
+ padding: 0;
+}
+
+blockquote {
+ font-size: 11px;
+ color: #777;
+ margin-left: 2px;
+ padding-left: 10px;
+ border-left: 5px solid #ddd;
+}
+
+code, pre {
+ font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
+ background: inherit;
+ color: #666;
+ font-size: 11px;
+}
+
+pre.literal-block {
+ margin: 10px;
+ background: #eee;
+ padding: 6px 8px;
+}
+
+code strong {
+ color: #930;
+}
+
+hr {
+ clear: both;
+ color: #eee;
+ background-color: #eee;
+ height: 1px;
+ border: none;
+ margin: 0;
+ padding: 0;
+ font-size: 1px;
+ line-height: 1px;
+}
+
+/* TEXT STYLES & MODIFIERS */
+
+.small {
+ font-size: 11px;
+}
+
+.tiny {
+ font-size: 10px;
+}
+
+p.tiny {
+ margin-top: -2px;
+}
+
+.mini {
+ font-size: 9px;
+}
+
+p.mini {
+ margin-top: -3px;
+}
+
+.help, p.help {
+ font-size: 10px !important;
+ color: #999;
+}
+
+img.help-tooltip {
+ cursor: help;
+}
+
+p img, h1 img, h2 img, h3 img, h4 img, td img {
+ vertical-align: middle;
+}
+
+.quiet, a.quiet:link, a.quiet:visited {
+ color: #999 !important;
+ font-weight: normal !important;
+}
+
+.quiet strong {
+ font-weight: bold !important;
+}
+
+.float-right {
+ float: right;
+}
+
+.float-left {
+ float: left;
+}
+
+.clear {
+ clear: both;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-right {
+ text-align: right;
+}
+
+.example {
+ margin: 10px 0;
+ padding: 5px 10px;
+ background: #efefef;
+}
+
+.nowrap {
+ white-space: nowrap;
+}
+
+/* TABLES */
+
+table {
+ border-collapse: collapse;
+ border-color: #ccc;
+}
+
+td, th {
+ font-size: 11px;
+ line-height: 13px;
+ border-bottom: 1px solid #eee;
+ vertical-align: top;
+ padding: 5px;
+ font-family: "Lucida Grande", Verdana, Arial, sans-serif;
+}
+
+th {
+ text-align: left;
+ font-size: 12px;
+ font-weight: bold;
+}
+
+thead th,
+tfoot td {
+ color: #666;
+ padding: 2px 5px;
+ font-size: 11px;
+ background: #e1e1e1 url(../img/nav-bg.gif) top left repeat-x;
+ border-left: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+}
+
+tfoot td {
+ border-bottom: none;
+ border-top: 1px solid #ddd;
+}
+
+thead th:first-child,
+tfoot td:first-child {
+ border-left: none !important;
+}
+
+thead th.optional {
+ font-weight: normal !important;
+}
+
+fieldset table {
+ border-right: 1px solid #eee;
+}
+
+tr.row-label td {
+ font-size: 9px;
+ padding-top: 2px;
+ padding-bottom: 0;
+ border-bottom: none;
+ color: #666;
+ margin-top: -1px;
+}
+
+tr.alt {
+ background: #f6f6f6;
+}
+
+.row1 {
+ background: #EDF3FE;
+}
+
+.row2 {
+ background: white;
+}
+
+/* SORTABLE TABLES */
+
+thead th {
+ padding: 2px 5px;
+ line-height: normal;
+}
+
+thead th a:link, thead th a:visited {
+ color: #666;
+}
+
+thead th.sorted {
+ background: #c5c5c5 url(../img/nav-bg-selected.gif) top left repeat-x;
+}
+
+table thead th .text span {
+ padding: 2px 5px;
+ display:block;
+}
+
+table thead th .text a {
+ display: block;
+ cursor: pointer;
+ padding: 2px 5px;
+}
+
+table thead th.sortable:hover {
+ background: white url(../img/nav-bg-reverse.gif) 0 -5px repeat-x;
+}
+
+thead th.sorted a.sortremove {
+ visibility: hidden;
+}
+
+table thead th.sorted:hover a.sortremove {
+ visibility: visible;
+}
+
+table thead th.sorted .sortoptions {
+ display: block;
+ padding: 4px 5px 0 5px;
+ float: right;
+ text-align: right;
+}
+
+table thead th.sorted .sortpriority {
+ font-size: .8em;
+ min-width: 12px;
+ text-align: center;
+ vertical-align: top;
+}
+
+table thead th.sorted .sortoptions a {
+ width: 14px;
+ height: 12px;
+ display: inline-block;
+}
+
+table thead th.sorted .sortoptions a.sortremove {
+ background: url(../img/sorting-icons.gif) -4px -5px no-repeat;
+}
+
+table thead th.sorted .sortoptions a.sortremove:hover {
+ background: url(../img/sorting-icons.gif) -4px -27px no-repeat;
+}
+
+table thead th.sorted .sortoptions a.ascending {
+ background: url(../img/sorting-icons.gif) -5px -50px no-repeat;
+}
+
+table thead th.sorted .sortoptions a.ascending:hover {
+ background: url(../img/sorting-icons.gif) -5px -72px no-repeat;
+}
+
+table thead th.sorted .sortoptions a.descending {
+ background: url(../img/sorting-icons.gif) -5px -94px no-repeat;
+}
+
+table thead th.sorted .sortoptions a.descending:hover {
+ background: url(../img/sorting-icons.gif) -5px -115px no-repeat;
+}
+
+/* ORDERABLE TABLES */
+
+table.orderable tbody tr td:hover {
+ cursor: move;
+}
+
+table.orderable tbody tr td:first-child {
+ padding-left: 14px;
+ background-image: url(../img/nav-bg-grabber.gif);
+ background-repeat: repeat-y;
+}
+
+table.orderable-initalized .order-cell, body>tr>td.order-cell {
+ display: none;
+}
+
+/* FORM DEFAULTS */
+
+input, textarea, select, .form-row p {
+ margin: 2px 0;
+ padding: 2px 3px;
+ vertical-align: middle;
+ font-family: "Lucida Grande", Verdana, Arial, sans-serif;
+ font-weight: normal;
+ font-size: 11px;
+}
+
+textarea {
+ vertical-align: top !important;
+}
+
+input[type=text], input[type=password], textarea, select, .vTextField {
+ border: 1px solid #ccc;
+}
+
+/* FORM BUTTONS */
+
+.button, input[type=submit], input[type=button], .submit-row input {
+ background: white url(../img/nav-bg.gif) bottom repeat-x;
+ padding: 3px 5px;
+ color: black;
+ border: 1px solid #bbb;
+ border-color: #ddd #aaa #aaa #ddd;
+}
+
+.button:active, input[type=submit]:active, input[type=button]:active {
+ background-image: url(../img/nav-bg-reverse.gif);
+ background-position: top;
+}
+
+.button[disabled], input[type=submit][disabled], input[type=button][disabled] {
+ background-image: url(../img/nav-bg.gif);
+ background-position: bottom;
+ opacity: 0.4;
+}
+
+.button.default, input[type=submit].default, .submit-row input.default {
+ border: 2px solid #5b80b2;
+ background: #7CA0C7 url(../img/default-bg.gif) bottom repeat-x;
+ font-weight: bold;
+ color: white;
+ float: right;
+}
+
+.button.default:active, input[type=submit].default:active {
+ background-image: url(../img/default-bg-reverse.gif);
+ background-position: top;
+}
+
+.button[disabled].default, input[type=submit][disabled].default, input[type=button][disabled].default {
+ background-image: url(../img/default-bg.gif);
+ background-position: bottom;
+ opacity: 0.4;
+}
+
+
+/* MODULES */
+
+.module {
+ border: 1px solid #ccc;
+ margin-bottom: 5px;
+ background: white;
+}
+
+.module p, .module ul, .module h3, .module h4, .module dl, .module pre {
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+.module blockquote {
+ margin-left: 12px;
+}
+
+.module ul, .module ol {
+ margin-left: 1.5em;
+}
+
+.module h3 {
+ margin-top: .6em;
+}
+
+.module h2, .module caption, .inline-group h2 {
+ margin: 0;
+ padding: 2px 5px 3px 5px;
+ font-size: 11px;
+ text-align: left;
+ font-weight: bold;
+ background: #7CA0C7 url(../img/default-bg.gif) top left repeat-x;
+ color: white;
+}
+
+.module table {
+ border-collapse: collapse;
+}
+
+/* MESSAGES & ERRORS */
+
+ul.messagelist {
+ padding: 0 0 5px 0;
+ margin: 0;
+}
+
+ul.messagelist li {
+ font-size: 12px;
+ display: block;
+ padding: 4px 5px 4px 25px;
+ margin: 0 0 3px 0;
+ border-bottom: 1px solid #ddd;
+ color: #666;
+ background: #ffc url(../img/icon_success.gif) 5px .3em no-repeat;
+}
+
+ul.messagelist li.warning{
+ background-image: url(../img/icon_alert.gif);
+}
+
+ul.messagelist li.error{
+ background-image: url(../img/icon_error.gif);
+}
+
+.errornote {
+ font-size: 12px !important;
+ display: block;
+ padding: 4px 5px 4px 25px;
+ margin: 0 0 3px 0;
+ border: 1px solid red;
+ color: red;
+ background: #ffc url(../img/icon_error.gif) 5px .3em no-repeat;
+}
+
+ul.errorlist {
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+.errorlist li {
+ font-size: 12px !important;
+ display: block;
+ padding: 4px 5px 4px 25px;
+ margin: 0 0 3px 0;
+ border: 1px solid red;
+ color: white;
+ background: red url(../img/icon_alert.gif) 5px .3em no-repeat;
+}
+
+.errorlist li a {
+ color: white;
+ text-decoration: underline;
+}
+
+td ul.errorlist {
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+td ul.errorlist li {
+ margin: 0 !important;
+}
+
+.errors {
+ background: #ffc;
+}
+
+.errors input, .errors select, .errors textarea {
+ border: 1px solid red;
+}
+
+div.system-message {
+ background: #ffc;
+ margin: 10px;
+ padding: 6px 8px;
+ font-size: .8em;
+}
+
+div.system-message p.system-message-title {
+ padding: 4px 5px 4px 25px;
+ margin: 0;
+ color: red;
+ background: #ffc url(../img/icon_error.gif) 5px .3em no-repeat;
+}
+
+.description {
+ font-size: 12px;
+ padding: 5px 0 0 12px;
+}
+
+/* BREADCRUMBS */
+
+div.breadcrumbs {
+ background: white url(../img/nav-bg-reverse.gif) 0 -10px repeat-x;
+ padding: 2px 8px 3px 8px;
+ font-size: 11px;
+ color: #999;
+ border-top: 1px solid white;
+ border-bottom: 1px solid #ccc;
+ text-align: left;
+}
+
+/* ACTION ICONS */
+
+.addlink {
+ padding-left: 12px;
+ background: url(../img/icon_addlink.gif) 0 .2em no-repeat;
+}
+
+.changelink {
+ padding-left: 12px;
+ background: url(../img/icon_changelink.gif) 0 .2em no-repeat;
+}
+
+.viewlink {
+ padding-left: 12px;
+ background: url(../img/icon_viewlink.gif) 0 .2em no-repeat;
+}
+
+.deletelink {
+ padding-left: 12px;
+ background: url(../img/icon_deletelink.gif) 0 .25em no-repeat;
+}
+
+a.deletelink:link, a.deletelink:visited {
+ color: #CC3434;
+}
+
+a.deletelink:hover {
+ color: #993333;
+}
+
+/* OBJECT TOOLS */
+
+.object-tools {
+ font-size: 10px;
+ font-weight: bold;
+ font-family: Arial,Helvetica,sans-serif;
+ padding-left: 0;
+ float: right;
+ position: relative;
+ margin-top: -2.4em;
+ margin-bottom: -2em;
+}
+
+.form-row .object-tools {
+ margin-top: 5px;
+ margin-bottom: 5px;
+ float: none;
+ height: 2em;
+ padding-left: 3.5em;
+}
+
+.object-tools li {
+ display: block;
+ float: left;
+ background: url(../img/tool-left.gif) 0 0 no-repeat;
+ padding: 0 0 0 8px;
+ margin-left: 2px;
+ height: 16px;
+}
+
+.object-tools li:hover {
+ background: url(../img/tool-left_over.gif) 0 0 no-repeat;
+}
+
+.object-tools a:link, .object-tools a:visited {
+ display: block;
+ float: left;
+ color: white;
+ padding: .1em 14px .1em 8px;
+ height: 14px;
+ background: #999 url(../img/tool-right.gif) 100% 0 no-repeat;
+}
+
+.object-tools a:hover, .object-tools li:hover a {
+ background: #5b80b2 url(../img/tool-right_over.gif) 100% 0 no-repeat;
+}
+
+.object-tools a.viewsitelink, .object-tools a.golink {
+ background: #999 url(../img/tooltag-arrowright.gif) top right no-repeat;
+ padding-right: 28px;
+}
+
+.object-tools a.viewsitelink:hover, .object-tools a.golink:hover {
+ background: #5b80b2 url(../img/tooltag-arrowright_over.gif) top right no-repeat;
+}
+
+.object-tools a.addlink {
+ background: #999 url(../img/tooltag-add.gif) top right no-repeat;
+ padding-right: 28px;
+}
+
+.object-tools a.addlink:hover {
+ background: #5b80b2 url(../img/tooltag-add_over.gif) top right no-repeat;
+}
+
+/* OBJECT HISTORY */
+
+table#change-history {
+ width: 100%;
+}
+
+table#change-history tbody th {
+ width: 16em;
+}
+
+/* PAGE STRUCTURE */
+
+#container {
+ position: relative;
+ width: 100%;
+ min-width: 760px;
+ padding: 0;
+}
+
+#content {
+ margin: 10px 15px;
+}
+
+#header {
+ width: 100%;
+}
+
+#content-main {
+ float: left;
+ width: 100%;
+}
+
+#content-related {
+ float: right;
+ width: 18em;
+ position: relative;
+ margin-right: -19em;
+}
+
+#footer {
+ clear: both;
+ padding: 10px;
+}
+
+/* COLUMN TYPES */
+
+.colMS {
+ margin-right: 20em !important;
+}
+
+.colSM {
+ margin-left: 20em !important;
+}
+
+.colSM #content-related {
+ float: left;
+ margin-right: 0;
+ margin-left: -19em;
+}
+
+.colSM #content-main {
+ float: right;
+}
+
+.popup .colM {
+ width: 95%;
+}
+
+.subcol {
+ float: left;
+ width: 46%;
+ margin-right: 15px;
+}
+
+.dashboard #content {
+ width: 500px;
+}
+
+/* HEADER */
+
+#header {
+ background: #417690;
+ color: #ffc;
+ overflow: hidden;
+}
+
+#header a:link, #header a:visited {
+ color: white;
+}
+
+#header a:hover {
+ text-decoration: underline;
+}
+
+#branding h1 {
+ padding: 0 10px;
+ font-size: 18px;
+ margin: 8px 0;
+ font-weight: normal;
+ color: #f4f379;
+}
+
+#branding h2 {
+ padding: 0 10px;
+ font-size: 14px;
+ margin: -8px 0 8px 0;
+ font-weight: normal;
+ color: #ffc;
+}
+
+#user-tools {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 1.2em 10px;
+ font-size: 11px;
+ text-align: right;
+}
+
+/* SIDEBAR */
+
+#content-related h3 {
+ font-size: 12px;
+ color: #666;
+ margin-bottom: 3px;
+}
+
+#content-related h4 {
+ font-size: 11px;
+}
+
+#content-related .module h2 {
+ background: #eee url(../img/nav-bg.gif) bottom left repeat-x;
+ color: #666;
+}
+
View
30 iadmin/static/iadmin/css/dashboard.css
@@ -0,0 +1,30 @@
+/* DASHBOARD */
+
+.dashboard .module table th {
+ width: 100%;
+}
+
+.dashboard .module table td {
+ white-space: nowrap;
+}
+
+.dashboard .module table td a {
+ display: block;
+ padding-right: .6em;
+}
+
+/* RECENT ACTIONS MODULE */
+
+.module ul.actionlist {
+ margin-left: 0;
+}
+
+ul.actionlist li {
+ list-style-type: none;
+}
+
+ul.actionlist li.changelink {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -o-text-overflow: ellipsis;
+}
View
10 iadmin/static/iadmin/css/ichangelists.css
@@ -88,7 +88,7 @@ li.iadmin-cell-menu-item:hover {
right: 0;
top: 0;
width: 150px;
- z-index: 1000;
+ z-index: 2000;
}
#changelist-filter {
@@ -118,7 +118,11 @@ div#sitemap {
.toplinks {
height: 30px;
}
-
+#aa{
+ position: relative;
+ float: right;
+ width:100%;
+}
#changelist-col-button {
float: right;
cursor: pointer;
@@ -131,7 +135,7 @@ div#sitemap {
position: absolute;
right: 0;
min-height: 200px;
- top: 82px;
+ top: 20px;
width: 160px;
z-index: 2000;
padding: 5px;
View
BIN iadmin/static/iadmin/css/images/ui-anim_basic_16x16.gif
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-bg_flat_0_aaaaaa_40x100.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-bg_flat_55_fbec88_40x100.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-bg_glass_75_d0e5f5_1x400.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-bg_glass_85_dfeffc_1x400.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-bg_glass_95_fef1ec_1x400.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-bg_inset-hard_100_f5f8f9_1x100.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-bg_inset-hard_100_fcfdfd_1x100.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-icons_217bc0_256x240.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-icons_2e83ff_256x240.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-icons_469bdd_256x240.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-icons_6da8d5_256x240.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-icons_cd0a0a_256x240.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-icons_d8e7f3_256x240.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/css/images/ui-icons_f9bd01_256x240.png
Deleted file not rendered
View
BIN iadmin/static/iadmin/img/gis/move_vertex_off.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN iadmin/static/iadmin/img/gis/move_vertex_on.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN iadmin/static/iadmin/img/icon_alert.gif
Deleted file not rendered
View
BIN iadmin/static/iadmin/img/icon_error.gif
Deleted file not rendered
View
BIN iadmin/static/iadmin/img/icon_success.gif
Deleted file not rendered
View
2 iadmin/templates/iadmin/base.html
@@ -2,7 +2,7 @@
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
<head>
<title>{% block title %}{% endblock %}</title>
-<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}" />
+<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "iadmin/css/base.css" %}{% endblock %}" />
{% block extrastyle %}{% endblock %}
<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="{% block stylesheet_ie %}{% static "admin/css/ie.css" %}{% endblock %}" /><![endif]-->
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}" />{% endif %}
View
45 iadmin/templates/iadmin/change_list_results.html
@@ -9,27 +9,30 @@
<table id="result_list">
<thead>
<tr>
-{% for header in result_headers %}
-<th scope="col" {{ header.class_attrib }}>
- {% block column-layout %}
- {% if result_headers|last == header %}
- {% iadmin_columns_panel original_list_display %}
- {% endif %}
- {% endblock column-layout %}
- {% if header.sortable %}
- {% if header.sort_priority > 0 %}
- <div class="sortoptions">
- <a class="sortremove" href="{{ header.url_remove }}" title="{% trans "Remove from sorting" %}"></a>
- {% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktrans with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktrans %}">{{ header.sort_priority }}</span>{% endif %}
- <a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% trans "Toggle sorting" %}"></a>
- </div>
- {% endif %}
- {% endif %}
- {% if header.filtered %}<a class="col-filtered" href="{{ header.clear_filter_url }}">&nbsp;</a>{% endif %}
- <div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
- <div class="clear"></div>
-</th>
-{% endfor %}
+{% with result_headers|visible as visible_result_headers %}
+ {% for header in visible_result_headers %}
+
+ <th scope="col" {{ header.class_attrib }}>
+ {% block column-layout %}
+ {% if visible_result_headers|last == header %}
+ {% iadmin_columns_panel %}
+ {% endif %}
+ {% endblock column-layout %}
+ {% if header.sortable %}
+ {% if header.sort_priority > 0 %}
+ <div class="sortoptions">
+ <a class="sortremove" href="{{ header.url_remove }}" title="{% trans "Remove from sorting" %}"></a>
+ {% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktrans with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktrans %}">{{ header.sort_priority }}</span>{% endif %}
+ <a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% trans "Toggle sorting" %}"></a>
+ </div>
+ {% endif %}
+ {% endif %}
+ {% if header.filtered %}<a class="col-filtered" href="{{ header.clear_filter_url }}">&nbsp;</a>{% endif %}
+ <div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
+ <div class="clear"></div>
+ </th>
+ {% endfor %}
+{% endwith %}
</tr>
</thead>
<tbody>
View
49 iadmin/templates/iadmin/delete_selected_confirmation.html
@@ -0,0 +1,49 @@
+{% extends "iadmin/base_site.html" %}
+{% load i18n l10n %}
+{% load url from future %}
+{% load iadmin_urls %}
+
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+&rsaquo; <a href="{% admin_url 'app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% admin_model_url queryset.model 'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
+&rsaquo; {% trans 'Delete multiple objects' %}
+</div>
+{% endblock %}
+
+{% block content %}
+{% if perms_lacking or protected %}
+ {% if perms_lacking %}
+ <p>{% blocktrans %}Deleting the selected {{ objects_name }} would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p>
+ <ul>
+ {% for obj in perms_lacking %}
+ <li>{{ obj }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ {% if protected %}
+ <p>{% blocktrans %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktrans %}</p>
+ <ul>
+ {% for obj in protected %}
+ <li>{{ obj }}</li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+{% else %}
+ <p>{% blocktrans %}Are you sure you want to delete the selected {{ objects_name }}? All of the following objects and their related items will be deleted:{% endblocktrans %}</p>
+ {% for deletable_object in deletable_objects %}
+ <ul>{{ deletable_object|unordered_list }}</ul>
+ {% endfor %}
+ <form action="" method="post">{% csrf_token %}
+ <div>
+ {% for obj in queryset %}
+ <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
+ {% endfor %}
+ <input type="hidden" name="action" value="delete_selected" />
+ <input type="hidden" name="post" value="yes" />
+ <input type="submit" value="{% trans "Yes, I'm sure" %}" />
+ </div>
+ </form>
+{% endif %}
+{% endblock %}
View
18 iadmin/templates/iadmin/includes/columns_panel.html
@@ -1,22 +1,17 @@
+<div id='aa'>
<a id="changelist-col-button" class="button" href="javascript:showhide('#changelist-cols-layout');">...</a>
<div id="changelist-cols-layout" style="display:none;">
- {% for c in list_columns %}
- <div class="col-item"><input type="checkbox" value="{{ forloop.counter }}">
- <a href="#">{{ c|capfirst }}</a></div>
+ {% for c in result_headers %}
+ <div class="col-item"><input type="checkbox" value="{{ forloop.counter }}"
+ {% if c.visible %} checked="checked" {% endif %}>
+ <a href="#">{{ c.text|capfirst }}</a></div>
{% endfor %}
</div>
+</div>
<script type="text/javascript">
(function ($) {
$(document).ready(function ($) {
args = $.url(location).param();
- var cols = ["1", "2", "3", "4", "5"];
- if (typeof args.ld != 'undefined') {
- cols = args.ld.split('.');
- }
-
- $("#changelist-cols-layout input").each(function () {
- $(this).attr('checked', $.inArray($(this).val(), cols) >= 0);
- });
toggle = function () {
var base = $.url(location).segment().join('/');
@@ -25,7 +20,6 @@
sel.push($(this).attr('value'));
});
args['ld'] = sel.join('.');
- console.log(sel);
location.href = '?' + $.param(args);
};
$('#changelist-cols-layout a').click(function () {
View
4 iadmin/templates/iadmin/index.html
@@ -1,7 +1,7 @@
{% extends "iadmin/base_site.html" %}
{% load i18n admin_static iadmin_urls%}
-{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}" />{% endblock %}
+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "iadmin/css/dashboard.css" %}" />{% endblock %}
{% block coltype %}colMS{% endblock %}
@@ -32,7 +32,7 @@
{% endif %}
{% if model.admin_url %}
- <td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
+ <td><a href="{{ model.admin_url }}" class="viewlink">{% trans 'Change' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}
View
125 iadmin/templatetags/iadmin_list.py
@@ -1,6 +1,7 @@
-from django.contrib.admin.util import lookup_field, display_for_field, quote
-from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE, SEARCH_VAR
+from django.contrib.admin.util import lookup_field, display_for_field, quote, label_for_field
+from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE, SEARCH_VAR, ORDER_VAR
from django.core.exceptions import ObjectDoesNotExist
+from django.utils import simplejson as json
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.base import Model
@@ -14,21 +15,117 @@
from django.contrib.admin.templatetags.admin_list import register, result_hidden_fields, _boolean_icon
import django.contrib.admin.templatetags.admin_list as al
from iadmin.utils import Null, iter_get_attr
+from iadmin.views import LIST_DISPLAY
al__result_headers = al.result_headers
+@register.filter
+def visible(headers):
+ """ returns only headers of visible columns """
+ return [el for el in headers if el.get('visible', True) ]
+
def iresult_headers(cl):
- original = list(al__result_headers(cl))
+ """
+ Generates the list column headers.
+ """
+ ordering_field_columns = cl.get_ordering_field_columns()
+ if cl.list_display[0] == 'action_checkbox':
+ text, attr = label_for_field('action_checkbox', cl.model,
+ model_admin = cl.model_admin,
+ return_attr = True
+ )
+ yield {
+ "text": text,
+ "visible" : True,
+ "class_attrib": mark_safe(' class="action-checkbox-column"'),
+ "sortable": False,
+ }
+
+ for i, field_name in enumerate(cl.full_list_display):
+ text, attr = label_for_field(field_name, cl.model,
+ model_admin = cl.model_admin,
+ return_attr = True
+ )
+ if attr:
+ admin_order_field = getattr(attr, "admin_order_field", None)
+ if not admin_order_field:
+ # Not sortable
+ yield {
+ "text": text,
+ "visible" : field_name in cl.list_display,
+ "sortable": False,
+ }
+ continue
+
+ # OK, it is sortable if we got this far
+ th_classes = ['sortable']
+ order_type = ''
+ new_order_type = 'asc'
+ sort_priority = 0
+ sorted = False
+ # Is it currently being sorted on?
+ if i in ordering_field_columns:
+ sorted = True
+ order_type = ordering_field_columns.get(i).lower()
+ sort_priority = ordering_field_columns.keys().index(i) + 1
+ th_classes.append('sorted %sending' % order_type)
+ new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type]
+
+ # build new ordering param
+ o_list_primary = [] # URL for making this field the primary sort
+ o_list_remove = [] # URL for removing this field from sort
+ o_list_toggle = [] # URL for toggling order type for this field
+ make_qs_param = lambda t, n: ('-' if t == 'desc' else '') + str(n)
+
+ for j, ot in ordering_field_columns.items():
+ if j == i: # Same column
+ param = make_qs_param(new_order_type, j)
+ # We want clicking on this header to bring the ordering to the
+ # front
+ o_list_primary.insert(0, param)
+ o_list_toggle.append(param)
+ # o_list_remove - omit
+ else:
+ param = make_qs_param(ot, j)
+ o_list_primary.append(param)
+ o_list_toggle.append(param)
+ o_list_remove.append(param)
+
+ if i not in ordering_field_columns:
+ o_list_primary.insert(0, make_qs_param(new_order_type, i))
- for i, field_name in enumerate(cl.list_display):
filter = cl.cell_filters.get(field_name, Null())
- original_data = original[i]
+ clear_filter_url = ""
if filter.is_active(cl):
- original_data['filtered'] = True
- original_data['clear_filter_url'] = cl.get_query_string({}, filter.expected_parameters())
- yield original_data
+ clear_filter_url = cl.get_query_string({}, filter.expected_parameters())
+
+ yield {
+ "visible" : field_name in cl.list_display,
+ "filtered" : filter.is_active(cl),
+ "clear_filter_url": clear_filter_url ,
+ "text": text,
+ "sortable": True,
+ "sorted": sorted,
+ "ascending": order_type == "asc",
+ "sort_priority": sort_priority,
+ "url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}),
+ "url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}),
+ "url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}),
+ "class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')
+ }
+@register.simple_tag(takes_context=True)
+def iadmin_columns_panel(context):
+ cl = context['cl']
+ tpl = select_template(['iadmin/includes/columns_panel.html'])
+ result_headers = context['result_headers']
+ if cl.list_display[0] == 'action_checkbox':
+ result_headers = result_headers[1:]
+ ctx = {
+ 'result_headers' : result_headers,
+ }
+ return tpl.render(Context(ctx))
def get_items_cell_filter(cl, column, obj):
menu_items = []
@@ -213,14 +310,15 @@ def iresult_list(context, cl):
"""
tpl = select_template(cl.model_admin.get_template(context['request'], cl.model_admin.results_list_template))
+# request = context['request']
headers = list(iresult_headers(cl))
num_sorted_fields = 0
for h in headers:
if h['sortable'] and h['sorted']:
num_sorted_fields += 1
return tpl.render(Context({'cl': cl,
- 'original_list_display' : context['original_list_display'],
- 'result_hidden_fields': list(result_hidden_fields(cl)),
+ 'request': context['request'],
+ 'result_hidden_fields': list(result_hidden_fields(cl)),
'result_headers': headers,
'num_sorted_fields': num_sorted_fields,
'results': list(iresults(cl, context))}))
@@ -258,9 +356,4 @@ def iadmin_actions(context):
context['action_index'] = context.get('action_index', -1) + 1
return tpl.render(Context(context))
-@register.simple_tag(takes_context=True)
-def iadmin_columns_panel(context, list_columns):
- cl = context['cl']
- tpl = select_template(['iadmin/includes/columns_panel.html'])
- ctx = {'list_columns': list_columns}
- return tpl.render(Context(ctx))
+
View
33 iadmin/tests/admin.py
@@ -1,3 +1,4 @@
+from django.contrib import admin
from django.contrib.auth.admin import GroupAdmin, UserAdmin
from django.contrib.auth.models import User, Group, Permission
from iadmin.actions import mass_update
@@ -19,26 +20,26 @@ def _groups(self, obj):
return ",".join([o.name for o in obj.groups.all()])
_groups.short_description = 'Groups'
-#
-#
-#class IUserInline(ITabularInline):
-# model = User.groups.through
-# extra = 0
-#
-#
-#class IGroupAdmin(GroupAdmin, IModelAdmin):
-# inlines = (IUserInline, )
-#
-#
-#class IContentType(IModelAdmin):
-# list_display = ('name', 'app_label', 'model', )
-#
-#
+
+class IUserInline(ITabularInline):
+ model = User.groups.through
+ extra = 0
+
+
+class IGroupAdmin(GroupAdmin, IModelAdmin):
+ inlines = (IUserInline, )
+
+
+class IContentType(IModelAdmin):
+ list_display = ('name', 'app_label', 'model', )
+
+
class IPermission(IModelAdmin):
list_display = ('name', 'content_type', 'codename', )
search_fields = ('name'),
list_filter = cell_filter = ('content_type', )
#
#__iadmin__ = ((User, IUserAdmin), (Group, IGroupAdmin), (Permission, IPermission), (ContentType, IContentType))
-__iadmin__ = ((User, IUserAdmin), (Permission, IPermission) )
+#admin.site.register(User)
+__iadmin__ = ((User, IUserAdmin), (Group, IGroupAdmin), (Permission, IPermission) )
View
2 iadmin/tests/changelist.py
@@ -15,6 +15,7 @@ def test_not_equals_to(self):
def test_equals_to(self):
url = reverse("iadmin:auth_user_changelist")
u = User.objects.get(email='sax@os4d.org')
+ response = self.client.get(url)
response = self.client.get(url, {'email__exact':u.email})
self.assertEqual(response.status_code, 200)
@@ -24,6 +25,7 @@ def test_equals_to(self):
class CellFilterFireFox(FireFoxLiveTest):
def setUp(self):
+ super(CellFilterFireFox, self).setUp()
self.url = reverse("iadmin:auth_user_changelist")
def _test_menu_open(self):
View
23 iadmin/tests/common.py
@@ -1,9 +1,13 @@
-from django.conf import settings
+from django.conf import settings, global_settings
from django.test import LiveServerTestCase
+import os
import selenium.webdriver.firefox.webdriver
import selenium.webdriver.chrome.webdriver
from django.test.testcases import TestCase
import time
+import iadmin
+TEST_TEMPLATES_DIR = os.path.join(os.path.dirname(iadmin.__file__), 'tests', 'templates')
+
class BaseTestCase(TestCase):
urls = 'iadmin.tests.urls'
@@ -12,15 +16,28 @@ class BaseTestCase(TestCase):
def setUp(self):
super(BaseTestCase, self).setUp()
assert self.client.login(username='sax', password='123')
+ self.sett = self.settings(MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES,
+ TEMPLATE_DIRS=[TEST_TEMPLATES_DIR])
+ self.sett.enable()
+
+ def tearDown(self):
+ self.sett.disable()
class SeleniumTestCase(LiveServerTestCase):
urls = 'iadmin.tests.urls'
fixtures = ['test.json', ]
- def tearDown(cls):
- # 'Broken pipe' issue. The browser closes during server transmission
+ def _pre_setup(self):
+ LiveServerTestCase._pre_setup(self)
+ self.sett = self.settings(MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES,
+ TEMPLATE_DIRS=[TEST_TEMPLATES_DIR])
+ self.sett.enable()
+
+ def _post_teardown(self):
+ LiveServerTestCase._post_teardown(self)
time.sleep(1)
+ self.sett.disable()
@property
def base_url(self):
View
1 iadmin/tests/export_csv.py
@@ -13,6 +13,7 @@
class ExportCSVFireFox(FireFoxLiveTest):
def setUp(self):
+ super(ExportCSVFireFox, self).setUp()
self.factory = RequestFactory()
self._url = reverse('iadmin:auth_user_changelist')
View
5 iadmin/tests/templates.py
@@ -9,8 +9,9 @@
TEST_TEMPLATE_LOADERS = ('django.template.loaders.filesystem.Loader',)
class CustomTemplateFirefox(FireFoxLiveTest):
- def setUp(self):
- self.factory = RequestFactory()
+# def setUp(self):
+# super(CustomTemplateFirefox, self).setUp()
+# self.factory = RequestFactory()
def test1(self):
driver = self.driver
View
77 iadmin/tests/templates/test/base.html
@@ -1,77 +0,0 @@
-{% load admin_static iadmin_urls%}{% load url from future %}<!DOCTYPE html>
-<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
-<head>
-<title>{% block title %}{% endblock %}</title>
-<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}" />
-{% block extrastyle %}{% endblock %}
-<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="{% block stylesheet_ie %}{% static "admin/css/ie.css" %}{% endblock %}" /><![endif]-->
-{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}" />{% endif %}
-<script type="text/javascript">window.__admin_media_prefix__ = "{% filter escapejs %}{% static "admin/" %}{% endfilter %}";</script>
-{% block extrahead %}{% endblock %}
-{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %}
-</head>
-{% load i18n %}
-
-<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}">
-
-<!-- Container -->
-<div id="container">
-
- {% if not is_popup %}
- <!-- Header -->
- <div id="header">
- <div id="branding">
- {% block branding %}{% endblock %}
- </div>
- {% if user.is_active and user.is_staff %}
- <div id="user-tools">
- {% trans 'Welcome,' %}
- <strong>{% filter force_escape %}{% firstof user.first_name user.username %}{% endfilter %}</strong>.
- {% block userlinks %}
- {% url 'django-admindocs-docroot' as docsroot %}
- {% if docsroot %}
- <a href="{{ docsroot }}">{% trans 'Documentation' %}</a> /
- {% endif %}
- <a href="{% admin_url 'password_change' %}">{% trans 'Change password' %}</a> /
- <a href="{% admin_url 'logout' %}">{% trans 'Log out' %}</a>
- {% endblock %}
- </div>
- {% endif %}
- {% block nav-global %}{% endblock %}
- </div>
- <!-- END Header -->
- {% block breadcrumbs %}
- <div class="breadcrumbs">
- <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
- {% if title %} &rsaquo; {{ title }}{% endif %}
- </div>
- {% endblock %}
- {% endif %}
-
- {% block messages %}
- {% if messages %}
- <ul class="messagelist">{% for message in messages %}
- <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
- {% endfor %}</ul>
- {% endif %}
- {% endblock messages %}
-
- <!-- Content -->
- <div id="content" class="{% block coltype %}colM{% endblock %}">
- {% block pretitle %}{% endblock %}
- {% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}
- {% block content %}
- {% block object-tools %}{% endblock %}
- {{ content }}
- {% endblock %}
- {% block sidebar %}{% endblock %}
- <br class="clear" />
- </div>
- <!-- END Content -->
-
- {% block footer %}<div id="footer"></div>{% endblock %}
-</div>
-<!-- END Container -->
-
-</body>
-</html>
View
2 iadmin/tests/templates/test/index.html
@@ -32,7 +32,7 @@
{% endif %}
{% if model.admin_url %}
- <td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
+ <td><a href="{{ model.admin_url }}" class="viewlink">{% trans 'List' %}</a></td>
{% else %}
<td>&nbsp;</td>
{% endif %}