]> git.openstreetmap.org Git - osqa.git/commitdiff
Closing OSQA 201, Add a "site maintenance" switch and page.
authorhernani <hernani@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Mon, 17 May 2010 12:30:57 +0000 (12:30 +0000)
committerhernani <hernani@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Mon, 17 May 2010 12:30:57 +0000 (12:30 +0000)
We can define some ips that are allowed to access the site normally, and also a message to display in the maintenance page.
These settings can be adjusted (add/remove more ips, change the message) while in maintenance mode.

git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@289 0cfe37f9-358a-4d5e-be75-b63607b5c754

12 files changed:
forum/forms.py
forum/middleware/request_utils.py
forum/settings/__init__.py
forum/settings/base.py
forum/settings/forms.py
forum/settings/repgain.py
forum/skins/default/media/style/djstyle_admin.css
forum/skins/default/templates/410.html [new file with mode: 0644]
forum/skins/default/templates/osqaadmin/djstyle_base.html
forum/skins/default/templates/osqaadmin/maintenance.html [new file with mode: 0644]
forum/urls.py
forum/views/admin.py

index b54d65b1d133b03b70f5db84dc0ae06ede96b8b7..509c1255dd5578aebce63017f75789f8756d97fe 100644 (file)
@@ -251,7 +251,3 @@ class SubscriptionSettingsForm(forms.Form):
     notify_comments = forms.BooleanField(required=False, initial=False)
     notify_accepted = forms.BooleanField(required=False, initial=False)
 
-
-class AwardPointsForm(forms.Form):
-    points = forms.IntegerField(min_value=1, initial=50, label=_('Points to award'))
-    message = forms.CharField(widget=forms.Textarea(), label=_('Message'), required=False)
index a96f372adea4b1e6b7248c24e89578b638f2f087..666956515c48be2618019406e439842fa556c2ae 100644 (file)
@@ -1,3 +1,7 @@
+from forum.settings import MAINTAINANCE_MODE, APP_LOGO, APP_TITLE\r
+from django.http import HttpResponseGone\r
+from django.template.loader import render_to_string\r
+\r
 \r
 class RequestUtils(object):\r
     def __init__(self):\r
@@ -23,6 +27,16 @@ class RequestUtils(object):
             return int(pagesize)\r
 \r
     def process_request(self, request):\r
+        if MAINTAINANCE_MODE.value is not None and isinstance(MAINTAINANCE_MODE.value.get('allow_ips', None), list):\r
+            ip = request.META['REMOTE_ADDR']\r
+\r
+            if not ip in MAINTAINANCE_MODE.value['allow_ips']:\r
+                return HttpResponseGone(render_to_string('410.html', {\r
+                    'message': MAINTAINANCE_MODE.value.get('message', ''),\r
+                    'app_logo': APP_LOGO,\r
+                    'app_title': APP_TITLE\r
+                }))\r
+\r
         self.request = request\r
         request.utils = self\r
         return None
\ No newline at end of file
index caf0eaf1505f0945b8d3d0e5711a33bff9fedccb..95f2441641dc56aca0da2a165ab704c38fda916b 100644 (file)
@@ -10,6 +10,8 @@ from django.utils.version import get_svn_revision
 OSQA_VERSION = "Development Build"
 SVN_REVISION = get_svn_revision(djsettings.SITE_SRC_ROOT)
 
+MAINTAINANCE_MODE = Setting('MAINTAINANCE_MODE', None)
+
 SETTINGS_PACK = Setting('SETTINGS_PACK', "default")
 DJSTYLE_ADMIN_INTERFACE = Setting('DJSTYLE_ADMIN_INTERFACE', True)
 
index e16097027102d0cb6f19b443aab9cf705897a2e8..398ed11907d7b33c214d168eb5036ffa2900ef40 100644 (file)
@@ -71,7 +71,11 @@ class BaseSetting(object):
                 return self.base_type(value)
             except:
                 pass
-        return value        
+        return value
+
+class AnyTypeSetting(BaseSetting):
+     def _parse(self, value):
+        return value
 
 
 class Setting(object):
@@ -79,6 +83,9 @@ class Setting(object):
     sets = {}
 
     def __new__(cls, name, default, set=None, field_context=None):
+        if default is None:
+            return AnyTypeSetting(name, default, set, field_context)
+            
         deftype = type(default)
 
         if deftype in Setting.emulators:
