# -*- coding: utf-8 # $Id$ # # Copyright 2005 by IVA Team and contributors # # This file is part of IVA. # # IVA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # IVA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with IVA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA __version__ = "$Revision$"[11:-2] from common import translate import AccessControl import OFS, Globals from OFS import SimpleItem import types import time from TraversableWrapper import Traversable from Globals import Persistent from AccessControl import ClassSecurityInfo from Cruft import Cruft from common import perm_view, perm_edit from input_checks import strip_tags, strip_all, normal_entry_tags_and_link from Products.ZCatalog.CatalogPathAwareness import CatalogAware from zope.interface import implements from interfaces import IBlog, IBlogEntry from zope.component import adapter from zope.app.container.interfaces import IObjectAddedEvent ### Container for blog ######################### class Blog( OFS.Folder.Folder, OFS.SimpleItem.Item, Persistent, AccessControl.Role.RoleManager, Traversable, Cruft ): """ Container for a blog """ meta_type = "Blog" security = ClassSecurityInfo() security.declareObjectProtected('View') entryID = 0 implements(IBlog) def getTimeAsText(self, REQUEST, tiime): "Displays nice time, translated as timestamp_format" return time.strftime(translate(self,'timestamp_format',default="%H:%M %Y-%m-%d"), time.localtime(tiime)) def course_id_from_url(self, REQUEST): "Extract course id from webtop course folder: /fle_users/uname/webtop/cID/Blog/" url = self.absolute_url() start = url.find('/webtop/c') if start>0: stop = url.find('/',start+9) return int(url[start+9:stop]) return -1 def __init__(self, id=''): "Initialize Blog" if id: self.id = id else: self.id = 'Blog' security.declareProtected(perm_view,'get_name') def get_name(self): "Get my name, for Leivapururiba" return "Blog" def get_author(self): """Return author (owner) of this item.""" return self.getOwnerTuple()[1] security.declareProtected(perm_view,'isOwner') def isOwner(self,REQUEST,user=False): """Wether authenticated user is the owner of this blog or teacher, when this is course blog""" x = self.absolute_url() if x.find('/fle_users/')>0: return self.kasutajaNimi(REQUEST)==str(REQUEST.AUTHENTICATED_USER) if x.find('/courses/')>0 and user: return user.has_role(('Manager','IVAAdmin','Teacher',),self) return False security.declareProtected(perm_view,'getNumberOfEntries') def getNumberOfEntries(self): """ Number of entries in blog """ return len(self.objectValues('Blog')) security.declareProtected(perm_view,'getEntries') def getEntries(self,nr=0): """Array with all entries in the blog""" objects = self.objectValues('Blog') tmp = [] [ tmp.append([x.getTime(), x]) for x in objects ] tmp.sort() res = [ x[1] for x in tmp ] res.reverse() if nr>0: return res[:nr] return res security.declareProtected(perm_view,'getArchiveEntries') def getArchiveEntries(self,REQUEST): "Array with entries from specified month" month = REQUEST.get('month', None) if month is None: return [] entries = self.getEntries() result = [] for x in entries: if time.strftime("%m.%Y",time.localtime(x.getTime()))==month: result.append(x) return result security.declareProtected(perm_view,'getEntriesArchive') def getEntriesArchive(self,REQUEST): "Array with entries archive data" months = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December') entries = self.getEntries() archive = [] pmonth = "" for x in entries: month = time.strftime('%m.%Y',time.localtime(x.getTime())) if month != pmonth: pmonth = month archive.append([month,months[int(month[:2])-1],month[-4:]]) return archive security.declareProtected(perm_edit,'addEntry') def addEntry(self, REQUEST=None, title='', intro='', content='', userLocation='', author='', ch_time=None, mod_time=None): """ Data from addEntryForm """ if not author: author = str(REQUEST.AUTHENTICATED_USER) k=Entry(author, title,intro,content, ch_time, mod_time) self.entryID += 1 k.id = "entry"+str(self.entryID) self._setObject(k.id, k) if hasattr(self, 'invoke_afterCreation'): self.invoke_afterCreation(getattr(self, k.id)) if REQUEST: return REQUEST.RESPONSE.redirect(self.absolute_url()+"?userLocation="+userLocation) return getattr(self, k.id) security.declarePublic('rss') def rss(self, REQUEST): """ Blogs RSS feed version 2.0 """ REQUEST.RESPONSE.setHeader("Content-type","text/xml; charset=UTF-8") REQUEST.RESPONSE.setHeader("X-Feedback", self.absolute_url()) t = """\n""" t += """\n""" t += "\t\n" # Create RSS channel description cid = self.course_id_from_url(REQUEST) cname = "" description = "" userLocation = "" uname = self.kasutajaNimi(REQUEST) url = self.absolute_url() if cid>0: #We are on somebodys webtop -> course nr cid cattr = getattr(self.fle_root().courses, str(cid)) cname = str(cattr.get_name()) description = str(cattr.get_description()) userLocation = "Webtop" else: # It's course blog ... if url.find('/courses/')>0: cname = str(self.parent().get_name()) description = str(self.parent().get_description()) userLocation = "Bookshelf" else: # ... or personal one description = self.firstAndLastNG(uname) userLocation = "Organizer" t += "\t\t"+description+"\n" # Create RSS channel title titel = "IVA Blog" if uname!="": titel += ": "+self.firstAndLastNG(uname) if cname!="": titel += " @ "+cname t += "\t\t"+titel+"\n" t += "\t\t"+self.absolute_url()+"?userLocation="+userLocation+"\n" #Create RSS channel image url = "" if cname=="": if self.parent().has_photo: url = self.parent().get_photo_url() else: if cid<=0: if self.parent().hasLogo(): url = self.parent().getLogoURL() #url = self.parent().absolute_url()+"/kysiLogo" else: if cattr.hasLogo(): url = cattr.getLogoURL() #url = cattr.absolute_url()+"/kysiLogo" if url != "": #If we have an image t += "\t\t\n" t += "\t\t\t"+url+"\n" t += "\t\t\t"+titel+"\n" t += "\t\t\t"+self.absolute_url()+"?userLocation="+userLocation+"\n" t += "\t\t\n" # Create RSS channel items - 20 last items for x in self.getEntries(20): if hasattr(x, 'isimported'): continue t += "\t\t\n" t += "\t\t\t"+x.getTitle()+" ("+str(x.getNumberOfComments())+")\n" t += "\t\t\t"+x.absolute_url()+"/blog_entry.html?userLocation="+userLocation+"\n" t += "\t\t\t"+x.getIntro()+"\n" t += "\t\t\t"+x.getFormattedTime(REQUEST)+"\n" t += "\t\t\t"+x.getAuthor()+"\n" t += "\t\t\t"+x.absolute_url()+"/blog_entry.html?showComments=1&userLocation="+userLocation+"#Comments\n" t += "\t\t\t"+x.absolute_url()+"\n" t += "\t\t\n" t += "\t\n" t += "\n" return t security.declareProtected(perm_view, 'getNumberOfEntriesOnCourse') def getNumberOfEntriesOnCourse(self, user_obj, course_id): """ return a number of user's blog entries on specific course """ cat = getattr(self.fle_root(), 'blogs_zcatalog') paths = [] bpath = '/'.join(user_obj.getPhysicalPath())+'/webtop/c'+course_id+'/portfolio' paths.append(bpath) query = {'getAuthor': user_obj.get_uname(), 'path':paths} results = cat.search(query) return len(results) def getBlog(self): """ get blog """ return self security.declareProtected(perm_view,'index_html') def index_html(self, REQUEST, userLocation="Webtop"): "Blogs index - should be redirected to blogs index page" page = "blog" if userLocation=="Bookshelf": page = "../course" return REQUEST.RESPONSE.redirect(self.absolute_url()+"/"+page+"_index.html?userLocation="+userLocation) ### Container for blog entry ############################### class Entry( OFS.Folder.Folder, OFS.SimpleItem.Item, Persistent, CatalogAware, AccessControl.Role.RoleManager, Traversable, Cruft ): "Blog entry - a folder, whose attributes are the entry and items are comments" meta_type = "Blog" #For links in left menu on webtop, Entry is also Blog security = ClassSecurityInfo() security.declareObjectProtected('View') commentID = 0 implements(IBlogEntry) def __init__(self, author, title, intro, content, creation_time=None, change_time=None): "Create an entry" if title=="": title="Untitled" self.author = author self.title = strip_all(title) self.intro = strip_tags(intro,normal_entry_tags_and_link) self.content = strip_tags(content,normal_entry_tags_and_link) if creation_time: self.time = creation_time else: self.time = time.time() if change_time: self.changeTime = changeTime else: self.changeTime = -1 self.default_catalog = 'blogs_zcatalog' security.declareProtected(perm_view, 'isEntry') def isEntry(self): """ is this blog or entry """ return 1 security.declareProtected(perm_view,'getNumberOfComments') def getNumberOfComments(self): "Number of comments for this entry" return len(self.objectValues('BlogComment')) def getID(self): "ID" return self.id security.declareProtected(perm_view,'get_name') def get_name(self): "Get my name, for Leivapururiba" return self.title security.declareProtected(perm_view,'getTitle') def getTitle(self): "Title of entry" return self.title security.declareProtected(perm_view,'getIntro') def getIntro(self,formatted=False): "Entrys intro. Formatted means that \n are replaced with
\n. Unformatted is for changeEntryForm" if formatted: import re return re.sub("\n","
\n",self.intro) return self.intro security.declareProtected(perm_view,'getContent') def getContent(self,formatted=False): "Content of entry. Formatted means that \n are replaced with
\n. Unformatted is for changeEntryForm" if formatted: import re return re.sub("\n","
\n",self.content) return self.content security.declareProtected(perm_view,'getAuthor') def getAuthor(self,formatted=False): """ Author of entry - formatted means first and last rather than user name """ if formatted: return self.firstAndLastNG(self.author) return self.author security.declareProtected(perm_view,'getTime') def getTime(self): "Entry submission time" return self.time security.declareProtected(perm_view,'getTime') def get_timestamp(self): "Entry submission time" return self.time security.declareProtected(perm_view,'getFormattedTime') def getFormattedTime(self, REQUEST): "Entry submission time as formatted text" return self.getTimeAsText(REQUEST, self.time) security.declareProtected(perm_view,'getComments') def getComments(self, REQUEST): "Array with 'count' comments starting from 'start' for this entry" start = int(REQUEST.get('start',1))-1 count = int(REQUEST.get('count',10)) comments = self.objectValues('BlogComment') return comments[start:start+count] security.declarePrivate('getAllComments') def getAllComments(self): return self.objectValues('BlogComment') security.declareProtected(perm_view,'getIntervals') def getIntervals(self, REQUEST): "Get comment intervals" count = int(REQUEST.get('count',10)) intervals = [] nr = self.getNumberOfComments() for x in range(int(nr/count)+1): f = x*count+1 l = (x+1)*count if l>nr: l = nr if f<=l: intervals.append([count, f, l]) return intervals security.declareProtected(perm_view,'hasBeenChanged') def hasBeenChanged(self): "Wether this entry has been changed or not" return self.changeTime>-1 security.declareProtected(perm_view,'getChangeTime') def getChangeTime(self): "Get entry's change time" return self.changeTime security.declareProtected(perm_view,'getFormattedChangeTime') def getFormattedChangeTime(self, REQUEST): "Get change time as formatted text" if self.hasBeenChanged(): return self.getTimeAsText(REQUEST,self.changeTime) return "*" security.declareProtected(perm_edit,'changeEntry') def changeEntry(self, REQUEST, title, intro, content, userLocation): "Change entry" if title=="": title = "Untitled" self.title = strip_all(title) self.intro = strip_tags(intro,normal_entry_tags_and_link) self.content = strip_tags(content,normal_entry_tags_and_link) self.changeTime=time.time() self.reindex_object() if hasattr(self.aq_parent, 'invoke_afterCreation'): self.aq_parent.invoke_afterCreation(self) return REQUEST.RESPONSE.redirect(self.absolute_url()+"/blog_entry.html?userLocation="+userLocation) security.declareProtected(perm_view,'addComment') def addComment(self, REQUEST, title, content, userLocation): "Add comment to this entry" c = Comment(str(REQUEST.AUTHENTICATED_USER), title, content) self.commentID += 1 c.id = "comment"+str(self.commentID) self._setObject(c.id, c) self.reindex_object() return REQUEST.RESPONSE.redirect(self.absolute_url()+"/blog_entry.html?showComments=1&start="+str(int(self.getNumberOfComments()/10)*10+1)+"&userLocation="+userLocation+"#"+c.id) security.declareProtected(perm_view,'index_html') def index_html(self, REQUEST, userLocation="Organizer"): "Entrys index page" return REQUEST.RESPONSE.redirect(self.absolute_url()+"/blog_entry.html?userLocation="+userLocation) ### Container for blog entry comments ######################################## class Comment( OFS.SimpleItem.Item, Persistent, AccessControl.Role.RoleManager, Traversable, Cruft, ): "Comments for entries" meta_type = "BlogComment" security = ClassSecurityInfo() security.declareObjectProtected('View') def __init__(self, author, title, content): "Create a comment" if title=="": title="Untitled" self.author = author self.title = strip_all(title) self.content = strip_all(content) self.time = time.time() def getID(self): "ID" return self.id security.declareProtected(perm_view, 'isEntry') def isEntry(self): """ is this not a blog or entry """ return 0 security.declareProtected(perm_view,'getAuthor') def getAuthor(self,formatted=False): "Author of comment" if formatted: return self.firstAndLastNG(self.author) return self.author security.declareProtected(perm_view,'getTitle') def getTitle(self): "Title of comment" return self.title security.declareProtected(perm_view,'getContent') def getContent(self,formatted=False): "Content of comment. Formatted means that \n is replaced with
\n" if formatted: import re return re.sub("\n","
\n",self.content) return self.content security.declareProtected(perm_view,'getTime') def getTime(self): "Entry submission time" return self.time security.declareProtected(perm_view,'getFormattedTime') def getFormattedTime(self, REQUEST): "Entry submission time as formatted text" return self.getTimeAsText(REQUEST, self.time) security.declareProtected(perm_view,'index_html') def index_html(self, REQUEST): "Comments index page" return REQUEST.RESPONSE.redirect(self.parent().absolute_url()) @adapter(IBlog, IObjectAddedEvent) def addedBlog(obj, event): """ blog events handler """ # give Change permission permission to Owner and Teacher obj.manage_permission('Change permissions', ('Owner', 'Teacher',), 1) @adapter(IBlogEntry, IObjectAddedEvent) def addedEntry(obj, event): """ blog entry events handler """ obj.index_object()