mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-09-18 21:50:56 +00:00
Updating shared story comments, displaying multiple comments for testing.
This commit is contained in:
parent
b94d48b793
commit
b166d37e2c
6 changed files with 640 additions and 5 deletions
|
@ -3,4 +3,6 @@ from apps.social import views
|
|||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^share_story/?$', views.mark_story_as_shared, name='mark-story-as-shared'),
|
||||
url(r'^(?P<user_id>\d+)/(?P<username>\w+)/?$', views.shared_story_feed, name='shared-story-feed'),
|
||||
url(r'^(?P<username>\w+)/?$', views.shared_stories_public, name='shared-stories-public'),
|
||||
)
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import datetime
|
||||
import zlib
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
||||
from apps.rss_feeds.models import MStory
|
||||
from apps.social.models import MSharedStory
|
||||
from utils import json_functions as json
|
||||
from utils.user_functions import ajax_login_required
|
||||
from utils import log as logging
|
||||
from utils import PyRSS2Gen as RSS
|
||||
|
||||
|
||||
@ajax_login_required
|
||||
@json.json_view
|
||||
|
@ -14,7 +20,11 @@ def mark_story_as_shared(request):
|
|||
comments = request.POST.get('comments', '')
|
||||
|
||||
story = MStory.objects(story_feed_id=feed_id, story_guid=story_id).limit(1)
|
||||
if story:
|
||||
if not story:
|
||||
return {'code': -1, 'message': 'Story not found.'}
|
||||
|
||||
shared_story = MSharedStory.objects.filter(user_id=request.user.pk, story_feed_id=feed_id, story_guid=story_id)
|
||||
if not shared_story:
|
||||
story_db = dict([(k, v) for k, v in story[0]._data.items()
|
||||
if k is not None and v is not None])
|
||||
now = datetime.datetime.now()
|
||||
|
@ -22,6 +32,54 @@ def mark_story_as_shared(request):
|
|||
MSharedStory.objects.create(**story_values)
|
||||
logging.user(request, "~FCSharing: ~SB~FM%s (~FB%s~FM)" % (story[0].story_title[:50], comments[:100]))
|
||||
else:
|
||||
code = -1
|
||||
shared_story = shared_story[0]
|
||||
shared_story.comments = comments
|
||||
shared_story.save()
|
||||
logging.user(request, "~FCUpdating shared story: ~SB~FM%s (~FB%s~FM)" % (story[0].story_title[:50], comments[:100]))
|
||||
|
||||
|
||||
return {'code': code}
|
||||
return {'code': code}
|
||||
|
||||
def shared_story_feed(request, user_id, username):
|
||||
try:
|
||||
user = User.objects.get(pk=user_id)
|
||||
except User.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
if user.username != username:
|
||||
return HttpResponseRedirect(reverse('shared-story-feed', kwargs={'username': user.username, 'user_id': user.pk}))
|
||||
|
||||
data = {}
|
||||
data['title'] = "%s - Shared Stories" % user.username
|
||||
link = reverse('shared-stories-public', kwargs={'username': user.username})
|
||||
data['link'] = "http://www.newsblur.com/%s" % link
|
||||
data['description'] = "Stories shared by %s on NewsBlur." % user.username
|
||||
data['lastBuildDate'] = datetime.datetime.utcnow()
|
||||
data['items'] = []
|
||||
data['generator'] = 'NewsBlur'
|
||||
data['docs'] = None
|
||||
|
||||
shared_stories = MSharedStory.objects.filter(user_id=user.pk)[:30]
|
||||
for shared_story in shared_stories:
|
||||
story_data = {
|
||||
'title': shared_story.story_title,
|
||||
'link': shared_story.story_permalink,
|
||||
'description': zlib.decompress(shared_story.story_content_z),
|
||||
'guid': shared_story.story_guid,
|
||||
'pubDate': shared_story.story_date,
|
||||
}
|
||||
data['items'].append(RSS.RSSItem(**story_data))
|
||||
|
||||
rss = RSS.RSS2(**data)
|
||||
|
||||
return HttpResponse(rss.to_xml())
|
||||
|
||||
def shared_stories_public(request, username):
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
shared_stories = MSharedStory.objects.filter(user_id=user.pk)
|
||||
|
||||
return HttpResponse("There are %s stories shared by %s." % (shared_stories.count(), username))
|
|
@ -1932,7 +1932,80 @@ background: transparent;
|
|||
max-width: 700px;
|
||||
min-height: 66px;
|
||||
}
|
||||
#story_pane .NB-feed-story-comments {
|
||||
margin: -24px 200px 32px 28px;
|
||||
padding: 1px 0 0;
|
||||
max-width: 700px;
|
||||
border-top: 2px solid #353535;
|
||||
border-bottom: 1px solid #353535;
|
||||
}
|
||||
#story_pane .NB-story-comment {
|
||||
border-top: 1px solid #A6A6A6;
|
||||
background-color: #FCFCFC;
|
||||
position: relative;
|
||||
padding: 0 12px 0 54px;
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#story_pane .NB-story-comment .NB-user-avatar {
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 6px;
|
||||
}
|
||||
#story_pane .NB-story-comment .NB-user-avatar img {
|
||||
border: 1px solid #A6A6A6;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
#story_pane .NB-story-comment .NB-story-comment-author-container {
|
||||
overflow: hidden;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
#story_pane .NB-story-comment .NB-story-comment-username {
|
||||
float: left;
|
||||
font-size: 11px;
|
||||
color: #1D4BA6;
|
||||
font-weight: bold;
|
||||
margin: 0 10px 0 0;
|
||||
text-shadow: 0 -1px 0 #F0F0F0;
|
||||
}
|
||||
#story_pane .NB-story-comment .NB-story-comment-date {
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
color: #9D9D9D;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 -1px 0 #F0F0F0;
|
||||
}
|
||||
#story_pane .NB-story-comment .NB-story-comment-content {
|
||||
float: left;
|
||||
color: #505050;
|
||||
}
|
||||
#story_pane .NB-story-comments-public-teaser-wrapper {
|
||||
border-top: 1px solid #353535;
|
||||
padding: 1px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
#story_pane .NB-story-comments-public-teaser {
|
||||
background-color: #B1B6B4;
|
||||
color: white;
|
||||
text-shadow: 0 1px 0 black;
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
padding: 2px 12px;
|
||||
-webkit-transition: all .12s ease-out;
|
||||
-moz-transition: all .12s ease-out;
|
||||
-o-transition: all .12s ease-out;
|
||||
-ms-transition: all .12s ease-out;
|
||||
}
|
||||
|
||||
#story_pane .NB-story-comments-public-teaser-wrapper:hover .NB-story-comments-public-teaser {
|
||||
background-color: #2B478C;
|
||||
background-image: none;
|
||||
}
|
||||
#story_pane .NB-story-comment .NB-story-comment-content {
|
||||
clear: both;
|
||||
padding: 0 0 6px 0;
|
||||
}
|
||||
#story_pane .NB-feed-story-sideoptions-container {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
|
|
|
@ -323,6 +323,13 @@
|
|||
// [self.sitesButton setTitle:@"All Sites"];
|
||||
}
|
||||
|
||||
NSIndexPath *topIndexPath = [[self.feedTitlesTable indexPathsForVisibleRows] objectAtIndex:0];
|
||||
NSString *topFolderName = [appDelegate.dictFoldersArray objectAtIndex:topIndexPath.section];
|
||||
NSArray *feeds = [appDelegate.dictFolders objectForKey:topFolderName];
|
||||
NSArray *activeFolderFeeds = [self.activeFeedLocations objectForKey:topFolderName];
|
||||
int location = [[activeFolderFeeds objectAtIndex:topIndexPath.row] intValue];
|
||||
id topFeedId = [feeds objectAtIndex:location];
|
||||
|
||||
NSInteger intelligenceLevel = [appDelegate selectedIntelligence];
|
||||
NSMutableArray *indexPaths = [NSMutableArray array];
|
||||
|
||||
|
@ -369,8 +376,20 @@
|
|||
}
|
||||
[self.feedTitlesTable endUpdates];
|
||||
|
||||
CGPoint offset = CGPointMake(0, 0);
|
||||
[self.feedTitlesTable setContentOffset:offset animated:YES];
|
||||
// Find the top feed and scroll to it
|
||||
NSArray *topActiveFolderFeeds = [self.activeFeedLocations objectForKey:topFolderName];
|
||||
for (int i=0; i < [topActiveFolderFeeds count]; i++) {
|
||||
int location = [[topActiveFolderFeeds objectAtIndex:i] intValue];
|
||||
id feedId = [feeds objectAtIndex:location];
|
||||
if (feedId == topFeedId) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i
|
||||
inSection:topIndexPath.section];
|
||||
CGRect rowFrame = [self.feedTitlesTable rectForRowAtIndexPath:indexPath];
|
||||
CGPoint origin = rowFrame.origin;
|
||||
[self.feedTitlesTable setContentOffset:origin animated:YES];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Forget still visible feeds, since they won't be populated when
|
||||
// all feeds are showing, and shouldn't be populated after this
|
||||
|
|
|
@ -3648,6 +3648,7 @@
|
|||
])
|
||||
]),
|
||||
$.make('div', { className: 'NB-feed-story-content' }, this.make_story_content(story.story_content)),
|
||||
$.make('div', { className: 'NB-feed-story-comments' }, this.make_story_share_comments(story)),
|
||||
$.make('div', { className: 'NB-feed-story-sideoptions-container' }, [
|
||||
$.make('div', { className: 'NB-sideoption NB-feed-story-train' }, [
|
||||
$.make('div', { className: 'NB-sideoption-icon'}, ' '),
|
||||
|
@ -3730,6 +3731,45 @@
|
|||
return $story_content;
|
||||
},
|
||||
|
||||
make_story_share_comments: function(story) {
|
||||
var $comments = $([]);
|
||||
|
||||
var $share = $.make('div', { className: 'NB-story-comments-sharers' }, 'Shared by: ');
|
||||
var $comment = this.make_story_share_comment({'content': 'Pour some sugar on this comment form. It\'s done.'});
|
||||
var $comment2 = this.make_story_share_comment({'content': 'These are all AWESOME changes and things we wished we\'d had when we were doing our campaign! Good job, guys. Go go gadget team!'});
|
||||
var $comment3 = this.make_story_share_comment({'content': 'So cool to get a glimpse of the NewsBlur team and offices! (Love the farm table and tin wainscotting). Thanks for sharing the love with my baby girl! And never apologize for delicious sandwiches. ;)'});
|
||||
|
||||
var $public_teaser = $.make('div', { className: 'NB-story-comments-public-teaser-wrapper' }, [
|
||||
$.make('div', { className: 'NB-story-comments-public-teaser' }, [
|
||||
'There are ',
|
||||
$.make('b', { style: 'padding: 0 1px' }, '3'),
|
||||
' public comments'
|
||||
])
|
||||
]);
|
||||
|
||||
Math.random() < .35 && $comments.push($comment);
|
||||
Math.random() < .45 && $comments.push($comment2);
|
||||
Math.random() < .35 && $comments.push($comment3);
|
||||
Math.random() < .45 && $comments.push($public_teaser);
|
||||
|
||||
return $comments;
|
||||
},
|
||||
|
||||
make_story_share_comment: function(comment) {
|
||||
var $comment = $.make('div', { className: 'NB-story-comment' }, [
|
||||
$.make('div', { className: 'NB-user-avatar' }, [
|
||||
$.make('img', { src: '/media/img/reader/account_standard_3.jpg' })
|
||||
]),
|
||||
$.make('div', { className: 'NB-story-comment-author-container' }, [
|
||||
$.make('div', { className: 'NB-story-comment-username' }, 'samuelclay'),
|
||||
$.make('div', { className: 'NB-story-comment-date' }, '2 hours ago')
|
||||
]),
|
||||
$.make('div', { className: 'NB-story-comment-content' }, comment.content)
|
||||
]);
|
||||
|
||||
return $comment;
|
||||
},
|
||||
|
||||
make_story_feed_title: function(story) {
|
||||
var title = story.story_title;
|
||||
var feed_titles = this.model.classifiers[story.story_feed_id] &&
|
||||
|
|
443
utils/PyRSS2Gen.py
Normal file
443
utils/PyRSS2Gen.py
Normal file
|
@ -0,0 +1,443 @@
|
|||
"""PyRSS2Gen - A Python library for generating RSS 2.0 feeds."""
|
||||
|
||||
__name__ = "PyRSS2Gen"
|
||||
__version__ = (1, 0, 0)
|
||||
__author__ = "Andrew Dalke <dalke@dalkescientific.com>"
|
||||
|
||||
_generator_name = __name__ + "-" + ".".join(map(str, __version__))
|
||||
|
||||
import datetime
|
||||
|
||||
# Could make this the base class; will need to add 'publish'
|
||||
class WriteXmlMixin:
|
||||
def write_xml(self, outfile, encoding = "iso-8859-1"):
|
||||
from xml.sax import saxutils
|
||||
handler = saxutils.XMLGenerator(outfile, encoding)
|
||||
handler.startDocument()
|
||||
self.publish(handler)
|
||||
handler.endDocument()
|
||||
|
||||
def to_xml(self, encoding = "iso-8859-1"):
|
||||
try:
|
||||
import cStringIO as StringIO
|
||||
except ImportError:
|
||||
import StringIO
|
||||
f = StringIO.StringIO()
|
||||
self.write_xml(f, encoding)
|
||||
return f.getvalue()
|
||||
|
||||
|
||||
def _element(handler, name, obj, d = {}):
|
||||
if isinstance(obj, basestring) or obj is None:
|
||||
# special-case handling to make the API easier
|
||||
# to use for the common case.
|
||||
handler.startElement(name, d)
|
||||
if obj is not None:
|
||||
handler.characters(obj)
|
||||
handler.endElement(name)
|
||||
else:
|
||||
# It better know how to emit the correct XML.
|
||||
obj.publish(handler)
|
||||
|
||||
def _opt_element(handler, name, obj):
|
||||
if obj is None:
|
||||
return
|
||||
_element(handler, name, obj)
|
||||
|
||||
|
||||
def _format_date(dt):
|
||||
"""convert a datetime into an RFC 822 formatted date
|
||||
|
||||
Input date must be in GMT.
|
||||
"""
|
||||
# Looks like:
|
||||
# Sat, 07 Sep 2002 00:00:01 GMT
|
||||
# Can't use strftime because that's locale dependent
|
||||
#
|
||||
# Isn't there a standard way to do this for Python? The
|
||||
# rfc822 and email.Utils modules assume a timestamp. The
|
||||
# following is based on the rfc822 module.
|
||||
return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
|
||||
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][dt.weekday()],
|
||||
dt.day,
|
||||
["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][dt.month-1],
|
||||
dt.year, dt.hour, dt.minute, dt.second)
|
||||
|
||||
|
||||
##
|
||||
# A couple simple wrapper objects for the fields which
|
||||
# take a simple value other than a string.
|
||||
class IntElement:
|
||||
"""implements the 'publish' API for integers
|
||||
|
||||
Takes the tag name and the integer value to publish.
|
||||
|
||||
(Could be used for anything which uses str() to be published
|
||||
to text for XML.)
|
||||
"""
|
||||
element_attrs = {}
|
||||
def __init__(self, name, val):
|
||||
self.name = name
|
||||
self.val = val
|
||||
def publish(self, handler):
|
||||
handler.startElement(self.name, self.element_attrs)
|
||||
handler.characters(str(self.val))
|
||||
handler.endElement(self.name)
|
||||
|
||||
class DateElement:
|
||||
"""implements the 'publish' API for a datetime.datetime
|
||||
|
||||
Takes the tag name and the datetime to publish.
|
||||
|
||||
Converts the datetime to RFC 2822 timestamp (4-digit year).
|
||||
"""
|
||||
def __init__(self, name, dt):
|
||||
self.name = name
|
||||
self.dt = dt
|
||||
def publish(self, handler):
|
||||
_element(handler, self.name, _format_date(self.dt))
|
||||
####
|
||||
|
||||
class Category:
|
||||
"""Publish a category element"""
|
||||
def __init__(self, category, domain = None):
|
||||
self.category = category
|
||||
self.domain = domain
|
||||
def publish(self, handler):
|
||||
d = {}
|
||||
if self.domain is not None:
|
||||
d["domain"] = self.domain
|
||||
_element(handler, "category", self.category, d)
|
||||
|
||||
class Cloud:
|
||||
"""Publish a cloud"""
|
||||
def __init__(self, domain, port, path,
|
||||
registerProcedure, protocol):
|
||||
self.domain = domain
|
||||
self.port = port
|
||||
self.path = path
|
||||
self.registerProcedure = registerProcedure
|
||||
self.protocol = protocol
|
||||
def publish(self, handler):
|
||||
_element(handler, "cloud", None, {
|
||||
"domain": self.domain,
|
||||
"port": str(self.port),
|
||||
"path": self.path,
|
||||
"registerProcedure": self.registerProcedure,
|
||||
"protocol": self.protocol})
|
||||
|
||||
class Image:
|
||||
"""Publish a channel Image"""
|
||||
element_attrs = {}
|
||||
def __init__(self, url, title, link,
|
||||
width = None, height = None, description = None):
|
||||
self.url = url
|
||||
self.title = title
|
||||
self.link = link
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.description = description
|
||||
|
||||
def publish(self, handler):
|
||||
handler.startElement("image", self.element_attrs)
|
||||
|
||||
_element(handler, "url", self.url)
|
||||
_element(handler, "title", self.title)
|
||||
_element(handler, "link", self.link)
|
||||
|
||||
width = self.width
|
||||
if isinstance(width, int):
|
||||
width = IntElement("width", width)
|
||||
_opt_element(handler, "width", width)
|
||||
|
||||
height = self.height
|
||||
if isinstance(height, int):
|
||||
height = IntElement("height", height)
|
||||
_opt_element(handler, "height", height)
|
||||
|
||||
_opt_element(handler, "description", self.description)
|
||||
|
||||
handler.endElement("image")
|
||||
|
||||
class Guid:
|
||||
"""Publish a guid
|
||||
|
||||
Defaults to being a permalink, which is the assumption if it's
|
||||
omitted. Hence strings are always permalinks.
|
||||
"""
|
||||
def __init__(self, guid, isPermaLink = 1):
|
||||
self.guid = guid
|
||||
self.isPermaLink = isPermaLink
|
||||
def publish(self, handler):
|
||||
d = {}
|
||||
if self.isPermaLink:
|
||||
d["isPermaLink"] = "true"
|
||||
else:
|
||||
d["isPermaLink"] = "false"
|
||||
_element(handler, "guid", self.guid, d)
|
||||
|
||||
class TextInput:
|
||||
"""Publish a textInput
|
||||
|
||||
Apparently this is rarely used.
|
||||
"""
|
||||
element_attrs = {}
|
||||
def __init__(self, title, description, name, link):
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.name = name
|
||||
self.link = link
|
||||
|
||||
def publish(self, handler):
|
||||
handler.startElement("textInput", self.element_attrs)
|
||||
_element(handler, "title", self.title)
|
||||
_element(handler, "description", self.description)
|
||||
_element(handler, "name", self.name)
|
||||
_element(handler, "link", self.link)
|
||||
handler.endElement("textInput")
|
||||
|
||||
|
||||
class Enclosure:
|
||||
"""Publish an enclosure"""
|
||||
def __init__(self, url, length, type):
|
||||
self.url = url
|
||||
self.length = length
|
||||
self.type = type
|
||||
def publish(self, handler):
|
||||
_element(handler, "enclosure", None,
|
||||
{"url": self.url,
|
||||
"length": str(self.length),
|
||||
"type": self.type,
|
||||
})
|
||||
|
||||
class Source:
|
||||
"""Publish the item's original source, used by aggregators"""
|
||||
def __init__(self, name, url):
|
||||
self.name = name
|
||||
self.url = url
|
||||
def publish(self, handler):
|
||||
_element(handler, "source", self.name, {"url": self.url})
|
||||
|
||||
class SkipHours:
|
||||
"""Publish the skipHours
|
||||
|
||||
This takes a list of hours, as integers.
|
||||
"""
|
||||
element_attrs = {}
|
||||
def __init__(self, hours):
|
||||
self.hours = hours
|
||||
def publish(self, handler):
|
||||
if self.hours:
|
||||
handler.startElement("skipHours", self.element_attrs)
|
||||
for hour in self.hours:
|
||||
_element(handler, "hour", str(hour))
|
||||
handler.endElement("skipHours")
|
||||
|
||||
class SkipDays:
|
||||
"""Publish the skipDays
|
||||
|
||||
This takes a list of days as strings.
|
||||
"""
|
||||
element_attrs = {}
|
||||
def __init__(self, days):
|
||||
self.days = days
|
||||
def publish(self, handler):
|
||||
if self.days:
|
||||
handler.startElement("skipDays", self.element_attrs)
|
||||
for day in self.days:
|
||||
_element(handler, "day", day)
|
||||
handler.endElement("skipDays")
|
||||
|
||||
class RSS2(WriteXmlMixin):
|
||||
"""The main RSS class.
|
||||
|
||||
Stores the channel attributes, with the "category" elements under
|
||||
".categories" and the RSS items under ".items".
|
||||
"""
|
||||
|
||||
rss_attrs = {"version": "2.0"}
|
||||
element_attrs = {}
|
||||
def __init__(self,
|
||||
title,
|
||||
link,
|
||||
description,
|
||||
|
||||
language = None,
|
||||
copyright = None,
|
||||
managingEditor = None,
|
||||
webMaster = None,
|
||||
pubDate = None, # a datetime, *in* *GMT*
|
||||
lastBuildDate = None, # a datetime
|
||||
|
||||
categories = None, # list of strings or Category
|
||||
generator = _generator_name,
|
||||
docs = "http://blogs.law.harvard.edu/tech/rss",
|
||||
cloud = None, # a Cloud
|
||||
ttl = None, # integer number of minutes
|
||||
|
||||
image = None, # an Image
|
||||
rating = None, # a string; I don't know how it's used
|
||||
textInput = None, # a TextInput
|
||||
skipHours = None, # a SkipHours with a list of integers
|
||||
skipDays = None, # a SkipDays with a list of strings
|
||||
|
||||
items = None, # list of RSSItems
|
||||
):
|
||||
self.title = title
|
||||
self.link = link
|
||||
self.description = description
|
||||
self.language = language
|
||||
self.copyright = copyright
|
||||
self.managingEditor = managingEditor
|
||||
|
||||
self.webMaster = webMaster
|
||||
self.pubDate = pubDate
|
||||
self.lastBuildDate = lastBuildDate
|
||||
|
||||
if categories is None:
|
||||
categories = []
|
||||
self.categories = categories
|
||||
self.generator = generator
|
||||
self.docs = docs
|
||||
self.cloud = cloud
|
||||
self.ttl = ttl
|
||||
self.image = image
|
||||
self.rating = rating
|
||||
self.textInput = textInput
|
||||
self.skipHours = skipHours
|
||||
self.skipDays = skipDays
|
||||
|
||||
if items is None:
|
||||
items = []
|
||||
self.items = items
|
||||
|
||||
def publish(self, handler):
|
||||
handler.startElement("rss", self.rss_attrs)
|
||||
handler.startElement("channel", self.element_attrs)
|
||||
_element(handler, "title", self.title)
|
||||
_element(handler, "link", self.link)
|
||||
_element(handler, "description", self.description)
|
||||
|
||||
self.publish_extensions(handler)
|
||||
|
||||
_opt_element(handler, "language", self.language)
|
||||
_opt_element(handler, "copyright", self.copyright)
|
||||
_opt_element(handler, "managingEditor", self.managingEditor)
|
||||
_opt_element(handler, "webMaster", self.webMaster)
|
||||
|
||||
pubDate = self.pubDate
|
||||
if isinstance(pubDate, datetime.datetime):
|
||||
pubDate = DateElement("pubDate", pubDate)
|
||||
_opt_element(handler, "pubDate", pubDate)
|
||||
|
||||
lastBuildDate = self.lastBuildDate
|
||||
if isinstance(lastBuildDate, datetime.datetime):
|
||||
lastBuildDate = DateElement("lastBuildDate", lastBuildDate)
|
||||
_opt_element(handler, "lastBuildDate", lastBuildDate)
|
||||
|
||||
for category in self.categories:
|
||||
if isinstance(category, basestring):
|
||||
category = Category(category)
|
||||
category.publish(handler)
|
||||
|
||||
_opt_element(handler, "generator", self.generator)
|
||||
_opt_element(handler, "docs", self.docs)
|
||||
|
||||
if self.cloud is not None:
|
||||
self.cloud.publish(handler)
|
||||
|
||||
ttl = self.ttl
|
||||
if isinstance(self.ttl, int):
|
||||
ttl = IntElement("ttl", ttl)
|
||||
_opt_element(handler, "tt", ttl)
|
||||
|
||||
if self.image is not None:
|
||||
self.image.publish(handler)
|
||||
|
||||
_opt_element(handler, "rating", self.rating)
|
||||
if self.textInput is not None:
|
||||
self.textInput.publish(handler)
|
||||
if self.skipHours is not None:
|
||||
self.skipHours.publish(handler)
|
||||
if self.skipDays is not None:
|
||||
self.skipDays.publish(handler)
|
||||
|
||||
for item in self.items:
|
||||
item.publish(handler)
|
||||
|
||||
handler.endElement("channel")
|
||||
handler.endElement("rss")
|
||||
|
||||
def publish_extensions(self, handler):
|
||||
# Derived classes can hook into this to insert
|
||||
# output after the three required fields.
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class RSSItem(WriteXmlMixin):
|
||||
"""Publish an RSS Item"""
|
||||
element_attrs = {}
|
||||
def __init__(self,
|
||||
title = None, # string
|
||||
link = None, # url as string
|
||||
description = None, # string
|
||||
author = None, # email address as string
|
||||
categories = None, # list of string or Category
|
||||
comments = None, # url as string
|
||||
enclosure = None, # an Enclosure
|
||||
guid = None, # a unique string
|
||||
pubDate = None, # a datetime
|
||||
source = None, # a Source
|
||||
):
|
||||
|
||||
if title is None and description is None:
|
||||
raise TypeError(
|
||||
"must define at least one of 'title' or 'description'")
|
||||
self.title = title
|
||||
self.link = link
|
||||
self.description = description
|
||||
self.author = author
|
||||
if categories is None:
|
||||
categories = []
|
||||
self.categories = categories
|
||||
self.comments = comments
|
||||
self.enclosure = enclosure
|
||||
self.guid = guid
|
||||
self.pubDate = pubDate
|
||||
self.source = source
|
||||
# It sure does get tedious typing these names three times...
|
||||
|
||||
def publish(self, handler):
|
||||
handler.startElement("item", self.element_attrs)
|
||||
_opt_element(handler, "title", self.title)
|
||||
_opt_element(handler, "link", self.link)
|
||||
self.publish_extensions(handler)
|
||||
_opt_element(handler, "description", self.description)
|
||||
_opt_element(handler, "author", self.author)
|
||||
|
||||
for category in self.categories:
|
||||
if isinstance(category, basestring):
|
||||
category = Category(category)
|
||||
category.publish(handler)
|
||||
|
||||
_opt_element(handler, "comments", self.comments)
|
||||
if self.enclosure is not None:
|
||||
self.enclosure.publish(handler)
|
||||
_opt_element(handler, "guid", self.guid)
|
||||
|
||||
pubDate = self.pubDate
|
||||
if isinstance(pubDate, datetime.datetime):
|
||||
pubDate = DateElement("pubDate", pubDate)
|
||||
_opt_element(handler, "pubDate", pubDate)
|
||||
|
||||
if self.source is not None:
|
||||
self.source.publish(handler)
|
||||
|
||||
handler.endElement("item")
|
||||
|
||||
def publish_extensions(self, handler):
|
||||
# Derived classes can hook into this to insert
|
||||
# output after the title and link elements
|
||||
pass
|
Loading…
Add table
Reference in a new issue