tgext.tagging 0.3.0
Tagging support for TurboGears2 applications
License: MIT
Keywords: turbogears2.extension

About Tagging
-------------------------

tgext.tagging is a TurboGears2 library that permits to quickly add tagging
to any project managing tags, tagging, tag clouds and widgets to
list, remove and add tags to entities.

Installing
-------------------------------

tgext.tagging can be installed both from pypi or from bitbucket::

    pip install tgext.tagging

should just work for most of the users

Enabling Tagging Management
--------------------------------

For TurboGears 2.2+ using **tgext.pluggables** is suggested::

    from tgext.pluggable import plug
    plug(base_config, 'tgext.tagging')

For previous versions edit ``model/__init__.py`` and add the following lines::

    import tgext.tagging
    Tag, Tagging = tgext.tagging.setup_model()

most of the utitiles to manage tagging are exposed by the Tagging class
they expose:

    - Tag.lookup(tag_name) -> Returns the Tag instance for the given tag name
    - Tag.lookup_list(comma_separated_tags) -> Returns the tags instances for each entry in the tags list.

    - Tagging.items_for_tags(Model, comma_separated_tags) -> Returns the list of items having the given tags
    - Tagging.tag_cloud_for_object(item) -> Returns the list of tags for the given object
    - Tagging.tag_cloud_for_set(Model, items=None) -> Returns the tag cloud for the given set of items.
      if a list of items is passed it will retrieve tags for the given list, otherwise for all the items
      of the given Model.
    - Tagging.tag_cloud_for_user(user, Model=None) -> Returns all the tags set by the given user.
      if any Model is passed it will retrieve tags only for that model.
    - Tagging.add_tags(item, comma_separated_tags) -> Add the given tags to the item
    - Tagging.del_tags(item, comma_separated_tags) -> Removes the given tags from the item
    - Tagging.set_tags(item, comma_separated_tags) -> Replaces all the tags of the item with the new list

Tagging Controller
-------------------

tgext.tagging provides a controller to manage tagging.
Enable it inside your project with the following code::

    from tgext.tagging.controllers import TaggingController

    class RootController(BaseController):
        tagging = TaggingController(model=Group, session=DBSession, allow_edit=None)

You can enable multiple *TaggingController* one for each model available
in your application. The *model* parameter indicates for which model objects tags will be
managed, *session* is the sqlalchemy session used to perform queries and *allow_edit*
is the repoze.what predicates used to check if to show editing functions.

The controller provides *tags*, *add*, *remove*, *search* actions:

    - /tags/id -> Partial view that can be loaded with jQuery.load that displays a taglist
      with form to add/remove tags for the given object.
    - /add/id?tags=tag1,tag2,tag3 -> Permits to add one or more tags to the item
    - /remove/id?tags=tag1,tag2 -> Permits to remove one or more tags from the item
    - /search?tags=tag1,tag -> Searches for items having the given tags, will use **Model.tagging_display**
      method to display the results if available. Otherwise *str(Model)* is performed.

Tagging Widgets
------------------

tgext.tagging provides some widgets to manage tags.
**tgext.tagging.widgets.TagList** and **tgext.tagging.widget.TagCloud** are provided.
Both take a **tagging_url** parameter at construction which points to the url of the tagging
controller that can be used by the widget to manage tags. By default this points to /tagging.
**TagList** widget also takes a editmode parameter which permits to specify if controls to
add and remove tags must be shown or not.

At rendering **TagList** takes an object as argument and will show the tag list for the given object,
while **TagCloud** takes a tag cloud returned by Tagging.tag_cloud_for_object, Tagging.tag_cloud_for_set or
Tagging.tag_cloud_for_user and will show a weighted tag cloud.

Using with SQLITE
-----------------

tgext.tagging relies heavily on ``SAVEPOINT`` to correctly handle
tagging uniqueness. The default SQLite behaviour is incompatible
with savepoints and requires some additional changes which are not
required when working with MySQL or Postgres.

Usually to make SQLite and zope.sqlalchemy properly behave the following code
is required before using your models::

    from sqlalchemy import event

    @event.listens_for(engine, "connect")
    def do_connect(dbapi_connection, connection_record):
        # Disable SQLITE automatic transactions
        dbapi_connection.isolation_level = None

    @event.listens_for(engine, "begin")
    def do_begin(conn):
        # Manually emit SQLITE transaction begin
        conn.execute("BEGIN")

    # Tell zope.sqlalchemy that SQLITE now supports SAVEPOINT
    from zope.sqlalchemy import datamanager
    datamanager.NO_SAVEPOINT_SUPPORT = set()

Forgetting to properly place this code will usually result in errors like::

    (OperationalError) no such savepoint: sa_savepoint_3 u'ROLLBACK TO SAVEPOINT sa_savepoint_3' ()

or::

    TypeError: ('Savepoints unsupported', <zope.sqlalchemy.datamanager.SessionDataManager object at 0x7fe9feae45d0>)

See `SQLite Serializable <http://docs.sqlalchemy.org/en/rel_0_9/dialects/sqlite.html#pysqlite-serializable>`_
for additional details.