Browse Source

Update plugin documentation

Peter Justin 7 years ago
parent
commit
6a7ccdbcd3

+ 1 - 0
docs/contents.rst.inc

@@ -8,6 +8,7 @@ Contents
    cli
    cli
    theming
    theming
    plugins
    plugins
+   plugin_development
    hooks
    hooks
    settings
    settings
    api
    api

+ 253 - 0
docs/plugin_development.rst

@@ -0,0 +1,253 @@
+.. _plugin_development:
+
+
+Developing new Plugins
+======================
+
+If you want to write a plugin, it's a very good idea to checkout existing
+plugins. A good starting point for example is the `Portal Plugin`_.
+
+Also make sure to check out the cookiecutter-flaskbb-plugin project, which is a
+cookiecutter template which helps you to create new plugins.
+
+For example, the structure of a plugin could look like this:
+
+.. sourcecode:: text
+
+    your_package_name
+    |-- setup.py
+    |-- my_plugin
+        |-- __init__.py
+        |-- views.py
+        |-- models.py
+        |-- forms.py
+        |-- static
+        |   |-- style.css
+        |-- templates
+            |-- myplugin.html
+        |-- migrations
+            |-- 59f7c49b6289_init.py
+
+Metadata
+--------
+
+FlaskBB Plugins are usually following the naming scheme of
+``flaskbb-plugin-YOUR_PLUGIN_NAME`` which should make them better
+distinguishable from other PyPI distributions.
+
+A proper plugin should have at least put the following metadata into
+the ``setup.py`` file.
+
+.. sourcecode:: python
+
+    setup(
+        name="flaskbb-plugin-YOUR_PLUGIN_NAME",  # name on PyPI
+        packages=["your_package_name"],  # name of the folder your plugin is located in
+        version='1.0',
+        url=<url to your project>,
+        license=<your license>,
+        author=<you>,
+        author_email=<your email>,
+        description=<your short description>,
+        long_description=__doc__,
+        include_package_data=True,
+        zip_safe=False,
+        platforms='any',
+
+        entry_points={
+            'flaskbb_plugin': [
+                'unique_name_of_plugin = your_package_name.pluginmodule',  # most important part
+            ]
+        }
+    )
+
+The most important part here is the ``entry_point``. Here you tell FlaskBB the
+unique name of your plugin and where your plugin module is located inside
+your project. Entry points are a feature that is provided by setuptools.
+FlaskBB looks up the ``flaskbb_plugin`` entrypoint to discover its plugins.
+Have a look at the `setup script`_ documentation and the `sample setup.py`_
+file to get a better idea what the ``setup.py`` file is all about it.
+
+For a full example, checkout the `Portal Plugin`_.
+
+.. _`setup script`: https://docs.python.org/3.6/distutils/setupscript.html#additional-meta-data
+.. _`sample setup.py`: https://github.com/pypa/sampleproject/blob/master/setup.py
+.. _`Portal Plugin`: https://github.com/sh4nks/flaskbb-plugins/tree/master/portal
+
+
+Settings
+--------
+Plugins can create settings which integrate with the 'Settings' tab of
+the Admin Panel.
+
+The settings are stored in a dictionary with a given structure. The name of
+the dictionary must be ``SETTINGS`` and be placed in the plugin module.
+
+The structure of the ``SETTINGS`` dictionary is best explained via an
+example::
+
+    SETTINGS = {
+        # This key has to be unique across FlaskBB.
+        # Using a prefix is recommended.
+        'forum_ids': {
+
+            # Default Value. The type of the default value depends on the
+            # SettingValueType.
+            'value': [1],
+
+            # The Setting Value Type.
+            'value_type': SettingValueType.selectmultiple,
+
+            # The human readable name of your configuration variable
+            'name': "Forum IDs",
+
+            # A short description of what the settings variable does
+            'description': ("The forum ids from which forums the posts "
+                            "should be displayed on the portal."),
+
+            # extra stuff like the 'choices' in a select field or the
+            # validators are defined in here
+            'extra': {"choices": available_forums, "coerce": int}
+        }
+    }
+
+.. currentmodule:: flaskbb.utils.forms
+
+.. table:: Available Setting Value Types
+    :widths: auto
+
+    ======================================== =================
+    Setting Value Type                       Parsed & Saved As
+    ======================================== =================
+    :attr:`SettingValueType.string`          :class:`str`
+    :attr:`SettingValueType.integer`         :class:`int`
+    :attr:`SettingValueType.float`           :class:`float`
+    :attr:`SettingValueType.boolean`         :class:`bool`
+    :attr:`SettingValueType.select`          :class:`list`
+    :attr:`SettingValueType.selectmultiple`  :class:`list`
+    ======================================== =================
+
+.. table:: Available Additional Options via the ``extra`` Keyword
+
+    =========== ====================== ========================================
+    Options     Applicable Types       Description
+    =========== ====================== ========================================
+    ``min``     string, integer, float **Optional.** The minimum required
+                                       length of the setting value. If used on
+                                       a numeric type, it will check the
+                                       minimum value.
+    ``max``     string, integer, float **Optional.** The maximum required
+                                       length of the setting value. If used on
+                                       a numeric type, it will check the
+                                       maximum value.
+    ``choices`` select, selectmultiple **Required.** A callable which returns
+                                       a sequence of (value, label) pairs.
+    ``coerce``  select, selectmultiple **Optional.** Coerces the select values
+                                       to the given type.
+    =========== ====================== ========================================
+
+
+Validating the size of the integer/float and the length of the string fields
+is also possible via the ``min`` and ``max`` keywords::
+
+    'recent_topics': {
+        ...
+        'extra': {"min": 1},
+    },
+
+The ``select`` and ``selectmultiple`` fields have to provide a callback which
+lists all the available choices. This is done via the ``choices`` keyword.
+In addition to that they can also specify the ``coerce`` keyword which will
+coerce the input value into the specified type.::
+
+    'forum_ids': {
+        ...
+        'extra': {"choices": available_forums, "coerce": int}
+    }
+
+For more information see the :doc:`settings` chapter.
+
+
+Using Hooks
+-----------
+Hooks are invoked based on an event occurring within FlaskBB. This makes it
+possible to change the behavior of certain actions without modifying the
+actual source code of FlaskBB.
+
+For your plugin to actually do something useful, you probably want to 'hook'
+your code into FlaskBB. This can be done throughout a lot of places in the
+code. FlaskBB loads and calls the hook calls hook functions from registered
+plugins for any given hook specification.
+
+Each hook specification has a corresponding hook implementation. By default,
+all hooks that are prefix with ``flaskbb_`` will be marked as a standard
+hook implementation. It is possible to modify the behavior of hooks.
+For example, default hooks are called in LIFO registered order.
+A hookimpl can influence its call-time invocation position using special
+attributes. If marked with a "tryfirst" or "trylast" option it will be executed
+first or last respectively in the hook call loop::
+
+    hookimpl = HookimplMarker('flaskbb')
+
+    @hookimpl(trylast=True)
+    def flaskbb_additional_setup(app):
+        return "save the best for last"
+
+
+In order to extend FlaskBB with your Plugin you will need to connect your
+callbacks to the hooks.
+
+Let's look at an actually piece of `used code`_.
+
+.. sourcecode:: python
+
+    def flaskbb_load_blueprints(app):
+        app.register_blueprint(portal, url_prefix="/portal")
+
+By defining a function called ``flaskbb_load_blueprints``, which has a
+corresponding hook specification under the same name. FlaskBB will pass
+in an ``app`` object as specified in the hook spec, which we will use to
+register a new blueprint. It is also possible to completely omit the ``app``
+argument from the function where it is **not possible** to add new arguments to
+the hook implemention.
+
+For a complete list of all available hooks in FlaskBB see the :doc:`hooks`
+section.
+
+pytest and pluggy are good resources to get better understanding on how to
+write `hook functions`_ using `pluggy`_.
+
+.. _`used code`: https://github.com/sh4nks/flaskbb-plugins/blob/master/portal/portal/__init__.py#L31
+.. _`hook functions`: https://docs.pytest.org/en/latest/writing_plugins.html#writing-hook-functions
+.. _`pluggy`: https://pluggy.readthedocs.io/en/latest/#defining-and-collecting-hooks
+
+
+Plugin Registry
+---------------
+The plugin registry holds all available plugins. It shows the plugin's status
+whether it is enabled or disabled, installable or installed. The registry also
+holds a reference to the plugin's instance, provides an interface to access
+the plugins metadata and stores its settings.
+
+You can query it like any SQLAlchemy Model::
+
+    plugin = PluginRegistry.query.filter_by(name="portal").first()
+
+
+.. autoclass:: flaskbb.plugins.models.PluginRegistry
+    :members:
+
+
+Plugin Manager
+--------------
+FlaskBB overrides the PluginManager from pluggy to provide some additional functionality like accessing the information stored in a setup.py file. The plugin manager will only list the currently enabled plugins and can be used to directly access the plugins instance by its name.
+
+
+Accessing a plugins instance is as easy as::
+
+    plugin_instance = current_app.pluggy.get_plugin(name)
+
+
+.. autoclass:: flaskbb.plugins.manager.FlaskBBPluginManager
+    :members:
+    :inherited-members:

