def feed_agenda_item_state_change(obj, event): """ Will add a feed entry when the state change on agenda items. """ request = get_current_request() userid = authenticated_userid(request) msg = None if event.old_state == "private" and event.new_state == "upcoming": msg = _(u"${agenda_item} has been added to the agenda.", mapping={"agenda_item": obj.title}) elif event.new_state == "ongoing": msg = _(u"${agenda_item} has been set to ongoing.", mapping={"agenda_item": obj.title}) elif event.new_state == "closed": msg = _(u"${agenda_item} has been closed.", mapping={"agenda_item": obj.title}) if msg: meeting = find_interface(obj, IMeeting) feed_handler = request.registry.getAdapter(meeting, IFeedHandler) feed_handler.add(obj.uid, msg, tags=("agenda_item", event.new_state))
def feed_poll_state_change(obj, event): """ Will add a feed entry when a poll is opened and closes """ if event.new_state in ("ongoing", "closed"): request = get_current_request() userid = authenticated_userid(request) agenda_item = find_interface(obj, IAgendaItem) if event.new_state == "ongoing": msg = _( u"A poll has started in ${agenda_item}, vote now!", mapping={"userid": userid, "agenda_item": agenda_item.title}, ) elif event.new_state == "closed": msg = _(u"The result of a poll in ${agenda_item} is set.", mapping={"agenda_item": agenda_item.title}) meeting = find_interface(obj, IMeeting) feed_handler = request.registry.getAdapter(meeting, IFeedHandler) feed_handler.add(obj.uid, msg, tags=("poll", event.new_state))
def create_feed_object(request): settings = IFeedSettings(request.meeting, {}) te = request.localizer.translate fg = FeedGenerator() fg.id(rss_feed_url(request, token=settings.get('token', None))) #Mandatory for atom feeds fg.title(request.meeting.title) fg.description(settings.get('description_text', te(_("No description")))) #fg.author({'name': 'XXXX', 'email': '*****@*****.**'}) fg.link(href=request.resource_url(request.meeting), rel='alternate') #fg.icon('http://ex.com/icon.jpg') #fg.logo('http://ex.com/logo.jpg') #fg.rights('cc-by') #fg.subtitle('This is a cool feed!') fg.link(href=rss_feed_url(request, token=settings.get('link_token')), rel='self') fg.language(request.localizer.locale_name) # Fetch objects we want to interact with limit = settings.get('limit', 50) type_names = list(settings.get('type_names', ())) items = get_feed_items(request, type_names, limit=limit) # Create feed entries for obj in items: # Newest first is already the case in items fe = fg.add_entry(order='append') author_txt = _cleanup_txt(request.creators_info(obj.creator, portrait=False, no_tag=True)) # Needed? fe.id(request.resource_url(obj)) fe.published(obj.created) if obj.type_name == 'Poll': fe.title(te(_("${state} poll: ${title}", mapping={'state': te(voteit_mf(obj.current_state_title(request))), 'title': obj.title}))) fe.content() #fe.summary() if obj.type_name == 'DiscussionPost': fe.title(te(_("${name} added a discussion post", mapping={'name': author_txt}))) fe.content(nl2br(obj.text)) #fe.summary() if obj.type_name == 'Proposal': fe.title(te(_("${name} added a proposal", mapping={'name': author_txt}))) fe.content(request.render_proposal_text(obj, tag_func=lambda x: x)) #fe.summary() fe.link(href=request.resource_url(obj), rel='alternate') fe.author(name=author_txt) return fg
def includeme(config): config.scan(__name__) config.add_view_action( control_panel_category, 'control_panel', 'feed', panel_group='control_panel_feed', title=_("RSS Feeds"), check_active=feeds_enabled, ) config.add_view_action( control_panel_link, 'control_panel_feed', 'settings', title=_("Settings"), view_name='feed_settings_form', ) config.add_view_action( rss_feed_link, 'control_panel_feed', 'link_to_feed', title=_("Link to RSS feed"), )
def feed_discussion_post_added(obj, event): """ Will add a feed entry when a discussion post is added if agenda item is not private. """ request = get_current_request() userid = authenticated_userid(request) agenda_item = find_interface(obj, IAgendaItem) if agenda_item.get_workflow_state() == "private": return msg = _( u"${userid} has written a post in ${agenda_item}.", mapping={"userid": userid, "agenda_item": agenda_item.title} ) meeting = find_interface(obj, IMeeting) feed_handler = request.registry.getAdapter(meeting, IFeedHandler) feed_handler.add(obj.uid, msg, tags=("discussion_post", "added"))
def _get_feed(self): ''' Makes a respone dict for renderers ''' def _get_url(entry): """ If something stored in the database is deleted, the query won't return any object since that UID won't exist. """ brains = self.api.get_metadata_for_query(uid=entry.context_uid) if brains: resource = find_resource(self.api.root, brains[0]['path']) return self.request.resource_url(resource) return self.request.resource_url(self.api.meeting) # Borrowed from PyRSS2Gen, thanks for this workaround 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. tz = pytz.timezone('GMT') dt = tz.normalize(dt.astimezone(tz)) 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) feed_handler = self.request.registry.getAdapter(self.context, IFeedHandler) self.response['entries'] = feed_handler.feed_storage.values() self.response['format_date'] = _format_date self.response['active'] = self.context.get_field_value('rss_feed', False) self.response['feed_not_active_notice'] = self.api.translate(_(u"This RSS-feed isn't enabled.")) # only show entries when meeting is ongoing self.response['closed'] = self.context.get_workflow_state() == 'closed' self.response['get_url'] = _get_url return self.response
def _next_free_key(self): try: return self.feed_storage.maxKey()+1 except ValueError: #Emptry tree return 0 def add(self, context_uid, message, tags=(), context=None): obj = createContent('FeedEntry', context_uid, message, tags=tags) for i in range(10): k = self._next_free_key() if self.feed_storage.insert(k, obj): return raise KeyError("Couln't find a free key for feed handler after 10 retries.") # pragma : no cover @content_factory('FeedEntry', title=_(u"Feed entry")) class FeedEntry(Persistent): """ FeedEntry lightweight content type. See :mod:`voteit.core.models.interfaces.IFeedEntry`. All methods are documented in the interface of this class. """ implements(IFeedEntry) def __init__(self, context_uid, message, tags=()): self.created = utcnow() self.context_uid = context_uid self.message = message self.tags = tuple(tags)
import colander from betahaus.pyracont.decorators import schema_factory from voteit.feed import FeedMF as _ @schema_factory('RssSettingsMeetingSchema', title = _(u"RSS settings")) class RssSettingsMeetingSchema(colander.MappingSchema): rss_feed = colander.SchemaNode(colander.Boolean(), title = _(u"Activate RSS feed"), description = _(u"rss_feed_checkbox_description", default=u"When the checkbox below is checked your meeting will be able to show a public RSS feed that can be followed with a RSS reader. This feed will contain info about when changes are made in the meeting and who did the changes. You can access the feed on: 'The meeting URL' + '/feed'. This should be something like 'www.yourdomain.com/yourmeetingname/feed'. If you want the feed to show up in an iframe you can use '/framefeed' instead. This is an advanced feature and read more about it in the manual on wiki.voteit.se. Please note a word of warning: the feed is public for all who can figure it out."), default = False, )
def validator(self, form, value): if value["enable_rss"] and not len(value['type_names']): exc = colander.Invalid(form, _("Types missing")) exc["type_names"] = _("If RSS is enabled, you need to pick content types to add to the feed.") raise exc
def default_feed_description(node, kw): request = kw['request'] return request.localizer.translate(_("An RSS feed this meeting"))
# -*- coding: utf-8 -*- from uuid import uuid4 import colander import deform from voteit.feed import _ RSS_TYPES = ( ("Proposal", _("Proposal")), ("DiscussionPost", _("Discussion post")), ("Poll", _("Poll")), ) @colander.deferred def default_feed_description(node, kw): request = kw['request'] return request.localizer.translate(_("An RSS feed this meeting")) class FeedSettingsSchema(colander.Schema): enable_rss = colander.SchemaNode( colander.Bool(), title = _("Enalbe RSS feeds"), ) link_token = colander.SchemaNode( colander.String(), title = _("Add token to link"), description=_("Adds the following token to the link to make it impossible to guess."),
except ValidationFailure, e: self.response['form'] = e.render() return self.response self.context.set_field_appstruct(appstruct) url = self.request.resource_url(self.context) return HTTPFound(location=url) if 'cancel' in post: self.api.flash_messages.add(_(u"Canceled")) url = self.request.resource_url(self.context) return HTTPFound(location=url) #No action - Render form appstruct = self.context.get_field_appstruct(schema) self.response['form'] = form.render(appstruct) return self.response @view_action('settings_menu', 'rss_settings', title = _(u"RSS settings"), link = "rss_settings", permission = security.MODERATE_MEETING) def generic_menu_link(context, request, va, **kw): """ This is for simple menu items for the meeting root """ api = kw['api'] url = "%s%s" % (api.meeting_url, va.kwargs['link']) return """<li><a href="%s">%s</a></li>""" % (url, api.translate(va.title)) @view_action('meeting', 'feed', title = _(u"RSS feed"), link = "feed", ) def feed_menu_link(context, request, va, **kw): """ This is for simple menu items for the meeting root """ api = kw['api'] url = request.resource_url(api.meeting, va.kwargs['link']) if api.meeting.get_field_value('rss_feed', False): return """<li><a href="%s">%s</a></li>""" % (url, api.translate(va.title)) return '' # pragma : no coverage