source: products/SimpleBlog/trunk/SimpleBlogTool.py @ 1

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

Building directory structure

  • Property svn:eol-style set to native
File size: 18.3 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    security.declarePublic('getMaxItemsInPortlet')
108    def getMaxItemsInPortlet(self):
109        return self._getMaxItemsInPortlet()
110
111    security.declarePublic('getFrontPage')
112    def getFrontPage(self, context):
113        """
114        returns the frontpage (Blog object) when viewing an Entry
115        """
116        if context.portal_type!='Blog':
117            portal = context.portal_url.getPortalObject()
118            if context!=portal:
119                parent=context.aq_parent
120            else:
121                parent=context
122            found=0
123            while parent!=portal and context.portal_type!='Blog':
124                if parent.portal_type=='Blog':
125                    found=1
126                    break
127                parent=parent.aq_parent
128
129            if found==1:
130                return parent
131            else:
132                return None
133        else:
134            return context
135    security.declarePublic('getStartpointForSearch')
136    def getStartpointForSearch(self, context):
137        """
138        When in the context of a blog, return the blog
139        Outside the context of a blog, return context or if context isn't
140        folderish, it's parent container
141        """
142        plone_utils = getToolByName(context, 'plone_utils')
143
144        startpoint = self.getFrontPage(context)
145        if not startpoint:
146            # we weren't in the context of a blog
147            if plone_utils.isStructuralFolder(context):
148                return context
149            else:
150                return context.aq_parent
151        else:
152            return startpoint
153
154    security.declarePublic('getAvailableCategories')
155    def getAvailableCategories(self, context, startpoint=None):
156        """
157        returns a dict of all the available categories with the number of posts inside
158        """
159        # get all EntryFolders
160        # first get the starting point in case we are inside a Blog section
161        # if we are higher in the tree than any Blog then we will end up in the portalobject itself
162        # in that case we just search for categories starting in context.
163        if not startpoint:
164            startpoint = self.getStartpoint(context, fromHere=0)
165        categories = context.portal_catalog.uniqueValuesFor('EntryCategory')
166        path = self.getObjectPath(startpoint)
167
168        # now we have a list of unique categories available from startpoint and deeper in tree
169        # next step is to count the number of entries for each category
170        rescats={}
171        [rescats.update({c:0}) for c in categories]
172        result = startpoint.portal_catalog.searchResults(review_state=self._getState(), meta_type='BlogEntry', path={'query':path,'level':0})
173
174        for r in result:
175            for c in r.EntryCategory: rescats[c] = rescats[c]+1
176        for c,n in rescats.items():
177            if n==0: del rescats[c]
178        return rescats
179
180    security.declarePublic('getSortedKeys')
181    def getSortedKeys(self, dict):
182        keys = dict.keys()
183        keys.sort()
184        return keys
185
186    security.declarePublic('getGlobalCategories')
187    def getGlobalCategories(self):
188        return self._getGlobalCategories()
189
190    security.declarePublic('getStartpoint')
191    def getStartpoint(self, context, fromHere=0):
192        if context.portal_type!='Blog' and fromHere==0:
193            portal = context.portal_url.getPortalObject()
194            if context!=portal:
195                parent=context.aq_parent
196            else:
197                parent=context
198            found=0
199            while parent!=portal and context.portal_type!='Blog':
200                if parent.portal_type=='Blog':
201                    found=1
202                    break
203                parent=parent.aq_parent
204
205            if found==1:
206                startpoint=parent
207            else:
208                if context.isPrincipiaFolderish:
209                    startpoint=context
210                else:
211                    startpoint=context.aq_parent
212        else:
213            startpoint=context
214
215        return startpoint
216
217    security.declarePublic('searchForEntries')
218    def searchForEntries(self, context, category=None, maxResults=None, fromHere=0, filterState=1, **kwargs):
219        # set maxResults=0 for all the results,
220        # leave it to None to get the max from the properties
221        # set fromHere=1 to search from the current location. Is used for BlogFolders
222
223        # first, get the context right
224        # when inside a Blog: search for the frontpage
225        # when outside a Blog: use context (or its container)
226
227        #filterState controls whether you want to return only published entries
228
229        startpoint = self.getStartpoint(context, fromHere)
230        query=kwargs
231        publishedState = self._getState()
232        if category!=None:
233            query['EntryCategory']=category
234
235        if filterState:
236            query['review_state']=publishedState           
237
238        results = startpoint.portal_catalog.searchResults(query, meta_type='BlogEntry', 
239                  path={'query':self.getObjectPath(startpoint),'level':0}, sort_order='reverse', 
240                  sort_on='effective', sort_limit=maxResults and maxResults or None)
241
242        if  maxResults==0:
243            return results
244        elif maxResults==None:
245            return results[:self._getMaxItemsInPortlet()]
246        else:
247            return results[:maxResults]   
248
249
250    security.declarePublic('searchForDay')
251    def searchForDay(self, context, date):
252        startpoint = self.getStartpoint(context, fromHere=0)
253        # now we have the starting point for our search
254
255        query={'start': DateTime(date).earliestTime(), 'start_usage': 'range:min',
256                    'end': DateTime(date).latestTime(), 'end_usage':'range:max'}
257        query['getAlwaysOnTop']=1
258        resultsTop = startpoint.portal_catalog.searchResults(query,
259                                                             review_state=self._getState(),
260                                                             meta_type='BlogEntry',
261                                                             path={'query':self.getObjectPath(startpoint),'level':0},
262                                                             sort_order='reverse', sort_on='effective')
263        query['getAlwaysOnTop']=0
264        resultsNoTop = startpoint.portal_catalog.searchResults(query,
265                                                             review_state=self._getState(),
266                                                             meta_type='BlogEntry',
267                                                             path={'query':self.getObjectPath(startpoint),'level':0},
268                                                             sort_order='reverse', sort_on='effective')
269        results = resultsTop + resultsNoTop
270        return results
271
272    security.declarePublic('getUnpublishedEntries')
273    def getUnpublishedEntries(self, blog):
274        states = self. getEntryWorkflowStates(blog)
275        pubstate = self.getPublishedState()
276        states = [s for s in states if s!=pubstate]
277        query={'review_state':states}
278        entries = self.searchForEntries(blog, filterState=0, maxResults=0, fromHere=1, **query)
279        return entries
280
281    security.declarePublic('blogHasEntries')
282    def blogHasEntries(self, context, fromHere=0):
283        """
284        returns if a blog has entries, either published or not published.
285        this function is used to display a message in the simpleblog(folder)_view when
286        there are entries but none of them published
287        """
288        startpoint = self.getStartpoint(context, fromHere=0)
289
290        # get all entries, doesn't matter what state they're in
291        results = startpoint.portal_catalog.searchResults(meta_type='BlogEntry', path={'query':self.getObjectPath(startpoint),'level':0})       
292
293        if results:
294            return True
295        else:
296            return False
297   
298    security.declarePublic('getEntryDate')
299    def getEntryDate(self, context):
300        if context.EffectiveDate()=='None':
301            return context.modification_date.aCommon()
302        else:
303            return context.EffectiveDate()
304
305    security.declarePublic('getCreatePortletOnBlogCreation')
306    def getCreatePortletOnBlogCreation(self):
307        return self._getCreatePortletOnBlogCreation()
308       
309    security.declareProtected(CMFCorePermissions.ManagePortal,'getAllWorkflowStates')
310    def getAllWorkflowStates(self, context):
311        lst=[]
312        for wf in context.portal_workflow.listWorkflows():
313            states = context.portal_workflow.getWorkflowById(wf).states
314            for s in states.keys():
315                if not states[s].id in lst:
316                    lst.append(states[s].id)
317        return lst
318
319    security.declareProtected(CMFCorePermissions.ManagePortal,'getEntryWorkflowStates')
320    def getEntryWorkflowStates(self, context):
321        chain = context.portal_workflow.getChainForPortalType('BlogEntry', 0)
322        lst=[]
323        for wf in chain:
324            states = context.portal_workflow.getWorkflowById(wf).states
325            for s in states.keys():
326                if not states[s].id in lst:
327                    lst.append(states[s].id)
328
329        return lst
330
331    # return object's url relative to the portal
332    def getObjectPath(self, object):
333        return os.path.join(*object.getPhysicalPath()).replace('\\', '/')
334
335    # ======================================================
336    # calendar stuff, copied from CMFCalender
337    # ======================================================
338
339    security.declarePublic('getCalendarTypes')
340    def getCalendarTypes(self):
341        """ Returns a list of type that will show in the calendar """
342        return self.calendar_types
343
344    security.declarePublic('getUseSession')
345    def getUseSession(self):
346        """ Returns the Use_Session option """
347        return self.use_session
348
349    security.declarePublic('getDays')
350    def getDays(self):
351        """ Returns a list of days with the correct start day first """       
352        return calendar.weekheader(2).split()
353
354    security.declarePublic('getWeeksList')
355    def getWeeksList(self, month='1', year='2002'):
356        """Creates a series of weeks, each of which contains an integer day number.
357           A day number of 0 means that day is in the previous or next month.
358        """
359        # daysByWeek is a list of days inside a list of weeks, like so:
360        # [[0, 1, 2, 3, 4, 5, 6],
361        #  [7, 8, 9, 10, 11, 12, 13],
362        #  [14, 15, 16, 17, 18, 19, 20],
363        #  [21, 22, 23, 24, 25, 26, 27],
364        #  [28, 29, 30, 31, 0, 0, 0]]
365        daysByWeek=calendar.monthcalendar(year, month)
366
367        return daysByWeek
368
369    security.declarePublic('getEventsForCalendar')
370    def getEventsForCalendar(self, context, month='1', year='2002'):
371        """ recreates a sequence of weeks, by days each day is a mapping.
372            {'day': #, 'url': None}
373        """
374        year=int(year)
375        month=int(month)
376        # daysByWeek is a list of days inside a list of weeks, like so:
377        # [[0, 1, 2, 3, 4, 5, 6],
378        #  [7, 8, 9, 10, 11, 12, 13],
379        #  [14, 15, 16, 17, 18, 19, 20],
380        #  [21, 22, 23, 24, 25, 26, 27],
381        #  [28, 29, 30, 31, 0, 0, 0]]
382        daysByWeek=calendar.monthcalendar(year, month)
383        weeks=[]
384
385        events=self.catalog_getevents(context, year, month)
386
387        for week in daysByWeek:
388            days=[]
389            for day in week:
390                if events.has_key(day):
391                    days.append(events[day])
392                else:
393                    days.append({'day': day, 'event': 0, 'eventslist':[]})
394
395            weeks.append(days)
396
397        return weeks
398
399    security.declarePublic('catalog_getevents')
400    def catalog_getevents(self, context, year, month):
401        """ given a year and month return a list of days that have events """
402        first_date=DateTime(str(month)+'/1/'+str(year))
403        last_day=calendar.monthrange(year, month)[1]
404        ## This line was cropping the last day of the month out of the
405        ## calendar when doing the query
406        ## last_date=DateTime(str(month)+'/'+str(last_day)+'/'+str(year))
407        last_date=first_date + last_day   
408
409        # get the starting point for our search. This is where we depart from the standard catalog_tool:
410        startpoint = self.getStartpoint(context, fromHere=0)
411
412        query=self.portal_catalog(portal_type=self.calendar_types,
413                              review_state=self._getState(),
414                              start=last_date,
415                              start_usage='range:max',
416                              end=first_date,
417                              end_usage='range:min',
418                              path={'query':self.getObjectPath(startpoint),'level':0},
419                              sort_on='start')
420
421        # compile a list of the days that have events
422        eventDays={}
423        for daynumber in range(1, 32): # 1 to 31
424            eventDays[daynumber] = {'eventslist':[], 'event':0, 'day':daynumber}
425        includedevents = []
426        for result in query:
427            if result.getRID() in includedevents:
428                break
429            else:
430                includedevents.append(result.getRID())
431            event={}
432            # we need to deal with events that end next month
433            if  result.end.month() != month:  # doesn't work for events that last ~12 months - fix it if it's a problem, otherwise ignore
434                eventEndDay = last_day
435                event['end'] = None
436            else:
437                eventEndDay = result.end.day()
438                event['end'] = result.end.Time()
439            # and events that started last month
440            if result.start.month() != month:  # same as above re: 12 month thing
441                eventStartDay = 1
442                event['start'] = None
443            else:
444                eventStartDay = result.start.day()
445                event['start'] = result.start.Time()
446            event['title'] = result.Title or result.id
447            if eventStartDay != eventEndDay:
448                allEventDays = range(eventStartDay, eventEndDay+1)
449                eventDays[eventStartDay]['eventslist'].append({'end':None, 'start':result.start.Time(), 'title':result.Title})
450                eventDays[eventStartDay]['event'] = 1
451                for eventday in allEventDays[1:-1]:
452                    eventDays[eventday]['eventslist'].append({'end':None, 'start':None, 'title':result.Title})
453                    eventDays[eventday]['event'] = 1
454                eventDays[eventEndDay]['eventslist'].append({'end':result.end.Time(), 'start':None, 'title':result.Title})
455                eventDays[eventEndDay]['event'] = 1
456            else:
457                eventDays[eventStartDay]['eventslist'].append(event)
458                eventDays[eventStartDay]['event'] = 1
459            # This list is not uniqued and isn't sorted
460            # uniquing and sorting only wastes time
461            # and in this example we don't need to because
462            # later we are going to do an 'if 2 in eventDays'
463            # so the order is not important.
464            # example:  [23, 28, 29, 30, 31, 23]
465        return eventDays
466
467        # ==================
468        # end calendar stuff
469        # ==================
470
471InitializeClass(SimpleBlogManager)
Note: See TracBrowser for help on using the repository browser.