Browse Source

Plugins can now be enabled/disabled and installed/uninstalled.

sh4nks 11 years ago
parent
commit
0c95ff8223

+ 23 - 3
flaskbb/admin/models.py

@@ -21,7 +21,7 @@ def normalize_to(value, value_type, reverse=False):
     """
     if reverse:
         if value_type == 'array':
-            return value.split(',')
+            return value
         if value_type == 'integer':
             return int(value)
         if value_type == 'float':
@@ -33,7 +33,7 @@ def normalize_to(value, value_type, reverse=False):
         return value
     else:
         if value_type == 'array':
-            return ",".join(value)
+            return value.replace(" ", "").strip(",")
         if value_type == 'integer':
             return int(value)
         if value_type == 'float':
@@ -51,12 +51,19 @@ class SettingsGroup(db.Model):
     key = db.Column(db.String, primary_key=True)
     name = db.Column(db.String, nullable=False)
     description = db.Column(db.String, nullable=False)
-    settings = db.relationship("Setting", lazy="dynamic", backref="group")
+    settings = db.relationship("Setting", lazy="dynamic", backref="group",
+                               cascade="all, delete-orphan")
 
     def save(self):
+        """Saves a settingsgroup."""
         db.session.add(self)
         db.session.commit()
 
+    def delete(self):
+        """Deletes a settingsgroup."""
+        db.session.delete(self)
+        db.session.commit()
+
 
 class Setting(db.Model):
     __tablename__ = "settings"
@@ -172,6 +179,13 @@ class Setting(db.Model):
                               description=setting.description)
                 )
 
+            if setting.input_type == "array":
+                setattr(
+                    SettingsForm, setting.key,
+                    TextField(setting.name, validators=field_validators,
+                              description=setting.description)
+                )
+
             # SelectField
             if setting.input_type == "choice" and "choices" in setting.extra:
                 setattr(
@@ -240,5 +254,11 @@ class Setting(db.Model):
         return settings
 
     def save(self):
+        """Saves a setting"""
         db.session.add(self)
         db.session.commit()
+
+    def delete(self):
+        """Deletes a setting"""
+        db.session.delete(self)
+        db.session.commit()

+ 68 - 6
flaskbb/admin/views.py

@@ -9,12 +9,13 @@
     :license: BSD, see LICENSE for more details.
 """
 import sys
+import os
 from datetime import datetime
 
 from flask import (Blueprint, current_app, request, redirect, url_for, flash,
                    __version__ as flask_version)
 from flask.ext.login import current_user
-from flask.ext.plugins import get_plugins_list, get_plugin
+from flask.ext.plugins import get_all_plugins, get_plugin, get_plugin_from_all
 from flask.ext.themes2 import get_themes_list
 
 from flaskbb import __version__ as flaskbb_version
@@ -146,20 +147,81 @@ def reports():
 @admin.route("/plugins")
 @admin_required
 def plugins():
-    return render_template("admin/plugins.html", plugins=get_plugins_list())
+    plugins = get_all_plugins()
+    return render_template("admin/plugins.html", plugins=plugins)
 
 
 @admin.route("/plugins/enable/<plugin>")
 def enable_plugin(plugin):
-    plugin = get_plugin(plugin)
-    current_app.plugin_manager.enable_plugins([plugin])
+    plugin = get_plugin_from_all(plugin)
+    if not plugin.enabled:
+        plugin_dir = os.path.join(
+            os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
+            "plugins", plugin.identifier
+        )
+
+        disabled_file = os.path.join(plugin_dir, "DISABLED")
+
+        os.remove(disabled_file)
+
+        flash("Plugin should be enabled. Please reload your app.", "success")
+
+        flash("If you are using a host which doesn't support writting on the "
+              "disk, this won't work - than you need to delete the "
+              "'DISABLED' file by yourself.", "info")
+    else:
+        flash("Plugin is not enabled", "danger")
+
     return redirect(url_for("admin.plugins"))
 
 
 @admin.route("/plugins/disable/<plugin>")
 def disable_plugin(plugin):
-    plugin = get_plugin(plugin)
-    current_app.plugin_manager.disable_plugins([plugin])
+    try:
+        plugin = get_plugin(plugin)
+    except KeyError:
+        flash("Plugin {} not found".format(plugin), "danger")
+        return redirect(url_for("admin.plugins"))
+
+    plugin_dir = os.path.join(
+        os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
+        "plugins", plugin.identifier
+    )
+
+    disabled_file = os.path.join(plugin_dir, "DISABLED")
+
+    open(disabled_file, "a").close()
+
+    flash("Plugin should be disabled. Please reload your app.", "success")
+
+    flash("If you are using a host which doesn't "
+          "support writting on the disk, this won't work - than you need to "
+          "create a 'DISABLED' file by yourself.", "info")
+
+    return redirect(url_for("admin.plugins"))
+
+
+@admin.route("/plugins/uninstall/<plugin>")
+def uninstall_plugin(plugin):
+    plugin = get_plugin_from_all(plugin)
+    if plugin.uninstallable:
+        plugin.uninstall()
+        flash("Plugin {} has been uninstalled.".format(plugin.name), "success")
+    else:
+        flash("Cannot uninstall Plugin {}".format(plugin.name), "danger")
+
+    return redirect(url_for("admin.plugins"))
+
+
+@admin.route("/plugins/install/<plugin>")
+def install_plugin(plugin):
+    plugin = get_plugin_from_all(plugin)
+    if plugin.installable and not plugin.uninstallable:
+        plugin.install()
+        flash("Plugin {} has been installed.".format(plugin.name), "success")
+    else:
+        flash("Cannot install Plugin {}".format(plugin.name), "danger")
+
     return redirect(url_for("admin.plugins"))
 
 

