Improve UI for enabling/disabling plugins config
authorSimo Sorce <simo@redhat.com>
Wed, 15 Oct 2014 04:17:53 +0000 (00:17 -0400)
committerPatrick Uiterwijk <puiterwijk@redhat.com>
Mon, 27 Oct 2014 15:31:23 +0000 (16:31 +0100)
Use the same templates for both info and login plugins

Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Patrick Uiterwijk <puiterwijk@redhat.com>
ipsilon/admin/common.py
ipsilon/admin/info.py
ipsilon/admin/login.py
ipsilon/admin/providers.py
templates/admin/info.html [deleted file]
templates/admin/info_order.html [deleted file]
templates/admin/login.html [deleted file]
templates/admin/login_order.html [deleted file]
templates/admin/plugins.html [new file with mode: 0644]

index 7f723ac..827038e 100755 (executable)
@@ -20,6 +20,7 @@
 import cherrypy
 from ipsilon.util.page import Page
 from ipsilon.util.page import admin_protect
+from ipsilon.util.plugin import PluginObject
 
 
 class AdminPage(Page):
@@ -34,10 +35,10 @@ class AdminPage(Page):
         self.auth_protect = True
 
 
-class AdminPluginPage(AdminPage):
+class AdminPluginConfig(AdminPage):
 
     def __init__(self, po, site, parent):
-        super(AdminPluginPage, self).__init__(site, form=True)
+        super(AdminPluginConfig, self).__init__(site, form=True)
         self._po = po
         self.title = '%s plugin' % po.name
         self.url = '%s/%s' % (parent.url, po.name)
@@ -114,6 +115,158 @@ class AdminPluginPage(AdminPage):
                               plugin=self._po)
 
 
