From 2530ef7a1f682dcd5c4386d6b66b1fb71e8cc554 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 23 May 2012 20:21:03 -0400 Subject: Split docs into siteadmin and pluginwriter guides * create initial bits for plugin writer's guide * move siteadmin stuff to site administrator's guide * rework index.rst to support multiple guides * tweak some text * move files into subdirectories I verified that this still works with html and texinfo build targets. There's still a lot of work to do, but this is a good start. --- docs/source/pluginwriter/foreward.rst | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 docs/source/pluginwriter/foreward.rst (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/foreward.rst b/docs/source/pluginwriter/foreward.rst new file mode 100644 index 00000000..fd3a0c22 --- /dev/null +++ b/docs/source/pluginwriter/foreward.rst @@ -0,0 +1,43 @@ +.. MediaGoblin Documentation + + Written in 2011, 2012 by MediaGoblin contributors + + To the extent possible under law, the author(s) have dedicated all + copyright and related and neighboring rights to this software to + the public domain worldwide. This software is distributed without + any warranty. + + You should have received a copy of the CC0 Public Domain + Dedication along with this software. If not, see + . + +======== +Foreword +======== + +About the Plugin Writer's Guide +=============================== + +This guide covers writing plugins for GNU MediaGoblin. It's very much +a work in progress partially because we just started writing it and +partially because the plugin API is currently in flux. + + +Improving the Plugin Writer's Guide +=================================== + +There are a few ways---please pick whichever method is convenient for +you! + +1. Write up a bug report in the bug tracker +2. Tell someone on IRC ``#mediagoblin`` on Freenode. +3. Write an email to the devel mailing list. + +Information about the bugtracker, IRC and the mailing list is all on +the `join page`_. + +.. _join page: http://mediagoblin.org/join/ + +Patches are the most helpful, but even feedback on what you think +could be improved and how to improve it is also helpful. + -- cgit v1.2.3 From 469f10e4a7d3cfdd6cc937344dc47b89608198fa Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Wed, 23 May 2012 21:16:18 -0400 Subject: Add plugin writer's quickstart guide --- docs/source/pluginwriter/quickstart.rst | 189 ++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/source/pluginwriter/quickstart.rst (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/quickstart.rst b/docs/source/pluginwriter/quickstart.rst new file mode 100644 index 00000000..73531381 --- /dev/null +++ b/docs/source/pluginwriter/quickstart.rst @@ -0,0 +1,189 @@ +.. MediaGoblin Documentation + + Written in 2011, 2012 by MediaGoblin contributors + + To the extent possible under law, the author(s) have dedicated all + copyright and related and neighboring rights to this software to + the public domain worldwide. This software is distributed without + any warranty. + + You should have received a copy of the CC0 Public Domain + Dedication along with this software. If not, see + . + + +=========== +Quick Start +=========== + +This is a quick start. It's not comprehensive, but it walks through +writing a basic plugin called "sampleplugin" which logs "I've been +started!" when ``setup_plugin()`` has been called. + +.. todo: Rewrite this to be a useful plugin + + +Step 1: Files and directories +============================= + +GNU MediaGoblin plugins are Python projects at heart. As such, you should +use a standard Python project directory tree:: + + sampleplugin/ + |- README + |- LICENSE + |- setup.py + |- sampleplugin/ + |- __init__.py + + +The outer ``sampleplugin`` directory holds all the project files. + +The ``README`` should cover what your plugin does, how to install it, +how to configure it, and all the sorts of things a README should +cover. + +The ``LICENSE`` should have the license under which you're +distributing your plugin. + +The inner ``sampleplugin`` directory is the Python package that holds +your plugin's code. + +The ``__init__.py`` denotes that this is a Python package. It also +holds the plugin code. + + +Step 2: README +============== + +Here's a rough ``README``. Generally, you want more information +because this is the file that most people open when they want to learn +more about your project. + +:: + + README + ====== + + This is a sample plugin. It logs a line when ``setup__plugin()`` is + run. + + +Step 3: LICENSE +=============== + +GNU MediaGoblin plugins must be licensed under the AGPLv3 or later. So +the LICENSE file should be the AGPLv3 text which you can find at +``_ + + +Step 4: setup.py +================ + +This file is used for packaging and distributing your plugin. + +We'll use a basic one:: + + from setuptools import setup, find_packages + + setup( + name='sampleplugin', + version='1.0', + packages=find_packages(), + include_package_data=True, + install_requires=[], + license='AGPLv3', + ) + + +See ``_ +for more details. + + +Step 5: the code +================ + +The code for ``__init__.py`` looks like this: + +.. code-block:: python + :linenos: + :emphasize-lines: 8,19 + + import logging + from mediagoblin.tools.pluginapi import Plugin, get_config + + + _log = logging.getLogger(__name__) + + + class SamplePlugin(Plugin): + """ + This is a sample plugin class. It automatically registers itself + with MediaGoblin when this module is imported. + + The setup_plugin method prints configuration for this plugin if + there is any. + """ + def __init__(self): + pass + + def setup_plugin(self): + _log.info("I've been started!") + config = get_config('sampleplugin') + if config: + _log.info('%r' % config) + else: + _log.info('There is no configuration set.') + + +Line 8 defines a class called ``SamplePlugin`` that subclasses +``Plugin`` from ``mediagoblin.tools.pluginapi``. When the class is +defined, it gets registered with MediaGoblin and MediaGoblin will then +call ``setup_plugin`` on it. + +Line 19 defines ``setup_plugin``. This gets called when MediaGoblin +starts up after it's registered all the plugins. This is where you can +do any initialization for your plugin. + + +Step 6: Installation and configuration +====================================== + +To install the plugin for development, you need to make sure it's +available to the Python interpreter that's running MediaGoblin. + +There are a couple of ways to do this, but we're going to pick the +easy one. + +Use ``python`` from your MediaGoblin virtual environment and do:: + + python setup.py develop + +Any changes you make to your plugin will be available in your +MediaGoblin virtual environment. + +Then adjust your ``mediagoblin.ini`` file to load the plugin:: + + [plugins] + + [[sampleplugin]] + + +Step 7: That's it! +================== + +When you launch MediaGoblin, it'll load the plugin and you'll see +evidence of that in the log file. + +That's it for the quick start! + + +Where to go from here +===================== + +See the documentation on the plugin API for code samples and other +things you can use when building your plugin. + +See `Hitchhiker's Guide to Packaging +`_ for more information on +packaging your plugin. -- cgit v1.2.3 From 05e007c1dbe7b5b8a092f1a99ed361c4e6b71f26 Mon Sep 17 00:00:00 2001 From: Will Kahn-Greene Date: Tue, 17 Jul 2012 21:02:12 -0400 Subject: Rework plugin infrastructure to nix side-effects This reworks the plugin infrastructure so as to remove module-loading side-effects which were making things a pain in the ass to test. With the new system, there's no auto-registering meta class. Instead plugins do whatever they want and then specify a hooks dict that maps hook names to callables for the things they're tying into. The most common one (and the only one we've implemented so far) is "setup". This also simplifies the sampleplugin a little by moving the code to __init__.py. --- docs/source/pluginwriter/quickstart.rst | 47 +++++++++++++++------------------ 1 file changed, 22 insertions(+), 25 deletions(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/quickstart.rst b/docs/source/pluginwriter/quickstart.rst index 73531381..b5a63f79 100644 --- a/docs/source/pluginwriter/quickstart.rst +++ b/docs/source/pluginwriter/quickstart.rst @@ -50,7 +50,8 @@ The inner ``sampleplugin`` directory is the Python package that holds your plugin's code. The ``__init__.py`` denotes that this is a Python package. It also -holds the plugin code. +holds the plugin code and the ``hooks`` dict that specifies which +hooks the sampleplugin uses. Step 2: README @@ -107,43 +108,39 @@ The code for ``__init__.py`` looks like this: .. code-block:: python :linenos: - :emphasize-lines: 8,19 + :emphasize-lines: 12,23 import logging from mediagoblin.tools.pluginapi import Plugin, get_config + # This creates a logger that you can use to log information to + # the console or a log file. _log = logging.getLogger(__name__) - class SamplePlugin(Plugin): - """ - This is a sample plugin class. It automatically registers itself - with MediaGoblin when this module is imported. + # This is the function that gets called when the setup + # hook fires. + def setup_plugin(): + _log.info("I've been started!") + config = get_config('sampleplugin') + if config: + _log.info('%r' % config) + else: + _log.info('There is no configuration set.') - The setup_plugin method prints configuration for this plugin if - there is any. - """ - def __init__(self): - pass - def setup_plugin(self): - _log.info("I've been started!") - config = get_config('sampleplugin') - if config: - _log.info('%r' % config) - else: - _log.info('There is no configuration set.') + # This is a dict that specifies which hooks this plugin uses. + # This one only uses one hook: setup. + hooks = { + 'setup': setup_plugin + } -Line 8 defines a class called ``SamplePlugin`` that subclasses -``Plugin`` from ``mediagoblin.tools.pluginapi``. When the class is -defined, it gets registered with MediaGoblin and MediaGoblin will then -call ``setup_plugin`` on it. +Line 12 defines the ``setup_plugin`` function. -Line 19 defines ``setup_plugin``. This gets called when MediaGoblin -starts up after it's registered all the plugins. This is where you can -do any initialization for your plugin. +Line 23 defines ``hooks``. When MediaGoblin loads this file, it sees +``hooks`` and registers all the callables with their respective hooks. Step 6: Installation and configuration -- cgit v1.2.3 From 92c597ca1cd0d93df7246eb2f81f84bcb08673ce Mon Sep 17 00:00:00 2001 From: Elrond Date: Sat, 26 Jan 2013 00:12:18 +0100 Subject: Allow doc string extraction and use for pluginapi. Allow us to extract docstrings from our sources using the sphinx.ext.autodoc module. First use: Extract some of the docs for the pluginapi and provide it in a new "Plugin API" section. --- docs/source/pluginwriter/api.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 docs/source/pluginwriter/api.rst (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst new file mode 100644 index 00000000..206c8b0b --- /dev/null +++ b/docs/source/pluginwriter/api.rst @@ -0,0 +1,23 @@ +.. MediaGoblin Documentation + + Written in 2013 by MediaGoblin contributors + + To the extent possible under law, the author(s) have dedicated all + copyright and related and neighboring rights to this software to + the public domain worldwide. This software is distributed without + any warranty. + + You should have received a copy of the CC0 Public Domain + Dedication along with this software. If not, see + . + + +========== +Plugin API +========== + +:mod:`pluginapi` Module +----------------------- + +.. automodule:: mediagoblin.tools.pluginapi + :members: get_config, register_routes, register_template_path -- cgit v1.2.3 From ae9f0aec381c7f04898f0dde3af322ec876b9651 Mon Sep 17 00:00:00 2001 From: Elrond Date: Sat, 26 Jan 2013 19:21:40 +0100 Subject: Docs: Add a database guide to the plugin docs. Plugin writers will often need to create new tables. So give them some hints, what they need to do and where they might find more info. --- docs/source/pluginwriter/database.rst | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 docs/source/pluginwriter/database.rst (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/database.rst b/docs/source/pluginwriter/database.rst new file mode 100644 index 00000000..58edf3a0 --- /dev/null +++ b/docs/source/pluginwriter/database.rst @@ -0,0 +1,111 @@ +.. MediaGoblin Documentation + + Written in 2013 by MediaGoblin contributors + + To the extent possible under law, the author(s) have dedicated all + copyright and related and neighboring rights to this software to + the public domain worldwide. This software is distributed without + any warranty. + + You should have received a copy of the CC0 Public Domain + Dedication along with this software. If not, see + . + + +======== +Database +======== + + +Accessing Existing Data +======================= + +If your plugin wants to access existing data, this is quite +straight forward. Just import the appropiate models and use +the full power of SQLAlchemy. Take a look at the (upcoming) +database section in the Developer's Chapter. + + +Creating new Tables +=================== + +If your plugin needs some new space to store data, you +should create a new table. Please do not modify core +tables. Not doing so might seem inefficient and possibly +is. It will help keep things sane and easier to upgrade +versions later. + +So if you create a new plugin and need new tables, create a +file named ``models.py`` in your plugin directory. You +might take a look at the core's db.models for some ideas. +Here's a simple one: + +.. code-block:: python + + from mediagoblin.db.base import Base + from sqlalchemy import Column, Integer, Unicode, ForeignKey + + class MediaSecurity(Base): + __tablename__ = "yourplugin__media_security" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), + primary_key=True) + get_media_entry = relationship("MediaEntry", + backref=backref("security_rating", cascade="all, delete-orphan")) + + rating = Column(Unicode) + + MODELS = [MediaSecurity] + +That's it. + +Some notes: + +* Make sure all your ``__tablename__`` start with your + plugin's name so the tables of various plugins can't + conflict in the database. (Conflicts in python naming are + much easier to fix later). +* Try to get your database design as good as possible in + the first attempt. Changing the database design later, + when people already have data using the old design, is + possible (see next chapter), but it's not easy. + + +Changing the Database Schema Later +================================== + +If your plugin is in use and instances use it to store some +data, changing the database design is a tricky thing. + +1. Make up your mind how the new schema should look like. +2. Change ``models.py`` to contain the new schema. Keep a + copy of the old version around for your personal + reference later. +3. Now make up your mind (possibly using your old and new + ``models.py``) what steps in SQL are needed to convert + the old schema to the new one. + This is called a "migration". +4. Create a file ``migrations.py`` that will contain all + your migrations and add your new migration. + +Take a look at the core's ``db/migrations.py`` for some +good examples on what you might be able to do. Here's a +simple one to add one column: + +.. code-block:: python + + from mediagoblin.db.migration_tools import RegisterMigration, inspect_table + from sqlalchemy import MetaData, Column, Integer + + MIGRATIONS = {} + + @RegisterMigration(1, MIGRATIONS) + def add_license_preference(db): + metadata = MetaData(bind=db.bind) + + security_table = inspect_table(metadata, 'yourplugin__media_security') + + col = Column('security_level', Integer) + col.create(security_table) + db.commit() -- cgit v1.2.3 From 75621003af128969c322d11aff74ec0c425a9ff4 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 30 Jan 2013 13:27:40 -0600 Subject: Added register_template_hooks and get_hook_templates to the plugin api auto module documentation. --- docs/source/pluginwriter/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 206c8b0b..f700e161 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -20,4 +20,4 @@ Plugin API ----------------------- .. automodule:: mediagoblin.tools.pluginapi - :members: get_config, register_routes, register_template_path + :members: get_config, register_routes, register_template_path, register_template_hooks, get_hook_templates -- cgit v1.2.3 From cf41e7d7444fb9d19a777a4720d9b00684e6fe7b Mon Sep 17 00:00:00 2001 From: Elrond Date: Thu, 31 Jan 2013 20:57:03 +0100 Subject: Improve formatting for hook template docs. --- docs/source/pluginwriter/api.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index f700e161..42dc3a3d 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -20,4 +20,5 @@ Plugin API ----------------------- .. automodule:: mediagoblin.tools.pluginapi - :members: get_config, register_routes, register_template_path, register_template_hooks, get_hook_templates + :members: get_config, register_routes, register_template_path, + register_template_hooks, get_hook_templates -- cgit v1.2.3 From 36748921c27deb3f8c9d88f9b7a3c368a427d418 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 11 Apr 2013 16:57:11 -0500 Subject: adding callable_runone and callable_runall to the docs --- docs/source/pluginwriter/api.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 42dc3a3d..44ffd6e8 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -21,4 +21,5 @@ Plugin API .. automodule:: mediagoblin.tools.pluginapi :members: get_config, register_routes, register_template_path, - register_template_hooks, get_hook_templates + register_template_hooks, get_hook_templates, + callable_runone, callable_runall -- cgit v1.2.3 From 4d0191dcb8b43f82bfacc77ed8c92d0d3c573d8a Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 19 Apr 2013 13:22:03 -0500 Subject: A warning about the plugin API being unstable. --- docs/source/pluginwriter/api.rst | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 44ffd6e8..85870d28 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -16,6 +16,15 @@ Plugin API ========== +This documents the general plugin API. + +Please note, at this point OUR PLUGIN HOOKS MAY AND WILL CHANGE. +Authors are encouraged to develop plugins and work with the +MediaGoblin community to keep them up to date, but this API will be a +moving target for a few releases. + +Please check the release notes for updates! + :mod:`pluginapi` Module ----------------------- -- cgit v1.2.3 From 51d5d3aa20b5f2d7dbe9c88e7d8c21db7ea172d1 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 19 Apr 2013 16:29:03 -0500 Subject: changing the things to document in api.rst --- docs/source/pluginwriter/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 85870d28..3a75d455 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -31,4 +31,4 @@ Please check the release notes for updates! .. automodule:: mediagoblin.tools.pluginapi :members: get_config, register_routes, register_template_path, register_template_hooks, get_hook_templates, - callable_runone, callable_runall + hook_handle, hook_runall, hook_transform, -- cgit v1.2.3 From b835e15319882477e71c7b03db2c1565dd674a96 Mon Sep 17 00:00:00 2001 From: Elrond Date: Tue, 30 Apr 2013 00:24:45 +0200 Subject: Add warning about crypt/itsdangeroussecret.bin. You should not leak that file, really. --- docs/source/pluginwriter/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 3a75d455..6323f713 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -31,4 +31,4 @@ Please check the release notes for updates! .. automodule:: mediagoblin.tools.pluginapi :members: get_config, register_routes, register_template_path, register_template_hooks, get_hook_templates, - hook_handle, hook_runall, hook_transform, + hook_handle, hook_runall, hook_transform -- cgit v1.2.3 From f65bf8983611b18ec3a6a042404c50b8558529df Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 8 May 2013 10:57:23 -0500 Subject: Documenting plugin configuration This commit sponsored by David Krupicz. Thanks, David! --- docs/source/pluginwriter/api.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 6323f713..df933511 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -32,3 +32,19 @@ Please check the release notes for updates! :members: get_config, register_routes, register_template_path, register_template_hooks, get_hook_templates, hook_handle, hook_runall, hook_transform + +Configuration +------------- + +Your plugin may define its own configuration defaults. + +Simply add to the directory of your plugin a config_spec.ini file. An +example might look like:: + + [plugin_spec] + some_string = string(default="blork") + some_int = integer(default=50) + +This means that when people enable your plugin in their config you'll +be able to provide defaults as well as type validation. + -- cgit v1.2.3 From b312d2cd83dbbfb32adc30c5eb3a9a4cc6ae9295 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Tue, 14 May 2013 16:09:55 -0500 Subject: Added documentation on view specific hooks This commit sponsored by Matthew Woodward. Thank you! --- docs/source/pluginwriter/api.rst | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index df933511..0d5c82d8 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -48,3 +48,65 @@ example might look like:: This means that when people enable your plugin in their config you'll be able to provide defaults as well as type validation. + +Context Hooks +------------- + +View specific hooks ++++++++++++++++++++ + +You can hook up to almost any template called by any specific view +fairly easily. As long as the view directly or indirectly uses the +method ``render_to_response`` you can access the context via a hook +that has a key in the format of the tuple:: + + (view_symbolic_name, view_template_path) + +Where the "view symbolic name" is the same parameter used in +``request.urlgen()`` to look up the test. So say we're wanting to add +something to the context of the user's homepage. We look in +mediagoblin/user_pages/routing.py and see:: + + add_route('mediagoblin.user_pages.user_home', + '/u//', + 'mediagoblin.user_pages.views:user_home') + +Aha! That means that the name is ``mediagoblin.user_pages.user_home``. +Okay, so then we look at the view at the +``mediagoblin.user_pages.views:user_home`` method:: + + @uses_pagination + def user_home(request, page): + # [...] whole bunch of stuff here + return render_to_response( + request, + 'mediagoblin/user_pages/user.html', + {'user': user, + 'user_gallery_url': user_gallery_url, + 'media_entries': media_entries, + 'pagination': pagination}) + +Nice! So the template appears to be +``mediagoblin/user_pages/user.html``. Cool, that means that the key +is:: + + ("mediagoblin.user_pages.views:user_home", + "mediagoblin/user_pages/user.html") + +The context hook uses ``hook_transform()`` so that means that if we're +hooking into it, our hook will both accept one argument, ``context``, +and should return that modified object, like so:: + + def add_to_user_home_context(context): + context['foo'] = 'bar' + return context + + hooks = { + ("mediagoblin.user_pages.views:user_home", + "mediagoblin/user_pages/user.html"): add_to_user_home_context} + + +Global context hook ++++++++++++++++++++ + + -- cgit v1.2.3 From 1344c561a0da28290bab860e7e628998463fca15 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 15 May 2013 10:37:41 -0500 Subject: Adding global context hooks & fixing method names->symbolic view names in docs This commit sponsored by Sheila Miguez. Thanks Sheila! --- docs/source/pluginwriter/api.rst | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 0d5c82d8..b411fa4d 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -73,7 +73,7 @@ mediagoblin/user_pages/routing.py and see:: Aha! That means that the name is ``mediagoblin.user_pages.user_home``. Okay, so then we look at the view at the -``mediagoblin.user_pages.views:user_home`` method:: +``mediagoblin.user_pages.user_home`` method:: @uses_pagination def user_home(request, page): @@ -90,7 +90,7 @@ Nice! So the template appears to be ``mediagoblin/user_pages/user.html``. Cool, that means that the key is:: - ("mediagoblin.user_pages.views:user_home", + ("mediagoblin.user_pages.user_home", "mediagoblin/user_pages/user.html") The context hook uses ``hook_transform()`` so that means that if we're @@ -102,11 +102,26 @@ and should return that modified object, like so:: return context hooks = { - ("mediagoblin.user_pages.views:user_home", + ("mediagoblin.user_pages.user_home", "mediagoblin/user_pages/user.html"): add_to_user_home_context} Global context hook +++++++++++++++++++ +If you need to add something to the context of *every* view, it is not +hard; there are two hooks hook that also uses hook_transform (like the +above) but make available what you are providing to *every* view. +Note that there is a slight, but critical, difference between the two. + +The most general one is the ``'template_global_context'`` hook. This +one is run only once, and is read into the global context... all views +will get access to what are in this dict. + +The slightly more expensive but more powerful one is +``'template_context_prerender'``. This one is not added to the global +context... it is added to the actual context of each individual +template render right before it is run! Because of this you also can +do some powerful and crazy things, such as checking the request object +or other parts of the context before passing them on. -- cgit v1.2.3 From 0553976187a4ec870f944d2d4d17a4951075d2a8 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 15 May 2013 11:10:25 -0500 Subject: Hook->hooks since there's more than one of them :) --- docs/source/pluginwriter/api.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index b411fa4d..56aaac77 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -106,8 +106,8 @@ and should return that modified object, like so:: "mediagoblin/user_pages/user.html"): add_to_user_home_context} -Global context hook -+++++++++++++++++++ +Global context hooks +++++++++++++++++++++ If you need to add something to the context of *every* view, it is not hard; there are two hooks hook that also uses hook_transform (like the -- cgit v1.2.3 From 38ebd05d1a759c3a057ab041124c313cf5724dc4 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 15 May 2013 11:29:43 -0500 Subject: Simple tyop, view->test... I was writing too many tests at the time :) --- docs/source/pluginwriter/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 56aaac77..5e0568fd 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -63,7 +63,7 @@ that has a key in the format of the tuple:: (view_symbolic_name, view_template_path) Where the "view symbolic name" is the same parameter used in -``request.urlgen()`` to look up the test. So say we're wanting to add +``request.urlgen()`` to look up the view. So say we're wanting to add something to the context of the user's homepage. We look in mediagoblin/user_pages/routing.py and see:: -- cgit v1.2.3 From d6d2c771bdc111cd26186b1bc42b44f2b3197e05 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 16 May 2013 10:38:45 -0500 Subject: Start of ability to have plugins provide static resources! Note I have not tested any of this yet ;) But we're already on our way: - We've got docs - The hook is there Lots to do still though. But, progress! :) This commit sponsored by Laura Arjona Reina. Thanks larjona! --- docs/source/pluginwriter/api.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 5e0568fd..cd06cbc5 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -125,3 +125,22 @@ context... it is added to the actual context of each individual template render right before it is run! Because of this you also can do some powerful and crazy things, such as checking the request object or other parts of the context before passing them on. + + +Adding static resources +----------------------- + +It's possible to add static resources for your plugin. Say your +plugin needs some special javascript and images... how to provide +them? Then how to access them? MediaGoblin has a way! + + +Attaching to the hook ++++++++++++++++++++++ + +First, you need to register your plugin's resources with the hook. +This is pretty easy actually: you just need to provide a function that +passes back a PluginStatic object. + +.. automodule:: mediagoblin.tools.staticdirect + :members: PluginStatic -- cgit v1.2.3 From 505b4b39b8ca7b273294e5d42d278dd1b89960ea Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 22 May 2013 11:51:46 -0500 Subject: Document assetlink and staticdirect usage for plugins. Still a bit to clean up around what the command to be run actually is, since that will likely change. This commit sponsored by David Decker. Thank you! --- docs/source/pluginwriter/api.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index cd06cbc5..1cfd65d7 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -144,3 +144,34 @@ passes back a PluginStatic object. .. automodule:: mediagoblin.tools.staticdirect :members: PluginStatic + + +Running plugin assetlink +++++++++++++++++++++++++ + +.. TODO: Fix this command when it lands elsewhere ;) + +In order for your plugin assets to be properly served by MediaGoblin, +your plugin's asset directory needs to be symlinked into the directory +that plugin assets are served from. To set this up, run:: + + ./bin/gmg theme assetlink + + +Using staticdirect +++++++++++++++++++ + +Once you have this, you will want to be able to of course link to your +assets! MediaGoblin has a "staticdirect" tool; you want to use this +like so in your templates:: + + staticdirect("css/monkeys.css", "mystaticname") + +Replace "mystaticname" with the name you passed to PluginStatic. The +staticdirect method is, for convenience, attached to the request +object, so you can access this in your templates like: + +.. code-block:: html + + A funny bunny -- cgit v1.2.3 From 24ede04415df1a79da83e2716ab3c714e2080563 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 23 May 2013 13:43:04 -0500 Subject: Documentation changes to reflect new plugin assetlink stuff - updated old theme assetlink section to reflect new location of ./bin/gmg assetlink and removed comment about the plugin command being temporary. - Added a new section to the standard config file on where to put the plugin_static section - Added release notes about said section This commit sponsored by Thomas Webber. Thanks, Dad! --- docs/source/pluginwriter/api.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 1cfd65d7..06295c77 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -149,13 +149,11 @@ passes back a PluginStatic object. Running plugin assetlink ++++++++++++++++++++++++ -.. TODO: Fix this command when it lands elsewhere ;) - In order for your plugin assets to be properly served by MediaGoblin, your plugin's asset directory needs to be symlinked into the directory that plugin assets are served from. To set this up, run:: - ./bin/gmg theme assetlink + ./bin/gmg assetlink Using staticdirect -- cgit v1.2.3 From 5de402781f9b5e9f4167d7d0d565e0c3ec8c4451 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Thu, 23 May 2013 15:56:07 -0500 Subject: Moving statcdirect automodule doc reference to autoclass per Elrond's suggestion. Cleaner! --- docs/source/pluginwriter/api.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 06295c77..44411965 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -142,8 +142,7 @@ First, you need to register your plugin's resources with the hook. This is pretty easy actually: you just need to provide a function that passes back a PluginStatic object. -.. automodule:: mediagoblin.tools.staticdirect - :members: PluginStatic +.. autoclass:: mediagoblin.tools.staticdirect.PluginStatic Running plugin assetlink -- cgit v1.2.3 From d861ffc9ad5118f251fa669028bf774d98b0b451 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 May 2013 14:10:07 -0500 Subject: Link to the plugin api stuff and the database plugin sections from the quickstart. This commit sponsored by Nathan Stephenson. Thank you! --- docs/source/pluginwriter/api.rst | 1 + docs/source/pluginwriter/database.rst | 9 ++++++--- docs/source/pluginwriter/quickstart.rst | 6 ++++-- 3 files changed, 11 insertions(+), 5 deletions(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 44411965..dc6bcc98 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -11,6 +11,7 @@ Dedication along with this software. If not, see . +.. _plugin-api-chapter: ========== Plugin API diff --git a/docs/source/pluginwriter/database.rst b/docs/source/pluginwriter/database.rst index 58edf3a0..603a19eb 100644 --- a/docs/source/pluginwriter/database.rst +++ b/docs/source/pluginwriter/database.rst @@ -12,9 +12,12 @@ . -======== -Database -======== +.. _plugin-database-chapter: + + +=========================== +Database models for plugins +=========================== Accessing Existing Data diff --git a/docs/source/pluginwriter/quickstart.rst b/docs/source/pluginwriter/quickstart.rst index b5a63f79..6d45ea36 100644 --- a/docs/source/pluginwriter/quickstart.rst +++ b/docs/source/pluginwriter/quickstart.rst @@ -178,8 +178,10 @@ That's it for the quick start! Where to go from here ===================== -See the documentation on the plugin API for code samples and other -things you can use when building your plugin. +See the documentation on the :ref:`plugin-api-chapter` for code +samples and other things you can use when building your plugin. If +your plugin needs its own database models, see +:ref:`plugin-database-chapter`. See `Hitchhiker's Guide to Packaging `_ for more information on -- cgit v1.2.3 From b21220e931e80fa9005f71c026eaa66f5ea225f4 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 May 2013 14:13:12 -0500 Subject: Actually link to the release notes when we say "see the release notes". This commit sponsored by Brian Kemp. Thank you! --- docs/source/pluginwriter/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index dc6bcc98..819fac2d 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -24,7 +24,7 @@ Authors are encouraged to develop plugins and work with the MediaGoblin community to keep them up to date, but this API will be a moving target for a few releases. -Please check the release notes for updates! +Please check the :ref:`release-notes` for updates! :mod:`pluginapi` Module ----------------------- -- cgit v1.2.3 From 8ae5d20f197291a1cd2e211e4e5d5ede92718f52 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 May 2013 15:35:37 -0500 Subject: Where do you find hooks? How do you add them? An explaination! This commit about talking to community members sponsored by community member Aeva Palecek. Thanks! --- docs/source/pluginwriter/api.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 819fac2d..33bb70c4 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -26,6 +26,26 @@ moving target for a few releases. Please check the :ref:`release-notes` for updates! + +How are hooks added? Where do I find them? +------------------------------------------- + +Much of this document talks about hooks, both as in terms of regular +hooks and template hooks. But where do they come from, and how can +you find a list of them? + +For the moment, the best way to find available hooks is to check the +source code itself. (Yes, we should start a more official hook +listing with descriptions soon.) But many hooks you may need do not +exist yet: what to do then? + +The plan at present is that we are adding hooks as people need them, +with community discussion. If you find that you need a hook and +MediaGoblin at present doesn't provide it at present, please +`http://mediagoblin.org/pages/join.html `_! We'll +evaluate what to do from there. + + :mod:`pluginapi` Module ----------------------- -- cgit v1.2.3 From baf2c1c96ec4dbcbb74518852ffdf516d670347c Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 May 2013 15:57:58 -0500 Subject: Additional hook tips! Documentation on how to modify a wtforms form. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit sponsored by Gian-Maria Daffré. Thank you! --- docs/source/pluginwriter/api.rst | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 33bb70c4..2a7f3c2d 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -193,3 +193,36 @@ object, so you can access this in your templates like: A funny bunny + + +Additional hook tips +-------------------- + +This section aims to explain some tips in regards to adding hooks to +the MediaGoblin repository. + +WTForms hooks +============= + +We haven't totally settled on a way to tranform wtforms form objects, +but here's one way. In your view:: + + from mediagoblin.foo.forms import SomeForm + + def some_view(request) + form_class = hook_transform('some_form_transform', SomeForm) + form = form_class(request.form) + +Then to hook into this form, do something in your plugin like:: + + import wtforms + + class SomeFormAdditions(wtforms.Form): + new_datefield = wtforms.DateField() + + def transform_some_form(orig_form): + class ModifiedForm(orig_form, SomeFormAdditions) + return ModifiedForm + + hooks = { + 'some_form_transform': transform_some_form} -- cgit v1.2.3 From 40019095748fef60ad08d10cbe69437f20d63735 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 May 2013 16:12:24 -0500 Subject: Actually use the right underlining for the wtforms hooks section --- docs/source/pluginwriter/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 2a7f3c2d..e5ac8df5 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -202,7 +202,7 @@ This section aims to explain some tips in regards to adding hooks to the MediaGoblin repository. WTForms hooks -============= ++++++++++++++ We haven't totally settled on a way to tranform wtforms form objects, but here's one way. In your view:: -- cgit v1.2.3 From 9d881aeeb4df2e9f02c4c1fea7d6435273081fdb Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 May 2013 17:21:15 -0500 Subject: Provide a tip on how to do interfaces via our plugin API. Uses a frogputer science approach to frobbing as an example (which is total nonsense, but fun). This commit sponsored by Ryan Kelln. Thank you! --- docs/source/pluginwriter/api.rst | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index e5ac8df5..3bb5f445 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -226,3 +226,69 @@ Then to hook into this form, do something in your plugin like:: hooks = { 'some_form_transform': transform_some_form} + + +Interfaces +++++++++++ + +If you want to add a pseudo-interface, it's not difficult to do so. +Just write the interface like so:: + + class FrobInterface(object): + """ + Interface for Frobbing. + + Classes implementing this interface should provide defrob and frob. + They may also implement double_frob, but it is not required; if + not provided, we will use a general technique. + """ + + def defrob(self, frobbed_obj): + """ + Take a frobbed_obj and defrob it. Returns the defrobbed object. + """ + raise NotImplementedError() + + def frob(self, normal_obj): + """ + Take a normal object and frob it. Returns the frobbed object. + """ + raise NotImplementedError() + + def double_frob(self, normal_obj): + """ + Frob this object and return it multiplied by two. + """ + return self.frob(normal_obj) * 2 + + + def some_frob_using_method(): + # something something something + frobber = hook_handle(FrobInterface) + frobber.frob(blah) + + # alternately you could have a default + frobber = hook_handle(FrobInterface) or DefaultFrobber + frobber.defrob(foo) + + +It's fine to use your interface as the key instead of a string if you +like. + +Then a plugin providing your interface can be like:: + + from mediagoblin.foo.frobfrogs import FrobInterface + from frogfrobber import utils + + class FrogFrobber(FrobInterface): + """ + Takes a frogputer science approach to frobbing. + """ + def defrob(self, frobbed_obj): + return utils.frog_defrob(frobbed_obj) + + def frob(self, normal_obj): + return utils.frog_frob(normal_obj) + + hooks = { + FrobInterface: lambda: return FrogFrobber} -- cgit v1.2.3 From ea49f37821410e4b46179853965cd9ac4f2b9688 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Wed, 29 May 2013 18:10:09 -0500 Subject: Explained more clearly why it's okay for interface classes to be keys. This commit sponsored by Nick Glynn. Thank you! --- docs/source/pluginwriter/api.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/api.rst b/docs/source/pluginwriter/api.rst index 3bb5f445..66def173 100644 --- a/docs/source/pluginwriter/api.rst +++ b/docs/source/pluginwriter/api.rst @@ -273,7 +273,9 @@ Just write the interface like so:: It's fine to use your interface as the key instead of a string if you -like. +like. (Usually this is messy, but since interfaces are public and +since you need to import them into your plugin anyway, interfaces +might as well be keys.) Then a plugin providing your interface can be like:: -- cgit v1.2.3 From 25aad338d4921ec76484c6d2af5e40c97904917d Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Fri, 7 Jun 2013 11:45:07 -0500 Subject: Added some test-writing docs for plugins, but not sure if they're good. ;) This commit sponsored by Joe Lee. Thank you! --- docs/source/pluginwriter/tests.rst | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/source/pluginwriter/tests.rst (limited to 'docs/source/pluginwriter') diff --git a/docs/source/pluginwriter/tests.rst b/docs/source/pluginwriter/tests.rst new file mode 100644 index 00000000..fe99688f --- /dev/null +++ b/docs/source/pluginwriter/tests.rst @@ -0,0 +1,64 @@ +.. MediaGoblin Documentation + + Written in 2013 by MediaGoblin contributors + + To the extent possible under law, the author(s) have dedicated all + copyright and related and neighboring rights to this software to + the public domain worldwide. This software is distributed without + any warranty. + + You should have received a copy of the CC0 Public Domain + Dedication along with this software. If not, see + . + +============================== +Writing unit tests for plugins +============================== + +Here's a brief guide to writing unit tests for plugins. However, it +isn't really ideal. It also hasn't been well tested... yes, there's +some irony there :) + +Some notes: we're using py.test and webtest for unit testing stuff. +Keep that in mind. + +My suggestion is to mime the behavior of `mediagoblin/tests/` and put +that in your own plugin, like `myplugin/tests/`. Copy over +`conftest.py` and `pytest.ini` to your tests directory, but possibly +change the `test_app` fixture to match your own tests' config needs. +For example:: + + import pkg_resources + # [...] + + @pytest.fixture() + def test_app(request): + return get_app( + request, + mgoblin_config=pkg_resources.resource_filename( + 'myplugin.tests', 'myplugin_mediagoblin.ini')) + +In any test module in your tests directory you can then do:: + + def test_somethingorother(test_app): + # real code goes here + pass + +And you'll get a mediagoblin application wrapped in webtest passed in +to your environment. + +If your plugin needs to define multiple configuration setups, you can +actually set up multiple fixtures very easily for this. You can just +set up multiple fixtures with different names that point to different +configs and pass them in as that named argument. + +To run the tests, from mediagoblin's directory (make sure that your +plugin has been added to your mediagoblin checkout's virtualenv!) do:: + + ./runtests.sh /path/to/myplugin/tests/ + +replacing `/path/to/myplugin/` with the actual path to your plugin. + +NOTE: again, the above is untested, but it should probably work. If +you run into trouble, `contact us +`_, preferably on IRC! -- cgit v1.2.3