Commits

Anonymous committed 6442f22

Started working on an admin panel.

Comments (0)

Files changed (7)

solace/application.py

                          error=True, login_could_fix=True)
 
 
+def require_admin(f):
+    """Decorates a view function so that it requires a user that is
+    logged in.
+    """
+    def decorated(request, **kwargs):
+        if not request.user.is_admin:
+            message = _(u'You cannot access this resource.')
+            if request.is_xhr:
+                return json_response(message=message, error=True)
+            raise Forbidden(message)
+        return f(request, **kwargs)
+    return require_login(update_wrapper(decorated, f))
+
+
 def require_login(f):
     """Decorates a view function so that it requires a user that is
     logged in.

solace/settings.py

     :copyright: (c) 2009 by Plurk Inc., see AUTHORS for more details.
     :license: BSD, see LICENSE for more details.
 """
+from __future__ import with_statement
+del with_statement
+
 # temporary imports, delete at end of file
 import os, sys, solace
 
             d[key] = value
 
 
+def describe_settings():
+    """Describes the settings.  Returns a list of
+    ``(key, current_value, description)`` tuples.
+    """
+    import re
+    from pprint import pformat
+    assignment_re = re.compile(r'\s*([A-Z_][A-Z0-9_]*)\s*=')
+
+    # use items() here instead of iteritems so that if a different
+    # thread somehow fiddles with the globals, we don't break
+    items = dict((k, (pformat(v).decode('utf-8', 'replace'), u''))
+                 for (k, v) in globals().items() if k.isupper())
+
+    with open(__file__.strip('c')) as f:
+        comment_buf = []
+        for line in f:
+            line = line.rstrip().decode('utf-8')
+            if line.startswith('#:'):
+                comment_buf.append(line[2:].lstrip())
+            else:
+                match = assignment_re.match(line)
+                if match is not None:
+                    key = match.group(1)
+                    tup = items.get(key)
+                    if tup is not None and comment_buf:
+                        items[key] = (tup[0], u'\n'.join(comment_buf))
+                    del comment_buf[:]
+
+    return sorted([(k,) + v for k, v in items.items()])
+
+
 if 'SOLACE_SETTINGS_FILE' in os.environ:
     configure_from_file(os.environ['SOLACE_SETTINGS_FILE'])
 del os, sys, solace

solace/static/layout.css

     width: 740px;
     top: 35px;
     height: 28px;
-    line-height: 28px;
-    text-align: right;
+            line-height: 28px;
+            text-align: right;
 }
 
 div.metanavigation p {
-    margin: 0;
-    font-size: 11px;
+margin: 0;
+        font-size: 11px;
 }
 
 div.metanavigation span.user span.badge {
 }
 
 ul.language_selection {
-    position: absolute;
-    right: 10px;
-    top: 10px;
-    margin: 0;
-    list-style: none;
-    width: 120px;
-    padding: 0;
-    border: 1px solid;
-    text-align: center;
-    z-index: 20;
+position: absolute;
+right: 10px;
+top: 10px;
+margin: 0;
+        list-style: none;
+width: 120px;
+padding: 0;
+border: 1px solid;
+        text-align: center;
+        z-index: 20;
 }
 
 ul.language_selection li {
-    display: none;
+display: none;
 }
 
 ul.language_selection li a,
-ul.language_selection li.title {
-    display: block;
-    padding: 0 7px;
-    line-height: 22px;
-    height: 22px;
-}
+    ul.language_selection li.title {
+display: block;
+padding: 0 7px;
+         line-height: 22px;
+height: 22px;
+    }
 
 ul.language_selection li.title {
-    display: block;
-    font-weight: bold;
-    cursor: pointer;
+display: block;
+         font-weight: bold;
+cursor: pointer;
 }
 
 ul.css_language_selection:hover li,
-ul.language_selection.hovered li {
-    display: block;
-}
+    ul.language_selection.hovered li {
+display: block;
+    }
 
 ul.language_selection li a {
     text-decoration: none;
 }
 
 ul.css_language_selection:hover li.title,
