source: products/SimpleBlog/branches/plone-2.5/SimpleBlogTool.py

Last change on this file was 1, checked in by myroslav, 14 years ago

Building directory structure

  • Property svn:eol-style set to native
File size: 20.7 KB
Line 
1from Products.CMFCore.utils import UniqueObject
2from OFS.SimpleItem import SimpleItem
3from OFS.PropertyManager import PropertyManager
4from Globals import InitializeClass
5from AccessControl import ClassSecurityInfo, Unauthorized
6from Products.CMFCore import CMFCorePermissions
7import zLOG,os
8from Products.CMFCore.utils import getToolByName
9import re
10import calendar
11calendar.setfirstweekday(6) #start day  Mon(0)-Sun(6)
12from DateTime import DateTime
13
14
15class 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_day)+'/'+str(year))
462        last_date=first_date + last_day   
463       
464        # get the starting point for our search. This is where we depart from the standard catalog_tool:
465        startpoint = self.getStartpoint(context, fromHere=0)
466       
467        query=self.portal_catalog(portal_type=self.calendar_types,
468                              review_state=self._getState(),
469                              start=last_date,
470                              start_usage='range:max',
471                              end=first_date,
472                              end_usage='range:min',
473                              path={'query':self.getObjectPath(startpoint),'level':0},
474                              sort_on='start')
475       
476        # compile a list of the days that have events
477        eventDays={}
478        for daynumber in range(1, 32): # 1 to 31
479            eventDays[daynumber] = {'eventslist':[], 'event':0, 'day':daynumber}
480        includedevents = []
481        for result in query:
482            if result.getRID() in includedevents:
483                break
484            else:
485                includedevents.append(result.getRID())
486            event={}
487            # we need to deal with events that end next month
488            if  result.end.month() != month:  # doesn't work for events that last ~12 months - fix it if it's a problem, otherwise ignore
489                eventEndDay = last_day
490                event['end'] = None
491            else:
492                eventEndDay = result.end.day()
493                event['end'] = result.end.Time()
494            # and events that started last month
495            if result.start.month() != month:  # same as above re: 12 month thing
496                eventStartDay = 1
497                event['start'] = None
498            else:
499                eventStartDay = result.start.day()
500                event['start'] = result.start.Time()
501            event['title'] = result.Title or result.id
502            if eventStartDay != eventEndDay:
503                allEventDays = range(eventStartDay, eventEndDay+1)
504                eventDays[eventStartDay]['eventslist'].append({'end':None, 'start':result.start.Time(), 'title':result.Title})
505                eventDays[eventStartDay]['event'] = 1
506                for eventday in allEventDays[1:-1]:
507                    eventDays[eventday]['eventslist'].append({'end':None, 'start':None, 'title':result.Title})
508                    eventDays[eventday]['event'] = 1
509                eventDays[eventEndDay]['eventslist'].append({'end':result.end.Time(), 'start':None, 'title':result.Title})
510                eventDays[eventEndDay]['event'] = 1
511            else:
512                eventDays[eventStartDay]['eventslist'].append(event)
513                eventDays[eventStartDay]['event'] = 1
514            # This list is not uniqued and isn't sorted
515            # uniquing and sorting only wastes time
516            # and in this example we don't need to because
517            # later we are going to do an 'if 2 in eventDays'
518            # so the order is not important.
519            # example:  [23, 28, 29, 30, 31, 23]
520        return eventDays
521
522        # ==================
523        # end calendar stuff
524        # ==================
525   
526   
527   
528InitializeClass(SimpleBlogManager)
Note: See TracBrowser for help on using the repository browser.