+class AdminPluginsOrder(AdminPage):
+
+    def __init__(self, site, parent, facility):
+        super(AdminPluginsOrder, self).__init__(site, form=True)
+        self.parent = parent
+        self.facility = facility
+        self.url = '%s/order' % parent.url
+        self.menu = [parent]
+
+    @admin_protect
+    def GET(self, *args, **kwargs):
+        return self.parent.root_with_msg()
+
+    @admin_protect
+    def POST(self, *args, **kwargs):
+        message = "Nothing was modified."
+        message_type = "info"
+        by_name = {p.name: p for p in self._site[self.facility]['enabled']}
+
+        if 'order' in kwargs:
+            order = kwargs['order'].split(',')
+            if len(order) != 0:
+                new_names = []
+                new_plugins = []
+                try:
+                    for v in order:
+                        val = v.strip()
+                        if val not in by_name:
+                            error = "Invalid plugin name: %s" % val
+                            raise ValueError(error)
+                        new_names.append(val)
+                        new_plugins.append(by_name[val])
+                    if len(new_names) < len(by_name):
+                        for val in by_name:
+                            if val not in new_names:
+                                new_names.append(val)
+                                new_plugins.append(by_name[val])
+
+                    self.parent.save_enabled_plugins(new_names)
+                    self.parent.reorder_plugins(new_names)
+
+                    # When all is saved update also live config. The
+                    # live config is a list of the actual plugin
+                    # objects.
+                    self._site[self.facility]['enabled'] = new_plugins
+
+                    message = "New configuration saved."
+                    message_type = "success"
+
+                except ValueError, e:
+                    message = str(e)
+                    message_type = "error"
+
+                except Exception, e:  # pylint: disable=broad-except
+                    message = "Failed to save data!"
+                    message_type = "error"
+
+        return self.parent.root_with_msg(message=message,
+                                         message_type=message_type)
+
+
+class AdminPlugins(AdminPage):
+    def __init__(self, name, site, parent, facility, ordered=True):
+        super(AdminPlugins, self).__init__(site)
+        self._master = parent
+        self.name = name
+        self.title = '%s plugins' % name
+        self.url = '%s/%s' % (parent.url, name)
+        self.facility = facility
+        self.template = 'admin/plugins.html'
+        self.order = None
+        parent.add_subtree(name, self)
+
+        for plugin in self._site[facility]['available']:
+            cherrypy.log.error('Admin info plugin: %s' % plugin)
+            obj = self._site[facility]['available'][plugin]
+            page = AdminPluginConfig(obj, self._site, self)
+            if hasattr(obj, 'admin'):
+                obj.admin.mount(page)
+            self.add_subtree(plugin, page)
+
+        if ordered:
+            self.order = AdminPluginsOrder(self._site, self, facility)
+
+    def save_enabled_plugins(self, names):
+        po = PluginObject()
+        po.name = "global"
+        globalconf = dict()
+        globalconf['order'] = ','.join(names)
+        po.set_config(globalconf)
+        po.save_plugin_config(self.facility)
+
+    def reorder_plugins(self, names):
+        return
+
+    def root_with_msg(self, message=None, message_type=None):
+        plugins = self._site[self.facility]
+        enabled = []
+        for p in plugins['enabled']:
+            enabled.append(p.name)
+        targs = {'title': self.title,
+                 'menu': self._master.menu,
+                 'message': message,
+                 'message_type': message_type,
+                 'available': plugins['available'],
+                 'enabled': enabled,
+                 'baseurl': self.url}
+        if self.order:
+            targs['order_name'] = '%s_order_form' % self.name
+            targs['order_action'] = self.order.url
+
+        # pylint: disable=star-args
+        return self._template(self.template, **targs)
+
+    def root(self, *args, **kwargs):
+        return self.root_with_msg()
+
+    @admin_protect
+    def enable(self, plugin):
+        msg = None
+        plugins = self._site[self.facility]
+        if plugin not in plugins['available']:
+            msg = "Unknown plugin %s" % plugin
+            return self.root_with_msg(msg, "error")
+        obj = plugins['available'][plugin]
+        if obj not in plugins['enabled']:
+            obj.enable(self._site)
+            if self.order:
+                enabled = list(x.name for x in plugins['enabled'])
+                self.save_enabled_plugins(enabled)
+            msg = "Plugin %s enabled" % obj.name
+        return self.root_with_msg(msg, "success")
+    enable.public_function = True
+
+    @admin_protect
+    def disable(self, plugin):
+        msg = None
+        plugins = self._site[self.facility]
+        if plugin not in plugins['available']:
+            msg = "Unknown plugin %s" % plugin
+            return self.root_with_msg(msg, "error")
+        obj = plugins['available'][plugin]
+        if obj in plugins['enabled']:
+            obj.disable(self._site)
+            if self.order:
+                enabled = list(x.name for x in plugins['enabled'])
+                self.save_enabled_plugins(enabled)
+            msg = "Plugin %s disabled" % obj.name
+        return self.root_with_msg(msg, "success")
+    disable.public_function = True
+
+
 class Admin(AdminPage):
 
     def __init__(self, site, mount):
index d3f5284..bda117a 100755 (executable)
 #
 # Copyright (C) 2014  Ipsilon Contributors see COPYING for license
 
-import cherrypy
-from ipsilon.util.page import admin_protect
-from ipsilon.util.plugin import PluginObject
-from ipsilon.admin.common import AdminPluginPage
-from ipsilon.admin.common import AdminPage
+from ipsilon.admin.common import AdminPlugins
 from ipsilon.info.common import FACILITY
 
 
-def save_enabled_plugins(names):
-    po = PluginObject()
-    po.name = "global"
-    globalconf = dict()
-    globalconf['order'] = ','.join(names)
-    po.set_config(globalconf)
-    po.save_plugin_config(FACILITY)
-
-
-class InfoPluginsOrder(AdminPage):
-
+class InfoPlugins(AdminPlugins):
     def __init__(self, site, parent):
