source: products/SimpleBlog/branches/plone-2.1-Blogging-APIs/SimpleBlogTool.py

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

Building directory structure

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