index a1876837acaaa47dfeec88195852e8b6b76045c3..27581d5412eb95c3e953865102c1386c545a6184 100644 (file)
@@ -1,4 +1,5 @@
 import os
+import socket
 from string import strip
 from django import forms
 from base import Setting
@@ -109,5 +110,36 @@ class CommaStringListWidget(forms.Textarea):
             return ', '.join(data[name])    
 
 
+class IPListField(forms.CharField):
+    def clean(self, value):
+        ips = [ip.strip() for ip in value.strip().strip(',').split(',')]
+        iplist = []
+
+        if len(ips) < 1:
+            raise forms.ValidationError(_('Please input at least one ip address'))    
+
+        for ip in ips:
+            try:
+                socket.inet_aton(ip)
+            except socket.error:
+                raise forms.ValidationError(_('Invalid ip address: %s' % ip))
+
+            if not len(ip.split('.')) == 4:
+                raise forms.ValidationError(_('Please use the dotted quad notation for the ip addresses'))
+
+            iplist.append(ip)
+
+        return iplist
+
+class MaintenanceModeForm(forms.Form):
+    ips = IPListField(label=_('Allow ips'),
+                      help_text=_('Comma separated list of ips allowed to access the site while in maintenance'),
+                      required=True,
+                      widget=forms.TextInput(attrs={'class': 'longstring'}))
+
+    message = forms.CharField(label=_('Message'),
+                              help_text=_('A message to display to your site visitors while in maintainance mode'),
+                              widget=forms.Textarea)
+
 
 
index f78120d010ad8f6bd4824502e7a6e0ccd4e59dd0..cfb566bbdf85019d9446cba04033b30dc7744de6 100644 (file)
@@ -15,10 +15,6 @@ REP_GAIN_BY_UPVOTED = Setting('REP_GAIN_BY_UPVOTED', 10, REP_GAIN_SET, dict(
 label = _("Rep gain by upvoted"),\r
 help_text = _("Reputation a user gains for having one of his posts up voted.")))\r
 \r
-REP_LOST_BY_UPVOTE_CANCELED = Setting('REP_LOST_BY_UPVOTE_CANCELED', 10, REP_GAIN_SET, dict(\r
-label = _("Rep lost bu upvote canceled"),\r
-help_text = _("Reputation a user loses for having one of the upvotes on his posts canceled.")))\r
-\r
 REP_LOST_BY_DOWNVOTED = Setting('REP_LOST_BY_DOWNVOTED', 2, REP_GAIN_SET, dict(\r
 label = _("Rep lost by downvoted"),\r
 help_text = _("Reputation a user loses for having one of his posts down voted.")))\r
@@ -27,30 +23,15 @@ REP_LOST_BY_DOWNVOTING = Setting('REP_LOST_BY_DOWNVOTING', 1, REP_GAIN_SET, dict
 label = _("Rep lost by downvoting"),\r
 help_text = _("Reputation a user loses for down voting a post.")))\r
 \r
-REP_GAIN_BY_DOWNVOTE_CANCELED = Setting('REP_GAIN_BY_DOWNVOTE_CANCELED', 2, REP_GAIN_SET, dict(\r
-label = _("Rep gain by downvote canceled"),\r
-help_text = _("Reputation a user gains for having one of the downvotes on his posts canceled.")))\r
-\r
-REP_GAIN_BY_CANCELING_DOWNVOTE = Setting('REP_GAIN_BY_CANCELING_DOWNVOTE', 1, REP_GAIN_SET, dict(\r
-label = _("Rep gain by canceling downvote"),\r
-help_text = _("Reputation a user gains for canceling a downvote.")))\r
 \r
 REP_GAIN_BY_ACCEPTED = Setting('REP_GAIN_BY_ACCEPTED', 15, REP_GAIN_SET, dict(\r
 label = _("Rep gain by accepted answer"),\r
 help_text = _("Reputation a user gains for having one of his answers accepted.")))\r
 \r
-REP_LOST_BY_ACCEPTED_CANCELED = Setting('REP_LOST_BY_ACCEPTED_CANCELED', 15, REP_GAIN_SET, dict(\r
-label = _("Rep lost by accepted canceled"),\r
-help_text = _("Reputation a user loses for having one of his accepted answers canceled.")))\r
-\r
 REP_GAIN_BY_ACCEPTING = Setting('REP_GAIN_BY_ACCEPTING', 2, REP_GAIN_SET, dict(\r
 label = _("Rep gain by accepting answer"),\r
 help_text = _("Reputation a user gains for accepting an answer to one of his questions.")))\r
 \r
