zinnia.feeds
Covered: 255 lines
Missed: 24 lines
Skipped 101 lines
Percent: 91 %
  1
"""Feeds for Zinnia"""
  2
from sgmllib import SGMLParser
  4
from django.contrib.auth.models import User
  5
from django.contrib.sites.models import Site
  6
from django.core.urlresolvers import reverse
  7
from django.core.urlresolvers import NoReverseMatch
  8
from django.utils.feedgenerator import Atom1Feed
  9
from django.utils.translation import ugettext as _
 10
from django.contrib.syndication.views import Feed
 11
from django.shortcuts import get_object_or_404
 13
from tagging.models import Tag
 14
from tagging.models import TaggedItem
 16
from zinnia.models import Entry
 17
from zinnia.settings import COPYRIGHT
 18
from zinnia.settings import PROTOCOL
 19
from zinnia.settings import FEEDS_MAX_ITEMS
 20
from zinnia.managers import entries_published
 21
from zinnia.views.categories import get_category_or_404
 24
class ImgParser(SGMLParser):
 25
    """Parser for getting IMG markups"""
 27
    def __init__(self):
 28
        SGMLParser.__init__(self)
 29
        self.img_locations = []
 31
    def start_img(self, attr):
 32
        """Save each image's location"""
 33
        attr = dict(attr)
 34
        if attr.get('src', ''):
 35
            self.img_locations.append(attr['src'])
 38
class EntryFeed(Feed):
 39
    """Base Entry Feed"""
 40
    title_template = 'feeds/entry_title.html'
 41
    description_template = 'feeds/entry_description.html'
 42
    feed_copyright = COPYRIGHT
 44
    def __init__(self):
 45
        self.site = Site.objects.get_current()
 47
    def item_pubdate(self, item):
 48
        """Publication date of an entry"""
 49
        return item.creation_date
 51
    def item_categories(self, item):
 52
        """Entry's categories"""
 53
        return [category.title for category in item.categories.all()]
 55
    def item_author_name(self, item):
 56
        """Returns the first author of an entry"""
 57
        return item.authors.all()[0].username
 59
    def item_author_email(self, item):
 60
        """Returns the first author's email"""
 61
        return item.authors.all()[0].email
 63
    def item_author_link(self, item):
 64
        """Returns the author's URL"""
 65
        url = '%s://%s' % (PROTOCOL, self.site.domain)
 66
        try:
 67
            author_url = reverse('zinnia_author_detail',
 68
                                 args=[item.authors.all()[0].username])
 69
            return url + author_url
 70
        except NoReverseMatch:
 71
            return url
 73
    def item_enclosure_url(self, item):
 74
        """Returns an image for enclosure"""
 75
        if item.image:
 76
            return item.image.url
 77
        parser = ImgParser()
 78
        try:
 79
            parser.feed(item.content)
 80
        except UnicodeEncodeError:
 81
            return
 82
        if len(parser.img_locations):
 83
            if self.site.domain in parser.img_locations[0]:
 84
                return parser.img_locations[0]
 85
            else:
 86
                return '%s://%s%s' % (PROTOCOL,
 87
                                      self.site.domain,
 88
                                      parser.img_locations[0])
 89
        return None
 91
    def item_enclosure_length(self, item):
 92
        """Hardcoded enclosure length"""
 93
        return '100000'
 95
    def item_enclosure_mime_type(self, item):
 96
        """Hardcoded enclosure mimetype"""
 97
        return 'image/jpeg'
100
class LatestEntries(EntryFeed):
101
    """Feed for the latest entries"""
102
    title = _('Latest entries')
104
    def link(self):
105
        """URL of latest entries"""
106
        return reverse('zinnia_entry_archive_index')
108
    def items(self):
109
        """Items are published entries"""
110
        return Entry.published.all()[:FEEDS_MAX_ITEMS]
112
    def description(self, obj):
113
        """Description of the feed"""
114
        return _('The latest entries for the site %s') % self.site.domain
117
class CategoryEntries(EntryFeed):
118
    """Feed filtered by a category"""
120
    def get_object(self, request, path):
121
        """Retrieve the category by his path"""