-        super(InfoPluginsOrder, self).__init__(site, form=True)
-        self.url = '%s/order' % parent.url
-        self.menu = [parent]
-
-    @admin_protect
-    def GET(self, *args, **kwargs):
-        opts = [p.name for p in self._site[FACILITY]['enabled']]
-        return self._template('admin/info_order.html',
-                              title='info plugins order',
-                              name='admin_info_order_form',
-                              menu=self.menu, action=self.url,
-                              options=opts)
-
-    @admin_protect
-    def POST(self, *args, **kwargs):
-        message = "Nothing was modified."
-        message_type = "info"
-        plugins_by_name = {p.name: p for p in self._site[FACILITY]['enabled']}
-
-        if 'order' in kwargs:
-            order = kwargs['order'].split(',')
-            if len(order) != 0:
-                new_names = []
-                new_plugins = []
-                try:
-                    for v in order:
-                        val = v.strip()
-                        if val not in plugins_by_name:
-                            error = "Invalid plugin name: %s" % val
-                            raise ValueError(error)
-                        new_names.append(val)
-                        new_plugins.append(plugins_by_name[val])
-                    if len(new_names) < len(plugins_by_name):
-                        for val in plugins_by_name:
-                            if val not in new_names:
-                                new_names.append(val)
-                                new_plugins.append(plugins_by_name[val])
-
-                    save_enabled_plugins(new_names)
-
-                    # When all is saved update also live config. The
-                    # live config is a list of the actual plugin
-                    # objects.
-                    self._site[FACILITY]['enabled'] = new_plugins
-
-                    message = "New configuration saved."
-                    message_type = "success"
-
-                except ValueError, e:
-                    message = str(e)
-                    message_type = "error"
-
-                except Exception, e:  # pylint: disable=broad-except
-                    message = "Failed to save data!"
-                    message_type = "error"
-
-        opts = [p.name for p in self._site[FACILITY]['enabled']]
-        return self._template('admin/info_order.html',
-                              message=message,
-                              message_type=message_type,
-                              title='info plugins order',
-                              name='admin_info_order_form',
-                              menu=self.menu, action=self.url,
-                              options=opts)
-
-
-class InfoPlugins(AdminPage):
-    def __init__(self, site, parent):
-        super(InfoPlugins, self).__init__(site)
-        self._master = parent
+        super(InfoPlugins, self).__init__('info', site, parent, FACILITY)
         self.title = 'Info Plugins'
-        self.url = '%s/info' % parent.url
-        self.facility = FACILITY
-        parent.add_subtree('info', self)
-
-        for plugin in self._site[FACILITY]['available']:
-            cherrypy.log.error('Admin info plugin: %s' % plugin)
-            obj = self._site[FACILITY]['available'][plugin]
-            self.__dict__[plugin] = AdminPluginPage(obj, self._site, self)
-
-        self.order = InfoPluginsOrder(self._site, self)
-
-    def root_with_msg(self, message=None, message_type=None):
-        info_plugins = self._site[FACILITY]
-        ordered = []
-        for p in info_plugins['enabled']:
-            ordered.append(p.name)
-        return self._template('admin/info.html', title=self.title,
-                              message=message,
-                              message_type=message_type,
-                              available=info_plugins['available'],
-                              enabled=ordered,
-                              menu=self._master.menu)
-
-    def root(self, *args, **kwargs):
-        return self.root_with_msg()
-
-    @admin_protect
-    def enable(self, plugin):
-        msg = None
-        plugins = self._site[FACILITY]
-        if plugin not in plugins['available']:
-            msg = "Unknown plugin %s" % plugin
-            return self.root_with_msg(msg, "error")
-        obj = plugins['available'][plugin]
-        if obj not in plugins['enabled']:
-            obj.enable(self._site)
-            save_enabled_plugins(list(x.name for x in plugins['enabled']))
-            msg = "Plugin %s enabled" % obj.name
-        return self.root_with_msg(msg, "success")
-    enable.public_function = True
-
-    @admin_protect
-    def disable(self, plugin):
-        msg = None
-        plugins = self._site[FACILITY]
-        if plugin not in plugins['available']:
-            msg = "Unknown plugin %s" % plugin
-            return self.root_with_msg(msg, "error")
-        obj = plugins['available'][plugin]
-        if obj in plugins['enabled']:
-            obj.disable(self._site)
-            save_enabled_plugins(list(x.name for x in plugins['enabled']))
-            msg = "Plugin %s disabled" % obj.name
-        return self.root_with_msg(msg, "success")
-    disable.public_function = True
index 4645917..c1a1c73 100755 (executable)
@@ -1,47 +1,17 @@
 #!/usr/bin/python
 #