-ul.language_selection.hovered li.title {
-    border-bottom: 1px solid;
-}
+    ul.language_selection.hovered li.title {
+        border-bottom: 1px solid;
+    }
 
 /* :::: NAVIGATION :::: */
 
 ul.navigation {
-    width: 760px;
-    margin: 0 auto;
-    padding: 0;
-    list-style: none;
-    height: 30px;
-    line-height: 30px;
-    border: 1px solid;
+width: 760px;
+margin: 0 auto;
+padding: 0;
+         list-style: none;
+height: 30px;
+        line-height: 30px;
+border: 1px solid;
 }
 
 ul.navigation li {
-    display: block;
-    float: left;
+display: block;
+float: left;
 }
 
 ul.navigation li a {
-    display: block;
-    float: left; /* this one is needed for ie6 */
-    height: 30px;
-    line-height: 30px;
-    padding: 0 10px;
-    text-decoration: none;
-    font-weight: bold;
-    font-size: 10px;
+display: block;
+float: left; /* this one is needed for ie6 */
+height: 30px;
+        line-height: 30px;
+padding: 0 10px;
+         text-decoration: none;
+         font-weight: bold;
+         font-size: 10px;
 }
 
 ul.navigation li.active a {
-    border: 1px solid;
-    border-bottom: none;
-    border-top: none;
-    padding: 0 9px;
-    height: 31px;
+border: 1px solid;
+        border-bottom: none;
+        border-top: none;
+padding: 0 9px;
+height: 31px;
 }
 
 ul.navigation li.view_lang {
-    float: right;
-    text-align: center;
+float: right;
+       text-align: center;
 }
 
 ul.navigation li.view_lang a {
     line-height: 1;
-    padding: 0 15px;
+padding: 0 15px;
 }
 
 ul.navigation li.view_lang a small {
     padding-top: 4px;
-    display: block;
-    font-size: 10px;
-    font-weight: normal;
+display: block;
+         font-size: 10px;
+         font-weight: normal;
 }
 
 ul.navigation li.view_lang a strong {
     margin-top: -2px;
-    display: block;
-    font-size: 13px;
+display: block;
+         font-size: 13px;
 }
 
 /* :::: CONTENTS :::: */
 
 div.contents {
-    width: 740px;
-    margin: 0 auto;
-    padding: 10px;
-    border: 1px solid;
-    border-top: none;
-    border-bottom: none;
+width: 740px;
+margin: 0 auto;
+padding: 10px;
+border: 1px solid;
+        border-top: none;
+        border-bottom: none;
 }
 
 div.footer {
-    width: 740px;
-    margin: 0 auto;
-    padding: 4px 10px;
-    border: 1px solid;
-    border-top: none;
-    font-size: 10px;
-    text-align: right;
+width: 740px;
+margin: 0 auto;
+padding: 4px 10px;
+border: 1px solid;
+        border-top: none;
+        font-size: 10px;
+        text-align: right;
 }
 
 div.contents h1 {
-    margin: -10px -10px 10px -10px;
-    padding: 7px 10px;
-    font-size: 24px;
+margin: -10px -10px 10px -10px;
+padding: 7px 10px;
+         font-size: 24px;
 }
 
 #flash_message {
 #recaptcha_area {
     margin: 10px 0;
 }