122
        return get_category_or_404(path)
124
    def items(self, obj):
125
        """Items are the published entries of the category"""
126
        return obj.entries_published_set()[:FEEDS_MAX_ITEMS]
128
    def link(self, obj):
129
        """URL of the category"""
130
        return obj.get_absolute_url()
132
    def title(self, obj):
133
        """Title of the feed"""
134
        return _('Entries for the category %s') % obj.title
136
    def description(self, obj):
137
        """Description of the feed"""
138
        return _('The latest entries for the category %s') % obj.title
141
class AuthorEntries(EntryFeed):
142
    """Feed filtered by an author"""
144
    def get_object(self, request, username):
145
        """Retrieve the author by his username"""
146
        return get_object_or_404(User, username=username)
148
    def items(self, obj):
149
        """Items are the published entries of the author"""
150
        return entries_published(obj.entry_set)[:FEEDS_MAX_ITEMS]
152
    def link(self, obj):
153
        """URL of the author"""
154
        return reverse('zinnia_author_detail', args=[obj.username])
156
    def title(self, obj):
157
        """Title of the feed"""
158
        return _('Entries for author %s') % obj.username
160
    def description(self, obj):
161
        """Description of the feed"""
162
        return _('The latest entries by %s') % obj.username
165
class TagEntries(EntryFeed):
166
    """Feed filtered by a tag"""
168
    def get_object(self, request, slug):
169
        """Retrieve the tag by his name"""
170
        return get_object_or_404(Tag, name=slug)
172
    def items(self, obj):
173
        """Items are the published entries of the tag"""
174
        return TaggedItem.objects.get_by_model(
175
            Entry.published.all(), obj)[:FEEDS_MAX_ITEMS]
177
    def link(self, obj):
178
        """URL of the tag"""
179
        return reverse('zinnia_tag_detail', args=[obj.name])
181
    def title(self, obj):
182
        """Title of the feed"""
183
        return _('Entries for the tag %s') % obj.name
185
    def description(self, obj):
186
        """Description of the feed"""
187
        return _('The latest entries for the tag %s') % obj.name
190
class SearchEntries(EntryFeed):
191
    """Feed filtered by a search pattern"""
193
    def get_object(self, request, slug):
194
        """The slug is the pattern to search"""
195
        return slug
197
    def items(self, obj):
198
        """Items are the published entries founds"""
199
        return Entry.published.search(obj)[:FEEDS_MAX_ITEMS]
201
    def link(self, obj):
202
        """URL of the search request"""
203
        return '%s?pattern=%s' % (reverse('zinnia_entry_search'), obj)
205
    def title(self, obj):
206
        """Title of the feed"""
207
        return _('Results of the search for %s') % obj
209
    def description(self, obj):
210
        """Description of the feed"""
211
        return _('The entries containing the pattern %s') % obj
214
class EntryDiscussions(Feed):
215
    """Feed for discussions in an entry"""
216
    title_template = 'feeds/discussion_title.html'
217
    description_template = 'feeds/discussion_description.html'
218
    feed_copyright = COPYRIGHT
220
    def get_object(self, request, slug):
221
        """Retrieve the discussions by entry's slug"""
222
        return get_object_or_404(Entry, slug=slug)
224
    def items(self, obj):
225
        """Items are the discussions on the entry"""
226
        return obj.discussions[:FEEDS_MAX_ITEMS]
228
    def item_pubdate(self, item):
229
        """Publication date of a discussion"""
230
        return item.submit_date
232
    def item_link(self, item):
233
        """URL of the discussion"""
234
        return item.get_absolute_url()
236
    def link(self, obj):
237
        """URL of the entry"""
238
        return obj.get_absolute_url()
240
    def item_author_name(self, item):
241
        """Author of the discussion"""
242
        return item.userinfo['name']
244
    def item_author_email(self, item):
245
        """Author's email of the discussion"""
246
        return item.userinfo['email']
248
    def item_author_link(self, item):
249
        """Author's URL of the discussion"""
250
        return item.userinfo['url']
252
    def title(self, obj):
253
        """Title of the feed"""