-# Copyright (C) 2014  Simo Sorce <simo@redhat.com>
-#
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# Copyright (C) 2014  Ipsilon Contributors see COPYING for license
 
-import cherrypy
-from ipsilon.util.page import admin_protect
-from ipsilon.util.plugin import PluginObject
-from ipsilon.admin.common import AdminPluginPage
-from ipsilon.admin.common import AdminPage
+from ipsilon.admin.common import AdminPlugins
 from ipsilon.login.common import FACILITY
 
 
-def save_enabled_plugins(names):
-    po = PluginObject()
-    po.name = "global"
-    globalconf = dict()
-    globalconf['order'] = ','.join(names)
-    po.set_config(globalconf)
-    po.save_plugin_config(FACILITY)
-
-
-class LoginPluginsOrder(AdminPage):
-
+class LoginPlugins(AdminPlugins):
     def __init__(self, site, parent):
-        super(LoginPluginsOrder, self).__init__(site, form=True)
-        self.url = '%s/order' % parent.url
-        self.menu = [parent]
+        super(LoginPlugins, self).__init__('login', site, parent, FACILITY)
+        self.title = 'Login Plugins'
 
-    def _reorder_plugins(self, order):
+    def reorder_plugins(self, order):
         plugins = self._site[FACILITY]['available']
         root = self._site[FACILITY]['root']
         prev_obj = None
@@ -52,127 +22,3 @@ class LoginPluginsOrder(AdminPage):
                 prev_obj.next_login = plugins[name]
             prev_obj = plugins[name]
         prev_obj.next_login = None
