Browse Source

Add management commands for plugins

Peter Justin 7 years ago
parent
commit
cc2fb24303
3 changed files with 126 additions and 13 deletions
  1. 124 12
      flaskbb/cli/plugins.py
  2. 2 0
      flaskbb/cli/utils.py
  3. 0 1
      flaskbb/management/views.py

+ 124 - 12
flaskbb/cli/plugins.py

@@ -8,42 +8,154 @@
     :copyright: (c) 2016 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
-import pluggy
+import sys
+
 import click
 from flask import current_app
 from flask.cli import with_appcontext
 
+from flaskbb.extensions import db
 from flaskbb.cli.main import flaskbb
+from flaskbb.cli.utils import validate_plugin, check_cookiecutter
+from flaskbb.plugins.models import PluginRegistry, PluginStore
+from flaskbb.plugins.utils import remove_zombie_plugins_from_db
 
 
 @flaskbb.group()
 def plugins():
-    """Plugins command sub group."""
+    """Plugins command sub group. If you want to run migrations or do some
+    i18n stuff checkout the corresponding command sub groups."""
     pass
 
 
-
 @plugins.command("list")
 @with_appcontext
 def list_plugins():
     """Lists all installed plugins."""
-    click.secho("[+] Listing all installed plugins...", fg="cyan")
-
     enabled_plugins = current_app.pluggy.list_plugin_distinfo()
     if len(enabled_plugins) > 0:
         click.secho("[+] Enabled Plugins:", fg="blue", bold=True)
         for plugin in enabled_plugins:
