source: trunk/GroupBlog.py @ 1922

Revision 1922, 15.0 KB checked in by jukka, 12 years ago (diff)

One more typo.

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 SharedMetadata import description, tags
20from Products.PythonScripts.standard import url_quote
21from Products.Archetypes.public import *
22from Products.ATContentTypes.content.folder import ATFolder
23from Products.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget
24from Products.CMFCore.permissions import ModifyPortalContent
25from Products.Archetypes.public import BaseFolder, BaseFolderSchema, registerType
26from Products.Archetypes.atapi import DisplayList
27from Globals import InitializeClass
28from Products.CMFCore.utils import getToolByName
29from AccessControl import ClassSecurityInfo, Unauthorized
30from Products.LeMill import LeMillMessageFactory as _
31
32from config import PROJECTNAME, MODIFY_CONTENT, VIEW, DEFAULT_ICONS
33from permissions import MODIFY_CONTENT
34from SharedMetadata import *
35from Resources import CommonMixIn
36
37from DateTime import DateTime
38import datetime
39import operator
40
41import time
42
43# Same thing as with MemberFolder but easier: MemberBlogs contain the values of groups itself in indexable storage.
44
45schema = BaseFolderSchema + description + tags + coverImage + deletionReason + score + subject_area_schema + latest_edit_schema + Schema((
46    StringField('title',
47        default_method='prefill_title',
48        searchable = True,
49    ),
50    StringField('description',
51        default_method='prefill_description',
52        schemata='default',
53        widget=TextAreaWidget(
54            rows=5,
55            cols=40,
56            label='Description',
57            label_msgid='label_description',
58            )
59    ),
60
61    LinesField('language_skills',
62        default = [],
63        index = 'KeywordIndex:schema',
64        vocabulary = 'getLanguagelist',
65        widget = PicklistWidget(           
66             label = 'Languages',
67             label_msgid = 'label_group_language_skills',
68             description = "Choose languages that the group is willing to use. This affects only when people try to find groups based on a language.",
69             description_msgid = 'help_group_language_skills',
70             i18n_domain = "lemill"
71             )
72        ),       
73    LinesField('recent_posts',
74        default= [],
75        widget = LinesWidget(
76            visible = {'view':'invisible', 'edit':'invisible'},
77            )
78        ),
79    LinesField('groupMembers',
80        default=[],
81        index = 'KeywordIndex:schema',
82        widget = LinesWidget(
83            visible = {'view':'invisible', 'edit':'invisible'},
84            ),
85        ),
86
87 ))
88
89schema = schema.copy()
90schema['allowDiscussion'].widget.visible = {'edit':'invisible', 'view':'invisible'}
91schema['subject'].widget.visible = {'edit':'invisible', 'view':'invisible'}
92schema['contributors'].widget.visible = {'edit':'invisible', 'view':'invisible'}
93schema['creators'].widget.visible = {'edit':'invisible', 'view':'invisible'}
94schema['language'].widget.visible = {'edit':'invisible', 'view':'invisible'}
95schema['rights'].widget.visible = {'edit':'invisible', 'view':'invisible'}
96schema['tags'].schemata='default'
97schema['subject_area'].schemata='default'
98schema.moveField('subject_area', after='language_skills')
99schema['subject_area'].searchable = True
100
101
102class GroupBlog(BaseFolder,CommonMixIn):
103    """Group blog"""
104
105    meta_type = "GroupBlog"
106    archetype_name = "GroupBlog" 
107    typeDescription="Shared blog for a group"
108    typeDescMsgId='description_groupblog'
109    global_allow = 1
110
111    allowed_content_types = ('BlogPost')
112    default_view = ('groupblog_view')
113    filter_content_types = True
114    security = ClassSecurityInfo()
115    schema = schema
116
117    actions = ({ 'id':'view',
118                 'name': 'View',
119                 'action': 'string:${object_url}/groupblog_view',
120                 'permissions': (VIEW,)
121               },
122               { 'id':'edit',
123                 'name': 'Edit',
124                 'action': 'string:${object_url}/base_edit',
125                 'permissions': (MODIFY_CONTENT,)
126               },
127               { 'id':'edit_links',
128                 'name': 'Edit links',
129                 'action': 'string:${object_url}/base_metadata',
130                 'permissions': (MODIFY_CONTENT,)
131               },
132               { 'id':'edit_categories',
133                 'name': 'Edit categories',
134                 'action': 'string:${object_url}/base_metadata',
135                 'permissions': (MODIFY_CONTENT,)
136               },
137               )
138    aliases = {
139        '(Default)' : '',
140        'view'      : 'groupblog_view',
141        'edit'      : 'base_edit',
142        'edit_categories' : 'base_metadata',
143        'edit_links' : 'base_metadata',
144    }
145
146    def manage_afterAdd(self, item, container):
147        # Replaces the left side portlets with the content type's own action portlet.
148        BaseFolder.manage_afterAdd(self, item, container)
149        mtool = getToolByName(self, 'portal_membership')
150        memberfolder=mtool.getHomeFolder()
151        if memberfolder!=None and type(memberfolder)=='MemberFolder':
152            memberfolder.recalculateScore()
153        if not hasattr(item.aq_base, 'left_slots'):
154            self._setProperty('left_slots', ['here/portlet_%s_actions/macros/portlet' % item.meta_type.lower(),], 'lines')
155        # Enable topic syndication by default
156        syn_tool = getToolByName(self, 'portal_syndication', None)
157        if syn_tool is not None:
158            if syn_tool.isSiteSyndicationAllowed():
159                try:
160                    syn_tool.enableSyndication(self)
161                except: # might get 'Syndication Information Exists'
162                    pass
163
164
165    def at_post_edit_script(self):
166        self.recalculateScore()
167           
168    def getGroupMaterials(self, n=False):
169        """ Get resources edited by this group """
170        pc=getToolByName(self, 'portal_catalog')       
171        results=pc({'getRawGroupEditing':self.UID()})
172        if n:
173            return len(results)
174        else:
175            return results
176
177    def getGroupMembersNamesAndUrls(self):
178        memberids= self.getGroupMembers()
179        reslist=[]
180        for mid in memberids:
181            memberfolder=self.getMemberFolderById(mid)
182            if memberfolder:
183                reslist.append((memberfolder.NiceName(), memberfolder.absolute_url()))
184        return reslist
185
186    def getLatestEditDate(self):
187        """Returns creation date, we don't want to notify about every edit"""
188        return DateTime(self.CreationDate())
189
190    def getSamples(self):
191        """ get n number of samples """
192        all = self.getGroupMaterials()
193        all = [a for a in all if a.review_state=='public' and a.portal_type in self.getFeaturedTypes() and a.getHasCoverImage==True]
194        import random
195        n = min(3, len(all))
196        return random.sample(all,n)
197
198    def getCoverImageURL(self, drafts=False):
199        """Returns the URL for the cover image."""
200        timestamp = time.time()
201        if self.getField('hasCoverImage').get(self)==True:
202            return self.absolute_url()+'/coverImage?newest='+str(timestamp)
203        return 'images/default_group.png'
204       
205    def isPost(self):
206        return False       
207       
208    def NiceName(self):
209        """ To make GroupBlog compatible with MemberFolders """
210        return self.getTitle()
211
212    def getLanguagelist(self):
213        languagelist=self.availableLanguages()
214        return DisplayList(languagelist)
215
216    def isMember(self, memberid):
217        return memberid in self.getGroupMembers()
218
219    def canIModerate(self):
220        roles = self.portal_membership.getAuthenticatedMember().getRolesInContext(self)
221        return 'Manager' in roles or 'Reviewer' in roles
222       
223    def getPosts(self, batch=True, b_size=30, b_start=0):
224        """ Return get posts as full objects, sort them by date and batch them """
225        wtool = getToolByName(self,'portal_workflow')
226        results= [(obj.CreationDate(), obj) for obj in self.objectValues() if obj.portal_type=='BlogPost' and wtool.getInfoFor(obj,'review_state',None)!='deleted']
227
228        results.sort(key=operator.itemgetter(0))
229        results.reverse()
230        results =[obj for (d,obj) in results]
231        if batch:
232            from Products.CMFPlone import Batch
233            results = Batch(results, b_size, int(b_start), orphan=0)       
234        return results
235
236    def getFullForumRSS(self):
237        """ Returns forum topics and comments for them """
238        full_results=[]
239        wtool = getToolByName(self,'portal_workflow')
240        results= [(obj.CreationDate(), obj) for obj in self.objectValues() if obj.portal_type=='BlogPost' and wtool.getInfoFor(obj,'review_state',None)!='deleted']
241        results.sort(key=operator.itemgetter(0))
242        results.reverse()
243        results =[obj for (d,obj) in results]
244        for result in results:
245            full_results.append(result)
246            for x in result.getComments():
247                full_results.append(x)
248        return full_results
249
250    def getBlog(self):
251        return self
252       
253    security.declareProtected(MODIFY_CONTENT,'addRecent_post')
254    def addRecent_post(self,postid):
255        """ Recent posts are lists of id's, because then picking the post objects when inside group is easy and quick """
256        # sometimes this method gets called before new blogposts get their proper id:s, so escape then:
257        if postid.startswith('blogpost.'):
258            return None
259        postlist= list(self.getField('recent_posts').get(self))
260        if postid in postlist:
261            postlist.remove(postid)
262        postlist=[postid]+postlist
263        self.setRecent_posts(postlist[:10])           
264
265    security.declareProtected(MODIFY_CONTENT,'removeRecent_post')
266    def removeRecent_post(self,postid):
267        postlist= list(self.getField('recent_posts').get(self))
268        if postid in postlist:
269            postlist.remove(postid)
270        self.setRecent_posts(postlist)           
271
272    security.declareProtected(MODIFY_CONTENT,'replaceRecent_post')
273    def replaceRecent_post(self, old, new):
274        postlist= list(self.getField('recent_posts').get(self))
275        postlist[postlist.index(old)]=new
276        self.setRecent_posts(postlist)           
277
278    def addMember(self, memberid):
279        mtool = getToolByName(self, 'portal_membership')
280        memberfolder=mtool.getHomeFolder(memberid)
281        if not memberfolder:
282            print "We have a problem: %s cannot be found and is not added to group" % memberid
283            return False
284        mid=memberfolder.getMemberId()
285        users=list(self.getGroupMembers())
286        if mid in users:
287            return False
288        self.setGroupMembers(users+[mid])
289        return True
290           
291    def removeMember(self, memberid):
292        mtool = getToolByName(self, 'portal_membership')
293        memberfolder=mtool.getHomeFolder(memberid)
294        if memberfolder:
295            mid=memberfolder.getMemberId()
296        else:
297            print "We have a problem: %s cannot be found. removing him from group anyway" % memberid
298            mid=memberid
299        users=list(self.getGroupMembers())
300        if mid not in users:
301            return False
302        users.remove(mid)
303        self.setGroupMembers(users)
304        if len(users)==0:
305            # Deleting group.
306            # safe but uncertain method: some links can still work, especially after reindexing
307            self.content_status_modify(workflow_action='delete', msg='Resource deleted')
308            self.unindexObject()
309        return True     
310       
311       
312           
313    def join_group(self):
314        """ The logged in user joins the current group """
315        REQUEST=self.REQUEST
316        mtool = getToolByName(self, 'portal_membership')
317        putils= getToolByName(self, 'plone_utils')
318        user = mtool.getAuthenticatedMember()       
319        userid = user.getId()
320        if self.addMember(userid):
321            msg = _("You have joined the group '${title}'.", mapping={u'title' : self.title_or_id()})
322        else:
323            msg= _(u"You are already member in this group.")
324        putils.addPortalMessage(msg)
325        return REQUEST.RESPONSE.redirect(self.absolute_url())
326   
327    def leave_group(self):
328        """ The logged in user leaves the current group """
329        REQUEST=self.REQUEST
330        mtool = getToolByName(self, 'portal_membership')
331        putils= getToolByName(self, 'plone_utils')
332        user = mtool.getAuthenticatedMember()
333        userid = user.getId()
334        if self.removeMember(userid):
335            msg = _("You have left the group '${title}'.", mapping={u'title' : self.title_or_id()})
336        else:
337            msg= _(u"You are not member in this group.")
338        putils.addPortalMessage(msg)
339        return REQUEST.RESPONSE.redirect(self.community.absolute_url())
340
341 
342       
343
344    # These have to be duplicated since its difficult to base these folderish objects on non-folderish Resources
345
346    security.declareProtected(MODIFY_CONTENT,'setCoverImage')
347    def setCoverImage(self, value, **kwargs):
348        """ Normal mutator, but flags object to have a coverImage (hasCoverImage = True) """
349        cover=self.getField('coverImage')
350        cover.set(self,value,**kwargs)
351        has_cover=self.getField('hasCoverImage')
352        has_cover.set(self,True)
353        self.reindexObject()
354
355    security.declareProtected(MODIFY_CONTENT,'delCoverImage')
356    def delCoverImage(self):
357        """ Reverse of setCoverImage """
358        cover=self.getField('coverImage')
359        cover.set(self,None)
360        has_cover=self.getField('hasCoverImage')
361        has_cover.set(self,False)
362        self.reindexObject()
363
364
365    def recalculateScore(self):
366        """ Calculates the score for Group Blog according to specifications """
367        lt = getToolByName(self, 'lemill_tool')
368        score = float(0)
369        members_score = len(self.getGroupMembers())
370        materials = self.getGroupMaterials()
371        posts = self.getPosts(batch=False)
372
373        score = score + members_score
374
375        for m in materials:
376            months_old = lt.getTimeDifference(m.getLatestEdit)
377            months_old = months_old['months']
378            score = score + 1 / float(months_old + 1)
379
380        for p in posts:
381            months_old = lt.getTimeDifference(p.getLatestEditDate())
382            months_old = months_old['months']
383            score = score + 1 / float(months_old + 1)
384
385        score = int(round(score))
386
387        # This one is not really needed, because Group must have at least one member
388        if score<1:
389            score = 1
390        # Now to set the value for field score
391        self.setScore(score)
392
393
394       
395
396registerType(GroupBlog, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.