+ 0 - 17
docs/plugin_tutorial/index.rst

@@ -1,17 +0,0 @@
-.. _tutorial:
-
-Plugin Tutorial (WIP)
-=====================
-
-
-This tutorial is based on the plugin which is shipped by default with FlaskBB.
-If you want the full sourcecode in advance or for comparison, check out
-the `portal plugin`_.
-
-.. _portal plugin:
-   https://github.com/sh4nks/flaskbb/tree/master/flaskbb/plugins/portal
-
-.. toctree::
-   :maxdepth: 2
-
-   structure

+ 0 - 4
docs/plugin_tutorial/structure.rst

@@ -1,4 +0,0 @@
-.. _structure:
-
-Step 1: Structure
-=================

+ 33 - 243
docs/plugins.rst

@@ -8,199 +8,14 @@ Plugins
 FlaskBB provides a full featured plugin system. This system allows you to
 FlaskBB provides a full featured plugin system. This system allows you to
 easily extend or modify FlaskBB without touching any FlaskBB code. Under the
 easily extend or modify FlaskBB without touching any FlaskBB code. Under the
 hood it uses the `pluggy plugin system`_ which does most of the heavy lifting
 hood it uses the `pluggy plugin system`_ which does most of the heavy lifting
-for us.
+for us. A list of available plugins can be found at the `GitHub Wiki`_. A
+proper index for FlaskBB Plugins and Themes still have to be built.
 
 
-.. _`pluggy plugin system`: https://pluggy.readthedocs.io/en/latest/
-
-Structure
----------
-
-A plugin has it's own folder where all the plugin specific files are living.
-For example, the structure of a plugin could look like this
-
-.. sourcecode:: text
-
-    my_plugin_package
-    |-- setup.py
-    |-- my_plugin
-        |-- __init__.py
-        |-- views.py
-        |-- models.py
-        |-- forms.py
-        |-- static
-        |   |-- style.css
-        |-- templates
-            |-- myplugin.html
-        |-- migrations
-            |-- 59f7c49b6289_init.py
-
-
-Metadata
-~~~~~~~~
-
-A proper plugin should have at least put the following metadata into
-the ``setup.py`` file.
-
-.. sourcecode:: python
-
-    setup(
-        name="myproject",
-        version='1.0',
-        url=<url to your project>,
-        license=<your license>,
-        author=<you>,
-        author_email=<your email>,
-        description=<your short description>,
-        long_description=__doc__,
-        packages=find_packages('.'),
-        include_package_data=True,
-        zip_safe=False,
-        platforms='any',
-
-        entry_points={
-            'flaskbb_plugin': [
-                'unique_name_of_plugin = myproject.pluginmodule',  # most important part
-            ]
-        }
-    )
-
-
-The most important part is the ``entry_point``. Here you tell FlaskBB the
-unique name of your plugin and where your plugin module is located inside
-your project. Entry points are a feature that is provided by setuptools.
-FlaskBB looks up the ``flaskbb_plugin`` entrypoint to discover its plugins.
-
-Have a look at the `setup script`_ documentation and the `sample setup.py`_
-file to get a better idea what the ``setup.py`` file is all about it.
-
-To get a better idea how a plugin looks like, checkout the `Portal Plugin`_.
-
-.. _`setup script`: https://docs.python.org/3.6/distutils/setupscript.html#additional-meta-data
-.. _`sample setup.py`: https://github.com/pypa/sampleproject/blob/master/setup.py
-.. _`Portal Plugin`: https://github.com/sh4nks/flaskbb-plugins/tree/master/portal
-
-
-Settings
---------
-
-Plugins can create settings which integrate with the 'Settings' tab of
-the Admin Panel.
-
-The settings are stored in a dictionary with a given structure. The name of
-the dictionary must be ``SETTINGS`` and be placed in the plugin module.
-
-The structure of the ``SETTINGS`` dictionary is best explained via an
-example::
-
-    SETTINGS = {
-        # This key has to be unique across FlaskBB.
-        # Using a prefix is recommended.
-        'forum_ids': {
-
-            # Default Value. The type of the default value depends on the
-            # SettingValueType.
-            'value': [1],
-
-            # The Setting Value Type.
-            'value_type': SettingValueType.selectmultiple,
-
-            # The human readable name of your configuration variable
-            'name': "Forum IDs",
-
-            # A short description of what the settings variable does
-            'description': ("The forum ids from which forums the posts "
-                            "should be displayed on the portal."),
-
-            # extra stuff like the 'choices' in a select field or the
-            # validators are defined in here
-            'extra': {"choices": available_forums, "coerce": int}
-        }
-    }
-
-.. currentmodule:: flaskbb.utils.forms
-
-.. table:: Available Setting Value Types
-    :widths: auto
-
-    ======================================== =================
-    Setting Value Type                       Parsed & Saved As
-    ======================================== =================
-    :attr:`SettingValueType.string`          :class:`str`
-    :attr:`SettingValueType.integer`         :class:`int`
-    :attr:`SettingValueType.float`           :class:`float`
-    :attr:`SettingValueType.boolean`         :class:`bool`
-    :attr:`SettingValueType.select`          :class:`list`
-    :attr:`SettingValueType.selectmultiple`  :class:`list`
-    ======================================== =================
-
-.. table:: Available Additional Options via the ``extra`` Keyword
-
-    =========== ====================== ========================================
-    Options     Applicable Types       Description
-    =========== ====================== ========================================
-    ``min``     string, integer, float **Optional.** The minimum required
-                                       length of the setting value. If used on
-                                       a numeric type, it will check the
-                                       minimum value.
-    ``max``     string, integer, float **Optional.** The maximum required
-                                       length of the setting value. If used on
-                                       a numeric type, it will check the
-                                       maximum value.
-    ``choices`` select, selectmultiple **Required.** A callable which returns
-                                       a sequence of (value, label) pairs.
-    ``coerce``  select, selectmultiple **Optional.** Coerces the select values
-                                       to the given type.
-    =========== ====================== ========================================
-
-
-Validating the size of the integer/float and the length of the string fields
-is also possible via the ``min`` and ``max`` keywords::
-
-    'recent_topics': {
-        ...
-        'extra': {"min": 1},
-    },
-
-The ``select`` and ``selectmultiple`` fields have to provide a callback which
-lists all the available choices. This is done via the ``choices`` keyword.
-In addition to that they can also specify the ``coerce`` keyword which will
-coerce the input value into the specified type.::
-
-    'forum_ids': {
-        ...
-        'extra': {"choices": available_forums, "coerce": int}
-    }
-
-For more information see the :doc:`settings` chapter.
-
-
-Database
---------
-
-Upgrading, downgrading and generating database revisions is all handled
-via alembic. We make use of alembic's branching feature to manage seperate
-migrations for the plugins. Each plugin will have it's own branch in alembic
-where migrations can be managed. Following commands are used for generaring,
-upgrading and downgrading your plugins database migrations:
-
-* (Auto-)Generating revisions
-    ``flaskbb db revision --branch <plugin_name> "<YOUR_MESSAGE>"``
-
-    Replace <YOUR_MESSAGE> with something like "initial migration" if it's
-    the first migration or with just a few words that will describe the
-    changes of the revision.
-
-* Applying revisions
-    ``flaskbb db upgrade <plugin_name>@head``
-
-    If you want to upgrade to specific revision, replace ``head`` with the
-    revision id.
-
-* Downgrading revisions
-    ``flaskbb db downgrade <plugin_name>@-1``
+If you are interested in creating new plugins, checkout out the
+:doc:`Developing new Plugins </plugin_development>` page.
 
 
-    If you just want to revert the latest revision, just use ``-1``.
-    To downgrade all database migrations, use ``base``.
+.. _`pluggy plugin system`: https://pluggy.readthedocs.io/en/latest/
+.. _`GitHub Wiki`: https://github.com/sh4nks/flaskbb/wiki
 
 
 
 
 Management
 Management
