diff --git a/.gitignore b/.gitignore
index d1a7c01..74e9a1c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/yawd-admin.sublime-workspace
/.pydevproject
/.project
/dist
diff --git a/CHANGES.rst b/CHANGES.rst
index dcbc750..f314255 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -3,6 +3,14 @@
Changelog
+++++++++
+v.0.7.2, 2016.05.27
+===================
+Django 1.7 compatibility
+
+v.0.7.1, 2016.05.24
+===================
+Django 1.6 compatibility
+
v.0.7.0, 2013.10.23
===================
@@ -39,4 +47,4 @@ v.0.6.0, 2013.02.28
v.0.5.0, 2012.11.01
====================
-* Initial Release
\ No newline at end of file
+* Initial Release
diff --git a/README.rst b/README.rst
index d14374a..8051f2b 100644
--- a/README.rst
+++ b/README.rst
@@ -1,14 +1,10 @@
yawd-admin, a django administration website
======================================================
-yawd-admin now has a live demo at
-`http://yawd-admin.yawd.eu/ `_.
-Use demo / demo as username & passowrd.
-
.. image:: docs/yawd-admin-screenshot.png
:align: center
-`yawd-admin `_ is an
+yawd-admin is an
administration website for django. It extends the default django admin
site and offers the following:
@@ -22,23 +18,35 @@ site and offers the following:
* Mechanism for opening the original django admin popup windows with fancybox
* Seamless integration with `yawd-translations` for multilingual admin websites
-.. note::
+History
+==============
+
+master (dev)
+++++++++++++
+is developped under django 1.8.x and does NOT work with older Django releases.
+
+v0.7.2
+++++++++++++
+is developped under django 1.7.x and does NOT work with older Django releases.
+
+v0.7.1
+++++++++++++
+is developped under django 1.6.x and does NOT work with older Django releases.
+
+v0.7.0
+++++++
+is developed under Django 1.5.x and does NOT work with older Django releases.
+
+v0.6.1
+++++++
- yawd-admin v0.6.1 is the last version intended to work with
- Django 1.4. yawd-admin v.0.7.0 and on is developed under Django 1.5.x
- and does NOT work with older Django releases. For those still using
- Django 1.4, you can checkout the ``0.6.x`` branch or use the yawd-admin
- v0.6.1 pypi package. New features will not be backported to the ``0.6.x``
- branch. Since many of us run production systems tied to Django 1.4, both
- v0.6.1 and the latest documentation will be online on readthedocs.org.
+is the last version intended to work with Django 1.4. New features will not be backported to the ``0.6.x`` branch. Since many of us run production systems tied to Django 1.4, both v0.6.1 and the latest documentation will be online on readthedocs.org.
Usage and demo
==============
See the `yawd-admin documentation `_
-for information on how to install the demo and use yawd-admin. There is also an
-online version of the demo at `http://yawd-admin.yawd.eu/ `_.
-Just use *demo*/*demo* as username and password.
+for information on how to install the demo and use yawd-admin.
Screenshots
===========
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 209bf7c..abe9d9e 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -3,6 +3,16 @@
Changelog
+++++++++
+v.0.7.2, 2016.05.27
+---------------------
+
+* Django 1.7 compatibility
+
+v.0.7.1, 2016.05.24
+---------------------
+
+* Django 1.6 compatibility
+
v.0.7.0, 2013.10.23
---------------------
diff --git a/example_project/example_project/settings.py b/example_project/example_project/settings.py
index c3d025a..839f921 100644
--- a/example_project/example_project/settings.py
+++ b/example_project/example_project/settings.py
@@ -31,7 +31,7 @@
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
-SECRET_KEY = '6()e7x_%6*@(_nm$ke)g!ellg)pffm*=288trl)@3u=@l$@5qn'
+SECRET_KEY = '6()e7x_%6*@(_nm$ke)g!ellg)pffm*=288trl)@3u=@l$@5qnm'
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
diff --git a/setup.py b/setup.py
index 24feeaa..98fb89a 100644
--- a/setup.py
+++ b/setup.py
@@ -2,30 +2,30 @@
from setuptools import setup, find_packages
setup(
- name='yawd-admin',
- url='http://yawd.eu/open-source-projects/yawd-admin/',
- version = '0.7.1-rc1',
- description='An administration website for Django',
- long_description=open('README.rst', 'rt').read(),
- author='yawd',
- author_email='info@yawd.eu',
- packages=find_packages(),
- license='BSD',
- classifiers = [
- 'Development Status :: 4 - Beta',
- 'Environment :: Web Environment',
- 'Framework :: Django',
- 'Intended Audience :: Developers',
- 'License :: OSI Approved :: BSD License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Topic :: Software Development :: Libraries'
- ],
- include_package_data = True,
- install_requires = [
- "httplib2",
- "django >= 1.5",
- "oauth2client"
- ],
- zip_safe=False
+ name='yawd-admin',
+ url='http://yawd.eu/open-source-projects/yawd-admin/',
+ version='0.7.3',
+ description='An administration website for Django',
+ long_description=open('README.rst', 'rt').read(),
+ author='yawd',
+ author_email='info@yawd.eu',
+ packages=find_packages(),
+ license='BSD',
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Web Environment',
+ 'Framework :: Django',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries'
+ ],
+ include_package_data=True,
+ install_requires=[
+ "httplib2",
+ "django >=1.6, <1.9",
+ "oauth2client"
+ ],
+ zip_safe=False
)
diff --git a/yawdadmin/__init__.py b/yawdadmin/__init__.py
index c35946a..82b3617 100644
--- a/yawdadmin/__init__.py
+++ b/yawdadmin/__init__.py
@@ -1,10 +1,10 @@
-__version__ = '0.7.1-rc1'
-
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
-from django.utils.importlib import import_module
+from importlib import import_module
from yawdadmin.sites import YawdAdminSite
+__version__ = '0.7.2'
+
def _get_site():
yawdadmin_site = getattr(settings, 'ADMIN_SITE', YawdAdminSite)
@@ -19,5 +19,5 @@ def _get_site():
if not isinstance(admin_site, YawdAdminSite):
- raise ImproperlyConfigured('The specified admin site is not a subclass of '\
+ raise ImproperlyConfigured('The specified admin site is not a subclass of'\
'yawdadmin.sites.YawdAdminSite')
diff --git a/yawdadmin/admin.py b/yawdadmin/admin.py
index 56cf429..a361ab7 100644
--- a/yawdadmin/admin.py
+++ b/yawdadmin/admin.py
@@ -1,8 +1,14 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
import json
from django.conf.urls import patterns, url
from django.contrib import admin
-from django.contrib.admin.options import InlineModelAdmin
-from django.contrib.admin.util import unquote, get_deleted_objects
+from django.contrib.admin.options import InlineModelAdmin
+try:
+ from django.contrib.admin.utils import unquote, get_deleted_objects
+except ImportError: # Django < 1.7
+ from django.contrib.admin.util import unquote, get_deleted_objects
from django.core.exceptions import PermissionDenied
from django.db import router
from django.http import Http404
@@ -15,8 +21,8 @@
from forms import PopupInlineFormSet
-try: #django 1.6 and above
- from django.contrib.admin.options import IS_POPUP_VAR #@UnresolvedImport
+try: # django 1.6 and above
+ from django.contrib.admin.options import IS_POPUP_VAR # @UnresolvedImport
except:
IS_POPUP_VAR = '_popup'
@@ -33,12 +39,13 @@ class PopupInline(InlineModelAdmin):
class PopupModelAdmin(admin.ModelAdmin):
linked_inline = None
popup_only = False
-
+
def add_view(self, request, form_url='', extra_context=None):
if self.popup_only and not IS_POPUP_VAR in request.REQUEST:
raise Http404
-
- return super(PopupModelAdmin, self).add_view(request, form_url, extra_context)
+
+ return super(PopupModelAdmin, self).add_view(request,
+ form_url, extra_context)
def ajaxdelete_view(self, request, object_id):
"The 'delete' admin view for this model."
@@ -71,11 +78,11 @@ def ajaxdelete_view(self, request, object_id):
self.delete_model(request, obj)
return HttpResponse('
OK')
-
+
def change_view(self, request, object_id, form_url='', extra_context=None):
if self.popup_only and not IS_POPUP_VAR in request.REQUEST:
raise Http404
-
+
return super(PopupModelAdmin, self).change_view(request, object_id,
form_url, extra_context)
@@ -84,7 +91,7 @@ def formfield_for_dbfield(self, db_field, **kwargs):
Override foreignkey widget if popup and field matches fk_name
"""
formfield = super(PopupModelAdmin, self).formfield_for_dbfield(db_field,
- **kwargs)
+ **kwargs)
request = kwargs.pop("request", None)
fk_name = request.GET.get('fk_name')
@@ -103,7 +110,7 @@ def get_urls(self):
"""
urls = super(PopupModelAdmin, self).get_urls()
- info = self.model._meta.app_label, self.model._meta.module_name
+ info = self.model._meta.app_label, self.model._meta.model_name
my_urls = patterns('',
url(r'^(.+)/ajax/delete/$',
self.admin_site.admin_view(self.ajaxdelete_view),
@@ -172,7 +179,7 @@ def get_urls(self):
"""
urls = super(SortableModelAdmin, self).get_urls()
- info = self.model._meta.app_label, self.model._meta.module_name
+ info = self.model._meta.app_label, self.model._meta.model_name
my_urls = patterns('',
url(r'^sortables/$',
self.admin_site.admin_view(self.sortables),
@@ -188,7 +195,7 @@ def get_urls(self):
def _reorder(self, data, request):
data = dict([(str(d['pk']), d) for d in data])
-
+
data_objects = self.model.objects.filter(pk__in=data.keys())
for item in data_objects:
data[str(item.pk)]['object'] = item
@@ -218,7 +225,7 @@ def sortables(self, request):
else 'admin/sortables/list.html',
{'mptt': self.sortable_mptt,
'objects': self.sortables_ordered(self.queryset(request))})
-
+
def reorder(self, request):
if not request.POST.get('data', None):
return HttpResponse(json.dumps({'message': _('No data sent with the request')}))
diff --git a/yawdadmin/admin_forms.py b/yawdadmin/admin_forms.py
index 2a0f76c..937040d 100644
--- a/yawdadmin/admin_forms.py
+++ b/yawdadmin/admin_forms.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
from django import forms
-from django.contrib.auth import get_user_model
+from django.contrib.auth.models import User
class AdminUserModelForm(forms.ModelForm):
class Meta:
- model = get_user_model()
+ model = User
fields = ('username', 'first_name', 'last_name', 'email')
diff --git a/yawdadmin/admin_options.py b/yawdadmin/admin_options.py
index 34e5115..395e2f2 100644
--- a/yawdadmin/admin_options.py
+++ b/yawdadmin/admin_options.py
@@ -1,51 +1,57 @@
-import re, copy, json
+# -*- coding: utf-8 -*-
+import re
+import copy
+import json
from django import forms
from django.core.cache import cache
from django.utils.encoding import force_text
from .utils import get_options, get_option_cache_key
from .models import AppOption
-#decouple yawd-admin and yawd-translations applications
+# decouple yawd-admin and yawd-translations applications
try:
- from translations.utils import get_supported_languages #@UnresolvedImport
+ from translations.utils import get_supported_languages # @UnresolvedImport
except:
- #mimic the yawd-translations get_supported languages() behavior
- #for the languages defined in settings.LANGUAGES
+ # mimic the yawd-translations get_supported languages() behavior
+ # for the languages defined in settings.LANGUAGES
from django.conf import settings
+
def get_supported_languages():
return [x[0] for x in settings.LANGUAGES]
-
+
_optionsetadmin_classes = {}
+
class SiteOption(object):
order_counter = 0
-
+
def __init__(self, field=None, lang_dependant=False):
-
+
if not isinstance(field, forms.Field):
raise Exception('The field must be a valid field instance')
-
+
self.field = field
self.lang_dependant = lang_dependant
- #remember the order in which fields were initialized
+ # remember the order in which fields were initialized
self.order_counter = SiteOption.order_counter
SiteOption.order_counter += 1
+
class OptionSetBase(type):
"""
metaclass for all OptionSets
- """
+ """
def __new__(self, name, bases, attrs):
super_new = super(OptionSetBase, self).__new__
parents = [b for b in bases if isinstance(b, OptionSetBase)]
if not parents:
# If this isn't a subclass of Model, don't do anything special.
return super_new(self, name, bases, attrs)
-
+
# Create the class.
# Add the options and form fields
module = attrs.pop('__module__')
-
+
try:
optionset_label = attrs.pop('optionset_label')
except KeyError:
@@ -57,58 +63,59 @@ def __new__(self, name, bases, attrs):
if not optionset_label or not re.match(r'[a-zA-z-]+', optionset_label):
raise TypeError("optionset_label must be set and contain only letters and underscores")
-
+
# Because of the way imports happen (recursively), it may or may not be
- # the first time this model tries to register with the framework.
+ # the first time this model tries to register with the framework.
# There should only be one class for each OptionSetAdmin.
global _optionsetadmin_classes
if '%s.%s' % (optionset_label, name) in _optionsetadmin_classes:
return _optionsetadmin_classes['%s.%s' % (optionset_label, name)]
- try:
+ try:
verbose_name = attrs.pop('verbose_name')
except KeyError:
- verbose_name = optionset_label.title().replace ('-', ' ')
+ verbose_name = optionset_label.title().replace('-', ' ')
- #gather parent attributes
+ # gather parent attributes
field_attrs = []
options = {}
lang_options = {}
for parent in parents:
if hasattr(parent, 'option_fields'):
- field_attrs += parent.option_fields
+ field_attrs += parent.option_fields
if hasattr(parent, 'options'):
options.update(parent.options)
if hasattr(parent, 'lang_options'):
lang_options.update(parent.lang_options)
new_class = super_new(self, name, bases, {
- '__module__': module,
- 'options' : options,
- 'lang_options' : lang_options,
+ '__module__': module,
+ 'options': options,
+ 'lang_options': lang_options,
'option_fields': field_attrs,
- 'optionset_label' : optionset_label,
- 'verbose_name' : verbose_name,
+ 'optionset_label': optionset_label,
+ 'verbose_name': verbose_name,
})
-
+
for attr, value in attrs.items():
if not isinstance(value, SiteOption):
raise TypeError('Invalid attribute %s - should be a SiteOption instance' % attr)
-
+
if not hasattr(value.field, 'label') or not value.field.label:
value.field.label = attr.title().replace('_', ' ')
new_class.option_fields.append((attr, value))
- new_class.options[attr] = value.field
-
+ new_class.options[attr] = value.field
+
new_class.option_fields.sort(lambda (attr1, value1), (attr2, value2): cmp(value1.order_counter, value2.order_counter))
-
+
_optionsetadmin_classes['%s.%s' % (optionset_label, name)] = new_class
return new_class
+
def _init_option(optionset_label, name, siteoption):
db_option, created = AppOption.objects.get_or_create(name = name, optionset_label = optionset_label)
-
+
if siteoption.lang_dependant:
ret = {}
for l in get_supported_languages():
@@ -133,8 +140,8 @@ class OptionSetAdmin(object):
def __init__(self, **kwargs):
self.form = forms.Form(**kwargs)
- #load option values from the database
- self.value_dict = get_options(optionset_label = self.optionset_label, current_only=False)
+ # load option values from the database
+ self.value_dict = get_options(optionset_label=self.optionset_label, current_only=False)
self.formfields = []
self.langformfields = {}
@@ -145,21 +152,21 @@ def __init__(self, **kwargs):
if value.lang_dependant:
for lang in get_supported_languages():
- #generate the form field
+ # generate the form field
field_name = '%s_%s' % (attr, lang)
lang_field = copy.deepcopy(value.field)
lang_field.label = '%s (%s)' % (force_text(lang_field.label), lang.upper())
-
+
self.form.fields[field_name] = lang_field
try:
self.form.fields[field_name].initial = self.value_dict[attr][lang]
except KeyError:
self.form.fields[field_name].initial = ''
-
- #add to land dependant options
+
+ # add to land dependant options
self.lang_options[field_name] = (attr, lang)
-
- #langformfields fieldset
+
+ # langformfields fieldset
if not lang in self.langformfields:
self.langformfields[lang] = []
self.langformfields[lang].append(self.form[field_name])
@@ -167,34 +174,34 @@ def __init__(self, **kwargs):
self.form.fields[attr] = value.field
self.form.fields[attr].initial = self.value_dict[attr]
self.formfields.append(self.form[attr])
-
+
def save(self):
- #clear cache
+ # clear cache
cache.delete(get_option_cache_key(self.optionset_label))
- #this dictionary is used to collect lang-dependant field values
+ # this dictionary is used to collect lang-dependant field values
lang_dependant_value_dict = {}
for field, value in self.form.cleaned_data.iteritems():
- #prepare value for save
+ # prepare value for save
prep_value = self.form.fields[field].prepare_value(value)
-
+
if field in self.lang_options:
original_field = self.lang_options[field][0]
lang = self.lang_options[field][1]
-
+
if not original_field in lang_dependant_value_dict:
lang_dependant_value_dict[original_field] = {}
-
+
lang_dependant_value_dict[original_field].update({
- lang : prep_value
+ lang: prep_value
})
else:
if self.value_dict[field] != prep_value:
AppOption.objects.filter(optionset_label=self.optionset_label,
name=field).update(value = prep_value)
-
- #save lang-dependant options
+
+ # save lang-dependant options
for key, value in lang_dependant_value_dict.iteritems():
if self.value_dict[key] != value:
AppOption.objects.filter(optionset_label=self.optionset_label,
diff --git a/yawdadmin/conf/settings.py b/yawdadmin/conf/settings.py
index f581e1c..618a181 100644
--- a/yawdadmin/conf/settings.py
+++ b/yawdadmin/conf/settings.py
@@ -33,7 +33,7 @@
#load the modelform if it's a string
if isinstance(ADMIN_USER_MODELFORM, str):
- from django.utils.importlib import import_module
+ from importlib import import_module
_user_modelform_split = ADMIN_USER_MODELFORM.split('.')
_user_modelform_module = import_module('.'.join(_user_modelform_split[:-1]))
ADMIN_USER_MODELFORM = getattr(_user_modelform_module, _user_modelform_split[-1])
diff --git a/yawdadmin/fields.py b/yawdadmin/fields.py
index b44d100..0327341 100644
--- a/yawdadmin/fields.py
+++ b/yawdadmin/fields.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
import re
from django.db import models
from django.core.exceptions import ValidationError
diff --git a/yawdadmin/forms.py b/yawdadmin/forms.py
index 30becaf..6032ad2 100644
--- a/yawdadmin/forms.py
+++ b/yawdadmin/forms.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.forms.formsets import TOTAL_FORM_COUNT
diff --git a/yawdadmin/locale/es/LC_MESSAGES/django.mo b/yawdadmin/locale/es/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..4e6373d
Binary files /dev/null and b/yawdadmin/locale/es/LC_MESSAGES/django.mo differ
diff --git a/yawdadmin/locale/es/LC_MESSAGES/django.po b/yawdadmin/locale/es/LC_MESSAGES/django.po
new file mode 100644
index 0000000..5e66e22
--- /dev/null
+++ b/yawdadmin/locale/es/LC_MESSAGES/django.po
@@ -0,0 +1,975 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-10-25 18:07+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: admin.py:49
+#, python-format
+msgid "%(name)s object with primary key %(key)r does not exist."
+msgstr ""
+
+#: admin.py:210
+msgid "No data sent with the request"
+msgstr ""
+
+#: admin.py:214
+msgid "Unable to reorder objects"
+msgstr ""
+
+#: admin.py:215 templates/admin/edit_inline/popup.html:99
+msgid "The order was saved"
+msgstr ""
+
+#: sites.py:386
+msgid "Site administration"
+msgstr ""
+
+#: sites.py:405
+#, python-format
+msgid "%s administration"
+msgstr ""
+
+#: views.py:48
+msgid "The options were succesfully saved."
+msgstr ""
+
+#: views.py:73
+msgid "The user was successfully connected."
+msgstr ""
+
+#: views.py:119
+msgid ""
+"The server does not have permissions to write to the token file. Please "
+"contact your system administrator."
+msgstr ""
+
+#: views.py:145
+msgid "Your account has been updated successfuly."
+msgstr ""
+
+#: views.py:153 templates/registration/password_change_done.html:6
+#: templates/registration/password_change_form.html:10
+msgid "My account"
+msgstr "Mi cuenta"
+
+#: widgets.py:105
+msgid "YES"
+msgstr "SI"
+
+#: widgets.py:106
+msgid "NO"
+msgstr "NO"
+
+#: templates/admin/404.html:4 templates/admin/404.html.py:8
+msgid "Page not found"
+msgstr "Página no encontrada"
+
+#: templates/admin/404.html:10
+msgid "We're sorry, but the requested page could not be found."
+msgstr ""
+
+#: templates/admin/500.html:4
+msgid "Server error"
+msgstr ""
+
+#: templates/admin/500.html:6
+msgid "Server error (500)"
+msgstr ""
+
+#: templates/admin/500.html:9
+msgid "Server Error (500)"
+msgstr ""
+
+#: templates/admin/500.html:10
+msgid ""
+"There's been an error. It's been reported to the site administrators via e-"
+"mail and should be fixed shortly. Thanks for your patience."
+msgstr ""
+
+#: templates/admin/actions.html:3
+msgid "Run the selected action"
+msgstr ""
+
+#: templates/admin/actions.html:3
+msgid "Go"
+msgstr "Ir"
+
+#: templates/admin/actions.html:8
+msgid "Click here to select the objects across all pages"
+msgstr ""
+
+#: templates/admin/actions.html:8
+#, python-format
+msgid "Select all %(total_count)s %(module_name)s"
+msgstr ""
+
+#: templates/admin/actions.html:11
+msgid "Clear selection"
+msgstr ""
+
+#: templates/admin/analytics.html:6 templates/admin/analytics.html.py:10
+#: templates/admin/includes/topmenu.html:35
+msgid "Configure Google Analytics"
+msgstr ""
+
+#: templates/admin/analytics.html:17
+msgid "Profile ID"
+msgstr ""
+
+#: templates/admin/analytics.html:21
+msgid "Showing last"
+msgstr ""
+
+#: templates/admin/analytics.html:22
+msgid "days"
+msgstr ""
+
+#: templates/admin/analytics.html:25
+msgid "Connected"
+msgstr ""
+
+#: templates/admin/analytics.html:26 templates/admin/analytics.html.py:34
+#: templates/admin/includes/translation_messages_list.html:5
+msgid "Yes"
+msgstr ""
+
+#: templates/admin/analytics.html:26 templates/admin/analytics.html.py:34
+#: templates/admin/includes/translation_messages_list.html:5
+msgid "No"
+msgstr ""
+
+#: templates/admin/analytics.html:29
+msgid "Current session expiry"
+msgstr ""
+
+#: templates/admin/analytics.html:33
+msgid "Refresh token"
+msgstr ""
+
+#: templates/admin/analytics.html:40 templates/admin/analytics.html.py:41
+msgid "Authenticate new account"
+msgstr ""
+
+#: templates/admin/analytics.html:41
+#: templates/admin/auth/user/change_password.html:37
+#: templates/admin/includes/base_form.html:17
+#: templates/admin/includes/fieldset-with-hidden-language.html:15
+#: templates/admin/includes/fieldset.html:17
+#: templates/admin/includes/optionfields.html:9
+msgid "help text"
+msgstr ""
+
+#: templates/admin/analytics.html:41
+msgid ""
+"
Use this button to connect to a Google account and enable statistics. The "
+"google account must have access to the above Google Analytics Profile ID."
+"p>
add button above to "
+"create a new"
+msgstr "No hay ningún registro todavía. Use el botón Agregar para crear un nuevo registro."
+
+#: templates/admin/delete_confirmation.html:3
+#: templates/admin/delete_confirmation.html:16
+#: templates/admin/delete_selected_confirmation.html:3
+#: templates/admin/submit_line.html:4
+#: templates/admin/edit_inline/popup.html:23
+#: templates/admin/edit_inline/popup.html:33
+#: templates/admin/edit_inline/popup.html:43
+#: templates/admin/import_export/import.html:105
+msgid "Delete"
+msgstr "Borrar"
+
+#: templates/admin/delete_confirmation.html:7
+#: templates/admin/delete_selected_confirmation.html:7
+msgid "delete"
+msgstr "borrar"
+
+#: templates/admin/delete_confirmation.html:21
+#, python-format
+msgid ""
+"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting "
+"related objects, but your account doesn't have permission to delete the "
+"following types of objects:"
+msgstr ""
+
+#: templates/admin/delete_confirmation.html:25
+#, python-format
+msgid ""
+"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the "
+"following protected related objects:"
+msgstr ""
+
+#: templates/admin/delete_confirmation.html:29
+#, python-format
+msgid ""
+"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
+"All of the following related items will be deleted:"
+msgstr ""
+
+#: templates/admin/delete_confirmation.html:36
+#: templates/admin/delete_selected_confirmation.html:37
+#: templates/admin/edit_inline/popup.html:42
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: templates/admin/delete_confirmation.html:37
+#: templates/admin/delete_selected_confirmation.html:38
+msgid "Yes, I'm sure"
+msgstr "Si, estoy seguro"
+
+#: templates/admin/delete_selected_confirmation.html:15
+msgid "Delete multiple objects"
+msgstr "Borrar varios registros"
+
+#: templates/admin/delete_selected_confirmation.html:20
+#, python-format
+msgid ""
+"Deleting the selected %(objects_name)s would result in deleting related "
+"objects, but your account doesn't have permission to delete the following "
+"types of objects:"
+msgstr ""
+
+#: templates/admin/delete_selected_confirmation.html:24
+#, python-format
+msgid ""
+"Deleting the selected %(objects_name)s would require deleting the following "
+"protected related objects:"
+msgstr ""
+
+#: templates/admin/delete_selected_confirmation.html:28
+#, python-format
+msgid ""
+"Are you sure you want to delete the selected %(objects_name)s? All of the "
+"following objects and their related items will be deleted:"
+msgstr ""
+
+#: templates/admin/edit_translation_messages.html:9
+msgid "Translate Static Messages"
+msgstr ""
+
+#: templates/admin/filter.html:1
+#, python-format
+msgid "%(filter_title)s"
+msgstr ""
+
+#: templates/admin/index.html:11
+msgid "Date"
+msgstr "Fecha"
+
+#: templates/admin/index.html:11 templates/admin/import_export/import.html:101
+msgid "New"
+msgstr "Nuevo"
+
+#: templates/admin/index.html:11
+msgid "Returning"
+msgstr ""
+
+#: templates/admin/index.html:11 templates/admin/index.html.py:45
+msgid "Total visits"
+msgstr ""
+
+#: templates/admin/index.html:16
+msgid "Last 30 days visits"
+msgstr ""
+
+#: templates/admin/index.html:36 templates/reversion/recover_list.html:9
+msgid "Home"
+msgstr ""
+
+#: templates/admin/index.html:46
+msgid "Visitors"
+msgstr ""
+
+#: templates/admin/index.html:47
+msgid "Pageviews"
+msgstr ""
+
+#: templates/admin/index.html:48
+msgid "Avg. visit time"
+msgstr ""
+
+#: templates/admin/index.html:49
+msgid "Bounce rate"
+msgstr ""
+
+#: templates/admin/index.html:50
+msgid "New visits"
+msgstr ""
+
+#: templates/admin/index.html:54
+#: templates/admin/edit_inline/translatable-inline.html:11
+msgid "Warning!"
+msgstr ""
+
+#: templates/admin/index.html:55
+msgid "Analytics data could not be generated"
+msgstr ""
+
+#: templates/admin/index.html:56
+msgid "The key has expired."
+msgstr ""
+
+#: templates/admin/index.html:57
+#, python-format
+msgid ""
+"The analytics user is not authenticated. Click "
+"here to connect a user with the analytics account."
+msgstr ""
+
+#: templates/admin/index.html:58
+msgid ""
+"Your account has no data yet. Just be patient, visits will come!"
+"strong> :)"
+msgstr ""
+
+#: templates/admin/index.html:59
+msgid "Error code"
+msgstr ""
+
+#: templates/admin/index.html:66
+#, python-format
+msgid "'%(name)s' section links."
+msgstr ""
+
+#: templates/admin/index.html:108
+msgid "Recent Actions"
+msgstr ""
+
+#: templates/admin/index.html:110
+msgid "My Actions"
+msgstr ""
+
+#: templates/admin/index.html:112
+msgid "No history yet"
+msgstr ""
+
+#: templates/admin/index.html:119
+msgid "Unknown content"
+msgstr ""
+
+#: templates/admin/invalid_setup.html:10
+msgid ""
+"Something's wrong with your database installation. Make sure the appropriate "
+"database tables have been created, and make sure the database is readable by "
+"the appropriate user."
+msgstr ""
+
+#: templates/admin/login.html:15
+msgid "Sign In"
+msgstr ""
+
+#: templates/admin/login.html:21
+msgid "Login"
+msgstr ""
+
+#: templates/admin/login.html:31
+#: templates/admin/auth/user/change_password.html:32
+msgid "Password"
+msgstr ""
+
+#: templates/admin/login.html:41
+#: templates/registration/password_reset_complete.html:10
+msgid "Log in"
+msgstr ""
+
+#: templates/admin/login.html:42
+msgid "Forgot password?"
+msgstr ""
+
+#: templates/admin/object_history.html:7
+msgid "history"
+msgstr ""
+
+#: templates/admin/object_history.html:25
+#: templates/reversion/object_history.html:12
+#: templates/reversion/recover_list.html:24
+#: templates/reversion-compare/object_history.html:24
+msgid "Date/time"
+msgstr ""
+
+#: templates/admin/object_history.html:26
+#: templates/reversion/object_history.html:13
+#: templates/reversion-compare/object_history.html:25
+msgid "User"
+msgstr ""
+
+#: templates/admin/object_history.html:27
+#: templates/reversion/object_history.html:14
+msgid "Action"
+msgstr ""
+
+#: templates/admin/object_history.html:38
+#: templates/reversion/object_history.html:30
+#: templates/reversion-compare/object_history.html:56
+msgid ""
+"This object doesn't have a change history. It probably wasn't added via this "
+"admin site."
+msgstr ""
+
+#: templates/admin/options.html:19 templates/admin/options.html.py:46
+#: templates/admin/pagination.html:12 templates/admin/submit_line.html:8
+msgid "Save"
+msgstr ""
+
+#: templates/admin/options.html:31
+msgid "Translatable options"
+msgstr ""
+
+#: templates/admin/pagination.html:9
+msgid "Show all"
+msgstr ""
+
+#: templates/admin/search_form.html:7
+#, python-format
+msgid "%(counter)s result"
+msgid_plural "%(counter)s results"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/admin/search_form.html:7
+#, python-format
+msgid "%(full_result_count)s total"
+msgstr ""
+
+#: templates/admin/search_form.html:7
+msgid "Search..."
+msgstr "Buscar..."
+
+#: templates/admin/submit_line.html:5
+msgid "Save as new"
+msgstr ""
+
+#: templates/admin/submit_line.html:6
+msgid "Save and add another"
+msgstr ""
+
+#: templates/admin/submit_line.html:7
+msgid "Save and continue editing"
+msgstr ""
+
+#: templates/admin/translation_messages.html:54
+msgid "Generate messages"
+msgstr ""
+
+#: templates/admin/translation_messages.html:54
+msgid "Regenerate messages"
+msgstr ""
+
+#: templates/admin/translation_messages.html:60
+msgid "Update messages"
+msgstr ""
+
+#: templates/admin/auth/user/add_form.html:4
+msgid ""
+"First, enter a username and password. Then, you'll be able to edit more user "
+"options."
+msgstr ""
+
+#: templates/admin/auth/user/add_form.html:5
+msgid "Enter a username and password."
+msgstr ""
+
+#: templates/admin/auth/user/change_form.html:6
+#: templates/admin/auth/user/change_password.html:14
+#: templates/admin/auth/user/change_password.html:41
+#: templates/registration/my_account.html:9
+msgid "Change password"
+msgstr ""
+
+#: templates/admin/auth/user/change_password.html:27
+#, python-format
+msgid "Enter a new password for the user %(username)s."
+msgstr ""
+
+#: templates/admin/auth/user/change_password.html:36
+#: templates/admin/auth/user/change_password.html:37
+msgid "Password (again)"
+msgstr ""
+
+#: templates/admin/auth/user/change_password.html:37
+msgid "Enter the same password as above, for verification."
+msgstr ""
+
+#: templates/admin/djcelery/change_list.html:8
+msgid ""
+"\n"
+"\t\t\tPeriodic tasks won't be dispatched unless you set the\n"
+"\t\t\tCELERYBEAT_SCHEDULER setting to\n"
+"\t\t\tdjcelery.schedulers.DatabaseScheduler,\n"
+"\t\t\tor specify it using the -S option to celerybeat"
+msgstr ""
+
+#: templates/admin/edit_inline/popup.html:38
+msgid "You are about to delete this object! Are you sure you want to continue?"
+msgstr ""
+
+#: templates/admin/edit_inline/popup.html:53
+msgid "Remove row"
+msgstr ""
+
+#: templates/admin/edit_inline/popup.html:54
+msgid "Add new row"
+msgstr "Agregar nuevo"
+
+#: templates/admin/edit_inline/popup.html:67
+msgid "was succesfully deleted"
+msgstr "ha sido eliminado correctamente"
+
+#: templates/admin/edit_inline/stacked.html:19
+#, python-format
+msgid "Edit %(modaltitle)s"
+msgstr "Editar %(modaltitle)s"
+
+#: templates/admin/edit_inline/stacked.html:41
+#: templates/admin/edit_inline/tabular.html:53
+msgid "Remove"
+msgstr "Eliminar?"
+
+#: templates/admin/edit_inline/stacked.html:42
+#: templates/admin/edit_inline/tabular.html:52
+msgid "Add new"
+msgstr "Agregar nuevo"
+
+#: templates/admin/edit_inline/tabular.html:15
+msgid "Delete?"
+msgstr "Eliminar?"
+
+#: templates/admin/edit_inline/translatable-inline.html:12
+msgid "Please set at least one language to manage translatable objects."
+msgstr ""
+
+#: templates/admin/edit_inline/translatable-inline.html:29
+msgid "Clear the "
+msgstr ""
+
+#: templates/admin/edit_inline/translatable-inline.html:29
+msgid "translation"
+msgstr ""
+
+#: templates/admin/import_export/change_list_export.html:8
+#: templates/admin/import_export/change_list_import_export.html:13
+#: templates/admin/import_export/export.html:8
+#: templates/admin/import_export/export.html:13
+msgid "Export"
+msgstr ""
+
+#: templates/admin/import_export/change_list_import.html:8
+#: templates/admin/import_export/change_list_import_export.html:8
+#: templates/admin/import_export/import.html:9
+#: templates/admin/import_export/import.html:13
+msgid "Import"
+msgstr ""
+
+#: templates/admin/import_export/export.html:38
+#: templates/admin/import_export/import.html:61
+msgid "Submit"
+msgstr ""
+
+#: templates/admin/import_export/import.html:23
+msgid ""
+"Bellow is a preview of applied import data. If you are satisfied with a "
+"result click Confirm import"
+msgstr ""
+
+#: templates/admin/import_export/import.html:26
+msgid "Confirm import"
+msgstr ""
+
+#: templates/admin/import_export/import.html:35
+msgid "This importer excpect and will import following fields: "
+msgstr ""
+
+#: templates/admin/import_export/import.html:69
+msgid "Errors"
+msgstr ""
+
+#: templates/admin/import_export/import.html:77
+msgid "Line number"
+msgstr ""
+
+#: templates/admin/import_export/import.html:86
+msgid "Preview"
+msgstr ""
+
+#: templates/admin/import_export/import.html:103
+msgid "Skipped"
+msgstr ""
+
+#: templates/admin/import_export/import.html:107
+msgid "Update"
+msgstr ""
+
+#: templates/admin/includes/topmenu.html:39
+msgid "Documentation"
+msgstr ""
+
+#: templates/admin/includes/topmenu.html:42
+msgid "My Account"
+msgstr "Mi cuenta"
+
+#: templates/admin/includes/topmenu.html:45
+msgid "Change Password"
+msgstr "Cambiar Password"
+
+#: templates/admin/includes/topmenu.html:48
+#: templates/registration/logged_out.html:6
+msgid "Logout"
+msgstr "Cerrar Sesion"
+
+#: templates/admin/includes/topmenu.html:72
+msgid "Options"
+msgstr ""
+
+#: templates/admin/includes/translation_messages_list.html:4
+msgid ""
+"Are you sure you want to regenerate the messages? This will override your "
+"changes. "
+msgstr ""
+
+#: templates/admin/includes/translation_messages_list.html:15
+msgid "Available files"
+msgstr ""
+
+#: templates/admin/translations/language/change_form.html:6
+msgid "Translate messages"
+msgstr ""
+
+#: templates/registration/logged_out.html:7
+msgid "Thanks for spending some quality time with the Web site today."
+msgstr ""
+
+#: templates/registration/logged_out.html:10
+msgid "Log in again"
+msgstr ""
+
+#: templates/registration/my_account.html:22
+msgid "Update account"
+msgstr ""
+
+#: templates/registration/password_change_done.html:7
+#: templates/registration/password_change_done.html:10
+#: templates/registration/password_change_done.html:14
+#: templates/registration/password_change_form.html:4
+#: templates/registration/password_change_form.html:11
+#: templates/registration/password_change_form.html:14
+msgid "Password change"
+msgstr ""
+
+#: templates/registration/password_change_done.html:16
+msgid "Your password was changed successfully."
+msgstr ""
+
+#: templates/registration/password_change_form.html:17
+msgid ""
+"Please enter your old password, for security's sake, and then enter your new "
+"password twice so we can verify you typed it in correctly."
+msgstr ""
+
+#: templates/registration/password_change_form.html:26
+msgid "Old password"
+msgstr ""
+
+#: templates/registration/password_change_form.html:35
+msgid "New password"
+msgstr ""
+
+#: templates/registration/password_change_form.html:44
+msgid "New password (again)"
+msgstr ""
+
+#: templates/registration/password_change_form.html:52
+#: templates/registration/password_reset_confirm.html:37
+msgid "Change my password"
+msgstr ""
+
+#: templates/registration/password_reset_complete.html:3
+#: templates/registration/password_reset_complete.html:7
+msgid "Password reset complete"
+msgstr ""
+
+#: templates/registration/password_reset_complete.html:8
+msgid "Your password has been set. You may go ahead and log in now."
+msgstr ""
+
+#: templates/registration/password_reset_confirm.html:3
+#: templates/registration/password_reset_confirm.html:43
+#: templates/registration/password_reset_done.html:3
+#: templates/registration/password_reset_done.html:7
+#: templates/registration/password_reset_form.html:3
+#: templates/registration/password_reset_form.html:12
+msgid "Password reset"
+msgstr ""
+
+#: templates/registration/password_reset_confirm.html:12
+msgid "Enter new password"
+msgstr ""
+
+#: templates/registration/password_reset_confirm.html:13
+msgid ""
+"Please enter your new password twice so we can verify you typed it in "
+"correctly."
+msgstr ""
+
+#: templates/registration/password_reset_confirm.html:40
+msgid "Password reset unsuccessful"
+msgstr ""
+
+#: templates/registration/password_reset_confirm.html:41
+msgid ""
+"The password reset link was invalid, possibly because it has already been "
+"used. Please request a new password reset."
+msgstr ""
+
+#: templates/registration/password_reset_done.html:9
+#: templates/registration/password_reset_form.html:14
+msgid ""
+"Forgotten your password? Enter your e-mail address below, and we'll e-mail "
+"instructions for setting a new one."
+msgstr ""
+
+#: templates/registration/password_reset_done.html:12
+msgid ""
+"We've e-mailed you instructions for setting your password to the e-mail "
+"address you submitted. You should be receiving it shortly."
+msgstr ""
+
+#: templates/registration/password_reset_email.html:2
+#, python-format
+msgid ""
+"You're receiving this e-mail because you requested a password reset for your "
+"user account at %(site_name)s."
+msgstr ""
+
+#: templates/registration/password_reset_email.html:4
+msgid "Please go to the following page and choose a new password:"
+msgstr ""
+
+#: templates/registration/password_reset_email.html:8
+msgid "Your username, in case you've forgotten:"
+msgstr ""
+
+#: templates/registration/password_reset_email.html:10
+msgid "Thanks for using our site!"
+msgstr ""
+
+#: templates/registration/password_reset_email.html:12
+#, python-format
+msgid "The %(site_name)s team"
+msgstr ""
+
+#: templates/registration/password_reset_form.html:30
+msgid "Reset my password"
+msgstr ""
+
+#: templates/registration/password_reset_form.html:30
+msgid "Take me to login"
+msgstr ""
+
+#: templates/reversion/change_list.html:9
+#, python-format
+msgid "Add %(name)s"
+msgstr ""
+
+#: templates/reversion/change_list.html:15
+#: templates/reversion/recover_form.html:11
+#: templates/reversion/recover_list.html:12
+#, python-format
+msgid "Recover deleted %(name)s"
+msgstr ""
+
+#: templates/reversion/object_history.html:6
+#: templates/reversion-compare/object_history.html:7
+msgid ""
+"Choose a date from the list below to revert to a previous version of this "
+"object."
+msgstr ""
+
+#: templates/reversion/recover_form.html:17
+msgid "Press the save button below to recover this version of the object."
+msgstr ""
+
+#: templates/reversion/recover_list.html:18
+msgid ""
+"Choose a date from the list below to recover a deleted version of an object."
+msgstr ""
+
+#: templates/reversion/recover_list.html:38
+msgid "There are no deleted objects to recover."
+msgstr ""
+
+#: templates/reversion/revision_form.html:13
+#, python-format
+msgid "Revert %(verbose_name)s"
+msgstr ""
+
+#: templates/reversion/revision_form.html:35
+msgid "Press the save button below to revert to this version of the object."
+msgstr ""
+
+#: templates/reversion-compare/compare.html:25
+#, python-format
+msgid "Compare %(name)s"
+msgstr ""
+
+#: templates/reversion-compare/compare.html:33
+#: templates/reversion-compare/object_history.html:42
+msgid "DATETIME_FORMAT"
+msgstr ""
+
+#: templates/reversion-compare/compare.html:33
+#, python-format
+msgid ""
+"\n"
+" Compare %(date1)s with %(date2)s"
+"strong>:\n"
+" "
+msgstr ""
+
+#: templates/reversion-compare/compare.html:38
+#: templates/reversion-compare/compare.html:67
+msgid "Go back to history list"
+msgstr ""
+
+#: templates/reversion-compare/compare.html:56
+msgid "Edit comment:"
+msgstr ""
+
+#: templates/reversion-compare/compare.html:60
+msgid "Note:"
+msgstr ""
+
+#: templates/reversion-compare/object_history.html:18
+#: templates/reversion-compare/object_history.html:20
+msgid "compare"
+msgstr ""
+
+#: templates/reversion-compare/object_history.html:26
+msgid "Comment"
+msgstr ""
+
+#: templatetags/yawdadmin_tags.py:65
+msgid "Django Administration"
+msgstr ""
+
+#: templatetags/yawdadmin_tags.py:67
+msgid ""
+"Welcome to the yawd-admin administration page. Please sign in to manage your "
+"website."
+msgstr ""
diff --git a/yawdadmin/middleware.py b/yawdadmin/middleware.py
index 66d492e..16190ed 100644
--- a/yawdadmin/middleware.py
+++ b/yawdadmin/middleware.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
class PopupMiddleware(object):
"""
This middleware must always be enabled when using yawd-admin WITH
diff --git a/yawdadmin/migrations/0001_initial.py b/yawdadmin/migrations/0001_initial.py
new file mode 100644
index 0000000..ba73fed
--- /dev/null
+++ b/yawdadmin/migrations/0001_initial.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import yawdadmin.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AppOption',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('optionset_label', models.CharField(max_length=50)),
+ ('name', yawdadmin.fields.OptionNameField(max_length=50)),
+ ('value', models.TextField(null=True)),
+ ('lang_dependant', models.BooleanField(default=False)),
+ ],
+ options={
+ 'ordering': ['optionset_label', 'lang_dependant'],
+ },
+ ),
+ migrations.AlterUniqueTogether(
+ name='appoption',
+ unique_together=set([('optionset_label', 'name')]),
+ ),
+ ]
diff --git a/yawdadmin/migrations/__init__.py b/yawdadmin/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/yawdadmin/models.py b/yawdadmin/models.py
index e3ced6b..0a50d22 100644
--- a/yawdadmin/models.py
+++ b/yawdadmin/models.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.db import models
from .fields import OptionNameField
@@ -11,6 +12,7 @@ class AppOption(models.Model):
class Meta:
unique_together = ('optionset_label', 'name')
ordering = ['optionset_label', 'lang_dependant']
+ app_label = 'yawdadmin'
def __unicode__(self):
return u'%s.%s' % (self.optionset_label, self.name)
diff --git a/yawdadmin/sites.py b/yawdadmin/sites.py
index 2fcc37f..af6ad5c 100644
--- a/yawdadmin/sites.py
+++ b/yawdadmin/sites.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
import httplib2
from functools import update_wrapper
from oauth2client.file import Storage
@@ -91,7 +92,7 @@ def _find_model(self, label, app_label):
for extra in self.app_dict[app_label]['extras']:
if 'label' in extra and extra['label'] == label:
return extra
-
+
@classmethod
def app_sorter(self, x):
return x['name']
@@ -117,7 +118,7 @@ def get_app_list(self, request, registry, label=''):
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
- info = (app_label, model._meta.module_name)
+ info = (app_label, model._meta.model_name)
model_dict = {
'classname': model.__name__,
'name': capfirst(model._meta.verbose_name_plural),
@@ -190,7 +191,7 @@ def get_index_template(self):
def get_urls(self):
global _optionset_labels
-
+
def wrap(view, cacheable=False):
def wrapper(*args, **kwargs):
return self.admin_view(view, cacheable)(*args, **kwargs)
@@ -274,7 +275,7 @@ def top_menu(self, request):
# Check whether user has any perm for this module.
# If so, add the module to the model_list.
if True in perms.values():
- info = (app_label, model._meta.module_name)
+ info = (app_label, model._meta.model_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
'show': perms['change'],
@@ -310,10 +311,11 @@ def top_menu(self, request):
#register custom menus
for app in self._top_menu.values():
if isinstance(app, dict):
- for child in app['models']:
- if not 'show' in child and 'perms' in app and hasattr(app['perms'], '__call__'):
- child['show'] = app['perms'](request, child)
- app_list.append(app)
+ if 'models' in app:
+ for child in app['models']:
+ if not 'show' in child and 'perms' in app and hasattr(app['perms'], '__call__'):
+ child['show'] = app['perms'](request, child)
+ app_list.append(app)
app_list.sort(key=lambda x: x['name'])
diff --git a/yawdadmin/static/admin/js/SelectBox.js b/yawdadmin/static/admin/js/SelectBox.js
new file mode 100644
index 0000000..a0e99cc
--- /dev/null
+++ b/yawdadmin/static/admin/js/SelectBox.js
@@ -0,0 +1,135 @@
+(function() {
+ 'use strict';
+ var SelectBox = {
+ cache: {},
+ init: function(id) {
+ var box = document.getElementById(id);
+ var node;
+ SelectBox.cache[id] = [];
+ var cache = SelectBox.cache[id];
+ for (var i = 0, j = box.options.length; i < j; i++) {
+ node = box.options[i];
+ cache.push({value: node.value, text: node.text, displayed: 1});
+ }
+ },
+ redisplay: function(id) {
+ // Repopulate HTML select box from cache
+ var box = document.getElementById(id);
+ var node;
+ box.options.length = 0; // clear all options
+ var cache = SelectBox.cache[id];
+ for (var i = 0, j = cache.length; i < j; i++) {
+ node = cache[i];
+ if (node.displayed) {
+ var new_option = new Option(node.text, node.value, false, false);
+ // Shows a tooltip when hovering over the option
+ new_option.setAttribute("title", node.text);
+ box.options[box.options.length] = new_option;
+ }
+ }
+ },
+ filter: function(id, text) {
+ // Redisplay the HTML select box, displaying only the choices containing ALL
+ // the words in text. (It's an AND search.)
+ var tokens = text.toLowerCase().split(/\s+/);
+ var node, token;
+ var cache = SelectBox.cache[id];
+ for (var i = 0, j = cache.length; i < j; i++) {
+ node = cache[i];
+ node.displayed = 1;
+ var numTokens = tokens.length;
+ for (var k = 0; k < numTokens; k++) {
+ token = tokens[k];
+ if (node.text.toLowerCase().indexOf(token) === -1) {
+ node.displayed = 0;
+ }
+ }
+ }
+ SelectBox.redisplay(id);
+ },
+ delete_from_cache: function(id, value) {
+ var node, delete_index = null;
+ var cache = SelectBox.cache[id];
+ for (var i = 0, j = cache.length; i < j; i++) {
+ node = cache[i];
+ if (node.value === value) {
+ delete_index = i;
+ break;
+ }
+ }
+ var k = cache.length - 1;
+ for (i = delete_index; i < k; i++) {
+ cache[i] = cache[i + 1];
+ }
+ cache.length--;
+ },
+ add_to_cache: function(id, option) {
+ SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});
+ },
+ cache_contains: function(id, value) {
+ // Check if an item is contained in the cache
+ var node;
+ var cache = SelectBox.cache[id];
+ for (var i = 0, j = cache.length; i < j; i++) {
+ node = cache[i];
+ if (node.value === value) {
+ return true;
+ }
+ }
+ return false;
+ },
+ move: function(from, to) {
+ var from_box = document.getElementById(from);
+ var option;
+ var boxOptions = from_box.options;
+ for (var i = 0, j = boxOptions.length; i < j; i++) {
+ option = boxOptions[i];
+ if (option.selected && SelectBox.cache_contains(from, option.value)) {
+ SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});
+ SelectBox.delete_from_cache(from, option.value);
+ }
+ }
+ SelectBox.redisplay(from);
+ SelectBox.redisplay(to);
+ },
+ move_all: function(from, to) {
+ var from_box = document.getElementById(from);
+ var option;
+ var boxOptions = from_box.options;
+ for (var i = 0, j = boxOptions.length; i < j; i++) {
+ option = boxOptions[i];
+ if (SelectBox.cache_contains(from, option.value)) {
+ SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});
+ SelectBox.delete_from_cache(from, option.value);
+ }
+ }
+ SelectBox.redisplay(from);
+ SelectBox.redisplay(to);
+ },
+ sort: function(id) {
+ SelectBox.cache[id].sort(function(a, b) {
+ a = a.text.toLowerCase();
+ b = b.text.toLowerCase();
+ try {
+ if (a > b) {
+ return 1;
+ }
+ if (a < b) {
+ return -1;
+ }
+ }
+ catch (e) {
+ // silently fail on IE 'unknown' exception
+ }
+ return 0;
+ } );
+ },
+ select_all: function(id) {
+ var box = document.getElementById(id);
+ for (var i = 0; i < box.options.length; i++) {
+ box.options[i].selected = 'selected';
+ }
+ }
+ };
+ window.SelectBox = SelectBox;
+})();
\ No newline at end of file
diff --git a/yawdadmin/static/admin/js/SelectFilter2.js b/yawdadmin/static/admin/js/SelectFilter2.js
index 507e180..5d013e3 100644
--- a/yawdadmin/static/admin/js/SelectFilter2.js
+++ b/yawdadmin/static/admin/js/SelectFilter2.js
@@ -1,168 +1,197 @@
+/*global SelectBox, addEvent, gettext, interpolate, quickElement, SelectFilter*/
/*
SelectFilter2 - Turns a multiple-select box into a filter interface.
-
Requires core.js, SelectBox.js and addevent.js.
*/
(function($) {
-function findForm(node) {
- // returns the node of the form containing the given node
- if (node.tagName.toLowerCase() != 'form') {
- return findForm(node.parentNode);
- }
- return node;
-}
-
-window.SelectFilter = {
- init: function(field_id, field_name, is_stacked, admin_static_prefix) {
- if (field_id.match(/__prefix__/)){
- // Don't intialize on empty forms.
- return;
- }
- var from_box = document.getElementById(field_id);
- from_box.id += '_from'; // change its ID
- from_box.className = 'filtered';
-
- var ps = from_box.parentNode.getElementsByTagName('p');
- for (var i=0; i, because it just gets in the way.
- from_box.parentNode.removeChild(ps[i]);
- } else if (ps[i].className.indexOf("help") != -1) {
- // Move help text up to the top so it isn't below the select
- // boxes or wrapped off on the side to the right of the add
- // button:
- from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
- }
+ 'use strict';
+ function findForm(node) {
+ // returns the node of the form containing the given node
+ if (node.tagName.toLowerCase() !== 'form') {
+ return findForm(node.parentNode);
}
+ return node;
+ }
- //
- var selector_chosen = quickElement('div', selector_div, '');
- selector_chosen.className = 'selector-chosen';
- var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
-
- var ic2 = quickElement('i', title_chosen, '', 'class', 'help help-tooltip icon-question-sign icon-white', 'data-placement', 'bottom', 'data-original-title', interpolate(gettext('Help text')), 'data-content', interpolate(gettext('This is the list of chosen %s. You may remove some by selecting them in the box below and then clicking the "Remove" arrow between the two boxes.'), [field_name]));
- $(ic2).popover({trigger : 'click'});
-
- var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
- to_box.className = 'filtered';
- var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + '_to", "' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_all_link');
- clear_all.className = 'selector-clearall';
-
- from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
-
- // Set up the JavaScript event handlers for the select box filter interface
- addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
- addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
- addEvent(from_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) });
- addEvent(to_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) });
- addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
- addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
- addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
- SelectBox.init(field_id + '_from');
- SelectBox.init(field_id + '_to');
- // Move selected from_box options to to_box
- SelectBox.move(field_id + '_from', field_id + '_to');
-
- if (!is_stacked) {
- // In horizontal mode, give the same height to the two boxes.
- var j_from_box = $(from_box);
- var j_to_box = $(to_box);
- var resize_filters = function() { j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight()); }
- if (j_from_box.outerHeight() > 0) {
- resize_filters(); // This fieldset is already open. Resize now.
- } else {
- // This fieldset is probably collapsed. Wait for its 'show' event.
- j_to_box.closest('fieldset').one('show.fieldset', resize_filters);
+ window.SelectFilter = {
+ init: function(field_id, field_name, is_stacked) {
+ if (field_id.match(/__prefix__/)) {
+ // Don't initialize on empty forms.
+ return;
+ }
+ var from_box = document.getElementById(field_id);
+ from_box.id += '_from'; // change its ID
+ from_box.className = 'filtered';
+
+ var ps = from_box.parentNode.getElementsByTagName('p');
+ for (var i = 0; i < ps.length; i++) {
+ if (ps[i].className.indexOf("info") !== -1) {
+ // Remove
, because it just gets in the way.
+ from_box.parentNode.removeChild(ps[i]);
+ } else if (ps[i].className.indexOf("help") !== -1) {
+ // Move help text up to the top so it isn't below the select
+ // boxes or wrapped off on the side to the right of the add
+ // button:
+ from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
+ }
}
- }
- // Initial icon refresh
- SelectFilter.refresh_icons(field_id);
- },
- refresh_icons: function(field_id) {
- var from = $('#' + field_id + '_from');
- var to = $('#' + field_id + '_to');
- var is_from_selected = from.find('option:selected').length > 0;
- var is_to_selected = to.find('option:selected').length > 0;
- // Active if at least one item is selected
- $('#' + field_id + '_add_link').toggleClass('active', is_from_selected);
- $('#' + field_id + '_remove_link').toggleClass('active', is_to_selected);
- // Active if the corresponding box isn't empty
- $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0);
- $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0);
- },
- filter_key_up: function(event, field_id) {
- var from = document.getElementById(field_id + '_from');
- // don't submit form if user pressed Enter
- if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
- from.selectedIndex = 0;
+ //
+ var selector_chosen = quickElement('div', selector_div);
+ selector_chosen.className = 'selector-chosen';
+ var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
+ quickElement(
+ 'span', title_chosen, '',
+ 'class', 'help help-tooltip help-icon',
+ 'title', interpolate(
+ gettext(
+ 'This is the list of chosen %s. You may remove some by ' +
+ 'selecting them in the box below and then clicking the ' +
+ '"Remove" arrow between the two boxes.'
+ ),
+ [field_name]
+ )
+ );
+
+ var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
+ to_box.className = 'filtered';
+ var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript:void(0);', 'id', field_id + '_remove_all_link');
+ clear_all.className = 'selector-clearall';
+
+ from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
+
+ // Set up the JavaScript event handlers for the select box filter interface
+ addEvent(choose_all, 'click', function() { SelectBox.move_all(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
+ addEvent(add_link, 'click', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
+ addEvent(remove_link, 'click', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
+ addEvent(clear_all, 'click', function() { SelectBox.move_all(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
+ addEvent(filter_input, 'keypress', function(e) { SelectFilter.filter_key_press(e, field_id); });
+ addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
+ addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
+ addEvent(from_box, 'change', function(e) { SelectFilter.refresh_icons(field_id); });
+ addEvent(to_box, 'change', function(e) { SelectFilter.refresh_icons(field_id); });
+ addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
+ addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
+ addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
+ SelectBox.init(field_id + '_from');
+ SelectBox.init(field_id + '_to');
+ // Move selected from_box options to to_box
SelectBox.move(field_id + '_from', field_id + '_to');
- from.selectedIndex = 0;
- return false;
- }
- var temp = from.selectedIndex;
- SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
- from.selectedIndex = temp;
- return true;
- },
- filter_key_down: function(event, field_id) {
- var from = document.getElementById(field_id + '_from');
- // right arrow -- move across
- if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
- var old_index = from.selectedIndex;
- SelectBox.move(field_id + '_from', field_id + '_to');
- from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
- return false;
- }
- // down arrow -- wrap around
- if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
- from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
- }
- // up arrow -- wrap around
- if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
- from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
+
+ if (!is_stacked) {
+ // In horizontal mode, give the same height to the two boxes.
+ var j_from_box = $(from_box);
+ var j_to_box = $(to_box);
+ var resize_filters = function() { j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight()); };
+ if (j_from_box.outerHeight() > 0) {
+ resize_filters(); // This fieldset is already open. Resize now.
+ } else {
+ // This fieldset is probably collapsed. Wait for its 'show' event.
+ j_to_box.closest('fieldset').one('show.fieldset', resize_filters);
+ }
+ }
+
+ // Initial icon refresh
+ SelectFilter.refresh_icons(field_id);
+ },
+ refresh_icons: function(field_id) {
+ var from = $('#' + field_id + '_from');
+ var to = $('#' + field_id + '_to');
+ var is_from_selected = from.find('option:selected').length > 0;
+ var is_to_selected = to.find('option:selected').length > 0;
+ // Active if at least one item is selected
+ $('#' + field_id + '_add_link').toggleClass('active', is_from_selected);
+ $('#' + field_id + '_remove_link').toggleClass('active', is_to_selected);
+ // Active if the corresponding box isn't empty
+ $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0);
+ $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0);
+ },
+ filter_key_press: function(event, field_id) {
+ var from = document.getElementById(field_id + '_from');
+ // don't submit form if user pressed Enter
+ if ((event.which && event.which === 13) || (event.keyCode && event.keyCode === 13)) {
+ from.selectedIndex = 0;
+ SelectBox.move(field_id + '_from', field_id + '_to');
+ from.selectedIndex = 0;
+ event.preventDefault();
+ return false;
+ }
+ },
+ filter_key_up: function(event, field_id) {
+ var from = document.getElementById(field_id + '_from');
+ var temp = from.selectedIndex;
+ SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
+ from.selectedIndex = temp;
+ return true;
+ },
+ filter_key_down: function(event, field_id) {
+ var from = document.getElementById(field_id + '_from');
+ // right arrow -- move across
+ if ((event.which && event.which === 39) || (event.keyCode && event.keyCode === 39)) {
+ var old_index = from.selectedIndex;
+ SelectBox.move(field_id + '_from', field_id + '_to');
+ from.selectedIndex = (old_index === from.length) ? from.length - 1 : old_index;
+ return false;
+ }
+ // down arrow -- wrap around
+ if ((event.which && event.which === 40) || (event.keyCode && event.keyCode === 40)) {
+ from.selectedIndex = (from.length === from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
+ }
+ // up arrow -- wrap around
+ if ((event.which && event.which === 38) || (event.keyCode && event.keyCode === 38)) {
+ from.selectedIndex = (from.selectedIndex === 0) ? from.length - 1 : from.selectedIndex - 1;
+ }
+ return true;
}
- return true;
- }
-}
+ };
})(yawdadmin.jQuery);
\ No newline at end of file
diff --git a/yawdadmin/static/admin/js/actions.js b/yawdadmin/static/admin/js/actions.js
index 329b7a2..34b574b 100644
--- a/yawdadmin/static/admin/js/actions.js
+++ b/yawdadmin/static/admin/js/actions.js
@@ -1,132 +1,146 @@
+/*global _actions_icnt, gettext, interpolate, ngettext*/
(function($) {
- $.fn.actions = function(opts) {
- var options = $.extend({}, $.fn.actions.defaults, opts);
- var actionCheckboxes = $(this);
- var list_editable_changed = false;
- var checker = function(checked) {
- if (checked) {
- showQuestion();
- } else {
- reset();
- }
- $(actionCheckboxes).attr("checked", checked)
- .parent().parent().toggleClass(options.selectedClass, checked);
- },
- updateCounter = function() {
- var sel = $(actionCheckboxes).filter(":checked").length;
- $(options.counterContainer).html(interpolate(
- ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
- sel: sel,
- cnt: _actions_icnt
- }, true));
- $(options.allToggle).attr("checked", function() {
- if (sel == actionCheckboxes.length) {
- value = true;
- showQuestion();
- } else {
- value = false;
- clearAcross();
- }
- return value;
- });
- },
- showQuestion = function() {
- $(options.acrossClears).hide();
- $(options.acrossQuestions).show();
- $(options.allContainer).hide();
- },
- showClear = function() {
- $(options.acrossClears).show();
- $(options.acrossQuestions).hide();
- $(options.actionContainer).toggleClass(options.selectedClass);
- $(options.allContainer).show();
- $(options.counterContainer).hide();
- },
- reset = function() {
- $(options.acrossClears).hide();
- $(options.acrossQuestions).hide();
- $(options.allContainer).hide();
- $(options.counterContainer).show();
- },
- clearAcross = function() {
- reset();
- $(options.acrossInput).val(0);
- $(options.actionContainer).removeClass(options.selectedClass);
- };
- // Show counter by default
- $(options.counterContainer).show();
- // Check state of checkboxes and reinit state if needed
- $(this).filter(":checked").each(function(i) {
- $(this).parent().parent().toggleClass(options.selectedClass);
- updateCounter();
- if ($(options.acrossInput).val() == 1) {
- showClear();
- }
- });
- $(options.allToggle).show().click(function() {
- checker($(this).is(':checked'));
- updateCounter();
- });
- $("div.actions span.question a").click(function(event) {
- event.preventDefault();
- $(options.acrossInput).val(1);
- showClear();
- });
- $("div.actions span.clear a").click(function(event) {
- event.preventDefault();
- $(options.allToggle).attr("checked", false);
- clearAcross();
- checker(0);
- updateCounter();
- });
- lastChecked = null;
- $(actionCheckboxes).click(function(event) {
- if (!event) { event = window.event; }
- var target = event.target ? event.target : event.srcElement;
- if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey === true) {
- var inrange = false;
- $(lastChecked).attr("checked", target.checked)
- .parent().parent().toggleClass(options.selectedClass, target.checked);
- $(actionCheckboxes).each(function() {
- if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) {
- inrange = (inrange) ? false : true;
- }
- if (inrange) {
- $(this).attr("checked", target.checked)
- .parent().parent().toggleClass(options.selectedClass, target.checked);
- }
- });
- }
- $(target).parent().parent().toggleClass(options.selectedClass, target.checked);
- lastChecked = target;
- updateCounter();
- });
- $('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() {
- list_editable_changed = true;
- });
- $(options.actionContainer + ' .action-option a').click(function(event) {
- if (list_editable_changed) {
- confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
- } else {
- $(options.actionContainer + ' select[name="action"]').val($(this).attr('href').replace('#',''));
- $(options.actionContainer + ' .action-wrapper').removeClass('open');
- $(options.actionContainer + ' input[name="index"]').prop('checked', true);
- $(options.actionLoader).removeClass('hidden');
- $('form#changelist-form').submit();
- }
- return false;
- });
- };
- /* Setup plugin defaults */
- $.fn.actions.defaults = {
- actionContainer: "div.actions",
- counterContainer: "span.action-counter",
- allContainer: "div.actions span.all",
- acrossInput: "div.actions input.select-across",
- acrossQuestions: "div.actions span.question",
- acrossClears: "div.actions span.clear",
- allToggle: "#action-toggle",
- selectedClass: "selected",
- actionLoader: ".actions-loader"
- };
+ 'use strict';
+ var lastChecked;
+
+ $.fn.actions = function(opts) {
+ var options = $.extend({}, $.fn.actions.defaults, opts);
+ var actionCheckboxes = $(this);
+ var list_editable_changed = false;
+ var showQuestion = function() {
+ $(options.acrossClears).hide();
+ $(options.acrossQuestions).show();
+ $(options.allContainer).hide();
+ },
+ showClear = function() {
+ $(options.acrossClears).show();
+ $(options.acrossQuestions).hide();
+ $(options.actionContainer).toggleClass(options.selectedClass);
+ $(options.allContainer).show();
+ $(options.counterContainer).hide();
+ },
+ reset = function() {
+ $(options.acrossClears).hide();
+ $(options.acrossQuestions).hide();
+ $(options.allContainer).hide();
+ $(options.counterContainer).show();
+ },
+ clearAcross = function() {
+ reset();
+ $(options.acrossInput).val(0);
+ $(options.actionContainer).removeClass(options.selectedClass);
+ },
+ checker = function(checked) {
+ if (checked) {
+ showQuestion();
+ } else {
+ reset();
+ }
+ $(actionCheckboxes).prop("checked", checked)
+ .parent().parent().toggleClass(options.selectedClass, checked);
+ },
+ updateCounter = function() {
+ var sel = $(actionCheckboxes).filter(":checked").length;
+ // _actions_icnt is defined in the generated HTML
+ // and contains the total amount of objects in the queryset
+ $(options.counterContainer).html(interpolate(
+ ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
+ sel: sel,
+ cnt: _actions_icnt
+ }, true));
+ $(options.allToggle).prop("checked", function() {
+ var value;
+ if (sel === actionCheckboxes.length) {
+ value = true;
+ showQuestion();
+ } else {
+ value = false;
+ clearAcross();
+ }
+ return value;
+ });
+ };
+ // Show counter by default
+ $(options.counterContainer).show();
+ // Check state of checkboxes and reinit state if needed
+ $(this).filter(":checked").each(function(i) {
+ $(this).parent().parent().toggleClass(options.selectedClass);
+ updateCounter();
+ if ($(options.acrossInput).val() === 1) {
+ showClear();
+ }
+ });
+ $(options.allToggle).show().click(function() {
+ checker($(this).prop("checked"));
+ updateCounter();
+ });
+ $("a", options.acrossQuestions).click(function(event) {
+ event.preventDefault();
+ $(options.acrossInput).val(1);
+ showClear();
+ });
+ $("a", options.acrossClears).click(function(event) {
+ event.preventDefault();
+ $(options.allToggle).prop("checked", false);
+ clearAcross();
+ checker(0);
+ updateCounter();
+ });
+ lastChecked = null;
+ $(actionCheckboxes).click(function(event) {
+ if (!event) { event = window.event; }
+ var target = event.target ? event.target : event.srcElement;
+ if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) {
+ var inrange = false;
+ $(lastChecked).prop("checked", target.checked)
+ .parent().parent().toggleClass(options.selectedClass, target.checked);
+ $(actionCheckboxes).each(function() {
+ if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) {
+ inrange = (inrange) ? false : true;
+ }
+ if (inrange) {
+ $(this).prop("checked", target.checked)
+ .parent().parent().toggleClass(options.selectedClass, target.checked);
+ }
+ });
+ }
+ $(target).parent().parent().toggleClass(options.selectedClass, target.checked);
+ lastChecked = target;
+ updateCounter();
+ });
+ $('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() {
+ list_editable_changed = true;
+ });
+ $('form#changelist-form button[name="index"]').click(function(event) {
+ if (list_editable_changed) {
+ return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
+ }
+ });
+ $('form#changelist-form input[name="_save"]').click(function(event) {
+ var action_changed = false;
+ $('select option:selected', options.actionContainer).each(function() {
+ if ($(this).val()) {
+ action_changed = true;
+ }
+ });
+ if (action_changed) {
+ if (list_editable_changed) {
+ return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action."));
+ } else {
+ return confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."));
+ }
+ }
+ });
+ };
+ /* Setup plugin defaults */
+ $.fn.actions.defaults = {
+ actionContainer: "div.actions",
+ counterContainer: "span.action-counter",
+ allContainer: "div.actions span.all",
+ acrossInput: "div.actions input.select-across",
+ acrossQuestions: "div.actions span.question",
+ acrossClears: "div.actions span.clear",
+ allToggle: "#action-toggle",
+ selectedClass: "selected"
+ };
})(yawdadmin.jQuery);
\ No newline at end of file
diff --git a/yawdadmin/static/admin/js/actions.min.js b/yawdadmin/static/admin/js/actions.min.js
deleted file mode 100644
index 688b702..0000000
--- a/yawdadmin/static/admin/js/actions.min.js
+++ /dev/null
@@ -1 +0,0 @@
-(function(a){a.fn.actions=function(b){var k=a.extend({},a.fn.actions.defaults,b);var c=a(this);var f=false;var e=function(l){if(l){j()}else{i()}a(c).attr("checked",l).parent().parent().toggleClass(k.selectedClass,l)},g=function(){var l=a(c).filter(":checked").length;a(k.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",l),{sel:l,cnt:_actions_icnt},true));a(k.allToggle).attr("checked",function(){if(l==c.length){value=true;j()}else{value=false;h()}return value})},j=function(){a(k.acrossClears).hide();a(k.acrossQuestions).show();a(k.allContainer).hide()},d=function(){a(k.acrossClears).show();a(k.acrossQuestions).hide();a(k.actionContainer).toggleClass(k.selectedClass);a(k.allContainer).show();a(k.counterContainer).hide()},i=function(){a(k.acrossClears).hide();a(k.acrossQuestions).hide();a(k.allContainer).hide();a(k.counterContainer).show()},h=function(){i();a(k.acrossInput).val(0);a(k.actionContainer).removeClass(k.selectedClass)};a(k.counterContainer).show();a(this).filter(":checked").each(function(l){a(this).parent().parent().toggleClass(k.selectedClass);g();if(a(k.acrossInput).val()==1){d()}});a(k.allToggle).show().click(function(){e(a(this).is(":checked"));g()});a("div.actions span.question a").click(function(l){l.preventDefault();a(k.acrossInput).val(1);d()});a("div.actions span.clear a").click(function(l){l.preventDefault();a(k.allToggle).attr("checked",false);h();e(0);g()});lastChecked=null;a(c).click(function(m){if(!m){m=window.event}var n=m.target?m.target:m.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(n)&&m.shiftKey===true){var l=false;a(lastChecked).attr("checked",n.checked).parent().parent().toggleClass(k.selectedClass,n.checked);a(c).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(n)){l=(l)?false:true}if(l){a(this).attr("checked",n.checked).parent().parent().toggleClass(k.selectedClass,n.checked)}})}a(n).parent().parent().toggleClass(k.selectedClass,n.checked);lastChecked=n;g()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){f=true});a(k.actionContainer+" .action-option a").click(function(l){if(f){confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))}else{a(k.actionContainer+' select[name="action"]').val(a(this).attr("href").replace("#",""));a(k.actionContainer+" .action-wrapper").removeClass("open");a(k.actionContainer+' input[name="index"]').prop("checked",true);a(k.actionLoader).removeClass("hidden");a("form#changelist-form").submit()}return false})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected",actionLoader:".actions-loader"}})(yawdadmin.jQuery);
\ No newline at end of file
diff --git a/yawdadmin/static/admin/js/actions_min.js b/yawdadmin/static/admin/js/actions_min.js
new file mode 100644
index 0000000..ff2d79f
--- /dev/null
+++ b/yawdadmin/static/admin/js/actions_min.js
@@ -0,0 +1,6 @@
+(function(a){var f;a.fn.actions=function(q){var b=a.extend({},a.fn.actions.defaults,q),g=a(this),e=!1,k=function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},l=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},m=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},n=function(){m();
+a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)},p=function(c){c?k():m();a(g).prop("checked",c).parent().parent().toggleClass(b.selectedClass,c)},h=function(){var c=a(g).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).prop("checked",function(){var a;c===g.length?(a=!0,k()):(a=!1,n());return a})};a(b.counterContainer).show();a(this).filter(":checked").each(function(c){a(this).parent().parent().toggleClass(b.selectedClass);
+h();1===a(b.acrossInput).val()&&l()});a(b.allToggle).show().click(function(){p(a(this).prop("checked"));h()});a("a",b.acrossQuestions).click(function(c){c.preventDefault();a(b.acrossInput).val(1);l()});a("a",b.acrossClears).click(function(c){c.preventDefault();a(b.allToggle).prop("checked",!1);n();p(0);h()});f=null;a(g).click(function(c){c||(c=window.event);var d=c.target?c.target:c.srcElement;if(f&&a.data(f)!==a.data(d)&&!0===c.shiftKey){var e=!1;a(f).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,
+d.checked);a(g).each(function(){if(a.data(this)===a.data(f)||a.data(this)===a.data(d))e=e?!1:!0;e&&a(this).prop("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);f=d;h()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"]').click(function(a){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});
+a('form#changelist-form input[name="_save"]').click(function(c){var d=!1;a("select option:selected",b.actionContainer).each(function(){a(this).val()&&(d=!0)});if(d)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};
+a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(yawdadmin.jQuery);
\ No newline at end of file
diff --git a/yawdadmin/static/admin/js/admin/DateTimeShortcuts.js b/yawdadmin/static/admin/js/admin/DateTimeShortcuts.js
index 7660537..5ea29fb 100644
--- a/yawdadmin/static/admin/js/admin/DateTimeShortcuts.js
+++ b/yawdadmin/static/admin/js/admin/DateTimeShortcuts.js
@@ -1,302 +1,364 @@
+/*global addEvent, Calendar, cancelEventPropagation, findPosX, findPosY, getStyle, get_format, gettext, interpolate, ngettext, quickElement, removeEvent*/
// Inserts shortcut buttons after all of the following:
//
//
+(function() {
+ 'use strict';
+ var DateTimeShortcuts = {
+ calendars: [],
+ calendarInputs: [],
+ clockInputs: [],
+ dismissClockFunc: [],
+ dismissCalendarFunc: [],
+ calendarDivName1: 'calendarbox', // name of calendar
that gets toggled
+ calendarDivName2: 'calendarin', // name of
that contains calendar
+ calendarLinkName: 'calendarlink',// name of the link that is used to toggle
+ clockDivName: 'clockbox', // name of clock
that gets toggled
+ clockLinkName: 'clocklink', // name of the link that is used to toggle
+ shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
+ timezoneWarningClass: 'timezonewarning', // class of the warning for timezone mismatch
+ timezoneOffset: 0,
+ init: function() {
+ var body = document.getElementsByTagName('body')[0];
+ var serverOffset = body.getAttribute('data-admin-utc-offset');
+ if (serverOffset) {
+ var localOffset = new Date().getTimezoneOffset() * -60;
+ DateTimeShortcuts.timezoneOffset = localOffset - serverOffset;
+ }
-var findPosXRel = function(el) {
- $el = yawdadmin.jQuery(el);
- if ($el.closest('.modal, body').prop("tagName").toLowerCase() == 'body') return findPosX(el);
- return $el.position().left;
-}
-
-var findPosYRel = function(el) {
- $el = yawdadmin.jQuery(el);
- $closest = $el.closest('.modal, body');
- if ($closest.prop("tagName").toLowerCase() == 'body') return findPosY(el);
- return $el.position().top + $closest.find('.modal-header').height() + 18;
-}
+ var inputs = document.getElementsByTagName('input');
+ for (var i = 0; i < inputs.length; i++) {
+ var inp = inputs[i];
+ if (inp.getAttribute('type') === 'text' && inp.className.match(/vTimeField/)) {
+ DateTimeShortcuts.addClock(inp);
+ DateTimeShortcuts.addTimezoneWarning(inp);
+ }
+ else if (inp.getAttribute('type') === 'text' && inp.className.match(/vDateField/)) {
+ DateTimeShortcuts.addCalendar(inp);
+ DateTimeShortcuts.addTimezoneWarning(inp);
+ }
+ }
+ },
+ // Return the current time while accounting for the server timezone.
+ now: function() {
+ var body = document.getElementsByTagName('body')[0];
+ var serverOffset = body.getAttribute('data-admin-utc-offset');
+ if (serverOffset) {
+ var localNow = new Date();
+ var localOffset = localNow.getTimezoneOffset() * -60;
+ localNow.setTime(localNow.getTime() + 1000 * (serverOffset - localOffset));
+ return localNow;
+ } else {
+ return new Date();
+ }
+ },
+ // Add a warning when the time zone in the browser and backend do not match.
+ addTimezoneWarning: function(inp) {
+ var $ = yawdadmin.jQuery;
+ var warningClass = DateTimeShortcuts.timezoneWarningClass;
+ var timezoneOffset = DateTimeShortcuts.timezoneOffset / 3600;
+ // Only warn if there is a time zone mismatch.
+ if (!timezoneOffset) {
+ return;
+ }
-var DateTimeShortcuts = {
- calendars: [],
- calendarInputs: [],
- clockInputs: [],
- calendarDivName1: 'calendarbox', // name of calendar
that gets toggled
- calendarDivName2: 'calendarin', // name of
that contains calendar
- calendarLinkName: 'calendarlink',// name of the link that is used to toggle
- clockDivName: 'clockbox', // name of clock
that gets toggled
- clockLinkName: 'clocklink', // name of the link that is used to toggle
- shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts
- admin_media_prefix: '',
- init: function() {
- // Get admin_media_prefix by grabbing it off the window object. It's
- // set in the admin/base.html template, so if it's not there, someone's
- // overridden the template. In that case, we'll set a clearly-invalid
- // value in the hopes that someone will examine HTTP requests and see it.
- if (window.__admin_media_prefix__ != undefined) {
- DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__;
- } else {
- DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/';
- }
+ // Check if warning is already there.
+ if ($(inp).siblings('.' + warningClass).length) {
+ return;
+ }
- var inputs = document.getElementsByTagName('input');
- for (i=0; i 0) {
+ message = ngettext(
+ 'Note: You are %s hour ahead of server time.',
+ 'Note: You are %s hours ahead of server time.',
+ timezoneOffset
+ );
}
- else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
- DateTimeShortcuts.addCalendar(inp);
+ else {
+ timezoneOffset *= -1;
+ message = ngettext(
+ 'Note: You are %s hour behind server time.',
+ 'Note: You are %s hours behind server time.',
+ timezoneOffset
+ );
}
- }
- },
- // Add clock widget to a given field
- addClock: function(inp) {
- var num = DateTimeShortcuts.clockInputs.length;
- DateTimeShortcuts.clockInputs[num] = inp;
+ message = interpolate(message, [timezoneOffset]);
+
+ var $warning = $('');
+ $warning.attr('class', warningClass);
+ $warning.text(message);
+
+ $(inp).parent()
+ .append($(' '))
+ .append($warning);
+ },
+ // Add clock widget to a given field
+ addClock: function(inp) {
+ var num = DateTimeShortcuts.clockInputs.length;
+ DateTimeShortcuts.clockInputs[num] = inp;
+ DateTimeShortcuts.dismissClockFunc[num] = function() { DateTimeShortcuts.dismissClock(num); return true; };
+
+ // Shortcut links (clock icon and "Now" link)
+ var shortcuts_span = document.createElement('span');
+ shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
+ inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
+ var now_link = document.createElement('a');
+ now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", -1);");
+ now_link.appendChild(document.createTextNode(gettext('Now')));
+ var clock_link = document.createElement('a');
+ clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
+ clock_link.id = DateTimeShortcuts.clockLinkName + num;
+ quickElement(
+ 'span', clock_link, '',
+ 'class', 'clock-icon',
+ 'title', gettext('Choose a Time')
+ );
+ shortcuts_span.appendChild(document.createTextNode('\u00A0'));
+ shortcuts_span.appendChild(now_link);
+ shortcuts_span.appendChild(document.createTextNode('\u00A0|\u00A0'));
+ shortcuts_span.appendChild(clock_link);
- // Shortcut links (clock icon and "Now" link)
- var shortcuts_span = document.createElement('span');
- shortcuts_span.className = DateTimeShortcuts.shortCutsClass;
- inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
- var now_link = document.createElement('a');
- now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + get_format('TIME_INPUT_FORMATS')[0] + "'));");
- now_link.appendChild(document.createTextNode(gettext('Now')));
- var clock_link = document.createElement('a');
- clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
- clock_link.id = DateTimeShortcuts.clockLinkName + num;
- quickElement('i', clock_link, '', 'class', 'icon-time');
- shortcuts_span.appendChild(document.createTextNode('\240'));
- shortcuts_span.appendChild(now_link);
- shortcuts_span.appendChild(document.createTextNode('\240|\240'));
- shortcuts_span.appendChild(clock_link);
-
- // Create clock link div
- //
- // Markup looks like:
- //
");
- } else {
- // Otherwise, just insert the remove button as the
- // last child element of the form's container:
- var modal = row.closest('.inline-related-modal');
- var extra_class = (modal.length) ? '' : ' icon-white';
- row.children(":first").append('' + options.deleteText + "");
- }
- // The delete button of each row triggers a bunch of other things
- $(this).find("a." + options.deleteCssClass).click(function(e) {
- e.preventDefault();
- // Remove the parent form containing this button:
- var row = $(this).parents("." + options.formCssClass);
- row.remove();
- nextIndex -= 1;
- // If a post-delete callback was provided, call it with the deleted form:
- if (options.removed) {
- options.removed(row);
- }
- // Update the TOTAL_FORMS form count.
- var forms = $("." + options.formCssClass);
- $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
- // Show add button again once we drop below max
- if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
- addButton.parent().show();
- }
- // Also, update names and ids for all remaining form controls
- // so they remain in sequence:
- for (var i=0, formCount=forms.length; i 0;
- $this.each(function(i) {
- $(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
- });
- if ($this.length && showAddButton) {
- var addButton;
- if ($this.prop("tagName") == "TR") {
- // If forms are laid out as table rows, insert the
- // "add" button in a new table row:
- var numCols = this.eq(-1).children().length;
- $parent.append('
");
+ addButton = $this.filter(":last").next().find("a");
+ }
+ addButton.click(function(e) {
+ e.preventDefault();
+ var template = $("#" + options.prefix + "-empty");
+ var row = template.clone(true);
+ row.removeClass(options.emptyCssClass)
+ .addClass(options.formCssClass)
+ .attr("id", options.prefix + "-" + nextIndex);
+ if (row.is("tr")) {
+ // If the forms are laid out in table rows, insert
+ // the remove button into the last table cell:
+ row.children(":last").append('
+{% endblock %}
\ No newline at end of file
diff --git a/yawdadmin/templates/admin/edit_inline/one-to-one-inline.html b/yawdadmin/templates/admin/edit_inline/one-to-one-inline.html
old mode 100644
new mode 100755
index 62e0b41..915d296
--- a/yawdadmin/templates/admin/edit_inline/one-to-one-inline.html
+++ b/yawdadmin/templates/admin/edit_inline/one-to-one-inline.html
@@ -1,15 +1,13 @@
-{% load i18n admin_static %}
{% if inline_admin_formset.opts.show_title %}
-
{{ inline_admin_formset.opts.verbose_name|title }}{% if inline_admin_formset.opts.collapse %} {% endif %}
+
\ No newline at end of file
diff --git a/yawdadmin/templates/admin/edit_inline/translatable-inline.html b/yawdadmin/templates/admin/edit_inline/translatable-inline.html
old mode 100644
new mode 100755
index b18b662..1c65fae
--- a/yawdadmin/templates/admin/edit_inline/translatable-inline.html
+++ b/yawdadmin/templates/admin/edit_inline/translatable-inline.html
@@ -12,9 +12,9 @@