diff --git a/requirements.txt b/requirements.txt index 09784c4619..630e8435ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,3 @@ Django>=1.5.8,<1.6 Djblets==0.6.14 django-compressor==1.4 django-statici18n==1.1.2 -django-constance[database] diff --git a/thirdpart/constance/__init__.py b/thirdpart/constance/__init__.py new file mode 100644 index 0000000000..db2511f7f4 --- /dev/null +++ b/thirdpart/constance/__init__.py @@ -0,0 +1,13 @@ +from django.utils.functional import LazyObject + +__version__ = '1.0.1' + +default_app_config = 'constance.apps.ConstanceConfig' + + +class LazyConfig(LazyObject): + def _setup(self): + from .base import Config + self._wrapped = Config() + +config = LazyConfig() diff --git a/thirdpart/constance/admin.py b/thirdpart/constance/admin.py new file mode 100644 index 0000000000..6e68ee4342 --- /dev/null +++ b/thirdpart/constance/admin.py @@ -0,0 +1,187 @@ +from datetime import datetime, date, time +from decimal import Decimal +import hashlib +from operator import itemgetter + +from django import forms +from django.contrib import admin, messages +from django.contrib.admin import widgets +from django.contrib.admin.options import csrf_protect_m +from django.core.exceptions import PermissionDenied, ImproperlyConfigured +from django.forms import fields +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template.context import RequestContext +from django.utils import six +from django.utils.formats import localize +from django.utils.translation import ugettext_lazy as _ + +try: + from django.utils.encoding import smart_bytes +except ImportError: + from django.utils.encoding import smart_str as smart_bytes + +try: + from django.conf.urls import patterns, url +except ImportError: # Django < 1.4 + from django.conf.urls.defaults import patterns, url + + +from . import LazyConfig, settings + +config = LazyConfig() + + +NUMERIC_WIDGET = forms.TextInput(attrs={'size': 10}) + +INTEGER_LIKE = (fields.IntegerField, {'widget': NUMERIC_WIDGET}) +STRING_LIKE = (fields.CharField, { + 'widget': forms.Textarea(attrs={'rows': 3}), + 'required': False, +}) + +FIELDS = { + bool: (fields.BooleanField, {'required': False}), + int: INTEGER_LIKE, + Decimal: (fields.DecimalField, {'widget': NUMERIC_WIDGET}), + str: STRING_LIKE, + datetime: (fields.DateTimeField, {'widget': widgets.AdminSplitDateTime}), + date: (fields.DateField, {'widget': widgets.AdminDateWidget}), + time: (fields.TimeField, {'widget': widgets.AdminTimeWidget}), + float: (fields.FloatField, {'widget': NUMERIC_WIDGET}), +} + +if not six.PY3: + FIELDS.update({ + long: INTEGER_LIKE, + unicode: STRING_LIKE, + }) + + +class ConstanceForm(forms.Form): + version = forms.CharField(widget=forms.HiddenInput) + + def __init__(self, initial, *args, **kwargs): + super(ConstanceForm, self).__init__(*args, initial=initial, **kwargs) + version_hash = hashlib.md5() + + for name, (default, help_text) in settings.CONFIG.items(): + config_type = type(default) + if config_type not in FIELDS: + raise ImproperlyConfigured(_("Constance doesn't support " + "config values of the type " + "%(config_type)s. Please fix " + "the value of '%(name)s'.") + % {'config_type': config_type, + 'name': name}) + field_class, kwargs = FIELDS[config_type] + self.fields[name] = field_class(label=name, **kwargs) + + version_hash.update(smart_bytes(initial.get(name, ''))) + self.initial['version'] = version_hash.hexdigest() + + def save(self): + for name in settings.CONFIG: + setattr(config, name, self.cleaned_data[name]) + + def clean_version(self): + value = self.cleaned_data['version'] + if value != self.initial['version']: + raise forms.ValidationError(_('The settings have been modified ' + 'by someone else. Please reload the ' + 'form and resubmit your changes.')) + return value + + +class ConstanceAdmin(admin.ModelAdmin): + + def get_urls(self): + info = self.model._meta.app_label, self.model._meta.module_name + return patterns('', + url(r'^$', + self.admin_site.admin_view(self.changelist_view), + name='%s_%s_changelist' % info), + url(r'^$', + self.admin_site.admin_view(self.changelist_view), + name='%s_%s_add' % info), + ) + + @csrf_protect_m + def changelist_view(self, request, extra_context=None): + # First load a mapping between config name and default value + if not self.has_change_permission(request, None): + raise PermissionDenied + default_initial = ((name, default) + for name, (default, help_text) in settings.CONFIG.items()) + # Then update the mapping with actually values from the backend + initial = dict(default_initial, + **dict(config._backend.mget(settings.CONFIG.keys()))) + form = ConstanceForm(initial=initial) + if request.method == 'POST': + form = ConstanceForm(data=request.POST, initial=initial) + if form.is_valid(): + form.save() + # In django 1.5 this can be replaced with self.message_user + messages.add_message( + request, + messages.SUCCESS, + _('Live settings updated successfully.'), + ) + return HttpResponseRedirect('.') + context = { + 'config': [], + 'title': _('Constance config'), + 'app_label': 'constance', + 'opts': Config._meta, + 'form': form, + 'media': self.media + form.media, + } + for name, (default, help_text) in settings.CONFIG.items(): + # First try to load the value from the actual backend + value = initial.get(name) + # Then if the returned value is None, get the default + if value is None: + value = getattr(config, name) + context['config'].append({ + 'name': name, + 'default': localize(default), + 'help_text': _(help_text), + 'value': localize(value), + 'modified': value != default, + 'form_field': form[name], + }) + context['config'].sort(key=itemgetter('name')) + context_instance = RequestContext(request, + current_app=self.admin_site.name) + return render_to_response('admin/constance/change_list.html', + context, context_instance=context_instance) + + def has_add_permission(self, *args, **kwargs): + return False + + def has_delete_permission(self, *args, **kwargs): + return False + + def has_change_permission(self, request, obj=None): + if settings.SUPERUSER_ONLY: + return request.user.is_superuser + return super(ConstanceAdmin, self).has_change_permission(request, obj) + + +class Config(object): + class Meta(object): + app_label = 'constance' + object_name = 'Config' + model_name = module_name = 'config' + verbose_name_plural = _('config') + get_ordered_objects = lambda x: False + abstract = False + swapped = False + + def get_change_permission(self): + return 'change_%s' % self.model_name + + _meta = Meta() + + +admin.site.register([Config], ConstanceAdmin) diff --git a/thirdpart/constance/apps.py b/thirdpart/constance/apps.py new file mode 100644 index 0000000000..77226f739a --- /dev/null +++ b/thirdpart/constance/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class ConstanceConfig(AppConfig): + name = 'constance' + verbose_name = _('Constance') diff --git a/thirdpart/constance/backends/__init__.py b/thirdpart/constance/backends/__init__.py new file mode 100644 index 0000000000..a64905eb90 --- /dev/null +++ b/thirdpart/constance/backends/__init__.py @@ -0,0 +1,26 @@ +""" +Defines the base constance backend +""" + + +class Backend(object): + + def get(self, key): + """ + Get the key from the backend store and return the value. + Return None if not found. + """ + raise NotImplementedError + + def mget(self, keys): + """ + Get the keys from the backend store and return a list of the values. + Return an empty list if not found. + """ + raise NotImplementedError + + def set(self, key, value): + """ + Add the value to the backend store given the key. + """ + raise NotImplementedError diff --git a/thirdpart/constance/backends/database/__init__.py b/thirdpart/constance/backends/database/__init__.py new file mode 100644 index 0000000000..aa1ceaad2d --- /dev/null +++ b/thirdpart/constance/backends/database/__init__.py @@ -0,0 +1,94 @@ +from django.core.exceptions import ImproperlyConfigured +from django.db.models.signals import post_save +from django.core.cache import get_cache + +try: + from django.core.cache.backends.locmem import LocMemCache +except ImportError: + from django.core.cache.backends.locmem import CacheClass as LocMemCache + +from .. import Backend +from ... import settings + + +class DatabaseBackend(Backend): + def __init__(self): + from .models import Constance + self._model = Constance + self._prefix = settings.DATABASE_PREFIX + self._autofill_timeout = settings.DATABASE_CACHE_AUTOFILL_TIMEOUT + self._autofill_cachekey = 'autofilled' + + if not self._model._meta.installed: + raise ImproperlyConfigured( + "The constance.backends.database app isn't installed " + "correctly. Make sure it's in your INSTALLED_APPS setting.") + + if settings.DATABASE_CACHE_BACKEND: + self._cache = get_cache(settings.DATABASE_CACHE_BACKEND) + if isinstance(self._cache, LocMemCache): + raise ImproperlyConfigured( + "The CONSTANCE_DATABASE_CACHE_BACKEND setting refers to a " + "subclass of Django's local-memory backend (%r). Please set " + "it to a backend that supports cross-process caching." + % settings.DATABASE_CACHE_BACKEND) + else: + self._cache = None + self.autofill() + # Clear simple cache. + post_save.connect(self.clear, sender=self._model) + + def add_prefix(self, key): + return "%s%s" % (self._prefix, key) + + def autofill(self): + if not self._autofill_timeout or not self._cache: + return + full_cachekey = self.add_prefix(self._autofill_cachekey) + if self._cache.get(full_cachekey): + return + autofill_values = {} + autofill_values[full_cachekey] = 1 + for key, value in self.mget(settings.CONFIG.keys()): + autofill_values[self.add_prefix(key)] = value + self._cache.set_many(autofill_values, timeout=self._autofill_timeout) + + def mget(self, keys): + if not keys: + return + keys = dict((self.add_prefix(key), key) for key in keys) + stored = self._model._default_manager.filter(key__in=keys.keys()) + for const in stored: + yield keys[const.key], const.value + + def get(self, key): + key = self.add_prefix(key) + if self._cache: + value = self._cache.get(key) + else: + value = None + if value is None: + try: + value = self._model._default_manager.get(key=key).value + except self._model.DoesNotExist: + pass + else: + if self._cache: + self._cache.add(key, value) + return value + + def set(self, key, value): + constance, created = self._model._default_manager.get_or_create( + key=self.add_prefix(key), defaults={'value': value} + ) + if not created: + constance.value = value + constance.save() + + def clear(self, sender, instance, created, **kwargs): + if self._cache and not created: + keys = [self.add_prefix(k) + for k in settings.CONFIG.keys()] + keys.append(self.add_prefix(self._autofill_cachekey)) + self._cache.delete_many(keys) + self.autofill() diff --git a/thirdpart/constance/backends/database/migrations/0001_initial.py b/thirdpart/constance/backends/database/migrations/0001_initial.py new file mode 100644 index 0000000000..e0ebd996c8 --- /dev/null +++ b/thirdpart/constance/backends/database/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import picklefield.fields + + +class Migration(migrations.Migration): + dependencies = [] + + operations = [ + migrations.CreateModel( + name='Constance', + fields=[ + ('id', models.AutoField(verbose_name='ID', primary_key=True, + auto_created=True, serialize=False)), + ('key', models.CharField(unique=True, max_length=255)), + ('value', picklefield.fields.PickledObjectField(editable=False)), + ], + options={ + 'verbose_name': 'constance', + 'verbose_name_plural': 'constances', + 'db_table': 'constance_config', + }, + bases=(models.Model,), + ), + ] diff --git a/thirdpart/constance/backends/database/migrations/__init__.py b/thirdpart/constance/backends/database/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/thirdpart/constance/backends/database/models.py b/thirdpart/constance/backends/database/models.py new file mode 100644 index 0000000000..e9964e8370 --- /dev/null +++ b/thirdpart/constance/backends/database/models.py @@ -0,0 +1,24 @@ +from django.db import models +from django.core.exceptions import ImproperlyConfigured + +from django.utils.translation import ugettext_lazy as _ + +try: + from picklefield import PickledObjectField +except ImportError: + raise ImproperlyConfigured("Couldn't find the the 3rd party app " + "django-picklefield which is required for " + "the constance database backend.") + + +class Constance(models.Model): + key = models.CharField(max_length=255, unique=True) + value = PickledObjectField() + + class Meta: + verbose_name = _('constance') + verbose_name_plural = _('constances') + db_table = 'constance_config' + + def __unicode__(self): + return self.key diff --git a/thirdpart/constance/backends/database/south_migrations/0001_initial.py b/thirdpart/constance/backends/database/south_migrations/0001_initial.py new file mode 100644 index 0000000000..f9b747327e --- /dev/null +++ b/thirdpart/constance/backends/database/south_migrations/0001_initial.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +from south.db import db +from south.v2 import SchemaMigration + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Constance' + db.create_table('constance_config', ( + ('id', self.gf('django.db.models.fields.AutoField')( + primary_key=True)), + ('key', self.gf('django.db.models.fields.TextField')()), + ('value', self.gf('picklefield.fields.PickledObjectField')()), + )) + db.send_create_signal('database', ['Constance']) + + def backwards(self, orm): + # Deleting model 'Constance' + db.delete_table('constance_config') + + models = { + 'database.constance': { + 'Meta': {'object_name': 'Constance', + 'db_table': "'constance_config'"}, + 'id': ('django.db.models.fields.AutoField', [], + {'primary_key': 'True'}), + 'key': ('django.db.models.fields.TextField', [], {}), + 'value': ('picklefield.fields.PickledObjectField', [], {}) + } + } + + complete_apps = ['database'] diff --git a/thirdpart/constance/backends/database/south_migrations/0002_auto__chg_field_constance_key__add_unique_constance_key.py b/thirdpart/constance/backends/database/south_migrations/0002_auto__chg_field_constance_key__add_unique_constance_key.py new file mode 100644 index 0000000000..ef8fb3ce72 --- /dev/null +++ b/thirdpart/constance/backends/database/south_migrations/0002_auto__chg_field_constance_key__add_unique_constance_key.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from south.db import db +from south.v2 import SchemaMigration + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Changing field 'Constance.key' + db.alter_column('constance_config', 'key', + self.gf('django.db.models.fields.CharField')( + max_length=255)) + # Adding unique constraint on 'Constance', fields ['key'] + db.create_unique('constance_config', ['key']) + + def backwards(self, orm): + # Removing unique constraint on 'Constance', fields ['key'] + db.delete_unique('constance_config', ['key']) + + # Changing field 'Constance.key' + db.alter_column('constance_config', 'key', + self.gf('django.db.models.fields.TextField')()) + + models = { + 'database.constance': { + 'Meta': {'object_name': 'Constance', + 'db_table': "'constance_config'"}, + 'id': ('django.db.models.fields.AutoField', [], + {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], + {'unique': 'True', 'max_length': '255'}), + 'value': ('picklefield.fields.PickledObjectField', [], {}) + } + } + + complete_apps = ['database'] diff --git a/thirdpart/constance/backends/database/south_migrations/__init__.py b/thirdpart/constance/backends/database/south_migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/thirdpart/constance/backends/redisd.py b/thirdpart/constance/backends/redisd.py new file mode 100644 index 0000000000..abf24f4606 --- /dev/null +++ b/thirdpart/constance/backends/redisd.py @@ -0,0 +1,51 @@ +from django.core.exceptions import ImproperlyConfigured +from django.utils import six +from django.utils.six.moves import zip + +from . import Backend +from .. import settings, utils + +try: + from cPickle import loads, dumps +except ImportError: + from pickle import loads, dumps + + +class RedisBackend(Backend): + + def __init__(self): + super(RedisBackend, self).__init__() + self._prefix = settings.REDIS_PREFIX + connection_cls = settings.REDIS_CONNECTION_CLASS + if connection_cls is not None: + self._rd = utils.import_module_attr(connection_cls)() + else: + try: + import redis + except ImportError: + raise ImproperlyConfigured( + "The Redis backend requires redis-py to be installed.") + if isinstance(settings.REDIS_CONNECTION, six.string_types): + self._rd = redis.from_url(settings.REDIS_CONNECTION) + else: + self._rd = redis.Redis(**settings.REDIS_CONNECTION) + + def add_prefix(self, key): + return "%s%s" % (self._prefix, key) + + def get(self, key): + value = self._rd.get(self.add_prefix(key)) + if value: + return loads(value) + return None + + def mget(self, keys): + if not keys: + return + prefixed_keys = [self.add_prefix(key) for key in keys] + for key, value in zip(keys, self._rd.mget(prefixed_keys)): + if value: + yield key, loads(value) + + def set(self, key, value): + self._rd.set(self.add_prefix(key), dumps(value)) diff --git a/thirdpart/constance/base.py b/thirdpart/constance/base.py new file mode 100644 index 0000000000..99afe62935 --- /dev/null +++ b/thirdpart/constance/base.py @@ -0,0 +1,30 @@ +from . import settings, utils + + +class Config(object): + """ + The global config wrapper that handles the backend. + """ + def __init__(self): + super(Config, self).__setattr__('_backend', + utils.import_module_attr(settings.BACKEND)()) + + def __getattr__(self, key): + try: + default, help_text = settings.CONFIG[key] + except KeyError: + raise AttributeError(key) + result = self._backend.get(key) + if result is None: + result = default + setattr(self, key, default) + return result + return result + + def __setattr__(self, key, value): + if key not in settings.CONFIG: + raise AttributeError(key) + self._backend.set(key, value) + + def __dir__(self): + return settings.CONFIG.keys() diff --git a/thirdpart/constance/context_processors.py b/thirdpart/constance/context_processors.py new file mode 100644 index 0000000000..3a3da33d7b --- /dev/null +++ b/thirdpart/constance/context_processors.py @@ -0,0 +1,15 @@ +import constance + + +def config(request): + """ + Simple context processor that puts the config into every + RequestContext. Just make sure you have a setting like this: + + TEMPLATE_CONTEXT_PROCESSORS = ( + # ... + 'constance.context_processors.config', + ) + + """ + return {"config": constance.config} diff --git a/thirdpart/constance/locale/cs_CZ/LC_MESSAGES/django.po b/thirdpart/constance/locale/cs_CZ/LC_MESSAGES/django.po new file mode 100644 index 0000000000..a08da79165 --- /dev/null +++ b/thirdpart/constance/locale/cs_CZ/LC_MESSAGES/django.po @@ -0,0 +1,79 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: django-constance\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-27 19:05+0100\n" +"PO-Revision-Date: 2014-11-27 18:13+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/django-constance/language/cs_CZ/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: cs_CZ\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" + +#: admin.py:72 +#, python-format +msgid "" +"Constance doesn't support config values of the type %(config_type)s. Please " +"fix the value of '%(name)s'." +msgstr "" + +#: admin.py:91 +msgid "" +"The settings have been modified by someone else. Please reload the form and " +"resubmit your changes." +msgstr "" + +#: admin.py:129 +msgid "Live settings updated successfully." +msgstr "Nastavení bylo úspěšně uloženo." + +#: admin.py:134 +msgid "Constance config" +msgstr "Nastavení konstant" + +#: admin.py:177 +msgid "config" +msgstr "nastavení" + +#: apps.py:9 +msgid "Constance" +msgstr "" + +#: backends/database/models.py:19 +msgid "constance" +msgstr "konstanta" + +#: backends/database/models.py:20 +msgid "constances" +msgstr "konstanty" + +#: templates/admin/constance/change_list.html:50 +msgid "Name" +msgstr "Název" + +#: templates/admin/constance/change_list.html:51 +msgid "Default" +msgstr "Výchozí hodnota" + +#: templates/admin/constance/change_list.html:52 +msgid "Value" +msgstr "Hodnota" + +#: templates/admin/constance/change_list.html:53 +msgid "Is modified" +msgstr "Je změněna?" + +#: templates/admin/constance/change_list.html:79 +msgid "Save" +msgstr "Uložit" + +#: templates/admin/constance/change_list.html:89 +msgid "Home" +msgstr "Domů" diff --git a/thirdpart/constance/locale/de/LC_MESSAGES/django.po b/thirdpart/constance/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000000..e7959df548 --- /dev/null +++ b/thirdpart/constance/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,80 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Jannis Leidel <jannis@leidel.info>, 2014 +msgid "" +msgstr "" +"Project-Id-Version: django-constance\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-27 19:05+0100\n" +"PO-Revision-Date: 2014-11-27 18:17+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: German (http://www.transifex.com/projects/p/django-constance/language/de/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: admin.py:72 +#, python-format +msgid "" +"Constance doesn't support config values of the type %(config_type)s. Please " +"fix the value of '%(name)s'." +msgstr "Konstanze unterstützt die Konfigurationswerte vom Typ %(config_type)s nicht. Bitte den Ausgangswert von '%(name)s' ändern." + +#: admin.py:91 +msgid "" +"The settings have been modified by someone else. Please reload the form and " +"resubmit your changes." +msgstr "Die Konfiguration wurde seit Öffnen dieser Seite verändert. Bitte die Seite neuladen und die Änderungen erneut vornehmen." + +#: admin.py:129 +msgid "Live settings updated successfully." +msgstr "Die Livekonfiguration wurde erfolgreich aktualisiert." + +#: admin.py:134 +msgid "Constance config" +msgstr "Constance Konfiguration" + +#: admin.py:177 +msgid "config" +msgstr "Konfiguration" + +#: apps.py:9 +msgid "Constance" +msgstr "Konstanze" + +#: backends/database/models.py:19 +msgid "constance" +msgstr "Konstanze" + +#: backends/database/models.py:20 +msgid "constances" +msgstr "Konstanzes" + +#: templates/admin/constance/change_list.html:50 +msgid "Name" +msgstr "Name" + +#: templates/admin/constance/change_list.html:51 +msgid "Default" +msgstr "Voreinstellung" + +#: templates/admin/constance/change_list.html:52 +msgid "Value" +msgstr "Wert" + +#: templates/admin/constance/change_list.html:53 +msgid "Is modified" +msgstr "Ist modifiziert" + +#: templates/admin/constance/change_list.html:79 +msgid "Save" +msgstr "Sichern" + +#: templates/admin/constance/change_list.html:89 +msgid "Home" +msgstr "Start" diff --git a/thirdpart/constance/locale/en/LC_MESSAGES/django.po b/thirdpart/constance/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000000..1bb6893da6 --- /dev/null +++ b/thirdpart/constance/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,79 @@ +# 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 <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-27 19:05+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:72 +#, python-format +msgid "" +"Constance doesn't support config values of the type %(config_type)s. Please " +"fix the value of '%(name)s'." +msgstr "" + +#: admin.py:91 +msgid "" +"The settings have been modified by someone else. Please reload the form and " +"resubmit your changes." +msgstr "" + +#: admin.py:129 +msgid "Live settings updated successfully." +msgstr "" + +#: admin.py:134 +msgid "Constance config" +msgstr "" + +#: admin.py:177 +msgid "config" +msgstr "" + +#: apps.py:9 +msgid "Constance" +msgstr "" + +#: backends/database/models.py:19 +msgid "constance" +msgstr "" + +#: backends/database/models.py:20 +msgid "constances" +msgstr "" + +#: templates/admin/constance/change_list.html:50 +msgid "Name" +msgstr "" + +#: templates/admin/constance/change_list.html:51 +msgid "Default" +msgstr "" + +#: templates/admin/constance/change_list.html:52 +msgid "Value" +msgstr "" + +#: templates/admin/constance/change_list.html:53 +msgid "Is modified" +msgstr "" + +#: templates/admin/constance/change_list.html:79 +msgid "Save" +msgstr "" + +#: templates/admin/constance/change_list.html:89 +msgid "Home" +msgstr "" diff --git a/thirdpart/constance/locale/it/LC_MESSAGES/django.po b/thirdpart/constance/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000000..f92dea6d1a --- /dev/null +++ b/thirdpart/constance/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,79 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: django-constance\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-27 19:05+0100\n" +"PO-Revision-Date: 2014-11-27 18:13+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: Italian (http://www.transifex.com/projects/p/django-constance/language/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: admin.py:72 +#, python-format +msgid "" +"Constance doesn't support config values of the type %(config_type)s. Please " +"fix the value of '%(name)s'." +msgstr "" + +#: admin.py:91 +msgid "" +"The settings have been modified by someone else. Please reload the form and " +"resubmit your changes." +msgstr "" + +#: admin.py:129 +msgid "Live settings updated successfully." +msgstr "Le impostazioni attive sono state aggiornate correttamente." + +#: admin.py:134 +msgid "Constance config" +msgstr "Configurazione Impostazioni" + +#: admin.py:177 +msgid "config" +msgstr "" + +#: apps.py:9 +msgid "Constance" +msgstr "" + +#: backends/database/models.py:19 +msgid "constance" +msgstr "impostazione" + +#: backends/database/models.py:20 +msgid "constances" +msgstr "impostazioni" + +#: templates/admin/constance/change_list.html:50 +msgid "Name" +msgstr "Nome" + +#: templates/admin/constance/change_list.html:51 +msgid "Default" +msgstr "Default" + +#: templates/admin/constance/change_list.html:52 +msgid "Value" +msgstr "Valore" + +#: templates/admin/constance/change_list.html:53 +msgid "Is modified" +msgstr "Modificato" + +#: templates/admin/constance/change_list.html:79 +msgid "Save" +msgstr "Salva" + +#: templates/admin/constance/change_list.html:89 +msgid "Home" +msgstr "Inizio" diff --git a/thirdpart/constance/locale/pl/LC_MESSAGES/django.po b/thirdpart/constance/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000000..0c56643e6a --- /dev/null +++ b/thirdpart/constance/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,79 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: django-constance\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-27 19:05+0100\n" +"PO-Revision-Date: 2014-11-27 18:13+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: Polish (http://www.transifex.com/projects/p/django-constance/language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: admin.py:72 +#, python-format +msgid "" +"Constance doesn't support config values of the type %(config_type)s. Please " +"fix the value of '%(name)s'." +msgstr "" + +#: admin.py:91 +msgid "" +"The settings have been modified by someone else. Please reload the form and " +"resubmit your changes." +msgstr "" + +#: admin.py:129 +msgid "Live settings updated successfully." +msgstr "Parametry zostały zaktualizowane" + +#: admin.py:134 +msgid "Constance config" +msgstr "Konfiguracja Constance" + +#: admin.py:177 +msgid "config" +msgstr "" + +#: apps.py:9 +msgid "Constance" +msgstr "" + +#: backends/database/models.py:19 +msgid "constance" +msgstr "parametr" + +#: backends/database/models.py:20 +msgid "constances" +msgstr "parametry" + +#: templates/admin/constance/change_list.html:50 +msgid "Name" +msgstr "Nazwa" + +#: templates/admin/constance/change_list.html:51 +msgid "Default" +msgstr "Domyślnie" + +#: templates/admin/constance/change_list.html:52 +msgid "Value" +msgstr "Wartość" + +#: templates/admin/constance/change_list.html:53 +msgid "Is modified" +msgstr "Zmodyfikowana" + +#: templates/admin/constance/change_list.html:79 +msgid "Save" +msgstr "Zapisz" + +#: templates/admin/constance/change_list.html:89 +msgid "Home" +msgstr "Początek" diff --git a/thirdpart/constance/locale/ru/LC_MESSAGES/django.po b/thirdpart/constance/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000000..23bcc0d218 --- /dev/null +++ b/thirdpart/constance/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,79 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: django-constance\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-27 19:05+0100\n" +"PO-Revision-Date: 2014-11-27 18:13+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: Russian (http://www.transifex.com/projects/p/django-constance/language/ru/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ru\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: admin.py:72 +#, python-format +msgid "" +"Constance doesn't support config values of the type %(config_type)s. Please " +"fix the value of '%(name)s'." +msgstr "" + +#: admin.py:91 +msgid "" +"The settings have been modified by someone else. Please reload the form and " +"resubmit your changes." +msgstr "" + +#: admin.py:129 +msgid "Live settings updated successfully." +msgstr "Настройки успешно сохранены" + +#: admin.py:134 +msgid "Constance config" +msgstr "Настройки" + +#: admin.py:177 +msgid "config" +msgstr "настройки" + +#: apps.py:9 +msgid "Constance" +msgstr "" + +#: backends/database/models.py:19 +msgid "constance" +msgstr "настройки" + +#: backends/database/models.py:20 +msgid "constances" +msgstr "настройки" + +#: templates/admin/constance/change_list.html:50 +msgid "Name" +msgstr "Название" + +#: templates/admin/constance/change_list.html:51 +msgid "Default" +msgstr "По умолчанию" + +#: templates/admin/constance/change_list.html:52 +msgid "Value" +msgstr "Текущее значение" + +#: templates/admin/constance/change_list.html:53 +msgid "Is modified" +msgstr "Было изменено" + +#: templates/admin/constance/change_list.html:79 +msgid "Save" +msgstr "Сохранить" + +#: templates/admin/constance/change_list.html:89 +msgid "Home" +msgstr "Главная" diff --git a/thirdpart/constance/locale/zh_CN/LC_MESSAGES/django.po b/thirdpart/constance/locale/zh_CN/LC_MESSAGES/django.po new file mode 100644 index 0000000000..c469e1ef35 --- /dev/null +++ b/thirdpart/constance/locale/zh_CN/LC_MESSAGES/django.po @@ -0,0 +1,79 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +msgid "" +msgstr "" +"Project-Id-Version: django-constance\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-27 19:05+0100\n" +"PO-Revision-Date: 2014-11-27 18:13+0000\n" +"Last-Translator: Jannis Leidel <jannis@leidel.info>\n" +"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/django-constance/language/zh_CN/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_CN\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: admin.py:72 +#, python-format +msgid "" +"Constance doesn't support config values of the type %(config_type)s. Please " +"fix the value of '%(name)s'." +msgstr "" + +#: admin.py:91 +msgid "" +"The settings have been modified by someone else. Please reload the form and " +"resubmit your changes." +msgstr "" + +#: admin.py:129 +msgid "Live settings updated successfully." +msgstr "成功更新实时配置" + +#: admin.py:134 +msgid "Constance config" +msgstr "常量配置" + +#: admin.py:177 +msgid "config" +msgstr "配置" + +#: apps.py:9 +msgid "Constance" +msgstr "" + +#: backends/database/models.py:19 +msgid "constance" +msgstr "常量" + +#: backends/database/models.py:20 +msgid "constances" +msgstr "常量" + +#: templates/admin/constance/change_list.html:50 +msgid "Name" +msgstr "名称" + +#: templates/admin/constance/change_list.html:51 +msgid "Default" +msgstr "默认值" + +#: templates/admin/constance/change_list.html:52 +msgid "Value" +msgstr "值" + +#: templates/admin/constance/change_list.html:53 +msgid "Is modified" +msgstr "是否修改" + +#: templates/admin/constance/change_list.html:79 +msgid "Save" +msgstr "保存" + +#: templates/admin/constance/change_list.html:89 +msgid "Home" +msgstr "首页" diff --git a/thirdpart/constance/models.py b/thirdpart/constance/models.py new file mode 100644 index 0000000000..e341795b26 --- /dev/null +++ b/thirdpart/constance/models.py @@ -0,0 +1,24 @@ +from django.db.models import signals + + +def create_perm(app, created_models, verbosity, db, **kwargs): + """ + Creates a fake content type and permission + to be able to check for permissions + """ + from django.contrib.auth.models import Permission + from django.contrib.contenttypes.models import ContentType + + if ContentType._meta.installed and Permission._meta.installed: + content_type, created = ContentType.objects.get_or_create( + name='config', + app_label='constance', + model='config') + + permission, created = Permission.objects.get_or_create( + name='Can change config', + content_type=content_type, + codename='change_config') + + +signals.post_syncdb.connect(create_perm, dispatch_uid="constance.create_perm") diff --git a/thirdpart/constance/settings.py b/thirdpart/constance/settings.py new file mode 100644 index 0000000000..6f1350d452 --- /dev/null +++ b/thirdpart/constance/settings.py @@ -0,0 +1,24 @@ +from django.conf import settings + +BACKEND = getattr(settings, 'CONSTANCE_BACKEND', + 'constance.backends.redisd.RedisBackend') + +CONFIG = getattr(settings, 'CONSTANCE_CONFIG', {}) + +DATABASE_CACHE_BACKEND = getattr(settings, 'CONSTANCE_DATABASE_CACHE_BACKEND', + None) + +DATABASE_CACHE_AUTOFILL_TIMEOUT = getattr(settings, + 'CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT', + 60 * 60 * 24) + +DATABASE_PREFIX = getattr(settings, 'CONSTANCE_DATABASE_PREFIX', '') + +REDIS_PREFIX = getattr(settings, 'CONSTANCE_REDIS_PREFIX', 'constance:') + +REDIS_CONNECTION_CLASS = getattr(settings, 'CONSTANCE_REDIS_CONNECTION_CLASS', + None) + +REDIS_CONNECTION = getattr(settings, 'CONSTANCE_REDIS_CONNECTION', {}) + +SUPERUSER_ONLY = getattr(settings, 'CONSTANCE_SUPERUSER_ONLY', True) diff --git a/thirdpart/constance/templates/admin/constance/change_list.html b/thirdpart/constance/templates/admin/constance/change_list.html new file mode 100644 index 0000000000..27e792cce6 --- /dev/null +++ b/thirdpart/constance/templates/admin/constance/change_list.html @@ -0,0 +1,100 @@ +{% extends "admin/base_site.html" %} +{% load admin_static admin_list i18n %} +{% load url from future %} + + +{% block extrastyle %} + {{ block.super }} + <link rel="stylesheet" type="text/css" href="{% static 'admin/css/changelists.css' %}" /> + <link rel="stylesheet" type="text/css" href="{% static 'admin/css/forms.css' %}" /> + {{ media.css }} + <style> + #result_list .changed { + background-color: #ffc; + } + #changelist table thead th .text { + padding: 2px 5px; + } + #changelist table tbody td:first-child { + text-align: left; + } + #changelist-form ul.errorlist { + margin: 0 !important; + } + .help { + font-weight: normal !important; + } + </style> +{% endblock %} + +{% block extrahead %} +{% url 'admin:jsi18n' as jsi18nurl %} +<script type="text/javascript" src="{{ jsi18nurl|default:'../../jsi18n/' }}"></script> +{{ block.super }} +{{ media.js }} +{% endblock %} + +{% block bodyclass %}change-list{% endblock %} + +{% block content %} + <div id="content-main"> + <div class="module" id="changelist"> + <form id="changelist-form" action="" method="post">{% csrf_token %} + {% if form.errors %} + <ul class="errorlist"> + {% endif %} + {% for field in form.hidden_fields %} + {% for error in field.errors %} + <li>{{ error }}</li> + {% endfor %} + {{ field }} + {% endfor %} + {% if form.errors %} + </ul> + {% endif %} + <table cellspacing="0" id="result_list"> + <thead> + <tr> + <th><div class="text">{% trans "Name" %}</div></th> + <th><div class="text">{% trans "Default" %}</div></th> + <th><div class="text">{% trans "Value" %}</div></th> + <th><div class="text">{% trans "Is modified" %}</div></th> + </tr> + </thead> + {% for item in config %} + <tr class="{% cycle 'row1' 'row2' %}"> + <th>{{ item.name }} + <div class="help">{{ item.help_text }}</div> + </th> + <td> + {{ item.default }} + </td> + <td> + {{ item.form_field.errors }} + {{ item.form_field }} + </td> + <td> + {% if item.modified %} + <img src="{% static 'admin/img/icon-yes.gif' %}" alt="{{ item.modified }}" /> + {% else %} + <img src="{% static 'admin/img/icon-no.gif' %}" alt="{{ item.modified }}" /> + {% endif %} + </td> + </tr> + {% endfor %} + </table> + <p class="paginator"> + <input type="submit" name="_save" class="default" value="{% trans 'Save' %}"/> + </p> + </form> + </div> + </div> +{% endblock %} + +{% block breadcrumbs %} +<div class="breadcrumbs"> +<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> +› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst|escape }}</a> +› {{ opts.verbose_name_plural|capfirst }} +</div> +{% endblock %} diff --git a/thirdpart/constance/utils.py b/thirdpart/constance/utils.py new file mode 100644 index 0000000000..96fc572e74 --- /dev/null +++ b/thirdpart/constance/utils.py @@ -0,0 +1,6 @@ +from django.utils.importlib import import_module + + +def import_module_attr(path): + package, module = path.rsplit('.', 1) + return getattr(import_module(package), module)