254
        return _('Discussions on %s') % obj.title
256
    def description(self, obj):
257
        """Description of the feed"""
258
        return _('The latest discussions for the entry %s') % obj.title
261
class EntryComments(EntryDiscussions):
262
    """Feed for comments in an entry"""
263
    title_template = 'feeds/comment_title.html'
264
    description_template = 'feeds/comment_description.html'
266
    def items(self, obj):
267
        """Items are the comments on the entry"""
268
        return obj.comments[:FEEDS_MAX_ITEMS]
270
    def item_link(self, item):
271
        """URL of the comment"""
272
        return item.get_absolute_url('#comment_%(id)s')
274
    def title(self, obj):
275
        """Title of the feed"""
276
        return _('Comments on %s') % obj.title
278
    def description(self, obj):
279
        """Description of the feed"""
280
        return _('The latest comments for the entry %s') % obj.title
283
class EntryPingbacks(EntryDiscussions):
284
    """Feed for pingbacks in an entry"""
285
    title_template = 'feeds/pingback_title.html'
286
    description_template = 'feeds/pingback_description.html'
288
    def items(self, obj):
289
        """Items are the pingbacks on the entry"""
290
        return obj.pingbacks[:FEEDS_MAX_ITEMS]
292
    def item_link(self, item):
293
        """URL of the pingback"""
294
        return item.get_absolute_url('#pingback_%(id)s')
296
    def title(self, obj):
297
        """Title of the feed"""
298
        return _('Pingbacks on %s') % obj.title
300
    def description(self, obj):
301
        """Description of the feed"""
302
        return _('The latest pingbacks for the entry %s') % obj.title
305
class EntryTrackbacks(EntryDiscussions):
306
    """Feed for trackbacks in an entry"""
307
    title_template = 'feeds/trackback_title.html'
308
    description_template = 'feeds/trackback_description.html'
310
    def items(self, obj):
311
        """Items are the trackbacks on the entry"""
312
        return obj.trackbacks[:FEEDS_MAX_ITEMS]
314
    def item_link(self, item):
315
        """URL of the trackback"""
316
        return item.get_absolute_url('#trackback_%(id)s')
318
    def title(self, obj):
319
        """Title of the feed"""
320
        return _('Trackbacks on %s') % obj.title
322
    def description(self, obj):
323
        """Description of the feed"""
324
        return _('The latest trackbacks for the entry %s') % obj.title
328
class AtomLatestEntries(LatestEntries):
329
    """Atom feed for the latest entries"""
330
    feed_type = Atom1Feed
331
    subtitle = LatestEntries.description
334
class AtomCategoryEntries(CategoryEntries):
335
    """Atom feed filtered by a category"""
336
    feed_type = Atom1Feed
337
    subtitle = CategoryEntries.description
340
class AtomAuthorEntries(AuthorEntries):
341
    """Atom feed filtered by an author"""
342
    feed_type = Atom1Feed
343
    subtitle = AuthorEntries.description
346
class AtomTagEntries(TagEntries):
347
    """Atom feed filtered by a tag"""
348
    feed_type = Atom1Feed
349
    subtitle = TagEntries.description
352
class AtomSearchEntries(SearchEntries):
353
    """Atom feed filtered by a search pattern"""
354
    feed_type = Atom1Feed
355
    subtitle = SearchEntries.description
358
class AtomEntryDiscussions(EntryDiscussions):
359
    """Atom feed for discussions in an entry"""
360
    feed_type = Atom1Feed
361
    subtitle = EntryDiscussions.description
364
class AtomEntryComments(EntryComments):
365
    """Atom feed for comments in an entry"""
366
    feed_type = Atom1Feed
367
    subtitle = EntryComments.description
370
class AtomEntryPingbacks(EntryPingbacks):
371
    """Atom feed for pingbacks in an entry"""
372
    feed_type = Atom1Feed
373
    subtitle = EntryPingbacks.description
376
class AtomEntryTrackbacks(EntryTrackbacks):
377
    """Atom feed for trackbacks in an entry"""
378
    feed_type = Atom1Feed
379
    subtitle = EntryTrackbacks.description