class CommentsFolder(Base, Folder, ContextACLMixin, LocalRolesMixin): """ Container for comments. """ nav_visible = False listing_visible = True search_visible = True title = _("Comments") type_name = "CommentsFolder" type_title = _("Comments folder") type_description = _("Container for comments.") add_permission = ENABLE_COMMENTS enabled = False def is_subscibing(self, userid): return userid in self.subscribers def get_subscribers(self): return self.subscribers.keys() def add_subscribing_userid(self, userid): if userid not in self.subscribers: self.subscribers[userid] = unicode(uuid4()) return self.subscribers[userid] def remove_subscribing_userid(self, userid): self.subscribers.pop(userid, None) def subscribe_url(self, request): userid = request.authenticated_userid if userid not in self.subscribers: return request.resource_url(self, 'subscribe') def unsubscribe_url(self, request, userid=None): userid = userid and userid or request.authenticated_userid if userid in self.subscribers: return request.resource_url(self, 'unsubscribe', userid, self.subscribers[userid]) def validate(self, request): """ Fetch userid if token is valid. """ if len(request.subpath) != 2: return userid = request.subpath[0] token = request.subpath[1] if self.subscribers.get(userid, object()) == token: return userid @property def subscribers(self): if not hasattr(self, '_subscribers'): self._subscribers = OOBTree() return self._subscribers @property def __acl__(self): """ Return the parents ACL and inject deny in case comments are closed. """ acl = _find_closest_parent_attr(self, '__acl__', []) if not self.enabled: acl.insert(0, (Deny, Everyone, ADD_COMMENT)) return acl
class CommentSchema(colander.Schema): body = colander.SchemaNode( colander.String(), title=_("Comment"), description=_("Note that HTML tags will be stripped"), widget=deform.widget.TextAreaWidget(cols=10), validator=colander.Length(max=5000), preparer=convert_text, )
class CommentsPortletSchema(colander.Schema): allowed_types = colander.SchemaNode( colander.Set(), title=_("Allow comments on these types?"), description= _("It will still require the user to have the correct permission to enable comments." ), widget=limit_types_widget, missing=(), )
def unsubscribe_notifications(self): if not len(self.request.subpath) == 2: raise HTTPNotFound() userid = self.context.validate(self.request) if userid: self.context.remove_subscribing_userid(userid) self.flash_messages.add(_("Notifications are now turned off.")) else: self.flash_messages.add(_("No subscription found.")) return HTTPFound( location=self.request.resource_url(self.context.__parent__))
def subscribe_notifications(self): userid = self.request.authenticated_userid if not userid: raise HTTPUnauthorized(_("You must be logged in")) if not self.context.is_subscibing(self.request.authenticated_userid): self.context.add_subscribing_userid(userid) self.flash_messages.add( _("You will receive email notifications when someone posts something here." )) else: self.flash_messages.add(_("You were already subscribing.")) return HTTPFound( location=self.request.resource_url(self.context.__parent__))
class CommentsPortlet(PortletType): name = "comments" title = _("Comments") tpl = "arche_comments:templates/portlet.pt" schema_factory = CommentsPortletSchema def visible(self, context, request, view, **kwargs): return context.get('_comments', None) is not None or request.has_permission( ENABLE_COMMENTS, context) def render(self, context, request, view, **kwargs): comments = context.get('_comments', None) allowed_types = self.portlet.settings.get('allowed_types', ()) if not allowed_types: return if getattr(context, 'type_name', '') not in allowed_types: return can_toggle = request.has_permission(ENABLE_COMMENTS, context) if comments: need_lib('deform') can_add = request.has_permission(ADD_COMMENT, comments) can_view = request.has_permission(PERM_VIEW, comments) else: can_add = False can_view = None return render(self.tpl, { 'portlet': self.portlet, 'comments': comments, 'view': view, 'can_toggle': can_toggle, 'can_add': can_add, 'can_view': can_view }, request=request)
def add_success(self, appstruct): factory = self.request.content_factories[self.type_name] obj = factory(**appstruct) name = generate_slug(self.context, obj.uid) self.context[name] = obj self.flash_messages.add(_("Added"), type="success") return _redirect_or_remove(self)
class AddCommentForm(DefaultAddForm): title = _("Add") type_name = 'Comment' use_ajax = True formid = "add-comment-form" ajax_options = """ {success: function (rText, sText, xhr, form) { arche.load_flash_messages(); } } """ @property def buttons(self): return (self.button_add, self.button_cancel) def add_success(self, appstruct): factory = self.request.content_factories[self.type_name] obj = factory(**appstruct) name = generate_slug(self.context, obj.uid) self.context[name] = obj self.flash_messages.add(_("Added"), type="success") return _redirect_or_remove(self) def cancel(self, *args): return _redirect_or_remove(self) cancel_failure = cancel_success = cancel
def notify_subscribing_users(context, event): """ Send an email to any user subscribing to this folder. """ request = get_current_request() comments = find_interface(context, ICommentsFolder) subject = _("Notification") comments_context = comments.__parent__ for userid in comments.get_subscribers(): if userid == request.authenticated_userid: continue try: user = request.root['users'][userid] except KeyError: continue if not user.email: continue try: comment_user = request.root['users'][context.creator[0]] except (KeyError, IndexError): comment_user = None values = dict( user=user, body=context.body, comment_context=comments_context, comment_user=comment_user, unsubscribe_url=comments.unsubscribe_url(request, userid), ) body = render('arche_comments:templates/notification_email.pt', values, request=request) request.send_email(subject, [user.email], body)
class AddCommentsFolderForm(DefaultAddForm): title = _("Create comments section?") description = _("You can disable this later on if you wish.") type_name = 'CommentsFolder' def save_success(self, appstruct): self.flash_messages.add(self.default_success, type="success") notify_user = appstruct.pop('notify', False) factory = self.request.content_factories[self.type_name] obj = factory(**appstruct) name = '_comments' if name in self.context: raise HTTPForbidden(_("Comments already exist here")) if notify_user: obj.add_subscribing_userid(self.request.authenticated_userid) self.context[name] = obj return HTTPFound(location=self.request.resource_url(self.context, anchor='comments'))
def __init__(self, context, request): buttons = [] if request.has_permission(EDIT_COMMENT, context): buttons.append(self.button_save) if request.has_permission(DELETE_COMMENT, context): buttons.append(self.button_delete) if not buttons: raise HTTPForbidden( _("You're not allowed to edit or delete this comment")) buttons.append(self.button_cancel) self.buttons = buttons super(EditCommentForm, self).__init__(context, request)
def save_success(self, appstruct): self.flash_messages.add(self.default_success, type="success") notify_user = appstruct.pop('notify', False) factory = self.request.content_factories[self.type_name] obj = factory(**appstruct) name = '_comments' if name in self.context: raise HTTPForbidden(_("Comments already exist here")) if notify_user: obj.add_subscribing_userid(self.request.authenticated_userid) self.context[name] = obj return HTTPFound(location=self.request.resource_url(self.context, anchor='comments'))
class Comment(Base, LocalRolesMixin): nav_visible = False listing_visible = True search_visible = True type_name = "Comment" type_title = _("Comment") body = "" _creator = () add_permission = ADD_COMMENT @property def creator(self): return self._creator @creator.setter def creator(self, value): self._creator = tuple(value)
class EditCommentForm(DefaultEditForm): title = _("Edit") type_name = 'Comment' def __init__(self, context, request): buttons = [] if request.has_permission(EDIT_COMMENT, context): buttons.append(self.button_save) if request.has_permission(DELETE_COMMENT, context): buttons.append(self.button_delete) if not buttons: raise HTTPForbidden( _("You're not allowed to edit or delete this comment")) buttons.append(self.button_cancel) self.buttons = buttons super(EditCommentForm, self).__init__(context, request) @property def formid(self): return "edit-comment-%s" % self.context.uid def appstruct(self): appstruct = super(EditCommentForm, self).appstruct() # It would be nice if this was handled by colander instead :/ appstruct['body'] = strip_tags(appstruct['body']) return appstruct def save_success(self, appstruct): self.flash_messages.add(_("Updated"), type='success') self.context.update(**appstruct) return redirect_parent(self.context, self.request) def delete_success(self, appstruct): self.flash_messages.add(_("Removed"), type='warning') redir = redirect_parent(self.context, self.request) parent = self.context.__parent__ del parent[self.context.__name__] return redir def cancel(self, *args): return redirect_parent(self.context, self.request) cancel_failure = cancel_success = cancel
class CommentsFolderSchema(colander.Schema): notify = colander.SchemaNode( colander.Bool(), title=_("Send an email notification when a comment is added?"), )
def delete_success(self, appstruct): self.flash_messages.add(_("Removed"), type='warning') redir = redirect_parent(self.context, self.request) parent = self.context.__parent__ del parent[self.context.__name__] return redir
def save_success(self, appstruct): self.flash_messages.add(_("Updated"), type='success') self.context.update(**appstruct) return redirect_parent(self.context, self.request)