source: trunk/Resources.py @ 581

Revision 581, 16.1 KB checked in by vahur, 13 years ago (diff)

re #613 spent 1h
tuning

Line 
1# Copyright 2006 by the LeMill Team (see AUTHORS)
2#
3# This file is part of LeMill.
4#
5# LeMill is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# LeMill is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with LeMill; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19from Products.Archetypes.public import *
20from Products.Archetypes.atapi import DisplayList
21import Globals
22import os.path
23from Globals import InitializeClass
24from Products.CMFCore.utils import getToolByName
25from AccessControl import ClassSecurityInfo, Unauthorized
26from config import PROJECTNAME, MATERIAL_TYPES
27from persistent.list import PersistentList
28import copy
29
30import time
31
32class Resource(BaseContent):
33    """Superclass for all resources (pieces and learning resources)."""
34    global_allow = 1
35    _at_rename_after_creation = True
36
37    security = ClassSecurityInfo()
38
39    def manage_afterAdd(self, item, container):
40        """Replaces the left side portlets with the content type's own action portlet."""
41        BaseContent.manage_afterAdd(self, item, container)
42        mtool = getToolByName(self, 'portal_membership')
43        memberfolder=mtool.getHomeFolder()
44        if memberfolder!=None:
45            memberfolder.note_action(item.UID(), item.portal_type, 'afterAdd')
46        if not hasattr(item.aq_base, 'left_slots'):
47            self._setProperty('left_slots', ['here/portlet_%s_actions/macros/portlet' % item.meta_type.lower(),], 'lines')
48
49    def at_post_create_script(self):
50        self._renameAfterCreation()
51        self.at_post_edit_script()
52
53    def at_post_edit_script(self):
54        self._renameAfterCreation()
55        history=self.getHistory()
56        changedFields=[]
57        schema=self.Schema()
58        if not history:
59            # First edit - object just created
60            for field in schema.editableFields(self):
61                fieldname=field.getName()
62                #if fieldname in form.keys():
63                changedFields.append(fieldname)
64        else:
65            # Subsequent edit - history exists
66            for field in schema.editableFields(self):
67                fieldname=field.getName()
68                #if fieldname in form.keys():
69                if field.getRaw(self) != \
70                       self.__getLatestHistoricalValueForField(fieldname):
71                    changedFields.append(fieldname)
72        if changedFields:
73            self.storeInHistory(changedFields)
74        mtool = getToolByName(self, 'portal_membership')
75        memberfolder=mtool.getHomeFolder()
76        if memberfolder!=None:
77            memberfolder.note_action(self.UID(), self.portal_type, 'post_edit')
78
79    def title_or_id(self):
80        """ return title or id. if is temporary object, then return Edit ... """
81        if self.isTemporary():
82            #XXX:TODO:needs translation
83            return "Edit %s" % self.meta_type
84        return BaseContent.title_or_id(self)
85
86    def amIOwner(self):
87        """ check owner of object """
88        return self.whoami() == str(self.getOwner())
89
90    def getCoverImageURL(self):
91        """Returns the URL for the cover image."""
92        if self.getField('hasCoverImage').get(self)==True:
93            return self.absolute_url()+'/coverImage'
94        return 'default_%s.png' % self.meta_type.lower()
95
96    def setCoverImage(self, value, **kwargs):
97        """ Normal mutator, but flags object to have a coverImage (hasCoverImage = True) """
98        cover=self.getField('coverImage')
99        cover.set(self,value,**kwargs)
100        has_cover=self.getField('hasCoverImage')
101        has_cover.set(self,True)
102        self.reindexObject()
103
104    def delCoverImage(self):
105        """ Reverse of setCoverImage """
106        cover=self.getField('coverImage')
107        cover.set(self,None)
108        orgCover = self.getField('originalCoverImage')
109        orgCover.set(self, "DELETE_IMAGE")
110        has_cover=self.getField('hasCoverImage')
111        has_cover.set(self,False)
112        self.reindexObject()
113
114    def getAuthors(self):
115        """ used to get the list of authors """
116        creators = self.getField('creators')
117        return creators.get(self)
118
119    def getUserGroups(self):
120        """ this is vocabulary for groups field """
121        req = self.REQUEST
122        ut = getToolByName(self, 'lemill_usertool')
123        groups = ut.getGroupsList(str(req.AUTHENTICATED_USER))
124        if not groups:
125            return list()
126        result = ()
127        pg = getToolByName(self, 'portal_groups')
128        for g in groups:
129            group_id = g.getGroupId()
130            ga = pg.getGroupareaFolder(group_id)
131            result += ((group_id, ga.TitleOrId()),)
132        result = (('', 'no sharing'),) + result
133        result += (('_new_group', '...or create a new group:'),)
134        return result
135
136    # private on purpose
137    def addMeAsAuthor(self):
138        curlist = list(self.getAuthors())
139        memberOb = self.getMember()
140        if not memberOb:
141            return
142        member = memberOb.getId()
143        if not member in curlist:
144            curlist.append(member)
145        creators=self.getField('creators')
146        creators.set(self,tuple(curlist))
147
148    def getAuthorsNames(self):
149        """ Get nice user names of authors. """
150        names = []
151        authors = self.getAuthors()
152        for author in authors:
153            auth = author.split(',')
154            for a in auth:
155                try:
156                    member=self.getMember(a)
157                    name = member.NiceName()
158                    names.append(name)
159                except AttributeError:
160                    names.append(a)
161        if not names:
162            return ""
163        return ', '.join(names)
164
165    def setBodyText(self,value,**kwargs):
166        bodyText=self.getField('bodyText')
167        bodyText.set(self,value,**kwargs)
168        self.addMeAsAuthor()
169
170    def getLanguagelist(self):
171        languagelist=self.availableLanguages()
172        return DisplayList(languagelist)
173       
174    def defaultLanguage(self):
175        """ Get logged users default language """
176        member = self.getMember()
177        if member:
178            #lang=member.getLanguage_skills()
179            try:
180                lang=member.getField('language_skills').get(member)
181                if lang:
182                    return lang[0]
183            except AttributeError:
184                raise 'Invalid User',"%s - %s" % (self.whoami(),str(member))
185        return ''
186
187    def getHistory(self):
188        try:
189            return self.__history
190        except AttributeError:
191            self.__history=PersistentList()
192            return self.__history
193
194    def getHistoryEntries(self):
195        """Return list of history entries for viewing."""
196        history=self.getHistory()
197        entries=[]
198        previous=None
199        wtool = getToolByName(self,'portal_workflow')
200        sort_history = []
201        [ sort_history.append([event['_timestamp'],event]) for event in history ]
202        sort_history.sort()
203        history = []
204        [ history.append(x[1]) for x in sort_history ]
205        counter = 0
206        for event in history:
207            counter += 1
208            entry = {}
209            entry['version'] = counter
210            entry['date']=time.asctime(time.localtime(event['_timestamp']))
211            entry['timestamp'] = event['_timestamp']
212            if wtool.getInfoFor(self,'review_state',None)=='draft':
213                entry['author']=""
214            else:
215                entry['author']=self.getMember(event['_by']).NiceName()
216            if '_summary' in event.keys() and event['_summary']:
217                entry['summary']=event['_summary']
218            elif not previous:
219                entry['summary']="Resource created"
220            else:
221                entry['summary']="Modified these: %s" % \
222                               ', '.join([self._prettyFieldName(x) for x in event.keys() \
223                                          if not x.startswith('_')])
224            entries.append(entry)
225            previous=event
226        entries.reverse()
227        return entries
228
229    def _prettyFieldName(self, fieldname):
230        """ return a widget's label """
231        f = self.getField(fieldname)
232        return f.widget.Label(self)
233
234    def __getLatestHistoricalValueForField(self,fieldname):
235        for entry in self.getHistory():
236            if fieldname in entry.keys():
237                return entry[fieldname]
238        return None
239
240    def storeInHistory(self,fields,summary=None,storeAuthor=True):
241        entry = {}
242        if storeAuthor:
243            entry['_by']=self.whoami()
244        else:
245            entry['_by']=''
246        entry['_timestamp']=time.time()
247        entry['_summary']=summary
248        for fieldname in fields:
249            f = self.getField(fieldname)
250            value=f.getRaw(self)
251            entry[fieldname]=value
252        history=self.getHistory()
253        # Newest first!
254        history.insert(0,entry)
255
256    def getHistoricalFields(self, timestamp):
257        """ get historical fields. changed fields from timestamp(including) """
258        history = self.getHistory()
259        entry = {}
260        curr_timest = 0
261        rec = 0
262        for x in history:
263            if int(timestamp) == int(x['_timestamp']):  # scroll back in time
264                rec = 1                                 # when requested time is here, start looking for fields
265            if not rec: continue
266            for k in x.keys():
267                if k.startswith('_'): continue
268                if entry.has_key(k): continue
269                entry[k] = x[k]
270        fields = self.Schema().editableFields(self)
271        for field in fields:
272            fieldname=field.getName()
273            if entry.has_key(fieldname): continue
274            f = self.getField(fieldname)
275            entry[fieldname] = f.getRaw(self)
276        return entry
277
278    def getDiffFields(self, x_timestamp, y_timestamp):
279        """ will return one version of document by timestamp """
280        obj_x = self.getHistoricalFields(x_timestamp)
281        obj_y = self.getHistoricalFields(y_timestamp)
282        keys = []
283        diffs = {}
284        for x in obj_x.keys():                  # I don't think it's necessary to merge but you never know
285            if x not in keys: keys.append(x)
286        for x in obj_y.keys():
287            if x not in keys: keys.append(x)
288        for x in keys:
289            if obj_x[x] == obj_y[x]: continue
290            if not obj_x[x] and not obj_y[x]: continue # '' vs. None
291            if x == 'modification_date':  # really need it here?
292                obj_x[x] = time.asctime(time.localtime(obj_x[x]))
293                obj_y[x] = time.asctime(time.localtime(obj_y[x]))
294            if x == 'coverImage': # saving coverImage like this isn't very sufficient and
295                obj_x[x] = 'cover image changed' # is accessible only by Manager
296                obj_y[x] = 'cover image changed'
297            entry = {}
298            entry['name'] = x
299            entry['old'] = obj_x[x]
300            entry['new'] = obj_y[x]
301            diffs[x] = entry
302        for x in diffs.keys():
303            diffs[x]['niceName'] = self._prettyFieldName(diffs[x]['name'])
304        return diffs
305
306    def setGroupsShared(self, value):
307        """ share a material/resource with a group(s) """
308        create_new = self.REQUEST.get('new_group_name', '')
309        if create_new:
310            pass
311            # create a new group here
312        f = self.getField('groups')
313        old_value = f.get(self)
314        if old_value:
315            self.manage_delLocalRoles((old_value[0],))
316        if value:
317            self.manage_setLocalRoles(value, ('Owner',))
318            f.set(self, value)
319
320InitializeClass(Resource)
321
322class LearningResource(Resource):
323    """Superclass for all learning resources (material, activity, tool)."""
324
325    security = ClassSecurityInfo()
326
327    def manage_afterAdd(self, item, container):
328        Resource.manage_afterAdd(self, item, container)
329
330    #def at_post_create_script(self):
331    #    Resource.at_post_create_script(self)
332    #    self.at_post_edit_script()
333
334    #at_post_create_script=at_post_edit_script
335
336    def addFieldReferences(self, ref, to_field):
337        """ add reference to image """
338        highest_order = -1
339        field = self.Schema().get(to_field)
340        prev = field.getRaw(self, aslist=True)
341        # get reference object and see what's the higher number
342        refsim = self.getReferenceImpl()
343        for x in refsim:
344            if hasattr(x, 'image_order'):
345                if x.image_order > highest_order:
346                    highest_order = x.image_order
347        if highest_order == -1: highest_order = 1001
348        highest_order += 10
349        new_value = []
350        from types import ListType, TupleType
351        if type(ref) == ListType or type(ref) == TupleType:
352            new_value = prev
353            for x in ref:
354                new_value.append(x)
355        else:
356            new_value = prev+[ref,]
357        field.set(self, new_value, image_order=highest_order)
358        self.__updateCoverImage()
359
360    def __updateCoverImage(self):
361        # Set cover image
362        refs = None
363        try:
364            refs = self.getField('refsToImages').get(self)
365        except AttributeError: # just in case...
366            return
367        cover=self.getField('coverImage')
368        has_cover=self.getField('hasCoverImage')
369        if refs:
370            first=refs[0]
371            value = first.getField('file').get(first)
372            cover.set(self,value)
373            has_cover.set(self,True)
374        else:
375            cover.set(self,None)
376            has_cover.set(self,False)
377
378    def embedIcon(self, image_value):
379        """ embed icon to topleft corner """
380        try:
381            from PIL import Image
382        except ImportError:
383            return image_value
384        lt = getToolByName(self, 'lemill_tool')
385        s, im = lt.resize_image(image_value)
386       
387        # making new image and pasting resized image to it.
388        large = Image.new('RGB', (160, 120), (0,0,0))
389        reg = im.crop((0,0)+im.size)
390        (im_width, im_heigth) = im.size
391        (reg_width, reg_heigth) = reg.size
392        top = (120-im_heigth)/2
393        left = (160-im_width)/2
394
395        large.paste(reg, (left, top, reg_width+left, reg_heigth+top))
396       
397        # choosing icon to embed, opening it
398        images_folder = os.path.join(Globals.INSTANCE_HOME, 'Products', 'LeMill', 'images')
399        wf = getToolByName(self, 'portal_workflow')
400        extension = '.jpg'
401        icon_filename = ''
402        if wf.getInfoFor(self, 'review_state', None) == 'draft':
403            icon_filename = 'draft'+extension
404        else:
405            icon_filename = self.meta_type.lower()+extension
406        icon = Image.open(os.path.join(images_folder, icon_filename))
407        ic_area = icon.crop((0,0)+icon.size)
408
409        # embeding icon
410        large.paste(icon, (0,0)+icon.size)
411       
412        import cStringIO
413        merged = cStringIO.StringIO()
414        large.save(merged, "JPEG", quality=100)
415        merged.seek(0)
416        return merged
417
418    def hideContent(self):
419        """Hide the content, replacing the delete function. Workflow takes care of security."""
420        if self.portal_type in MATERIAL_TYPES or self.portal_type=='Piece':
421            self.content_status_modify(workflow_action='hide')
422
423    def revealContent(self):
424        """Reveal hidden content"""
425        if self.portal_type in MATERIAL_TYPES:
426            self.content_status_modify(workflow_action='retract')
427        if self.portal_type =='Piece':
428            self.content_status_modify(workflow_action='publish')
429
430    def getGroupsEditing(self):
431        """ return a list of groups that are this editing material """
432        local_roles = self.get_local_roles()
433        grouptool = getToolByName(self, 'portal_groups')
434        groups = []
435        for role in local_roles:
436            gr = grouptool.getGroupById(role[0])
437            if gr:
438                groups.append(gr)
439        if groups:
440            return groups[0]
441
442InitializeClass(LearningResource)
Note: See TracBrowser for help on using the repository browser.