Author: rspivak Date: Thu Nov 3 01:39:00 2005 New Revision: 28921 Added: z3lab/cpsblog/trunk/comments.py (contents, props changed) Modified: z3lab/cpsblog/trunk/browser/blogentry.py z3lab/cpsblog/trunk/browser/skin/blogentry.pt z3lab/cpsblog/trunk/configure.zcml z3lab/cpsblog/trunk/interfaces.py Log: Initial backend support for comments.
Modified: z3lab/cpsblog/trunk/browser/blogentry.py ============================================================================== --- z3lab/cpsblog/trunk/browser/blogentry.py (original) +++ z3lab/cpsblog/trunk/browser/blogentry.py Thu Nov 3 01:39:00 2005 @@ -6,7 +6,7 @@ from zope.app.dublincore.interfaces import IZopeDublinCore from zope.app.pagetemplate import ViewPageTemplateFile -from cpsblog.interfaces import IBlogEntry +from cpsblog.interfaces import IBlogEntry, IComments from cpsblog.browser.widgets import FCKEditorWidget import zope.interface @@ -60,3 +60,8 @@ # for d in absolute_url.breadcrumbs()[:-1]: # result += "<a href='%(url)s'>%(name)s</a>/" % d # return result + + def getComments(self): + """Return comments.""" + comments = IComments(self.context) + return comments Modified: z3lab/cpsblog/trunk/browser/skin/blogentry.pt ============================================================================== --- z3lab/cpsblog/trunk/browser/skin/blogentry.pt (original) +++ z3lab/cpsblog/trunk/browser/skin/blogentry.pt Thu Nov 3 01:39:00 2005 @@ -12,6 +12,27 @@ <span tal:content="view/created" /> </div> + <div class="blog-entry"> + <a name="comments"> + </a> + <table width="100%" border="0" cellpadding="0" cellspacing="0"> + <tr> + <td class="blog-footer"> + <h3> + <span tal:replace="python: len(view.getComments())"> + X + </span> + Comments: + </h3> + </td> + <td class="blog-footer" align="right"> + <a href="#" tal:attributes="href string:./comments.html"> + Post a Comment + </a> + </td> + </tr> + </table> + </div> </div> </html> \ No newline at end of file Added: z3lab/cpsblog/trunk/comments.py ============================================================================== --- (empty file) +++ z3lab/cpsblog/trunk/comments.py Thu Nov 3 01:39:00 2005 @@ -0,0 +1,201 @@ +# $Id$ +from persistent import Persistent +from persistent.dict import PersistentDict + +from zope import interface +from zope import event +from zope import component + +from zope.app.file import File +from zope.app.location import Location +from zope.app.event.objectevent import ObjectCreatedEvent, ObjectModifiedEvent +from zope.app.event.objectevent import Attributes, Sequence +from zope.app.annotation.interfaces import IAnnotations + +from zope.cachedescriptors.property import Lazy + +from cpsblog.interfaces import IComment +from cpsblog.interfaces import IComments, IAttributeAnnotatableComments + + +COMMENTS_KEY = 'cpsblog.comments' + +class Comment(File): + interface.implements(IComment) + + +class Comments(Location, Persistent): + interface.implements(IComments) + + def __init__(self): + self.comments = PersistentDict() + + def _get_next_key(self): + key = self.__dict__.get('_next_key', 0) + self.__dict__['_next_key'] = next_key = key + 1 + return next_key + + def keys(self): + "See zope.interface.common.mapping.IEnumerableMapping" + return self.comments.keys() + + def __iter__(self): + "See zope.interface.common.mapping.IEnumerableMapping" + return self.comments.__iter__() + + def values(self): + "See zope.interface.common.mapping.IEnumerableMapping" + return self.comments.values() + + def items(self): + "See zope.interface.common.mapping.IEnumerableMapping" + return self.comments.items() + + def __len__(self): + "See zope.interface.common.mapping.IEnumerableMapping" + return self.comments.__len__() + + def get(self, key, default=None): + "See zope.interface.common.mapping.IReadMapping" + return self.comments.get(key, default) + + def __contains__(self, key): + "See zope.interface.common.mapping.IReadMapping" + return key in self.comments + + def __getitem__(self, key): + "See zope.interface.common.mapping.IItemMapping" + return self.comments[key] + + def __delitem__(self, key): + "See cpsblog.interfaces.IDeleteComments" + del self.comments[key] + + def addComment(self, data, contentType='text/plain'): + "See cpsblog.interfaces.IAddComments" + comment = Comment(data, contentType) + event.notify(ObjectCreatedEvent(comment)) + key = self._get_next_key() + self.comments[key] = comment + return key + + def editComment(self, key, data, contentType='text/plain'): + "See cpsblog.interfaces.IEditComments" + comment = self.comments[key] + + changed = () + if comment.data != data: + comment.data = data + changed += ('data', ) + + if comment.contentType != contentType: + comment.contentType = contentType + changed += ('contentType', ) + + if changed: + event.notify(ObjectModifiedEvent(comment, + Attributes(IComment, *changed))) + return chnaged + + +class AnnotatableComments(Location): + component.adapts(IAttributeAnnotatableComments) + interface.implements(IComments) + + def __init__(self, context): + self.context = context + + def _get_comments(self): + _dict = self.__dict__ + annotations = _dict.get('_annotations') + if annotations is None: + _dict['_annotations'] = annotations = IAnnotations(self.context) + return annotations.get(COMMENTS_KEY, None) + + comments = Lazy(_get_comments) + + def _clear_comments_attr(self): + try: + del self.comments + except AttributeError: + pass + + def keys(self): + "See zope.interface.common.mapping.IEnumerableMapping" + if self.comments is not None: + return self.comments.keys() + else: + return [] + + def __iter__(self): + "See zope.interface.common.mapping.IEnumerableMapping" + if self.comments is not None: + return self.comments.__iter__() + else: + return __iter__([]) + + def values(self): + "See zope.interface.common.mapping.IEnumerableMapping" + if self.comments is not None: + return self.comments.values() + else: + return [] + + def items(self): + "See zope.interface.common.mapping.IEnumerableMapping" + if self.comments is not None: + return self.comments.items() + else: + return [] + + def __len__(self): + "See zope.interface.common.mapping.IEnumerableMapping" + if self.comments is not None: + return self.comments.__len__() + else: + return 0 + + def get(self, key, default=None): + "See zope.interface.common.mapping.IReadMapping" + if self.comments is not None: + return self.comments.get(key, default) + else: + return default + + def __contains__(self, key): + "See zope.interface.common.mapping.IReadMapping" + if self.comments is not None: + return key in self.comments + else: + return False + + def __getitem__(self, key): + "See zope.interface.common.mapping.IItemMapping" + return self.comments[key] + + def __delitem__(self, key): + "See cpsblog.interfaces.IDeleteComments" + del self.comments[key] + self._clear_comments_attr() + event.notify(ObjectModifiedEvent(self.context, Attributes(IComments))) + + def addComment(self, data, contentType='text/plain'): + "See cpsblog.interfaces.IAddComments" + if self.comments is None: + self.__dict__['_annotations'][COMMENTS_KEY] = Comments() + key = self.comments.addComment(data, contentType) + self._clear_comments_attr() + evt_obj = ObjectModifiedEvent(self.context, Sequence(IComments, key)) + event.notify(evt_obj) + return key + + def editComment(self, key, data, contentType='text/plain'): + "See cpsblog.interfaces.IEditComments" + if self.comments is not None: + changed = self.comments.editComment(key, data, contentType) + self._clear_comments_attr() + context = self.context + event.notify(ObjectModifiedEvent(context, Sequence(IComments, key))) + return changed + else: + raise KeyError(key) Modified: z3lab/cpsblog/trunk/configure.zcml ============================================================================== --- z3lab/cpsblog/trunk/configure.zcml (original) +++ z3lab/cpsblog/trunk/configure.zcml Thu Nov 3 01:39:00 2005 @@ -91,6 +91,9 @@ <implements interface="zope.app.annotation.interfaces.IAttributeAnnotatable" /> + <implements + interface="cpsblog.interfaces.IAttributeAnnotatableComments" + /> <factory id="blogentry.BlogEntry" description="Blog Entry" @@ -155,6 +158,47 @@ /> </content> + + <!-- Comment --> + <content class=".comments.Comment"> + <factory id="comment.interfaces.IComment" /> + <implements + interface="zope.app.annotation.interfaces.IAttributeAnnotatable" + /> + <require + permission="zope.Public" + interface=".interfaces.IComment" + /> + <require + permission="zope.ManageContent" + set_schema=".interfaces.IComment" + /> + </content> + + + <!-- Comments --> + <class class=".comments.AnnotatableComments"> + <factory id="comment.interfaces.IComments" /> + <require + permission="zope.Public" + interface=".interfaces.IReadComments" + /> + <require + permission="zope.View" + interface=".interfaces.IDeleteComments" + /> + <require + permission="zope.ManageContent" + set_schema=".interfaces.IComment" + /> + </class> + + <adapter + provides=".interfaces.IComments" + factory=".comments.AnnotatableComments" + trusted="True" /> + + <subscriber for="zope.app.container.interfaces.IObjectAddedEvent" handler=".blogportal.setupSiteManager" Modified: z3lab/cpsblog/trunk/interfaces.py ============================================================================== --- z3lab/cpsblog/trunk/interfaces.py (original) +++ z3lab/cpsblog/trunk/interfaces.py Thu Nov 3 01:39:00 2005 @@ -1,6 +1,8 @@ # $Id$ from zope.interface import Interface, Attribute from zope.schema import Text, TextLine, Field, Choice, List +from zope.interface.common.mapping import IEnumerableMapping +from zope.app.annotation.interfaces import IAttributeAnnotatable from zope.app.container.constraints import ContainerTypesConstraint from zope.app.container.constraints import ItemTypePrecondition @@ -8,7 +10,7 @@ from zope.app.container.interfaces import IContained, IContainer from zope.app.authentication.principalfolder import IInternalPrincipalContainer -from zope.app.file.interfaces import IImage +from zope.app.file.interfaces import IImage, IFile class IBlogEntry(Interface): """Interface for blog entry objects.""" @@ -130,3 +132,37 @@ class IPortrait(IImage): """Holds portrait image of member.""" + + +# Comments + +class IComment(IFile): + """ """ + +class IReadComments(IEnumerableMapping): + """Read comments from comments collection.""" + +class IDeleteComments(Interface): + """Remove comment from comments collection.""" + def __delitem__(key): + """ """ + +class IAddComments(Interface): + def addComment(data, contentType='text/plain'): + """Add comment to collection. + + Return key of newly added comment. + """ + +class IEditComments(Interface): + def editComment(key, data, contentType='text/plain'): + """Edit comment from collection. + + Return changed attributes. + """ + +class IComments(IReadComments, IDeleteComments, IAddComments, IEditComments): + """Collection of comments.""" + +class IAttributeAnnotatableComments(IAttributeAnnotatable): + """Marker interface""" -- http://lists.nuxeo.com/mailman/listinfo/z3lab-checkins