PluginsTips: Difference between revisions

From GNU MediaGoblin Wiki
Jump to navigation Jump to search
(Created page with "==Quickstart== In the [https://gitorious.org/mediagoblin mediagoblin repository], there's a sample plugin under mediagoblin/plugins/sampleplugin that you can use to get starte...")
 
 
(12 intermediate revisions by one other user not shown)
Line 1: Line 1:
==Quickstart==
==Quickstart==
In the [https://gitorious.org/mediagoblin mediagoblin repository], there's a sample plugin under mediagoblin/plugins/sampleplugin that you can use to get started with plugin creation. The simplest way to get up and running is to
In the [https://gitorious.org/mediagoblin mediagoblin repository], there's a sample plugin under mediagoblin/plugins/sampleplugin that you can use to get started with plugin creation. The simplest way to get up and running is to
# Follow [[HackingHowto#How_to_set_up_and_maintain_an_environment_for_hacking_with_virtualenv]] to set up a local mediagoblin instance
# Follow [[HackingHowto#How_to_set_up_and_maintain_an_environment_for_hacking_with_virtualenv]] to set up a local virtualenv mediagoblin instance
#* run it with ./lazyserver.sh so you get debug output (if you run celeryd separately, you won't see logging.info from stuff run in celery tasks)
#* run it with ./lazyserver.sh so you get debug output (if you run celeryd separately, you won't see logging.info from stuff run in celery tasks)
# <tt>cp -r mediagoblin/plugins/sampleplugin mediagoblin/plugins/myplugin</tt> (where "myplugin" is your plugin name)
# <tt>cp -r mediagoblin/plugins/sampleplugin mediagoblin/plugins/myplugin</tt> (where "myplugin" is your plugin name)
# edit <tt>mediagoblin.ini</tt> and add <nowiki>[[mediagoblin.plugins.myplugin]]</nowiki> under the <nowiki>[plugins]</nowiki> section to enable your plugin
# <tt>cp mediagoblin.ini mediagoblin_local.ini</tt>
# edit <tt>mediagoblin_local.ini</tt> and add [[mediagoblin.plugins.myplugin]] to enable your plugin


Now you can look at e.g. other core or non-core plugins for inspiration. See [[Available Plugins]] for non-core plugins.
Now you can look at e.g. other core or non-core plugins for inspiration. See [[Available Plugins]] for non-core plugins.


The way you change things in plugins is by use of '''hooks'''. At certain points in the mediagoblin code, a function will say "run all hooks with name XYZ", and if you've defined a hook with such a name in a plugin you've enabled, it'll get run there. The [http://mediagoblin.readthedocs.org/en/latest/pluginwriter/api.html documentation on Plugin API] is your friend for defining hooks. The sample plugin shows a use of the 'setup' hook.

Note: templates are hooked with a call to pluginapi.register_template_hooks, instead of adding to the hooks variable.


==Making an installable plugin==
==Making an installable plugin==
If you followed the steps above, you can use your plugin by copying it into the plugins folder; however, to get a plugin that is easily installable by users (e.g. with <tt>pip install myplugin</tt>), it should have a certain folder layout. A good example is the [https://github.com/commonsmachinery/mg-rdfa/ RDFa plugin].
If you followed the steps above, you can use your plugin by copying it into the plugins folder; however, to get a plugin that is easily installable by users (e.g. with <tt>pip install myplugin</tt>), it should have a certain folder layout.

A good example is the [https://github.com/commonsmachinery/mg-rdfa/ RDFa plugin]. This uses a setup.py file to install the files under the mediagoblin_rdfa folder into the lib/ folder of your mediagoblin installation. If you've checked out both mediagoblin and mg-rdfa in the same folder, e.g. ~/src/, you can do <pre>cd ~/src/mg-rdfa/
../mediagoblin/bin/python setup.py build
../mediagoblin/bin/python setup.py install</pre> to install it to your mediagoblin instance.

The file layout of the repo is:
<pre>
setup.py
MANIFEST.in
README.md
mediagoblin_rdfa/
__init.py__
templates/
mediagoblin/
plugins/
rdfa/
metadata.html
</pre>

The setup.py file defines an option include_package_data=True, which makes it read the file MANIFEST.in; MANIFEST.in contains rules for which files to include when installing. In this case, rule <tt>recursive-include mediagoblin_rdfa *.html</tt> makes it include the HTML template file (and any other you put under mediagoblin_rdfa), retaining the folder layout.

==Referencing the mediagoblin test set in a plugin==

The tests in mediagoblin define a nice "test_app" and functions for logging in and posting and so on. You can use this in your installable plugin.

From your plugin (assuming a layout like mg-rdfa described above), doing

cp -r ../mediagoblin/mediagoblin/tests .

lets you run

../mediagoblin/bin/py.test tests --boxed

In fact, the only files you need are `conftest.py` and `pytest.ini`, and then you can copy over a single `test_foo.py` (only making sure import statements are absolute) and it should run fine.

You also need to call setup_plugins() in your setup() function for your plugin to be properly loaded before testing.

An example of this test method is in the [https://gitorious.org/mediagoblin-stock/mediagoblin-hidden_original/ "hidden original" plugin], which subclasses the submission tests.


See also [http://mediagoblin.readthedocs.org/en/latest/pluginwriter/tests.html Writing unit tests for plugins] in the docs.

== Translating / Localising plugins ==
''How do we do [[Translations]] in plugins?''

(At least, a plugin would have to install a .mo file in e.g. lib/python2.7/site-packages/pluginname-0.1.3-py2.7.egg/pluginname/i18n/nn_NO/LC_MESSAGES/pluginname.mo (with domain "pluginname"?). Then mediagoblin would have to either discover that the file exists when setting up the plugin, or the plugin would have to add that to some list in some hook.

mg_globals.py seems to create a gettext variable pointing at mediagoblin/i18n:
<pre>
thread_scope.translations = gettext.translation(
'mediagoblin',
pkg_resources.resource_filename(
'mediagoblin', 'i18n'), ['en'], fallback=True)
</pre>
which is installed into the jinja template in template.py:
<pre>
template_env.install_gettext_callables(
mg_globals.thread_scope.translations.ugettext,
mg_globals.thread_scope.translations.ungettext)
</pre>
in a function called by app.py:
<pre>
request.template_env = template.get_jinja_env(
self.template_loader, request.locale)
</pre>
)

== Making database models ==
See [[SQLAlchemy Tips]]

Latest revision as of 03:48, 11 May 2020

Quickstart

In the mediagoblin repository, there's a sample plugin under mediagoblin/plugins/sampleplugin that you can use to get started with plugin creation. The simplest way to get up and running is to

  1. Follow HackingHowto#How_to_set_up_and_maintain_an_environment_for_hacking_with_virtualenv to set up a local virtualenv mediagoblin instance
    • run it with ./lazyserver.sh so you get debug output (if you run celeryd separately, you won't see logging.info from stuff run in celery tasks)
  2. cp -r mediagoblin/plugins/sampleplugin mediagoblin/plugins/myplugin (where "myplugin" is your plugin name)
  3. edit mediagoblin.ini and add [[mediagoblin.plugins.myplugin]] under the [plugins] section to enable your plugin

Now you can look at e.g. other core or non-core plugins for inspiration. See Available Plugins for non-core plugins.


The way you change things in plugins is by use of hooks. At certain points in the mediagoblin code, a function will say "run all hooks with name XYZ", and if you've defined a hook with such a name in a plugin you've enabled, it'll get run there. The documentation on Plugin API is your friend for defining hooks. The sample plugin shows a use of the 'setup' hook.

Note: templates are hooked with a call to pluginapi.register_template_hooks, instead of adding to the hooks variable.

Making an installable plugin

If you followed the steps above, you can use your plugin by copying it into the plugins folder; however, to get a plugin that is easily installable by users (e.g. with pip install myplugin), it should have a certain folder layout.

A good example is the RDFa plugin. This uses a setup.py file to install the files under the mediagoblin_rdfa folder into the lib/ folder of your mediagoblin installation. If you've checked out both mediagoblin and mg-rdfa in the same folder, e.g. ~/src/, you can do

cd ~/src/mg-rdfa/
../mediagoblin/bin/python setup.py build
../mediagoblin/bin/python setup.py install

to install it to your mediagoblin instance.

The file layout of the repo is:

setup.py
MANIFEST.in
README.md
mediagoblin_rdfa/
                 __init.py__
                 templates/
                           mediagoblin/
                                       plugins/
                                               rdfa/
                                                    metadata.html

The setup.py file defines an option include_package_data=True, which makes it read the file MANIFEST.in; MANIFEST.in contains rules for which files to include when installing. In this case, rule recursive-include mediagoblin_rdfa *.html makes it include the HTML template file (and any other you put under mediagoblin_rdfa), retaining the folder layout.

Referencing the mediagoblin test set in a plugin

The tests in mediagoblin define a nice "test_app" and functions for logging in and posting and so on. You can use this in your installable plugin.

From your plugin (assuming a layout like mg-rdfa described above), doing

   cp -r ../mediagoblin/mediagoblin/tests .

lets you run

   ../mediagoblin/bin/py.test tests --boxed               

In fact, the only files you need are `conftest.py` and `pytest.ini`, and then you can copy over a single `test_foo.py` (only making sure import statements are absolute) and it should run fine.

You also need to call setup_plugins() in your setup() function for your plugin to be properly loaded before testing.

An example of this test method is in the "hidden original" plugin, which subclasses the submission tests.


See also Writing unit tests for plugins in the docs.

Translating / Localising plugins

How do we do Translations in plugins?

(At least, a plugin would have to install a .mo file in e.g. lib/python2.7/site-packages/pluginname-0.1.3-py2.7.egg/pluginname/i18n/nn_NO/LC_MESSAGES/pluginname.mo (with domain "pluginname"?). Then mediagoblin would have to either discover that the file exists when setting up the plugin, or the plugin would have to add that to some list in some hook.

mg_globals.py seems to create a gettext variable pointing at mediagoblin/i18n:

thread_scope.translations = gettext.translation(
    'mediagoblin',
    pkg_resources.resource_filename(
        'mediagoblin', 'i18n'), ['en'], fallback=True)

which is installed into the jinja template in template.py:

    template_env.install_gettext_callables(
        mg_globals.thread_scope.translations.ugettext,
        mg_globals.thread_scope.translations.ungettext)

in a function called by app.py:

        request.template_env = template.get_jinja_env(
            self.template_loader, request.locale)

)

Making database models

See SQLAlchemy Tips