@@ -209,8 +24,8 @@ Management
 Before plugins can be used in FlaskBB, they have to be downloaded, installed
 Before plugins can be used in FlaskBB, they have to be downloaded, installed
 and activated.
 and activated.
 Plugins can be very minimalistic with nothing to install at all (just enabling
 Plugins can be very minimalistic with nothing to install at all (just enabling
-and disabling) to be very complex where you have to run migrations and add
-some additional settings.
+and disabling) to be very complex where you have to `run migrations <./plugins.html#database>`_ and add
+some `additional settings <./plugins.html#install>`_.
 
 
 Download
 Download
 ~~~~~~~~
 ~~~~~~~~
@@ -252,15 +67,16 @@ In our context, by installing a plugin, we mean, to install the settings
 and apply the migrations. Personal Note: I can't think of a better name and
 and apply the migrations. Personal Note: I can't think of a better name and
 I am open for suggestions.
 I am open for suggestions.
 
 
-The migrations have to be applied this way (if any, check the plugins docs)::
-
-    flaskbb db upgrade <plugin_name>@head
-
 The plugin can be installed via the Admin Panel (in tab 'Plugins') or by
 The plugin can be installed via the Admin Panel (in tab 'Plugins') or by
 running::
 running::
 
 
     flaskbb plugins install <plugin_name>
     flaskbb plugins install <plugin_name>
 
 
