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 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