Completed
Pull Request — master (#345)
by
unknown
01:05
created

IsFollowing.render_result()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
from django.contrib.contenttypes.models import ContentType
2
from django.core.urlresolvers import reverse
3
from django.template import Variable, Library, Node, TemplateSyntaxError
4
from django.template.loader import render_to_string
5
6
from actstream.models import Follow, Action
7
8
9
register = Library()
10
11
12
class DisplayActivityFollowUrl(Node):
13
    def __init__(self, actor, actor_only=True, follow_type=None):
14
        self.actor = Variable(actor)
15
        self.actor_only = actor_only
16
        self.follow_type = follow_type
17
18
    def render(self, context):
19
        actor_instance = self.actor.resolve(context)
20
        content_type = ContentType.objects.get_for_model(actor_instance).pk
21
22
        kwargs = {
23
            'content_type_id': content_type,
24
            'object_id': actor_instance.pk
25
        }
26
27
        if self.follow_type:
28
            kwargs['follow_type'] = self.follow_type
29
30
        if Follow.objects.is_following(context.get('user'), actor_instance, follow_type=self.follow_type):
31
            return reverse('actstream_unfollow', kwargs=kwargs)
32
        if self.actor_only:
33
            return reverse('actstream_follow', kwargs=kwargs)
34
        return reverse('actstream_follow_all', kwargs=kwargs)
35
36
37
class DisplayActivityActorUrl(Node):
38
    def __init__(self, actor):
39
        self.actor = Variable(actor)
40
41
    def render(self, context):
42
        actor_instance = self.actor.resolve(context)
43
        content_type = ContentType.objects.get_for_model(actor_instance).pk
44
        return reverse('actstream_actor', kwargs={
45
            'content_type_id': content_type, 'object_id': actor_instance.pk})
46
47
48
class AsNode(Node):
49
    """
50
    Base template Node class for template tags that takes a predefined number
51
    of arguments, ending in an optional 'as var' section.
52
    """
53
    args_count = 1
54
55
    @classmethod
56
    def handle_token(cls, parser, token):
57
        """
58
        Class method to parse and return a Node.
59
        """
60
        tag_error = "Accepted formats {%% %(tagname)s %(args)s %%} or " \
61
                    "{%% %(tagname)s %(args)s as [var] %%}"
62
        bits = token.split_contents()
63
        args_count = len(bits) - 1
64
        if args_count >= 2 and bits[-2] == 'as':
65
            as_var = bits[-1]
66
            args_count -= 2
67
        else:
68
            as_var = None
69
        if args_count != cls.args_count:
70
            arg_list = ' '.join(['[arg]' * cls.args_count])
71
            raise TemplateSyntaxError(tag_error % {'tagname': bits[0],
72
                                                   'args': arg_list})
73
        args = [parser.compile_filter(tkn)
74
                for tkn in bits[1:args_count + 1]]
75
        return cls(args, varname=as_var)
76
77
    def __init__(self, args, varname=None):
78
        self.args = args
79
        self.varname = varname
80
81
    def render(self, context):
82
        result = self.render_result(context)
83
        if self.varname is not None:
84
            context[self.varname] = result
85
            return ''
86
        return result
87
88
    def render_result(self, context):
89
        raise NotImplementedError("Must be implemented by a subclass")
90
91
92
class DisplayAction(AsNode):
93
94
    def render_result(self, context):
95
        action_instance = context['action'] = self.args[0].resolve(context)
96
        templates = [
97
            'actstream/%s/action.html' % action_instance.verb.replace(' ', '_'),
98
            'actstream/action.html',
99
        ]
100
        return render_to_string(templates, {'action': action_instance})
101
102
103
def display_action(parser, token):
104
    """
105
    Renders the template for the action description
106
107
    ::
108
109
        {% display_action action %}
110
    """
111
    return DisplayAction.handle_token(parser, token)
112
113
114
def is_following(user, actor):
115
    """
116
    Returns true if the given user is following the actor
117
118
    ::
119
120
        {% if request.user|is_following:another_user %}
121
            You are already following {{ another_user }}
122
        {% endif %}
123
    """
124
    return Follow.objects.is_following(user, actor)
125
126
127
class IsFollowing(AsNode):
128
    args_count = 3
129
130
    def render_result(self, context):
131
        user= self.args[0].resolve(context)
132
        actor = self.args[1].resolve(context)
133
        follow_type = self.args[2]
134
135
        return Follow.objects.is_following(user, actor, follow_type=follow_type)
136
137
138
def is_following_tag(parser, token):
139
    return IsFollowing.handle_token(parser, token)
140
141
142
def follow_url(parser, token):
143
    """
144
    Renders the URL of the follow view for a particular actor instance
145
146
    ::
147
148
        <a href="{% follow_url other_user %}">
149
            {% if request.user|is_following:other_user %}
150
                stop following
151
            {% else %}
152
                follow
153
            {% endif %}
154
        </a>
155
    """
156
    bits = token.split_contents()
157
158
    if len(bits) > 3:
159
        raise TemplateSyntaxError("Accepted format {% follow_url [instance] %}")
160
    elif len(bits) == 2:
161
        return DisplayActivityFollowUrl(bits[1])
162
    else:
163
        return DisplayActivityFollowUrl(bits[1], follow_type=bits[2])
164
165
166
def follow_all_url(parser, token):
167
    """
168
    Renders the URL to follow an object as both actor and target
169
170
    ::
171
172
        <a href="{% follow_all_url other_user %}">
173
            {% if request.user|is_following:other_user %}
174
                stop following
175
            {% else %}
176
                follow
177
            {% endif %}
178
        </a>
179
    """
180
    bits = token.split_contents()
181
    if len(bits) > 3:
182
        raise TemplateSyntaxError("Accepted format {% follow_all_url [instance] %}")
183
    elif len(bits) == 2:
184
        return DisplayActivityFollowUrl(bits[1], actor_only=False)
185
    else:
186
        return DisplayActivityFollowUrl(bits[1], actor_only=False, follow_type=bits[2])
187
188
189
def actor_url(parser, token):
190
    """
191
    Renders the URL for a particular actor instance
192
193
    ::
194
195
        <a href="{% actor_url request.user %}">View your actions</a>
196
        <a href="{% actor_url another_user %}">{{ another_user }}'s actions</a>
197
198
    """
199
    bits = token.split_contents()
200
    if len(bits) != 2:
201
        raise TemplateSyntaxError("Accepted format "
202
                                  "{% actor_url [actor_instance] %}")
203
    else:
204
        return DisplayActivityActorUrl(*bits[1:])
205
206
207
def activity_stream(context, stream_type, *args, **kwargs):
208
    """
209
    Renders an activity stream as a list into the template's context.
210
    Streams loaded by stream_type can be the default ones (eg user, actor, etc.) or a user defined stream.
211
    Extra args/kwargs are passed into the stream call.
212
213
    ::
214
215
        {% activity_stream 'actor' user %}
216
        {% for action in stream %}
217
            {% display_action action %}
218
        {% endfor %}
219
    """
220
    if stream_type == 'model':
221
        stream_type = 'model_actions'
222
    if not hasattr(Action.objects, stream_type):
223
        raise TemplateSyntaxError('Action manager has no attribute: %s' % stream_type)
224
    ctxvar = kwargs.pop('as', 'stream')
225
    context[ctxvar] = getattr(Action.objects, stream_type)(*args, **kwargs)
226
    return ''
227
228
229
register.filter(activity_stream)
230
register.filter(is_following)
231
register.tag('is_following', is_following_tag)
232
register.tag(display_action)
233
register.tag(follow_url)
234
register.tag(follow_all_url)
235
register.tag(actor_url)
236
register.simple_tag(takes_context=True)(activity_stream)
237