-
-    @admin_protect
-    def GET(self, *args, **kwargs):
-        opts = [p.name for p in self._site[FACILITY]['enabled']]
-        return self._template('admin/login_order.html',
-                              title='login plugins order',
-                              name='admin_login_order_form',
-                              menu=self.menu, action=self.url,
-                              options=opts)
-
-    @admin_protect
-    def POST(self, *args, **kwargs):
-        message = "Nothing was modified."
-        message_type = "info"
-        plugins_by_name = {p.name: p for p in self._site[FACILITY]['enabled']}
-
-        if 'order' in kwargs:
-            order = kwargs['order'].split(',')
-            if len(order) != 0:
-                new_names = []
-                new_plugins = []
-                try:
-                    for v in order:
-                        val = v.strip()
-                        if val not in plugins_by_name:
-                            error = "Invalid plugin name: %s" % val
-                            raise ValueError(error)
-                        new_names.append(val)
-                        new_plugins.append(plugins_by_name[val])
-                    if len(new_names) < len(plugins_by_name):
-                        for val in plugins_by_name:
-                            if val not in new_names:
-                                new_names.append(val)
-                                new_plugins.append(plugins_by_name[val])
-
-                    save_enabled_plugins(new_names)
-                    self._reorder_plugins(new_names)
-
-                    # When all is saved update also live config. The
-                    # live config is a list of the actual plugin
-                    # objects.
-                    self._site[FACILITY]['enabled'] = new_plugins
-
-                    message = "New configuration saved."
-                    message_type = "success"
-
-                except ValueError, e:
-                    message = str(e)
-                    message_type = "error"
-
-                except Exception, e:  # pylint: disable=broad-except
-                    message = "Failed to save data!"
-                    message_type = "error"
-
-        opts = [p.name for p in self._site[FACILITY]['enabled']]
-        return self._template('admin/login_order.html',
-                              message=message,
-                              message_type=message_type,
-                              title='login plugins order',
-                              name='admin_login_order_form',
-                              menu=self.menu, action=self.url,
-                              options=opts)
-
-
-class LoginPlugins(AdminPage):
-    def __init__(self, site, parent):
-        super(LoginPlugins, self).__init__(site)
-        self._master = parent
-        self.title = 'Login Plugins'
-        self.url = '%s/login' % parent.url
-        self.facility = FACILITY
-        parent.add_subtree('login', self)
-
-        for plugin in self._site[FACILITY]['available']:
-            cherrypy.log.error('Admin login plugin: %s' % plugin)
-            obj = self._site[FACILITY]['available'][plugin]
-            self.__dict__[plugin] = AdminPluginPage(obj, self._site, self)
-
-        self.order = LoginPluginsOrder(self._site, self)
-
-    def root_with_msg(self, message=None, message_type=None):
-        login_plugins = self._site[FACILITY]
-        ordered = []
-        for p in login_plugins['enabled']:
-            ordered.append(p.name)
-        return self._template('admin/login.html', title=self.title,
-                              message=message,
-                              message_type=message_type,
-                              available=login_plugins['available'],
-                              enabled=ordered,
-                              menu=self._master.menu)
-
-    def root(self, *args, **kwargs):
-        return self.root_with_msg()
-
-    @admin_protect
-    def enable(self, plugin):
-        msg = None
-        plugins = self._site[FACILITY]
-        if plugin not in plugins['available']:
-            msg = "Unknown plugin %s" % plugin
-            return self.root_with_msg(msg, "error")
-        obj = plugins['available'][plugin]
-        if obj not in plugins['enabled']:
-            obj.enable(self._site)
-            save_enabled_plugins(list(x.name for x in plugins['enabled']))
-            msg = "Plugin %s enabled" % obj.name
-        return self.root_with_msg(msg, "success")
-    enable.public_function = True
-
-    @admin_protect
-    def disable(self, plugin):
-        msg = None
-        plugins = self._site[FACILITY]
-        if plugin not in plugins['available']:
-            msg = "Unknown plugin %s" % plugin
-            return self.root_with_msg(msg, "error")
-        obj = plugins['available'][plugin]
-        if obj in plugins['enabled']:
-            obj.disable(self._site)
-            save_enabled_plugins(list(x.name for x in plugins['enabled']))
-            msg = "Plugin %s disabled" % obj.name
-        return self.root_with_msg(msg, "success")
-    disable.public_function = True
index eea61e7..06e5f54 100755 (executable)
@@ -21,7 +21,7 @@
 import cherrypy
 from ipsilon.util.page import admin_protect
 from ipsilon.providers.common import FACILITY
-from ipsilon.admin.common import AdminPluginPage
+from ipsilon.admin.common import AdminPluginConfig
 from ipsilon.admin.common import AdminPage
 
 
@@ -37,7 +37,7 @@ class ProviderPlugins(AdminPage):
         for plugin in self._site[FACILITY]['available']:
             cherrypy.log.error('Admin provider plugin: %s' % plugin)
             obj = self._site[FACILITY]['available'][plugin]
-            page = AdminPluginPage(obj, self._site, self)
+            page = AdminPluginConfig(obj, self._site, self)
             if hasattr(obj, 'admin'):
                 obj.admin.mount(page)
             self.add_subtree(plugin, page)
