source: trunk/BlogPost.py @ 1907

Revision 1907, 9.2 KB checked in by jukka, 12 years ago (diff)

Closed #1476, spent 20h. I'm allowing editing and deleting resource discussions like any other forum posts: if the discussion is deleted, new one will be created next time someone tries discussion. The default discussion post bodytext is i18n:translated, but once someone changes it, it won't try to translate it anymore.

Line 
1# Copyright 2006 by the LeMill Team (see AUTHORS)
2#
3# This file is part of LeMill.
4#
5# LeMill is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# LeMill is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with LeMill; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19from Products.Archetypes.public import *
20from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget
21from Products.CMFCore.permissions import ModifyPortalContent
22from Products.Archetypes.public import BaseSchema, BaseContent, registerType
23from Products.Archetypes.atapi import DisplayList
24from Globals import InitializeClass
25from Products.CMFCore.utils import getToolByName
26from AccessControl import ClassSecurityInfo, Unauthorized
27from config import PROJECTNAME, MODIFY_CONTENT, VIEW
28from Resources import CommonMixIn
29from SharedMetadata import bodytext, bodytext_simple, tags, coverImage, latest_edit_schema, score
30from Products.Archetypes.atapi import DisplayList
31from FieldsWidgets import TagsField, TagsWidget, LeStringWidget
32from permissions import ModerateContent, MODIFY_CONTENT
33from DateTime import DateTime
34
35from GroupBlog import monthNames
36
37# Same thing as with MemberFolder but easier: MemberBlogs contain the values of groups itself in indexable storage.
38
39schema = BaseSchema + bodytext_simple + coverImage + latest_edit_schema + score + Schema((
40    StringField(
41        name='title',
42        required=1,
43        searchable=1,
44        default='',
45        accessor='Title',
46        widget=LeStringWidget(
47            label='Topic',
48            label_msgid='label_topic',
49            visible={'view' : 'invisible'},
50            i18n_domain='lemill',
51        ),
52    ),
53    BooleanField(
54        name="is_discussion",
55        default=False,
56        widget=ComputedWidget(
57            visible={'edit':'invisible', 'view':'invisible'}
58            ),
59        ),
60))
61schema = schema.copy()
62schema['bodyText'].required = True
63schema['bodyText'].widget.rows = 10
64
65class BlogPost(BaseContent,CommonMixIn):
66    """Blog post"""
67
68    meta_type = "BlogPost"
69    archetype_name = "BlogPost"
70    allow_discussion = True
71    _at_rename_after_creation = True
72
73    security = ClassSecurityInfo()
74    default_view = ('blogpost_view')
75    security.declareObjectPublic()
76    schema = schema
77
78    security.declarePrivate('manage_afterAdd')
79    def manage_afterAdd(self, item, container):
80        """1) Replaces the left side portlets with the content type's own action portlet. 2) add obj id to list of recent entries in parent GroupBlog. """
81        BaseContent.manage_afterAdd(self, item, container)
82        if not hasattr(item.aq_base, 'left_slots'):
83            self._setProperty('left_slots', ['here/portlet_groupblog_actions/macros/portlet',], 'lines')
84        if hasattr(container, 'recent_posts'):
85            container.addRecent_post(self.getId())
86        mtool = getToolByName(self, 'portal_membership')
87        memberfolder=mtool.getHomeFolder()
88        if memberfolder!=None:
89            memberfolder.note_action(self.UID(), item.portal_type, 'afterAdd')
90        syn_tool = getToolByName(self, 'portal_syndication', None)
91        if syn_tool is not None:
92            try:
93                syn_tool.enableSyndication(self)
94            except: # might get 'Syndication Information Exists'
95                pass
96        BaseContent.manage_afterAdd(self, item, container)
97
98
99
100    security.declarePrivate('at_post_edit_script')
101    def at_post_edit_script(self):
102        """ Rename id to resemble title, add to list of collaboration proposals of community if such, and add to recent posts of blog. """
103        old=self.getId()
104        uid=self.UID()
105        self._renameAfterCreation()
106        new=self.getId()
107
108        if old != new: # new id is updated to recent posts list
109            try: # if for some reason posts are not inside blogs
110                blog=self.getBlog()
111                blog.replaceRecent_post(old, new)
112            except:
113                pass
114        mtool = getToolByName(self, 'portal_membership')
115        memberfolder=mtool.getHomeFolder()
116        if memberfolder!=None:
117            memberfolder.note_action(uid, self.portal_type, 'post_edit')
118
119               
120    def getAuthors(self):
121        """Emulate the method that Resource has"""
122        return [self.Creator(),]
123
124    def getCoverImageURL(self):
125        """BlogPosts shouldn't have their own cover images -- returns authors portrait."""
126        return getattr(self.community,self.Creator).getPortraitURL()
127       
128    def NiceName(self):
129        """ I don't think this will get called but just to make sure. """
130        return self.getTitle()
131
132    def isPost(self):
133        return True
134
135    security.declareProtected(ModerateContent, 'delete_post')
136    def delete_post(self):
137        """ Hide, remove from recent posts and remove from Collaboration proposals """
138        container=self.getBlog()
139        uid=self.UID()
140        self.content_status_modify(workflow_action='delete')
141        self.reindexObject()
142        if hasattr(container, 'recent_posts'):
143            container.removeRecent_post(self.getId())
144
145    security.declareProtected(ModerateContent, 'undelete_post')
146    def undelete_post(self):
147        """ Bring back post but currently doesn't add to collaboration proposals or recent posts """
148        container=self.getBlog()
149        uid=self.UID()
150        self.content_status_modify(workflow_action='publish')
151        self.reindexObject()
152        if hasattr(container, 'recent_posts'):
153            container.addRecent_post(self.getId())
154   
155#    def setProperties(self, newprops=None, **kw):
156#        """ just a helper method """
157#        if newprops is None:
158#            newprops = kw
159#        for (key, value) in newprops.items():
160#            accessor='get'+str(key.capitalize())
161#            mutator='set'+str(key.capitalize())
162#
163#            if hasattr(self, accessor):
164#                if eval('self.'+accessor+'()') != value:
165#                    eval('self.'+mutator+'(value)')
166
167    security.declareProtected(MODIFY_CONTENT, 'setCoverImage')
168    def setCoverImage(self, value, **kwargs):
169        """ This modification for a mutator flags object to have a coverImage """
170        cover=self.getField('coverImage')
171        cover.set(self,value,**kwargs)
172        has_cover=self.getField('hasCoverImage')
173        has_cover.set(self,True)
174
175
176    def getComments(self, n=False):
177        """ Get discussion comments or their number """
178        pd=getToolByName(self, 'portal_discussion')
179        rp=pd.getDiscussionFor(self).getReplies()               
180        if n:
181            return len(rp)
182        rp=self.sort_modified_ascending(rp)
183        return rp
184
185    def getLatestComment(self):
186        """ Return time to latest comment in dictionary of days, hours, minutes """
187        pd=getToolByName(self, 'portal_discussion')
188        letool=getToolByName(self,'lemill_tool')
189        rp=pd.getDiscussionFor(self).getReplies()               
190        if not rp:
191            return None
192        #rp=self.sort_modified_ascending(rp)
193        latest=rp[-1]       
194        return letool.getTimeDifference(latest.CreationDate())
195       
196
197    def Description(self):
198        """ Redirect to full body text """
199        return self.getBodyText()
200   
201    def getDefaultIcon(self, meta_type='', obj=None):
202        """ blogposts don't currently have icons """
203        # in my opinion this should return creator's portrait
204        # but I currently don't have time to find methods for getting them
205        return 'images/default_member.png'
206
207    def myDateToArchive(self):
208        # Get year and month
209        myresults = []
210        # This function returns array like ['2006', 'October'] for the the current element.
211        myCdate = self.CreationDate()[:7]
212        myresults = (myCdate.split("-"))
213        item = myresults[1]
214        if item in monthNames.keys():
215            myresults[1] = monthNames[item]
216
217        return myresults
218
219    def getLatestEditDate(self):
220        """ Returns creation date as blog posts do not have History """
221        return DateTime(self.CreationDate())
222
223    def getLastEditor(self):
224        """ Returns id of the Creator """
225        return self.Creator()
226   
227    def getShortenBlog(self):
228        """ Creates shorten link names """
229        lt = getToolByName(self, 'lemill_tool')
230        return lt.split_at_p_or_br(self.getBlog(),min_len=50, max_len=200);
231
232    def getDiscussedResource(self):
233        """ if this is related to some resource, get the resource """
234        reftool= getToolByName(self, 'reference_catalog')
235        obj_uid = self.UID()
236        found = []
237        ref_query = { 'sourceUID': obj_uid, 'type': 'is_discussion_about' }
238        refresults = reftool(ref_query)
239        for q in refresults:
240            refobject = reftool.lookupObject(q.UID)
241            target = refobject.getTargetObject()
242            if target:
243                return target
244        return None
245
246
247registerType(BlogPost, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.