-REP_LOST_BY_CANCELING_ACCEPTED = Setting('REP_LOST_BY_CANCELING_ACCEPTED', 2, REP_GAIN_SET, dict(\r
-label = _("Rep lost by canceling accepted"),\r
-help_text = _("Reputation a user loses by canceling an accepted answer.")))\r
-\r
 REP_LOST_BY_FLAGGED = Setting('REP_LOST_BY_FLAGGED', 2, REP_GAIN_SET, dict(\r
 label = _("Rep lost by post flagged"),\r
 help_text = _("Reputation a user loses by having one of his posts flagged.")))\r
index 87fe341d6b029025f8a53504e700bfc7257f3191..e8e85734e931ea7fa17bf948a776d43bef3d8f44 100644 (file)
@@ -47,3 +47,9 @@ input.longstring {
     position: relative;
     left: 612px;
 }
+
+.admin_message {
+    background-color: #ffffe0;
+    border: 3px double #b8860b;
+    padding: 4px;
+}
\ No newline at end of file
diff --git a/forum/skins/default/templates/410.html b/forum/skins/default/templates/410.html
new file mode 100644 (file)
index 0000000..8f1664c
--- /dev/null
@@ -0,0 +1,22 @@
+{% load i18n %}
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>{% trans "System down for maintenance" %}</title>
+        <style>
+            #content {
+                margin: auto;
+            }
+        </style>
+    </head>
+    <body>
+        <div id="content">
+            <img src="{{ app_logo }}">
+            <p>{{ app_title }}</p>
+            <h3>{% trans "System down for maintenance" %}</h3>
+            <br/>
+            {{ message }}
+        </div>
+    </body>
+</html>
index c91218f84a8d453dc7479b5133847df71a2a74c8..f9e941fe1b8fa3eea14c5f9b785b96a1bc17f626 100644 (file)
@@ -45,6 +45,9 @@
         </div>
         <div id="content" class="colMS">
             <div id="content-main">
+                {% for message in user_messages %}
+                    <p class="admin_message">{{ message }}</p>
+                {% endfor %}
                 {% block admincontent %}{% endblock %}
             </div>
             <div id="content-related">
                     {% endfor %}
                     </ul>
                 </div>
-                {% comment %}<div id="tools-menu" class="module">
+                <div id="tools-menu" class="module">
                     <h2>{% trans "Tools" %}</h2>
                     <ul>
-                        <li><a href="{% url admin_statistics %}">{% trans "Statistics" %}</a></li>
+                        <li><a href="{% url admin_maintenance %}">{% trans "Maintenance mode" %}</a></li>
                     </ul>
-                </div>{% endcomment %}
+                </div>
             </div>
         </div>
         <div id="footer" class="breadcumbs">
diff --git a/forum/skins/default/templates/osqaadmin/maintenance.html b/forum/skins/default/templates/osqaadmin/maintenance.html
new file mode 100644 (file)
index 0000000..38d1d0e
--- /dev/null
@@ -0,0 +1,25 @@
+{% extends basetemplate %}
+
+{% load i18n %}
+
+{% block subtitle %}{% trans "Maintenance mode" %}{% endblock %}
+{% block pagename %}{% trans "Maintenance mode" %}{% endblock %}
+{% block description %}{% trans "Maintainance mode allows you to close your site for maintainance, allowing only a predetermined set of ip addresses to access it normally." %}{% endblock %}
+
+{% block admincontent %}
+<form method="POST" action="">
+    {% if in_maintenance %}
+        <h1>{% trans "Your site is currently running on maintenance mode." %}</h1>
+        <p>{% trans "You can adjust the settings bellow" %}</p>
+    {% endif %}
+    <table>
+    {{ form.as_table }}
+    </table>
+    {% if in_maintenance %}
+        <input type="submit" name="adjust" value="{% trans "Adjust settings" %}" />
+        <input type="submit" name="open" value="{% trans "Open site" %}" />
+    {% else %}
+        <input type="submit" name="close" value="{% trans "Close for maintenance" %}" />
+    {% endif %}
+</form>
+{% endblock %}
\ No newline at end of file
index c173bcf740f538bd32eadd10e10d35334be9c786..6f053fd256db7930b99eb2fc17fc83f239b925af 100644 (file)
@@ -134,8 +134,10 @@ urlpatterns += patterns('',
     url(r'^%s%s$' % (_('admin/'), _('denormalize/')), app.admin.recalculate_denormalized, name="admin_denormalize"),
     url(r'^%s%s$' % (_('admin/'), _('go_bootstrap/')), app.admin.go_bootstrap, name="admin_go_bootstrap"),
     url(r'^%s%s$' % (_('admin/'), _('go_defaults/')), app.admin.go_defaults, name="admin_go_defaults"),
-    url(r'^%s(?P<set_name>\w+)/$' % _('admin/'), app.admin.settings_set, name="admin_set"),
-    url(r'^%s(?P<set_name>\w+)/(?P<var_name>\w+)/$' % _('admin/'), app.admin.get_default, name="admin_default"),
+    url(r'^%s%s(?P<set_name>\w+)/(?P<var_name>\w+)/$' % (_('admin/'), _('settings/')), app.admin.get_default, name="admin_default"),
+    url(r'^%s%s$' % (_('admin/'), _('maintenance/')), app.admin.maintenance, name="admin_maintenance"),
+
+    url(r'^%s%s(?P<set_name>\w+)/$' % (_('admin/'), _('settings/')), app.admin.settings_set, name="admin_set"),
 
     url(r'^feeds/rss/$', RssLastestQuestionsFeed, name="latest_questions_feed"),
 )
