root/SimpleBlog/branches/plone-2.5/SimpleBlogTool.py

Revision 594 (checked in by piv, 2 years ago)

fixed collectEntries method

  • Property svn:eol-style set to native
Line 
1 from Products.CMFCore.utils import UniqueObject
2 from OFS.SimpleItem import SimpleItem
3 from OFS.PropertyManager import PropertyManager
4 from Globals import InitializeClass
5 from AccessControl import ClassSecurityInfo, Unauthorized
6 from Products.CMFCore import CMFCorePermissions
7 import zLOG,os
8 from Products.CMFCore.utils import getToolByName
9 import re
10 import calendar
11 calendar.setfirstweekday(6) #start day  Mon(0)-Sun(6)
12 from DateTime import DateTime
13
14
15 class SimpleBlogManager(UniqueObject, SimpleItem,PropertyManager):
16     """ This tool provides some functions for SimpleBlog objects """
17     id = 'simpleblog_tool'
18     meta_type= 'SimpleBlog manager'
19     plone_tool = 1
20        
21     manage_options=PropertyManager.manage_options
22    
23     security = ClassSecurityInfo()
24     calendar_types=['BlogEntry']
25     use_session=""
26    
27     def __init__(self):
28         self.manage_addProperty('publishedState', 'published', 'string')
29         self.manage_addProperty('maxItemsInPortlet', 5, 'int')
30         self.manage_addProperty('globalCategories', '', 'lines')
31         self.manage_addProperty('createPortletOnBlogCreation', 1,'boolean')
32
33     security.declarePublic('getByUID')
34     def getByUID(self, uid):
35         "Shortcut method for the [Blogger,MetaWeblog]API code"
36
37         uid_catalog = getToolByName(self, 'uid_catalog')
38         lazy_cat = uid_catalog(UID=uid)
39         o = lazy_cat[0].getObject()
40         return o
41
42     security.declarePublic('findRPCAuth')
43     def findRPCAuth(self, parent):
44         while hasattr(parent,'aq_parent'):
45             RPCAuths = parent.objectValues('RPC Auth')
46             for RPCAuth in RPCAuths:
47                 return RPCAuth
48             parent = parent.aq_parent
49         return None
50
51
52     security.declarePublic('idFromTitle')
53     def idFromTitle(self, title):
54         id = re.sub('[^A-Za-z0-9_]', '', re.sub(' ', '_', title)).lower()
55         return id
56
57     def _getState(self):
58         try:
59             return self.publishedState
60         except:
61             return 'published'
62
63     def _getMaxItemsInPortlet(self):
64         try:
65             return self.maxItemsInPortlet
66         except:
67             return 5
68     def _getGlobalCategories(self):
69         try:
70             cats = self.globalCategories
71             ret=[]
72             for c in cats:
73                 if c!='':
74                     ret.append(c)
75             return ret
76         except:
77             return []
78        
79     def _getCreatePortletOnBlogCreation(self):
80         try:
81             return self.createPortletOnBlogCreation
82         except:
83             return 1
84
85     security.declareProtected(CMFCorePermissions.ManagePortal,'setProperties')       
86     def setProperties(self, publishedState='published', createPortletOnBlogCreation=None, maxItemsInPortlet=5, globalCategories=''):
87         self.publishedState = publishedState
88         if createPortletOnBlogCreation==1 or createPortletOnBlogCreation=='on':
89             self.createPortletOnBlogCreation=1
90         else:
91             self.createPortletOnBlogCreation=0
92
93         self.maxItemsInPortlet=int(maxItemsInPortlet)
94
95         value=''
96         if globalCategories<>'':
97             value =  globalCategories.split('\n')
98             value = [v.strip() for v in value if v.strip()]
99             value = filter(None, value)
100
101         self.globalCategories=value
102    
103     security.declarePublic('getPublishedState')
104     def getPublishedState(self):
105         return self._getState()
106    
107    
108     security.declarePublic('getMaxItemsInPortlet')
109     def getMaxItemsInPortlet(self):
110         return self._getMaxItemsInPortlet()
111    
112     security.declarePublic('getFrontPage')
113     def getFrontPage(self, context):
114         """
115         returns the frontpage (Blog object) when viewing an Entry
116         """
117         if context.portal_type!='Blog':
118             portal = context.portal_url.getPortalObject()
119             if context!=portal:
120                 parent=context.aq_parent
121             else:
122                 parent=context
123             found=0
124             while parent!=portal and context.portal_type!='Blog':
125                 if parent.portal_type=='Blog':
126                     found=1
127                     break
128                 parent=parent.aq_parent
129            
130             if found==1:
131                 return parent
132             else:
133                 return None
134         else:
135             return context
136     security.declarePublic('getStartpointForSearch')
137     def getStartpointForSearch(self, context):
138         """
139         When in the context of a blog, return the blog
140         Outside the context of a blog, return context or if context isn't
141         folderish, it's parent container
142         """
143         plone_utils = getToolByName(context, 'plone_utils')
144        
145         startpoint = self.getFrontPage(context)
146         if not startpoint:
147             # we weren't in the context of a blog
148             if plone_utils.isStructuralFolder(context):
149                 return context
150             else:
151                 return context.aq_parent
152         else:
153             return startpoint
154      
155     security.declarePublic('getAvailableCategories')
156     def getAvailableCategories(self, context, startpoint=None):
157         """
158         returns a dict of all the available categories with the number of posts inside
159         """
160         # get all EntryFolders
161         # first get the starting point in case we are inside a Blog section
162         # if we are higher in the tree than any Blog then we will end up in the portalobject itself
163         # in that case we just search for categories starting in context.
164
165         if not startpoint:
166             startpoint = self.getStartpoint(context, fromHere=0)
167
168         # now we have the starting point for our search
169        
170         result = startpoint.portal_catalog.searchResults(meta_type=['BlogFolder', 'Blog'], path={'query':self.getObjectPath(startpoint),'level':0})
171        
172         # now fetch all the available categories
173         categories=[]
174         for o in result:
175             obj=o.getObject()
176             cats = obj.getCategories()
177             for c in cats:
178                 if not c in categories:
179                     categories.append(c)
180        
181         # add the global categories
182         for c in self.getGlobalCategories():
183             if not c in categories:
184                 categories.append(c)
185        
186         # now we have a list of unique categories available from startpoint and deeper in tree
187         # next step is to count the number of entries for each category
188         rescats={}
189         for c in categories:
190             result = startpoint.portal_catalog.searchResults(review_state=self._getState(), meta_type='BlogEntry', EntryCategory=c,  path={'query':self.getObjectPath(startpoint),'level':0})
191             rescats[c]=len(result)
192         return rescats
193    
194     security.declarePublic('getSortedKeys')
195     def getSortedKeys(self, dict):
196         keys = dict.keys()
197         keys.sort()
198         return keys
199    
200     security.declarePublic('getGlobalCategories')
201     def getGlobalCategories(self):
202         return self._getGlobalCategories()
203    
204     security.declarePublic('getStartpoint')
205     def getStartpoint(self, context, fromHere=0):
206         if context.portal_type!='Blog' and fromHere==0:
207             portal = context.portal_url.getPortalObject()
208             if context!=portal:
209                 parent=context.aq_parent
210             else:
211                 parent=context
212             found=0
213             while parent!=portal and context.portal_type!='Blog':
214                 if parent.portal_type=='Blog':
215                     found=1
216                     break
217                 parent=parent.aq_parent
218            
219             if found==1:
220                 startpoint=parent
221             else:
222                 if context.isPrincipiaFolderish:
223                     startpoint=context
224                 else:
225                     startpoint=context.aq_parent
226         else:
227             startpoint=context
228
229         return startpoint
230    
231     security.declarePublic('searchForEntries')
232     def searchForEntries(self, context, category=None, maxResults=None, fromHere=0, filterState=1, **kwargs):
233         # set maxResults=0 for all the results,
234         # leave it to None to get the max from the properties
235         # set fromHere=1 to search from the current location. Is used for BlogFolders
236        
237         # first, get the context right
238         # when inside a Blog: search for the frontpage
239         # when outside a Blog: use context (or its container)
240        
241         #filterState controls whether you want to return only published entries
242            
243         startpoint = self.getStartpoint(context, fromHere)
244         # now we have the starting point for our search
245        
246         query=kwargs
247
248         publishedState = self._getState()
249        
250         if category!=None:
251             query['EntryCategory']=category
252
253         query['getAlwaysOnTop']=1
254        
255         if filterState:
256             query['review_state']=publishedState           
257
258            
259         resultsTop = startpoint.portal_catalog.searchResults(query, meta_type='BlogEntry', path={'query':self.getObjectPath(startpoint),'level':0}, sort_order='reverse', sort_on='effective')
260        
261         query['getAlwaysOnTop']=0
262         resultsNoTop = startpoint.portal_catalog.searchResults(query, meta_type='BlogEntry', path={'query':self.getObjectPath(startpoint),'level':0}, sort_order='reverse', sort_on='effective')
263        
264         results = resultsTop + resultsNoTop
265
266         if maxResults==0:
267             return results
268         elif maxResults==None:
269             return results[:self._getMaxItemsInPortlet()]
270         else:
271             return results[:maxResults]   
272
273
274     security.declarePublic('searchForEntries')
275     def collectEntries(self, context, category=None, maxResults=None,  filterState=1, allBlogs=0, **kwargs):
276         # first get all the blogs
277         if allBlogs:
278             query = {'meta_type':'Blog',
279                         'path':{'query':self.getObjectPath(context),'level':0}
280                         } # used meta_type because for some reason, syndication objects also show up.
281             blogs = [b.getObject() for b in self.portal_catalog.searchResults(query)]
282             onTop=[]
283             atBottom=[]
284             # now collect all the entries
285             for blog in blogs:
286                 tmpTop, tmpBottom = blog.getEntries(category=category, maxResults=maxResults, filterState = filterState, sort=0, **kwargs)
287                 onTop = onTop+tmpTop
288                 atBottom = atBottom+tmpBottom
289             #sort
290             onTop.sort((lambda x,y:cmp(y.effective(), x.effective())))
291             atBottom.sort((lambda x,y:cmp(y.effective(), x.effective())))
292    
293             results = onTop+atBottom
294         else:
295             results = context.getEntries(category=category, maxResults=maxResults, filterState = filterState, sort=0, join=1, skipOnTop=1, **kwargs)
296            
297
298         if maxResults==0:
299             return results
300         elif maxResults==None:
301             return results[:self._getMaxItemsInPortlet()]
302         else:
303             return results[:maxResults]
304
305     security.declarePublic('searchForDay')
306     def searchForDay(self, context, date):
307         startpoint = self.getStartpoint(context, fromHere=0)
308         # now we have the starting point for our search
309
310         query={'start': DateTime(date).earliestTime(), 'start_usage': 'range:min',
311                     'end': DateTime(date).latestTime(), 'end_usage':'range:max'}
312         query['getAlwaysOnTop']=1
313         resultsTop = startpoint.portal_catalog.searchResults(query,
314                                                              review_state=self._getState(),
315                                                              meta_type='BlogEntry',
316                                                              path={'query':self.getObjectPath(startpoint),'level':0},
317                                                              sort_order='reverse', sort_on='effective')
318         query['getAlwaysOnTop']=0
319         resultsNoTop = startpoint.portal_catalog.searchResults(query,
320                                                              review_state=self._getState(),
321                                                              meta_type='BlogEntry',
322                                                              path={'query':self.getObjectPath(startpoint),'level':0},
323                                                              sort_order='reverse', sort_on='effective')
324         results = resultsTop + resultsNoTop
325         return results
326
327     security.declarePublic('getUnpublishedEntries')
328     def getUnpublishedEntries(self, blog):
329         states = self. getEntryWorkflowStates(blog)
330         pubstate = self.getPublishedState()
331         states = [s for s in states if s!=pubstate]
332         query={'review_state':states}
333         entries = self.searchForEntries(blog, filterState=0, maxResults=0, fromHere=1, **query)
334         return entries
335
336     security.declarePublic('blogHasEntries')
337     def blogHasEntries(self, context, fromHere=0):
338         """
339         returns if a blog has entries, either published or not published.
340         this function is used to display a message in the simpleblog(folder)_view when
341         there are entries but none of them published
342         """
343         startpoint = self.getStartpoint(context, fromHere=0)
344
345         # get all entries, doesn't matter what state they're in
346         results = startpoint.portal_catalog.searchResults(meta_type='BlogEntry', path={'query':self.getObjectPath(startpoint),'level':0})       
347
348         if results:
349             return True
350         else:
351             return False
352    
353     security.declarePublic('getEntryDate')
354     def getEntryDate(self, context):
355         if context.EffectiveDate()=='None':
356             return context.modification_date.aCommon()
357         else:
358             return context.EffectiveDate()
359
360     security.declarePublic('getCreatePortletOnBlogCreation')
361     def getCreatePortletOnBlogCreation(self):
362         return self._getCreatePortletOnBlogCreation()
363        
364     security.declareProtected(CMFCorePermissions.ManagePortal,'getAllWorkflowStates')
365     def getAllWorkflowStates(self, context):
366         lst=[]
367         for wf in context.portal_workflow.listWorkflows():
368             states = context.portal_workflow.getWorkflowById(wf).states
369             for s in states.keys():
370                 if not states[s].id in lst:
371                     lst.append(states[s].id)
372         return lst
373    
374     security.declareProtected(CMFCorePermissions.ManagePortal,'getEntryWorkflowStates')
375     def getEntryWorkflowStates(self, context):
376         chain = context.portal_workflow.getChainForPortalType('BlogEntry', 0)
377         lst=[]
378         for wf in chain:
379             states = context.portal_workflow.getWorkflowById(wf).states
380             for s in states.keys():
381                 if not states[s].id in lst:
382                     lst.append(states[s].id)
383        
384         return lst
385
386     # return object's url relative to the portal
387     def getObjectPath(self, object):
388         return os.path.join(*object.getPhysicalPath()).replace('\\', '/')
389        
390     # ======================================================
391     # calendar stuff, copied from CMFCalender
392     # ======================================================
393
394     security.declarePublic('getCalendarTypes')
395     def getCalendarTypes(self):
396         """ Returns a list of type that will show in the calendar """
397         return self.calendar_types
398
399     security.declarePublic('getUseSession')
400     def getUseSession(self):
401         """ Returns the Use_Session option """
402         return self.use_session
403
404     security.declarePublic('getDays')
405     def getDays(self):
406         """ Returns a list of days with the correct start day first """       
407         return calendar.weekheader(2).split()
408        
409     security.declarePublic('getWeeksList')
410     def getWeeksList(self, month='1', year='2002'):
411         """Creates a series of weeks, each of which contains an integer day number.
412            A day number of 0 means that day is in the previous or next month.
413         """
414         # daysByWeek is a list of days inside a list of weeks, like so:
415         # [[0, 1, 2, 3, 4, 5, 6],
416         #  [7, 8, 9, 10, 11, 12, 13],
417         #  [14, 15, 16, 17, 18, 19, 20],
418         #  [21, 22, 23, 24, 25, 26, 27],
419         #  [28, 29, 30, 31, 0, 0, 0]]
420         daysByWeek=calendar.monthcalendar(year, month)
421    
422         return daysByWeek
423
424     security.declarePublic('getEventsForCalendar')
425     def getEventsForCalendar(self, context, month='1', year='2002'):
426         """ recreates a sequence of weeks, by days each day is a mapping.
427             {'day': #, 'url': None}
428         """
429         year=int(year)
430         month=int(month)
431         # daysByWeek is a list of days inside a list of weeks, like so:
432         # [[0, 1, 2, 3, 4, 5, 6],
433         #  [7, 8, 9, 10, 11, 12, 13],
434         #  [14, 15, 16, 17, 18, 19, 20],
435         #  [21, 22, 23, 24, 25, 26, 27],
436         #  [28, 29, 30, 31, 0, 0, 0]]
437         daysByWeek=calendar.monthcalendar(year, month)
438         weeks=[]
439        
440         events=self.catalog_getevents(context, year, month)
441    
442         for week in daysByWeek:
443             days=[]
444             for day in week:
445                 if events.has_key(day):
446                     days.append(events[day])
447                 else:
448                     days.append({'day': day, 'event': 0, 'eventslist':[]})
449                
450             weeks.append(days)
451            
452         return weeks
453    
454     security.declarePublic('catalog_getevents')
455     def catalog_getevents(self, context, year, month):
456         """ given a year and month return a list of days that have events """
457         first_date=DateTime(str(month)+'/1/'+str(year))
458         last_day=calendar.monthrange(year, month)[1]
459         ## This line was cropping the last day of the month out of the
460         ## calendar when doing the query
461         ## last_date=DateTime(str(month)+'/'+str(last