+ 21 - 0
flaskbb/plugins/__init__.py

@@ -1,8 +1,29 @@
 from flask.ext.plugins import Plugin
 from flask import current_app
 
+from flaskbb.admin.models import SettingsGroup
+
 
 class FlaskBBPlugin(Plugin):
+
+    #: Set this to true if the plugin needs to install additional things
+    settings_key = None
+
+    @property
+    def installable(self):
+        if self.settings_key is not None:
+            return True
+        return False
+
+    @property
+    def uninstallable(self):
+        if self.installable:
+            group = SettingsGroup.query.filter_by(key=self.settings_key).first()
+            if group and len(group.settings.all()) > 0:
+                return True
+            return False
+        return False
+
         # Some helpers
     def register_blueprint(self, blueprint, **kwargs):
         """Registers a blueprint."""

+ 26 - 0
flaskbb/plugins/portal/__init__.py

@@ -1,11 +1,29 @@
 from flask.ext.plugins import connect_event
 
 from flaskbb.plugins import FlaskBBPlugin
+from flaskbb.utils.populate import (create_settings_from_fixture,
+                                    delete_settings_from_fixture)
 from .views import portal, inject_portal_link
 
 __version__ = "0.1"
 __plugin__ = "PortalPlugin"
 
+fixture = (
+    ('plugin_portal', {
+        'name': "Portal Settings",
+        "description": "Configure the portal",
+        "settings": (
+            ('plugin_portal_forum_ids', {
+                'value':        "1",
+                'value_type':   "array",
+                'input_type':   "array",
+                'name':         "Forum IDs",
+                'description':  "The forum ids from which forums the posts should be displayed on the portal."
+            }),
+        ),
+    }),
+)
+
 
 class PortalPlugin(FlaskBBPlugin):
 
@@ -19,6 +37,14 @@ class PortalPlugin(FlaskBBPlugin):
 
     version = __version__
 
+    settings_key = 'plugin_portal'
+
     def setup(self):
         self.register_blueprint(portal, url_prefix="/portal")
         connect_event("before-first-navigation-element", inject_portal_link)
+
+    def install(self):
+        create_settings_from_fixture(fixture)
+
+    def uninstall(self):
+        delete_settings_from_fixture(fixture)

+ 14 - 2
flaskbb/templates/admin/plugins.html

@@ -22,8 +22,20 @@
                 {{ plugin.author }}
             </td>
             <td>
-                <a href="{{ url_for('admin.enable_plugin', plugin=plugin.name) }}">Enable</a> |
-                <a href="{{ url_for('admin.disable_plugin', plugin=plugin.name) }}">Disable</a>
+                {% if not plugin.enabled %}
+                <a href="{{ url_for('admin.enable_plugin', plugin=plugin.identifier) }}">Enable</a>
+                {% else %}
+                <a href="{{ url_for('admin.disable_plugin', plugin=plugin.identifier) }}">Disable</a>
+                {% endif %}
+
+                {% set uninstallable = plugin.uninstallable %}
+                {% if plugin.installable and not uninstallable %}
+                <br />
+                <a href="{{ url_for('admin.install_plugin', plugin=plugin.identifier) }}">Install</a>
+                {% endif %}
+                {% if uninstallable %}
+                <a href="{{ url_for('admin.uninstall_plugin', plugin=plugin.identifier) }}">Uninstall</a>
+                {% endif %}
             </td>
         </tr>
         {% endfor %}

+ 15 - 3
flaskbb/utils/populate.py

@@ -15,11 +15,18 @@ from flaskbb.user.models import User, Group
 from flaskbb.forum.models import Post, Topic, Forum, Category
 
 
-def create_default_settings():
-    from flaskbb.fixtures.settings import fixture
-
+def delete_settings_from_fixture(fixture):
     for settingsgroup in fixture:
+        group = SettingsGroup.query.filter_by(key=settingsgroup[0]).first()
+
+        for settings in settingsgroup[1]['settings']:
+            setting = Setting.query.filter_by(key=settings[0]).first()
+            setting.delete()
+        group.delete()
+
 
+def create_settings_from_fixture(fixture):
+    for settingsgroup in fixture:
         group = SettingsGroup(
             key=settingsgroup[0],
             name=settingsgroup[1]['name'],
@@ -43,6 +50,11 @@ def create_default_settings():
             setting.save()
 
 
+def create_default_settings():
+    from flaskbb.fixtures.settings import fixture
+    create_settings_from_fixture(fixture)
+
+
 def create_default_groups():
     """
     This will create the 5 default groups