index d277954b7b278989e1937b1a27e7d48cf338e612..f154ea18fe1b7fe2d60fa13d086e7b81c9e92841 100644 (file)
@@ -10,7 +10,7 @@ from django.utils import simplejson
 from django.db.models import Sum
 
 from forum.settings.base import Setting
-from forum.settings.forms import SettingsSetForm
+from forum.settings.forms import SettingsSetForm, MaintenanceModeForm
 
 from forum.models import Question, Answer, User, Node, Action
 from forum import settings
@@ -200,15 +200,10 @@ def go_bootstrap(request):
     settings.INITIAL_REP.set_value(1)
     settings.MAX_REP_BY_UPVOTE_DAY.set_value(300)
     settings.REP_GAIN_BY_UPVOTED.set_value(15)
-    settings.REP_LOST_BY_UPVOTE_CANCELED.set_value(15)
     settings.REP_LOST_BY_DOWNVOTED.set_value(1)
     settings.REP_LOST_BY_DOWNVOTING.set_value(0)
-    settings.REP_GAIN_BY_DOWNVOTE_CANCELED.set_value(1)
-    settings.REP_GAIN_BY_CANCELING_DOWNVOTE.set_value(0)
     settings.REP_GAIN_BY_ACCEPTED.set_value(25)
-    settings.REP_LOST_BY_ACCEPTED_CANCELED.set_value(25)
     settings.REP_GAIN_BY_ACCEPTING.set_value(5)
-    settings.REP_LOST_BY_CANCELING_ACCEPTED.set_value(5)
     settings.REP_LOST_BY_FLAGGED.set_value(2)
     settings.REP_LOST_BY_FLAGGED_3_TIMES.set_value(30)
     settings.REP_LOST_BY_FLAGGED_5_TIMES.set_value(100)
@@ -248,3 +243,33 @@ def recalculate_denormalized(request):
     request.user.message_set.create(message=_('All values recalculated'))
     return HttpResponseRedirect(reverse('admin_index'))
 
+@admin_page
+def maintenance(request):
+    if request.POST:
+        if 'close' in request.POST or 'adjust' in request.POST:
+            form = MaintenanceModeForm(request.POST)
+
+            if form.is_valid():
+                settings.MAINTAINANCE_MODE.set_value({
+                    'allow_ips': form.cleaned_data['ips'],
+                    'message': form.cleaned_data['message']})
+
+                if 'close' in request.POST:
+                    message = _('Maintenance mode enabled')
+                else:
+                    message = _('Settings adjusted')
+
+                request.user.message_set.create(message=message)
+
+                return HttpResponseRedirect(reverse('admin_maintenance'))
+        elif 'open' in request.POST:
+            settings.MAINTAINANCE_MODE.set_value(None)
+            request.user.message_set.create(message=_("Your site is now running normally"))
+            return HttpResponseRedirect(reverse('admin_maintenance'))
+    else:
+        form = MaintenanceModeForm(initial={'ips': request.META['REMOTE_ADDR'],
+                                            'message': _('Currently down for maintenance. We\'ll be back soon')})
+
+    return ('osqaadmin/maintenance.html', {'form': form, 'in_maintenance': settings.MAINTAINANCE_MODE.value is not None})
+
+