Plugin architecture
===================

:Author: Edward Z. Yang <ezyang@mit.edu>

Wizard uses the standard :mod:`pkg_resources` plugin infrastructure from
`setuptools <http://pypi.python.org/pypi/setuptools>`_ to add support
for plugins in Wizard.  We recommend the following excellent tutorials
in for learning how to register plugins using this infrastructure:

* `Using Entry Points to Write Plugins
  <http://pylonshq.com/docs/en/0.9.7/advanced_pylons/entry_points_and_plugins/>`_
* `Dynamic Discovery of Services and Plugins
  <http://peak.telecommunity.com/DevCenter/setuptools#dynamic-discovery-of-services-and-plugins>`_

Wizard will automatically use any plugins that it sees registered, so
plugin authors should take care to "do no harm" if a plugin is unable to
do anything useful.



Registering plugins without eggs
--------------------------------

To register a plugin without an egg, you need to prepare a special
directory that you will add to your :envvar:`PYTHONPATH`.  Create a
setuptools (not distutils) ``setup.py`` file that sets ``entry_points``
to be the plugins that are to be registered, and then run ``python
setup.py egg_info``.



``wizard.app``
--------------

Used to register custom applications that Wizard has
support for.  Key is user-visible name of application (e.g.
``wordpress``) and value is application class (a subclass of
:class:`wizard.app.Application`) to be instantiated to
represent this application (e.g. ``wizard.app.wordpress:Application``)::

    [wizard.app]
    wordpress = wizard.app.wordpress:Application

For more information on how to create an application, check
:doc:`Creating a Repository <create>`.



.. _wizard.install.strategy:

``wizard.install.strategy``
---------------------------

Used during installation to automatically determine values for
installation parameters the user may have omitted.  Plugin should be a
subclass of :class:`wizard.install.Strategy`.  The class that runs this
plugin is :class:`wizard.install.ArgSchema`.



.. _wizard.sql.auth:

``wizard.sql.auth``
--------------------

Used to fill in a user's database authentication credentials if none are
able to be determined from an application's configuration files.  Plugin
should be a function that takes a single required argument ``url``,
which is a :class:`sqlalchemy.engine.url.URL` with partial
authentication information, including a database name, and should return either
``None`` if it was unable to fill in authentication credentials for the
user, or the completed URL.  Mutating the passed argument and then
returning it is the expected modus operandi of a plugin.  The function
that runs this plugin is :func:`wizard.sql.auth`.

.. note::

    If Wizard is able to determine the login credentials from the
    application's source files, these plugins will not be run.

.. _wizard.sql.drop:

``wizard.sql.drop``
------------------------------

Used to remove a SQL database, as might be done when an application is
being uninstalled.  Plugin should be a function that takes a single
required argument ``dsn``, which is a
:class:`sqlalchemy.engine.url.URL` and should return either ``None`` if
it was unable to delete the database, or ``True`` in the event of
success.  This is a good plugin to implement if you don't give your
users ``DROP DATABASE`` permissions, and instead they have to create and
delete databases through another interface.  The function that runs this
plugin is :func:`wizard.sql.drop`.

.. _wizard.deploy.web:

``wizard.deploy.web``
---------------------

Used to fill in a user's web URL if it is not able to be determined from
an application's configuration files.  Plugin should be a function that
takes a single required argument ``dir``, which is the directory to
determint the web URL(s) for, and return a list or generator of
:class:`urlparse.ParseResult` objects or URL strings of possible web locations
for an application.  The function that runs this plugin is
:func:`wizard.deploy.web`.

.. note::

    If Wizard is able to determine the web URL from the application's
    source files or database, these plugins will not run.



.. _wizard.user.email:

``wizard.user.email``
---------------------

Used to determine a user's email address.  By default, we use a
heuristic approach;  if your system offers this information in a
canonical form we recommend taking advantage of it.  Plugin should be a
function that takes a single required argument ``name`` and returns a
string email address corresponding to that user, or ``None`` if it
was unable to determine an email address.  The function that runs this
plugin is :func:`wizard.user.email`.

.. note::

    If the :envvar:`EMAIL` environment variable is set and we are
    requesting the current user's email, these plugins will not run.



.. _wizard.user.operator:

``wizard.user.operator``
------------------------

If Wizard is running as root or as a superuser masquerading as another
user, it is still useful to record who was in front of the screen.  The default
fallback is checking :envvar:`SUDO_USER`; if your system gives more
information we recommend taking advantage of it.



.. _wizard.user.passwd:

``wizard.user.passwd``
----------------------

If Wizard is running on a system with a network filesystem (such as NFS
or AFS), the standard system ``passwd`` database may not actually
resolve the username from a UID.  Use this hook to implement an
alternative ``passwd`` lookup.  Plugin should be a function that takes a
directory (to determine filesystem off of) and a user ID (to perform the
lookup on).  The directory is guaranteed to be a real path.  The plugin
should return an instance of :class:`wizard.user.Info` or ``None``, if
it was unable to perform the lookup.  The function that runs this plugin
is :func:`wizard.user.passwd`.



.. _wizard.user.quota:

``wizard.user.quota``
---------------------

Wizard has safeguards for avoiding exceeding user quotas when an
automated upgrade is being performed.  Unfortunately, methods for
enforcing quotas are frequently highly system dependent.  Use this hook
to implement quota usage and limit reporting.  Plugin should be a
function that takes a single required argument ``dir``, which is the
directory to determine the quota for, and return a tuple ``(quota usage,
quota limit)`` or ``None`` if it could not determine quota.  The
function that runs this plugin is :func:`wizard.user.quota`.
