Source code for actstream.templatetags.activity_tags

from django.contrib.contenttypes.models import ContentType
from django.template import Variable, Library, Node, TemplateSyntaxError
from django.template.loader import render_to_string
from django.urls import reverse

from actstream.models import Follow, Action


register = Library()


class DisplayActivityFollowUrl(Node):
    def __init__(self, actor, actor_only=True, flag=''):
        self.actor = Variable(actor)
        self.actor_only = actor_only
        self.flag = flag

    def render(self, context):
        actor_instance = self.actor.resolve(context)
        content_type = ContentType.objects.get_for_model(actor_instance).pk

        kwargs = {
            'content_type_id': content_type,
            'object_id': actor_instance.pk
        }

        if self.flag:
            kwargs['flag'] = self.flag

        if Follow.objects.is_following(context.get('user'), actor_instance, flag=self.flag):
            return reverse('actstream_unfollow', kwargs=kwargs)
        if self.actor_only:
            return reverse('actstream_follow', kwargs=kwargs)
        return reverse('actstream_follow_all', kwargs=kwargs)


class DisplayActivityActorUrl(Node):
    def __init__(self, actor):
        self.actor = Variable(actor)

    def render(self, context):
        actor_instance = self.actor.resolve(context)
        content_type = ContentType.objects.get_for_model(actor_instance).pk
        return reverse('actstream_actor', kwargs={
            'content_type_id': content_type, 'object_id': actor_instance.pk})


class AsNode(Node):
    """
    Base template Node class for template tags that takes a predefined number
    of arguments, ending in an optional 'as var' section.
    """
    args_count = 1

    @classmethod
    def handle_token(cls, parser, token):
        """
        Class method to parse and return a Node.
        """
        tag_error = "Accepted formats {%% %(tagname)s %(args)s %%} or " \
                    "{%% %(tagname)s %(args)s as [var] %%}"
        bits = token.split_contents()
        args_count = len(bits) - 1
        if args_count >= 2 and bits[-2] == 'as':
            as_var = bits[-1]
            args_count -= 2
        else:
            as_var = None
        if args_count != cls.args_count:
            arg_list = ' '.join(['[arg]' * cls.args_count])
            raise TemplateSyntaxError(tag_error % {'tagname': bits[0],
                                                   'args': arg_list})
        args = [parser.compile_filter(tkn)
                for tkn in bits[1:args_count + 1]]
        return cls(args, varname=as_var)

    def __init__(self, args, varname=None):
        self.args = args
        self.varname = varname

    def render(self, context):
        result = self.render_result(context)
        if self.varname is not None:
            context[self.varname] = result
            return ''
        return result

    def render_result(self, context):
        raise NotImplementedError("Must be implemented by a subclass")


class DisplayAction(AsNode):

    def render_result(self, context):
        action_instance = context['action'] = self.args[0].resolve(context)
        templates = [
            'actstream/%s/action.html' % action_instance.verb.replace(' ', '_'),
            'actstream/action.html',
        ]
        return render_to_string(templates, context.flatten())


[docs]def display_action(parser, token): """ Renders the template for the action description :: {% display_action action %} """ return DisplayAction.handle_token(parser, token)
[docs]def is_following(user, actor): """ Returns true if the given user is following the actor :: {% if request.user|is_following:another_user %} You are already following {{ another_user }} {% endif %} """ return Follow.objects.is_following(user, actor)
class IsFollowing(AsNode): args_count = 3 def render_result(self, context): user = self.args[0].resolve(context) actor = self.args[1].resolve(context) flag = self.args[2].resolve(context) return Follow.objects.is_following(user, actor, flag=flag) def is_following_tag(parser, token): """ Returns true if the given user is following the actor marked by a flag, such as 'liking', 'watching' etc.. You can also save the returned value to a template variable by as syntax. If you don't want to specify a flag, pass an empty string or use `is_following` template filter. :: {% is_following user group "liking" %} {% is_following user group "liking" as is_liking %} {% is_following user group "" as is_following %} """ return IsFollowing.handle_token(parser, token)
[docs]def follow_url(parser, token): """ Renders the URL of the follow view for a particular actor instance :: <a href="{% follow_url other_user %}"> {% if request.user|is_following:other_user %} stop following {% else %} follow {% endif %} </a> <a href="{% follow_url other_user 'watching' %}"> {% is_following user group "watching" as is_watching %} {% if is_watching %} stop watching {% else %} watch {% endif %} </a> """ bits = token.split_contents() if len(bits) > 3: raise TemplateSyntaxError("Accepted format {% follow_url [instance] %} or {% follow_url [instance] [flag] %}") elif len(bits) == 2: return DisplayActivityFollowUrl(bits[1]) else: flag = bits[2][1:-1] return DisplayActivityFollowUrl(bits[1], flag=flag)
[docs]def follow_all_url(parser, token): """ Renders the URL to follow an object as both actor and target :: <a href="{% follow_all_url other_user %}"> {% if request.user|is_following:other_user %} stop following {% else %} follow {% endif %} </a> <a href="{% follow_all_url other_user 'watching' %}"> {% is_following user group "watching" as is_watching %} {% if is_watching %} stop watching {% else %} watch {% endif %} </a> """ bits = token.split_contents() if len(bits) > 3: raise TemplateSyntaxError( "Accepted format {% follow_all_url [instance] %} or {% follow_url [instance] [flag] %}" ) elif len(bits) == 2: return DisplayActivityFollowUrl(bits[1], actor_only=False) else: flag = bits[2][1:-1] return DisplayActivityFollowUrl(bits[1], actor_only=False, flag=flag)
[docs]def actor_url(parser, token): """ Renders the URL for a particular actor instance :: <a href="{% actor_url request.user %}">View your actions</a> <a href="{% actor_url another_user %}">{{ another_user }}'s actions</a> """ bits = token.split_contents() if len(bits) != 2: raise TemplateSyntaxError("Accepted format " "{% actor_url [actor_instance] %}") else: return DisplayActivityActorUrl(*bits[1:])
[docs]def activity_stream(context, stream_type, *args, **kwargs): """ Renders an activity stream as a list into the template's context. Streams loaded by stream_type can be the default ones (eg user, actor, etc.) or a user defined stream. Extra args/kwargs are passed into the stream call. :: {% activity_stream 'actor' user %} {% for action in stream %} {% display_action action %} {% endfor %} """ if stream_type == 'model': stream_type = 'model_actions' if not hasattr(Action.objects, stream_type): raise TemplateSyntaxError('Action manager has no attribute: %s' % stream_type) ctxvar = kwargs.pop('as', 'stream') context[ctxvar] = getattr(Action.objects, stream_type)(*args, **kwargs) return ''
register.filter(activity_stream) register.filter(is_following) register.tag(name='is_following', compile_function=is_following_tag) register.tag(display_action) register.tag(follow_url) register.tag(follow_all_url) register.tag(actor_url) register.simple_tag(takes_context=True)(activity_stream)