+
+/* :::: ADMIN PANEL :::: */
+div.admin_panel {
+    margin: -10px;
+    padding: 10px;
+}
+
+div.admin_panel ul.admin_navigation {
+    margin: -10px -10px 10px -10px;
+    padding: 0;
+    list-style: none;
+    height: 20px;
+    line-height: 20px;
+    font-size: 10px;
+    font-weight: bold;
+    border-bottom: 1px solid;
+}
+
+div.admin_panel ul.admin_navigation li {
+    float: left;
+}
+
+div.admin_panel ul.admin_navigation li a {
+    display: block;
+    padding: 0 10px;
+    text-decoration: none;
+}
+
+div.admin_panel table.settings {
+    border-collapse: collapse;
+}
+
+div.admin_panel table.settings thead th {
+    text-align: left;
+    border-bottom: 1px solid;
+}
+
+div.admin_panel table.settings tbody th {
+    text-align: left;
+    font-size: 10px;
+}
+
+div.admin_panel table.settings th.key {
+    width: 220px;
+}
+
+div.admin_panel table.settings tbody tr.description td {
+    padding-top: 4px;
+}
+
+div.admin_panel table.settings tbody tr.item td,
+div.admin_panel table.settings tbody tr.item th {
+    padding-bottom: 10px;
+    vertical-align: top;
+}
+
+div.admin_panel table.settings tbody td.value pre {
+    padding: 0;
+    margin: 0;
+}

solace/templates/admin/layout.html

+{% extends 'layout.html' %}
+{% set page_title = _('Admin Panel') %}
+{% block body %}
+  <div class="admin_panel">
+    <h1>{{ _('Admin Panel') }}</h1>
+    <ul class="admin_navigation">
+    {%- for endpoint, title in [('admin.status', _('Status')),
+                                ('admin.bans', _('Bans'))] %}
+      <li{% if endpoint == request.endpoint %} class="active"{%
+        endif %}><a href="{{ url_for(endpoint) }}">{{ title }}</a>
+    {%- endfor %}
+    </ul>
+    {% block admin_body %}{% endblock %}
+  </div>
+{% endblock %}

solace/templates/admin/status.html

+{% extends 'admin/layout.html' %}
+{% block admin_body %}
+  <h2>{{ _('Status') }}</h2>
+  <p>{% trans -%}
+    Monitor the current system status.
+  {%- endtrans %}
+  <h3>{{ _('Active settings') }}</h3>
+  <p>{% trans -%}
+    Lists the current active settings and the description for each key
+    if available.  In order to change these settings you have to modify
+    the configuration file or the WSGI script that sets these values.
+  {%- endtrans %}
+  <table class="settings">
+    <thead>
+      <tr>
+        <th class="key">{{ _('Key') }}
+        <th class="value">{{ _('Value') }}
+    </thead>
+    <tbody>
+    {%- for key, value, description in active_settings %}
+      {%- if description %}
+      <tr class="description">
+        <td colspan="2">{{ description|e }}
+      {%- endif %}
+      <tr class="item">
+        <th class="key">{{ key|e }}
+        <td class="value"><pre>{{ value|e }}</pre>
+    {%- endfor %}
+    </tbody>
+  </table>
+{% endblock %}

solace/themes/teal/static/style.css

 #recaptcha_table, .recaptcha_image_cell
                             { background: #eee!important; }
 #recaptcha_response_field   { border-color: #ccc!important; }
+
+/* admin panel */
+div.admin_panel ul.admin_navigation
+                            { background: #ccd8dd; border-color: #b3c5cd; }
+div.admin_panel ul.admin_navigation li a
+                            { color: #333; }
+div.admin_panel ul.admin_navigation li a:hover
+                            { background: white; }
+div.admin_panel ul.admin_navigation li.active a
+                            { background: #9eb2ba; }
+div.admin_panel table.settings thead th
+                            { border-color: #9eb2ba; }

solace/views/admin.py

+# -*- coding: utf-8 -*-
+"""
+    solace.views.admin
+    ~~~~~~~~~~~~~~~~~~
+
+    This module implements the views for the admin interface.
+
+    :copyright: (c) 2009 by Plurk Inc., see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+from werkzeug import redirect, Response
+from werkzeug.exceptions import Forbidden
+
+from solace.application import require_admin, url_for
+from solace.settings import describe_settings
+from solace.templating import render_template
+
+
+@require_admin
+def overview(request):
+    """Currently just a redirect."""
+    return redirect(url_for('admin.status'))
+
+
+@require_admin
+def status(request):
+    """Displays system statistics such as the database settings."""
+    return render_template('admin/status.html',
+                           active_settings=describe_settings())