Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Pootle-2.0.0/external_apps/djblets/siteconfig
diff options
context:
space:
mode:
Diffstat (limited to 'Pootle-2.0.0/external_apps/djblets/siteconfig')
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/__init__.py0
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/admin.py35
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/context_processors.py36
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/django_settings.py170
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/forms.py76
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/managers.py71
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/middleware.py13
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/models.py127
-rw-r--r--Pootle-2.0.0/external_apps/djblets/siteconfig/views.py58
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))