diff --git a/templates/admin/info.html b/templates/admin/info.html
deleted file mode 100644 (file)
index d51231f..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "master-admin.html" %}
-{% block main %}
-{% if user.is_admin %}
-    <h2>Info plugins</h2>
-    {% if message %}
-    <div class="alert alert-{{message_type}}">
-        <p>{{ message }}</p>
-    </div>
-    {% endif %}
-
-    {% for p in available %}
-        <div class="row">
-        <div class="col-md-3 col-sm-3 col-xs-6">{{ p }}</div>
-        <div class="col-md-3 col-sm-3 col-xs-6">
-        {% if p in enabled %}
-            <a class="btn btn-default" href="{{ basepath }}/admin/info/disable/{{ p }}">Disable</a>
-            <a class="btn btn-default" href="{{ basepath }}/admin/info/{{ p }}">Configure</a>
-        {% else %}
-            <a class="btn btn-default" href="{{ basepath }}/admin/info/enable/{{ p }}">Enable</a>
-        {% endif %}
-        </div>
-        </div>
-    {% endfor %}
-
-    <h3>Plugins order</h3>
-    <div class="col-md-3 col-sm-3 col-xs-6">{{ ', '.join(enabled) }}</div>
-    <div class="col-md-3 col-sm-3 col-xs-6">
-        <a class="btn btn-default" href="{{ basepath }}/admin/info/order">configure</a>
-    </div>
-{% endif %}
-{% endblock %}
diff --git a/templates/admin/info_order.html b/templates/admin/info_order.html
deleted file mode 100644 (file)
index 5d929be..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{% extends "master-admin.html" %}
-{% block main %}
-    <h2>{{ title }}</h2>
-    {% if message %}
-    <div class="alert alert-{{message_type}}">
-        <p>{{ message }}</p>
-    </div>
-    {% endif %}
-    <div id="options">
-        <form role="form" id="{{ name }}" action="{{ action }}" method="post" enctype="application/x-www-form-urlencoded">
-
-        <div class="form-group">
-        <label for="order">Info plugins order:</label>
-            <input type="text" class="form-control" name="order" value="{{ ', '.join(options) }}" />
-        </div>
-        <span class="help-block">Plugins order</span>
-
-        <button id="submit" class="btn btn-primary" name="submit" type="submit" value="Submit">
-            Save
-        </button>
-        <a href="{{ basepath }}/admin/info" class="btn btn-default" title="Back">Back</a>
-        </form>
-    </div>
-{% endblock %}
-
diff --git a/templates/admin/login.html b/templates/admin/login.html
deleted file mode 100644 (file)
index 9f51d02..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "master-admin.html" %}
-{% block main %}
-{% if user.is_admin %}
-    <h2>Login plugins</h2>
-    {% if message %}
-    <div class="alert alert-{{message_type}}">
-        <p>{{ message }}</p>
-    </div>
-    {% endif %}
-
-    {% for p in available %}
-        <div class="row">
-        <div class="col-md-3 col-sm-3 col-xs-6">{{ p }}</div>
-        <div class="col-md-3 col-sm-3 col-xs-6">
-        {% if p in enabled %}
-            <a class="btn btn-default" href="{{ basepath }}/admin/login/disable/{{ p }}">Disable</a>
-            <a class="btn btn-default" href="{{ basepath }}/admin/login/{{ p }}">Configure</a>
-        {% else %}
-            <a class="btn btn-default" href="{{ basepath }}/admin/login/enable/{{ p }}">Enable</a>
-        {% endif %}
-        </div>
-        </div>
-    {% endfor %}
-
-    <h3>Plugins order</h3>
-    <div class="col-md-3 col-sm-3 col-xs-6">{{ ', '.join(enabled) }}</div>
-    <div class="col-md-3 col-sm-3 col-xs-6">
-        <a class="btn btn-default" href="{{ basepath }}/admin/login/order">configure</a>
-    </div>
-{% endif %}
-{% endblock %}
diff --git a/templates/admin/login_order.html b/templates/admin/login_order.html
deleted file mode 100644 (file)
index b14c4d9..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{% extends "master-admin.html" %}
-{% block main %}
-    <h2>{{ title }}</h2>
-    {% if message %}
-    <div class="alert alert-{{message_type}}">
-        <p>{{ message }}</p>
-    </div>
-    {% endif %}
-    <div id="options">
-        <form role="form" id="{{ name }}" action="{{ action }}" method="post" enctype="application/x-www-form-urlencoded">
-
-        <div class="form-group">
-        <label for="order">Login plugins order:</label>
-            <input type="text" class="form-control" name="order" value="{{ ', '.join(options) }}" />
-        </div>
-        <span class="help-block">Plugins order</span>
-
-        <button id="submit" class="btn btn-primary" name="submit" type="submit" value="Submit">
-            Save
-        </button>
-        <a href="{{ basepath }}/admin/login" class="btn btn-default" title="Back">Back</a>
-        </form>
-    </div>
-{% endblock %}
-
diff --git a/templates/admin/plugins.html b/templates/admin/plugins.html
new file mode 100644 (file)
index 0000000..7bbe544
--- /dev/null
@@ -0,0 +1,108 @@
+{% extends "master-admin.html" %}
+{% block main %}
+{% if user.is_admin %}
+
+    <div class = "row">
+        <div class="col-md-6 col-sm-6 col-xs-6">
+            <h2>{{ title }}</h2>
+        </div>
+        <div class="col-md-6 col-sm-6 col-xs-6" role="alert">
+          {% if message and message_type != 'success' %}
+            <div class="alert alert-{{message_type}}">
+                <p>{{ message }}</p>
+            </div>
+          {% endif %}
+        </div>
+    </div>
+
+    <table class="table">
+    {% for p in enabled %}
+    <tr><td>
+      <div class="row">
+        <div class="col-md-3 col-sm-3 col-xs-5">
+          <strong>{{ p }}</strong>
+        </div>
+        <div class="col-md-7 col-sm-7 col-xs-5">
+          <div class="row">
+          <div class="col-md-6 col-sm-6 col-xs-12">
+              <a class="text-info" href="{{ baseurl }}/disable/{{ p }}">Disable</a>
+          </div>
+          <div class="col-md-6 col-sm-6 col-xs-12">
+            {%- if available[p].get_config_desc() %}
+              <a class="text-primary" href="{{ baseurl }}/{{ p }}">Configure</a>
+            {% endif %}
+          </div>
+          </div>
+        </div>
+        <div class="col-md-2 col-sm-2 col-xs-1">
+          {%- if not (loop.first and loop.last) %}
+          <form role="form" id="{{ order_name }}" action="{{ order_action }}" method="post" enctype="application/x-www-form-urlencoded">
+            {%- set outer_loop = loop %}
+            {%- for move in ['&uarr;', '&darr;'] %}
+              {%- if move == '&uarr;' %}
+                {%- if outer_loop.first %}
+                  {%- set state='disabled' %}
+                {%- else %}
+                  {%- set state='btn-default' %}
+                  {%- set idx0=outer_loop.index0-1 %}
+                  {%- set idx1=outer_loop.index0 %}
+                {%- endif %}
+              {%- else %}
+                {%- if outer_loop.last %}
+                  {%- set state='disabled' %}
+                {%- else %}
+                  {%- set state='btn-default' %}
+                  {%- set idx0=outer_loop.index0 %}
+                  {%- set idx1=outer_loop.index0+1 %}
+                {%- endif %}
+              {%- endif %}
+              <button id="submit" class="btn {{ state }}" name="order" type="submit" value="
+              {%- for i in range(enabled|length) %}
+                {%- if i == idx0 -%}
+                  {{- enabled[idx1] -}}
+                {%- elif i == idx1 -%}
+                  {{- enabled[idx0] -}}
+                {%- else -%}
+                  {{- enabled[i] -}}
+                {%- endif -%}
+                {%- if not loop.last -%},{%- endif -%}
+              {%- endfor -%}
+              ">{{ move }}</button>
+            {%- endfor %}
+          </form>
+          {%- endif %}
+        </div>
+      </div>
+    </td></tr>
+    {% endfor %}
+
+    {% for p in available if not p in enabled %}
+    <tr><td>
+      <div class="row">
+        <div class="col-md-3 col-sm-3 col-xs-5">
+            <strong>{{ p }}</strong>
+        </div>
+        <div class="col-md-7 col-sm-7 col-xs-6">
+          <div class="row">
+          <div class="col-md-6 col-sm-6 col-xs-12">
+              <a class="text-info" href="{{ baseurl }}/enable/{{ p }}">Enable</a>
+          </div>
+          <div class="col-md-6 col-sm-6 col-xs-12">
+            {%- if available[p].get_config_desc() %}
+              <span class="text-muted">Configure</span>
+            {% endif %}
+          </div>
+          </div>
+        </div>
+        <div class="col-md-2 col-sm-2 col-xs-1">
+        </div>
+      </div>
+    </td></tr>
+    {% endfor %}
+
+    <tr><td>
+    </td></tr>
+    </table>
+
+{% endif %}
+{% endblock %}