Source code for abilian.web.tags.extension

# coding=utf-8
"""
"""
from __future__ import absolute_import, print_function, division

from abilian.i18n import _l
from abilian.core.models.tag import Tag, TAGS_ATTR, is_support_tagging
from abilian.web import url_for
from abilian.web.forms import Form
from abilian.web.views.object import EDIT_BUTTON

from .forms import TagsField
from .views import bp as tags_bp, entity_bp

ENTITY_DEFAULT_NS_ATTR = '__tags_default_ns__'

def ns(ns):
  """
  Class decorator that set default tags namespace to use with its instances.
  """
  def setup_ns(cls):
    setattr(cls, ENTITY_DEFAULT_NS_ATTR, ns)
    return cls

  return setup_ns


class _TagsForm(Form):
  """
  Form to workaround a wtforms limitation: fields cannot start with an
  underscore, so '__tags__' is not accepted.

  This form help process tags (and only tags).

  .. seealso:: :py:meth:`~TagsExtension.entity_tags_form`
  """
  def process(self, formdata=None, obj=None, data=None, **kwargs):
    tags = None
    if obj is not None:
      tags = getattr(obj, TAGS_ATTR, set())

    super(_TagsForm, self).process(formdata=formdata, obj=None, data=data,
                                   tags=tags, **kwargs)


[docs]class TagsExtension(object): """ API for tags, installed as an application extension It is also available in templates as `tags` """ def __init__(self, app): app.extensions['tags'] = self app.add_template_global(self, 'tags') app.register_blueprint(tags_bp) app.register_blueprint(entity_bp)
[docs] def is_support_tagging(self, entity): return is_support_tagging(entity)
[docs] def entity_tags(self, entity): return getattr(entity, TAGS_ATTR)
[docs] def tags_from_hit(self, tag_ids): """ :param tag_ids: indexed ids of tags in hit result. Do not pass hit instance. :returns: an iterable of :class:`Tag` instances. """ ids = [] for t in tag_ids.split(): t = t.strip() try: t = int(t) except ValueError: pass else: ids.append(t) if not ids: return [] return Tag.query.filter(Tag.id.in_(ids)).all()
[docs] def entity_default_ns(self, entity): return getattr(entity, ENTITY_DEFAULT_NS_ATTR, 'default')
[docs] def entity_tags_form(self, entity, ns=None): """ Construct a form class with a field for tags in namespace `ns` """ if ns is None: ns = self.entity_default_ns(entity) field = TagsField(label=_l(u'Tags'), ns=ns) cls = type('EntityNSTagsForm', (_TagsForm,), {'tags': field}) return cls
[docs] def get(self, ns, label=None): """ Return :class:`tags instances<~Tag>` for the namespace `ns`, ordered by label. If `label` is not None the only one instance may be returned, or `None` if no tags exists for this label. """ query = Tag.query.filter(Tag.ns == ns) if label is not None: return query.filter(Tag.label == label).first() return query.all()
[docs] def add(self, entity, tag=None, ns=None, label=None): if tag is None: assert None not in (ns, label) tag = self.get(ns, label) if tag is None: tag = Tag(ns=ns, label=label) tags = self.entity_tag(entity) tags.add(tag)
[docs] def remove(self, entity, tag=None, ns=None, label=None): if tag is None: assert None not in (ns, label) tag = self.get(ns, label) tags = self.entity_tag(entity) try: tags.remove(tag) except KeyError: pass
[docs] def get_form_context(self, obj, ns=None): """ Return a dict: form instance, action button, submit url... Used by macro m_tags_form(entity) """ ctx = {} ctx['url'] = url_for('entity_tags.edit', object_id=obj.id) ctx['form'] = self.entity_tags_form(obj)(obj=obj, ns=ns) ctx['buttons'] = [EDIT_BUTTON] return ctx