diff options
Diffstat (limited to 'Pootle-2.0.0/external_apps/djblets/siteconfig')
9 files changed, 586 insertions, 0 deletions
diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/__init__.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/__init__.py diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/admin.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/admin.py new file mode 100644 index 0000000..e03cdcd --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/admin.py @@ -0,0 +1,35 @@ +# +# djblets/siteconfig/admin.py +# +# Copyright (c) 2008 Christian Hammond +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +from django.contrib import admin + +from djblets.siteconfig.models import SiteConfiguration + + +class SiteConfigurationAdmin(admin.ModelAdmin): + list_display = ('site', 'version') + + +admin.site.register(SiteConfiguration, SiteConfigurationAdmin) diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/context_processors.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/context_processors.py new file mode 100644 index 0000000..a8f846d --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/context_processors.py @@ -0,0 +1,36 @@ +# +# djblets/siteconfig/context_processors.py +# +# Copyright (c) 2008 Christian Hammond +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +from djblets.siteconfig.models import SiteConfiguration + + +def siteconfig(request): + """ + Exposes the site configuration as a siteconfig variable in templates. + """ + try: + return {'siteconfig': SiteConfiguration.objects.get_current()} + except: + return {'siteconfig': None} diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/django_settings.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/django_settings.py new file mode 100644 index 0000000..a38c573 --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/django_settings.py @@ -0,0 +1,170 @@ +# +# djblets/siteconfig/django_settings.py +# +# Copyright (c) 2008 Christian Hammond +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +import os +import time + +from django.conf import settings + + +locale_settings_map = { + 'locale_timezone': { 'key': 'TIME_ZONE', + 'deserialize_func': str }, + 'locale_language_code': 'LANGUAGE_CODE', + 'locale_date_format': 'DATE_FORMAT', + 'locale_datetime_format': 'DATETIME_FORMAT', + 'locale_default_charset': { 'key': 'DEFAULT_CHARSET', + 'deserialize_func': str }, + 'locale_language_code': 'LANGUAGE_CODE', + 'locale_month_day_format': 'MONTH_DAY_FORMAT', + 'locale_time_format': 'TIME_FORMAT', + 'locale_year_month_format': 'YEAR_MONTH_FORMAT', +} + +mail_settings_map = { + 'mail_server_address': 'SERVER_EMAIL', + 'mail_default_from': 'DEFAULT_FROM_EMAIL', + 'mail_host': 'EMAIL_HOST', + 'mail_port': 'EMAIL_PORT', + 'mail_host_user': 'EMAIL_HOST_USER', + 'mail_host_password': 'EMAIL_HOST_PASSWORD', + 'mail_use_tls': 'EMAIL_USE_TLS', +} + +site_settings_map = { + 'site_media_root': 'MEDIA_ROOT', + 'site_media_url': 'MEDIA_URL', + 'site_prepend_www': 'PREPEND_WWW', + 'site_upload_temp_dir': 'FILE_UPLOAD_TEMP_DIR', + 'site_upload_max_memory_size': 'FILE_UPLOAD_MAX_MEMORY_SIZE', +} + +cache_settings_map = { + 'cache_backend': 'CACHE_BACKEND', + 'cache_expiration_time': 'CACHE_EXPIRATION_TIME', +} + + +# Don't build unless we need it. +_django_settings_map = {} + + +def get_django_settings_map(): + """ + Returns the settings map for all Django settings that users may need + to customize. + """ + if not _django_settings_map: + _django_settings_map.update(locale_settings_map) + _django_settings_map.update(mail_settings_map) + _django_settings_map.update(site_settings_map) + _django_settings_map.update(cache_settings_map) + + return _django_settings_map + + +def generate_defaults(settings_map): + """ + Utility function to generate a defaults mapping. + """ + defaults = {} + + for siteconfig_key, setting_data in settings_map.iteritems(): + if isinstance(setting_data, dict): + setting_key = setting_data['key'] + else: + setting_key = setting_data + + if hasattr(settings, setting_key): + defaults[siteconfig_key] = getattr(settings, setting_key) + + return defaults + + +def get_locale_defaults(): + """ + Returns the locale-related Django defaults that projects may want to + let users customize. + """ + return generate_defaults(locale_settings_map) + + +def get_mail_defaults(): + """ + Returns the mail-related Django defaults that projects may want to + let users customize. + """ + return generate_defaults(mail_settings_map) + + +def get_site_defaults(): + """ + Returns the site-related Django defaults that projects may want to + let users customize. + """ + return generate_defaults(site_settings_map) + + +def get_cache_defaults(): + """ + Returns the cache-related Django defaults that projects may want to + let users customize. + """ + return generate_defaults(cache_settings_map) + + +def get_django_defaults(): + """ + Returns all Django defaults that projects may want to let users customize. + """ + return generate_defaults(get_django_settings_map()) + + +def apply_django_settings(siteconfig, settings_map=None): + """ + Applies all settings from the site configuration to the Django settings + object. + """ + if settings_map is None: + settings_map = get_django_settings_map() + + for key, setting_data in settings_map.iteritems(): + if key in siteconfig.settings: + value = siteconfig.get(key) + + if isinstance(setting_data, dict): + setting_key = setting_data['key'] + + if ('deserialize_func' in setting_data and + callable(setting_data['deserialize_func'])): + value = setting_data['deserialize_func'](value) + else: + setting_key = setting_data + + setattr(settings, setting_key, value) + + if hasattr(time, 'tzset'): + os.environ['TZ'] = settings.TIME_ZONE + time.tzset() diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/forms.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/forms.py new file mode 100644 index 0000000..ec75863 --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/forms.py @@ -0,0 +1,76 @@ +# +# djblets/siteconfig/forms.py +# +# Copyright (c) 2008 Christian Hammond +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +from django import forms + + +class SiteSettingsForm(forms.Form): + """ + A base form for loading/saving settings for a SiteConfiguration. This is + meant to be subclassed for different settings pages. Any fields defined + by the form will be loaded/saved automatically. + """ + def __init__(self, siteconfig, *args, **kwargs): + forms.Form.__init__(self, *args, **kwargs) + self.siteconfig = siteconfig + self.disabled_fields = {} + self.disabled_reasons = {} + + self.load() + + def load(self): + """ + Loads settings from the ```SiteConfiguration''' into this form. + The default values in the form will be the values in the settings. + + This also handles setting disabled fields based on the + ```disabled_fields''' and ```disabled_reasons''' variables set on + this form. + """ + if hasattr(self, "Meta"): + save_blacklist = getattr(self.Meta, "save_blacklist", []) + + for field in self.fields: + value = self.siteconfig.get(field) + + if isinstance(value, bool) or value: + self.fields[field].initial = value + + if field in self.disabled_fields: + self.fields[field].widget.attrs['disabled'] = 'disabled' + + def save(self): + """ + Saves settings from the form back into the ```SiteConfiguration'''. + """ + if not self.errors: + if hasattr(self, "Meta"): + save_blacklist = getattr(self.Meta, "save_blacklist", []) + + for key, value in self.cleaned_data.iteritems(): + if key not in save_blacklist: + self.siteconfig.settings[key] = value + + self.siteconfig.save() diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/managers.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/managers.py new file mode 100644 index 0000000..feac99e --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/managers.py @@ -0,0 +1,71 @@ +# +# djblets/siteconfig/managers.py +# +# Copyright (c) 2008 Christian Hammond +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +from django.contrib.sites.models import Site +from django.db import models + + +_SITECONFIG_CACHE = {} + + +class SiteConfigurationManager(models.Manager): + """ + A Manager that provides a get_current function for retrieving the + SiteConfiguration for this particular running site. + """ + def get_current(self): + """ + Returns the site configuration on the active site. + """ + from djblets.siteconfig.models import SiteConfiguration + global _SITECONFIG_CACHE + + # This will handle raising a ImproperlyConfigured if not set up + # properly. + site = Site.objects.get_current() + + if site.id not in _SITECONFIG_CACHE: + _SITECONFIG_CACHE[site.id] = \ + SiteConfiguration.objects.get(site=site) + + return _SITECONFIG_CACHE[site.id] + + def clear_cache(self): + global _SITECONFIG_CACHE + _SITECONFIG_CACHE = {} + + def check_expired(self): + """ + Checks each cached SiteConfiguration to find out if its settings + have expired. This should be called on each request to ensure that + the copy of the settings is up-to-date in case another web server + worker process modifies the settings in the database. + """ + global _SITECONFIG_CACHE + + for key, siteconfig in _SITECONFIG_CACHE.copy().iteritems(): + if siteconfig.is_expired(): + # This is stale. Get rid of it so we can load it next time. + del _SITECONFIG_CACHE[key] diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/middleware.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/middleware.py new file mode 100644 index 0000000..0be851f --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/middleware.py @@ -0,0 +1,13 @@ +from djblets.siteconfig.models import SiteConfiguration + + +class SettingsMiddleware(object): + """ + Middleware that performs necessary operations for siteconfig settings. + + Right now, the primary responsibility is to check on each request if + the settings have expired, so that a web server worker process doesn't + end up with a stale view of the site settings. + """ + def process_request(self, request): + SiteConfiguration.objects.check_expired() diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/models.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/models.py new file mode 100644 index 0000000..2d92d4a --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/models.py @@ -0,0 +1,127 @@ +# +# djblets/siteconfig/models.py +# +# Copyright (c) 2008 Christian Hammond +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +from datetime import datetime + +from django.contrib.sites.models import Site +from django.core.cache import cache +from django.db import models + +from djblets.siteconfig.managers import SiteConfigurationManager +from djblets.util.fields import JSONField + + +_DEFAULTS = {} + + +class SiteConfiguration(models.Model): + """ + Configuration data for a site. The version and all persistent settings + are stored here. + + The usual way to retrieve a SiteConfiguration is to use + ```SiteConfiguration.objects.get_current()''' + """ + site = models.ForeignKey(Site, related_name="config") + version = models.CharField(max_length=20) + settings = JSONField() + + objects = SiteConfigurationManager() + + def __init__(self, *args, **kwargs): + models.Model.__init__(self, *args, **kwargs) + self._last_sync_time = datetime.now() + + def get(self, key, default=None): + """ + Retrieves a setting. If the setting is not found, the default value + will be returned. This is represented by the default parameter, if + passed in, or a global default if set. + """ + if default is None and self.id in _DEFAULTS: + default = _DEFAULTS[self.id].get(key, None) + + return self.settings.get(key, default) + + def set(self, key, value): + """ + Sets a setting. The key should be a string, but the value can be + any native Python object. + """ + self.settings[key] = value + + def add_defaults(self, defaults_dict): + """ + Adds a dictionary of defaults to this SiteConfiguration. These + defaults will be used when calling ```get''', if that setting wasn't + saved in the database. + """ + if self.id not in _DEFAULTS: + _DEFAULTS[self.id] = {} + + _DEFAULTS[self.id].update(defaults_dict) + + def add_default(self, key, default_value): + """ + Adds a single default setting. + """ + self.add_defaults({key: default_value}) + + def get_defaults(self): + """ + Returns all default settings registered with this SiteConfiguration. + """ + if self.id not in _DEFAULTS: + _DEFAULTS[self.id] = {} + + return _DEFAULTS[self.id] + + def is_expired(self): + """ + Returns whether or not this SiteConfiguration is expired and needs + to be reloaded. + """ + last_updated = cache.get(self.__get_sync_cache_key()) + return (isinstance(last_updated, datetime) and + last_updated > self._last_sync_time) + + def save(self, **kwargs): + now = datetime.now() + self._last_sync_time = now + cache.set(self.__get_sync_cache_key(), now) + + # The cached siteconfig might be stale now. We'll want a refresh. + # Also refresh the Site cache, since callers may get this from + # Site.config. + SiteConfiguration.objects.clear_cache() + Site.objects.clear_cache() + + super(SiteConfiguration, self).save(**kwargs) + + def __get_sync_cache_key(self): + return "%s:siteconfig:%s:last-updated" % (self.site.domain, self.id) + + def __unicode__(self): + return "%s (version %s)" % (unicode(self.site), self.version) diff --git a/Pootle-2.0.0/external_apps/djblets/siteconfig/views.py b/Pootle-2.0.0/external_apps/djblets/siteconfig/views.py new file mode 100644 index 0000000..6340bfa --- /dev/null +++ b/Pootle-2.0.0/external_apps/djblets/siteconfig/views.py @@ -0,0 +1,58 @@ +# +# djblets/siteconfig/views.py +# +# Copyright (c) 2008 Christian Hammond +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +from django.contrib.admin.views.decorators import staff_member_required +from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response +from django.template.context import RequestContext + +from djblets.siteconfig.models import SiteConfiguration + + +@staff_member_required +def site_settings(request, form_class, + template_name="siteconfig/settings.html", + extra_context={}): + """ + Provides a front-end for customizing Review Board settings. + """ + siteconfig = SiteConfiguration.objects.get_current() + + if request.method == "POST": + form = form_class(siteconfig, request.POST, request.FILES) + + if form.is_valid(): + form.save() + return HttpResponseRedirect(".?saved=1") + else: + form = form_class(siteconfig) + + context = { + 'form': form, + 'saved': request.GET.get('saved', 0) + } + context.update(extra_context) + + return render_to_response(template_name, RequestContext(request, context)) |