-            # plugin[0] is the module
-            plugin = plugin[1]
-            click.secho("\t- {} (version {})".format(
-                plugin.key, plugin.version), bold=True
+            p_mod = plugin[0]
+            p_dist = plugin[1]
+            click.secho("\t- {}\t({}), version {}".format(
+                current_app.pluggy.get_name(p_mod).title(), p_dist.key,
+                p_dist.version), bold=True
             )
 
     disabled_plugins = current_app.pluggy.list_disabled_plugins()
     if len(disabled_plugins) > 0:
         click.secho("[+] Disabled Plugins:", fg="yellow", bold=True)
         for plugin in disabled_plugins:
-            plugin = plugin[1]
-            click.secho("\t- {} (version {})".format(
-                plugin.key, plugin.version), bold=True
+            p_mod = plugin[0]
+            p_dist = plugin[1]
+            click.secho("\t- {}\t({}), version {}".format(
+                p_mod.title(), p_dist.key,
+                p_dist.version), bold=True
             )
+
+
+@plugins.command("enable")
+@click.argument("plugin_name")
+@with_appcontext
+def enable_plugin(plugin_name):
+    """Enables a plugin."""
+    validate_plugin(plugin_name)
+    plugin = PluginRegistry.query.filter_by(name=plugin_name).first_or_404()
+
+    if plugin.enabled:
+        click.secho("Plugin '{}' is already enabled.".format(plugin.name))
+
+    plugin.enabled = True
+    plugin.save()
+    click.secho("[+] Plugin '{}' enabled.".format(plugin.name), fg="green")
+
+
+@plugins.command("disable")
+@click.argument("plugin_name")
+@with_appcontext
+def disable_plugin(plugin_name):
+    """Disables a plugin."""
+    validate_plugin(plugin_name)
+    plugin = PluginRegistry.query.filter_by(name=plugin_name).first_or_404()
+
+    if not plugin.enabled:
+        click.secho("Plugin '{}' is already disabled.".format(plugin.name))
+
+    plugin.enabled = False
+    plugin.save()
+    click.secho("[+] Plugin '{}' disabled.".format(plugin.name), fg="green")
+
+
+@plugins.command("install")
+@click.argument("plugin_name")
+@click.option("--force", "-f", default=False, is_flag=True,
+              help="Overwrites existing settings")
+def install(plugin_name, force):
+    """Installs a plugin (no migrations)."""
+    validate_plugin(plugin_name)
+    plugin = PluginRegistry.query.filter_by(name=plugin_name).first_or_404()
+
+    if not plugin.enabled:
+        click.secho("[+] Can't install disabled plugin. "
+                    "Enable '{}' Plugin first.".format(plugin.name), fg="red")
+        sys.exit(0)
+
+    if plugin.is_installable:
+        plugin_module = current_app.pluggy.get_plugin(plugin.name)
+        plugin.add_settings(plugin_module.SETTINGS, force)
+        click.secho("[+] Plugin has been installed.", fg="green")
+    else:
+        click.secho("[+] Nothing to install.", fg="green")
+
+
+@plugins.command("uninstall")
+@click.argument("plugin_name")
+def uninstall(plugin_name):
+    """Uninstalls a plugin (no migrations)."""
+    validate_plugin(plugin_name)
+    plugin = PluginRegistry.query.filter_by(name=plugin_name).first_or_404()
+
+    if plugin.is_installed:
+        PluginStore.query.filter_by(plugin_id=plugin.id).delete()
+        db.session.commit()
+        click.secho("[+] Plugin has been uninstalled.", fg="green")
+    else:
+        click.secho("[+] Nothing to uninstall.", fg="green")
+
+
+@plugins.command("cleanup")
+@with_appcontext
+def cleanup():
+    """Removes zombie plugins from FlaskBB.
+
+    A zombie plugin is a plugin
+    which exists in the database but isn't installed in the env anymore.
+    """
+    deleted_plugins = remove_zombie_plugins_from_db()
+    if len(deleted_plugins) > 0:
+        click.secho("[+] Removed following zombie plugins from FlaskBB: ",
+                    fg="green", nl=False)
+        click.secho("{}".format(", ".join(deleted_plugins)))
+    else:
+        click.secho("[+] No zombie plugins found.", fg="green")
+
+
+@plugins.command("new")
+@click.argument("plugin_name", callback=check_cookiecutter)
+@click.option("--template", "-t", type=click.STRING,
+              default="https://github.com/sh4nks/cookiecutter-flaskbb-plugin",
+              help="Path to a cookiecutter template or to a valid git repo.")
+@click.option("--out-dir", "-o", type=click.STRING,
+              help="The location for the new FlaskBB plugin.")
+def new_plugin(plugin_name, template, out_dir):
+    """Creates a new plugin based on the cookiecutter plugin
+    template. Defaults to this template:
+    https://github.com/sh4nks/cookiecutter-flaskbb-plugin.
+    It will either accept a valid path on the filesystem
+    or a URL to a Git repository which contains the cookiecutter template.
+    """
+    from cookiecutter.main import cookiecutter  # noqa
+    cookiecutter(template, output_dir=out_dir)
+    click.secho("[+] Created new plugin {} in {}".format(plugin_name, out_dir),
+                fg="green", bold=True)

+ 2 - 0
flaskbb/cli/utils.py

@@ -74,6 +74,8 @@ def validate_plugin(plugin):
           the appcontext can't be found and using with_appcontext doesn't
           help either.
     """
+    # list_name holds all plugin names, also the disabled ones (they won't do
+    # anything as they are set as 'blocked' on pluggy)
     if plugin not in current_app.pluggy.list_name():
         raise FlaskBBCLIError("Plugin {} not found.".format(plugin), fg="red")
     return True

+ 0 - 1
flaskbb/management/views.py

@@ -19,7 +19,6 @@ from flask_allows import Not, Permission
 from flask_babelplus import gettext as _
 from flask_login import current_user, login_fresh
 from flaskbb import __version__ as flaskbb_version
-from flaskbb._compat import iteritems
 from flaskbb.extensions import allows, celery, db
 from flaskbb.forum.forms import UserSearchForm
 from flaskbb.forum.models import Category, Forum, Post, Report, Topic