+
+Make sure to to apply the migrations of the plugin as well (**if any**, check the plugins docs)::
+
+    flaskbb db upgrade <plugin_name>@head
+
 Uninstall
 Uninstall
 ~~~~~~~~~
 ~~~~~~~~~
 
 
@@ -289,7 +105,6 @@ via the Admin Panel or by running::
     You must restart the wsgi/in-built server in order to make the changes
     You must restart the wsgi/in-built server in order to make the changes
     effect your forum.
     effect your forum.
 
 
-
 Enable
 Enable
 ~~~~~~
 ~~~~~~
 
 
@@ -300,55 +115,30 @@ activation command::
     flaskbb plugins enable <plugin_name>
     flaskbb plugins enable <plugin_name>
 
 
 
 
-Hooks
------
-Hooks are invoked based on an event occurring within FlaskBB. This makes it
-possible to change the behavior of certain actions without modifying the
-actual source code of FlaskBB.
-
-For your plugin to actually do something useful, you probably want to 'hook'
-your code into FlaskBB. This can be done throughout a lot of places in the
-code. FlaskBB loads and calls the hook calls hook functions from registered
-plugins for any given hook specification.
-
-Each hook specification has a corresponding hook implementation. By default,
-all hooks that are prefix with ``flaskbb_`` will be marked as a standard
-hook implementation. It is possible to modify the behavior of hooks.
-For example, default hooks are called in LIFO registered order.
-A hookimpl can influence its call-time invocation position using special
-attributes. If marked with a "tryfirst" or "trylast" option it will be executed
-first or last respectively in the hook call loop::
-
-    hookimpl = HookimplMarker('flaskbb')
-
-    @hookimpl(trylast=True)
-    def flaskbb_additional_setup(app):
-        return "save the best for last"
-
-
-In order to extend FlaskBB with your Plugin you will need to connect your
-callbacks to the hooks.
+Database
+--------
 
 
-Let's look at an actually piece of `used code`_.
+Upgrading, downgrading and generating database revisions is all handled
+via alembic. We make use of alembic's branching feature to manage seperate
+migrations for the plugins. Each plugin will have it's own branch in alembic
+where migrations can be managed. Following commands are used for generaring,
+upgrading and downgrading your plugins database migrations:
 
 
-.. sourcecode:: python
+* (Auto-)Generating revisions
+    ``flaskbb db revision --branch <plugin_name> "<YOUR_MESSAGE>"``
 
 
-    def flaskbb_load_blueprints(app):
-        app.register_blueprint(portal, url_prefix="/portal")
+    Replace <YOUR_MESSAGE> with something like "initial migration" if it's
+    the first migration or with just a few words that will describe the
+    changes of the revision.
 
 
-By defining a function called ``flaskbb_load_blueprints``, which has a
-corresponding hook specification under the same name. FlaskBB will pass
-in an ``app`` object as specified in the hook spec, which we will use to
-register a new blueprint. It is also possible to completely omit the ``app``
-argument from the function where it is **not possible** to add new arguments to
-the hook implemention.
+* Applying revisions
+    ``flaskbb db upgrade <plugin_name>@head``
 
 
-For a complete list of all available hooks in FlaskBB see the :doc:`hooks`
-section.
+    If you want to upgrade to specific revision, replace ``head`` with the
+    revision id.
 
 
-pytest and pluggy are good resources to get better understanding on how to
-write `hook functions`_ using `pluggy`_.
+* Downgrading revisions
+    ``flaskbb db downgrade <plugin_name>@-1``
 
 
-.. _`used code`: https://github.com/sh4nks/flaskbb-plugins/blob/master/portal/portal/__init__.py#L31
-.. _`hook functions`: https://docs.pytest.org/en/latest/writing_plugins.html#writing-hook-functions
-.. _`pluggy`: https://pluggy.readthedocs.io/en/latest/#defining-and-collecting-hooks
+    If you just want to revert the latest revision, just use ``-1``.
+    To downgrade all database migrations, use ``base``.