source: trunk/Resources.py @ 1150

Revision 1150, 30.4 KB checked in by meelis, 13 years ago (diff)

closes #916 spent 3h

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, LANGUAGE_INDEPENDENT_FIELDS, DEFAULT_ICONS
27from persistent.list import PersistentList
28from Products.CMFPlone import PloneMessageFactory as PMF
29from permissions import ModerateContent, MODIFY_CONTENT, DELETE_CONTENT
30
31import copy
32import time
33from itertools import count
34import difflib
35import traceback
36import sys
37
38# retains the first occurence of an object and drops the others
39def _remove_duplicates(d):      # XXX both _remove_duplicates and remove_duplicates does the same (to catch some bugs early)
40    d = list(d)
41    d.reverse()
42    creators_pos = dict([(c, -i) for i, c in enumerate(d)])
43    creators_pos_rev = dict([(v, k) for k, v in creators_pos.iteritems()])
44    return [v for k, v in sorted(creators_pos_rev.iteritems())]
45
46def remove_duplicates(d):
47    creator_positions = {}
48    for pos, creator in enumerate(d):
49        creator_positions.setdefault(creator, []).append(pos)
50
51    best_pos = sorted(((min(positions), creator) for creator, positions in creator_positions.iteritems()))
52    assert len(set((pos for pos, creator in best_pos))) == len(best_pos)
53    assert len(set((creator for pos, creator in best_pos))) == len(best_pos)
54
55    result = [creator for pos, creator in best_pos]
56    assert result == _remove_duplicates(d), "%s, %s" % (result, _remove_duplicates(d))
57    return result
58assert remove_duplicates((1, 2, 1, 7, 4, 2, 4, 6, 7, 8, 3, 2, 1)) == [1, 2, 7, 4, 6, 8, 3]
59
60class CommonMixIn:
61    """Superclass for all objects."""
62
63    def getDefaultIcon(self, meta_type='', obj=None):
64        """ general method for getting proper icon for object, used when only catalog-metadata is available """
65        # this combines folderish getDefaultIcon(for-this-type, object) and resource-specific object.getDefaultIcon()
66        # folderish behaviour is needed because members have these created resources-pages. 
67        if meta_type=='':
68            return  DEFAULT_ICONS[self.meta_type]
69        else:     
70            address=DEFAULT_ICONS[meta_type]
71            if address!='piece':
72                return address
73            else:
74                obj=obj.getObject()
75                return obj.getDefaultIcon()
76
77InitializeClass(CommonMixIn)
78
79
80class Resource(BaseContent,CommonMixIn):
81    """Superclass for all resources (pieces and learning resources)."""
82    global_allow = 1
83    _at_rename_after_creation = True
84
85    security = ClassSecurityInfo()
86
87    def manage_afterAdd(self, item, container):
88        # Replaces the left side portlets with the content type's own action portlet.
89        BaseContent.manage_afterAdd(self, item, container)
90        mtool = getToolByName(self, 'portal_membership')
91        memberfolder=mtool.getHomeFolder()
92        if memberfolder!=None:
93            memberfolder.note_action(item.UID(), item.portal_type, 'afterAdd')
94        if not hasattr(item.aq_base, 'left_slots'):
95            self._setProperty('left_slots', ['here/portlet_%s_actions/macros/portlet' % item.meta_type.lower(),], 'lines')
96
97    def at_post_create_script(self):
98        self.at_post_edit_script()
99
100    def at_post_edit_script(self):
101        # Store current ID
102        old_id = self.getId();
103        # See what would be the optimal ID currently
104        optimal_new_id = self.generateNewId()
105        if optimal_new_id and optimal_new_id != old_id:
106            execute_rename=True
107            if old_id.startswith(optimal_new_id):
108                # Check that the old_id isn't optimal + "-n" suffix
109                suffix = old_id[len(optimal_new_id):]
110                if suffix[0]=='-' and suffix[1:].isdigit():
111                    execute_rename=False
112            # Attempt rename, adding "-n" so that no duplicates occur
113            if execute_rename:
114                # Ff the new name is a redirector for this object, delete it
115                ob = getattr(self.aq_inner.aq_parent,optimal_new_id,None)
116                if ob and ob.meta_type == 'Redirector':
117                    if ob.redirect_to == self.UID():
118                        self.aq_inner.aq_parent._delObject(optimal_new_id)
119                # Execute rename
120                self._renameAfterCreation()
121                new_id = self.getId();
122                # If the id did change
123                if old_id != new_id:
124                    # Need to create redirection from old URL
125                    red = Redirector(old_id)
126                    red.redirect_to = self.UID()
127                    self.aq_inner.aq_parent._setObject(old_id,red)
128
129        history=self.getHistory()
130        changedFields=[]
131        schema=self.Schema()
132        if not history:
133            # First edit - object just created
134            for field in schema.editableFields(self):
135                fieldname=field.getName()
136                #if fieldname in form.keys():
137                changedFields.append(fieldname)
138        else:
139            # Subsequent edit - history exists
140            for field in schema.editableFields(self):
141                fieldname=field.getName()
142                #if fieldname in form.keys():
143                if field.getRaw(self) != \
144                       self.__getLatestHistoricalValueForField(fieldname):
145                    changedFields.append(fieldname)
146        if [x for x in changedFields if x not in ('creators', 'modification_date')]:
147            hist = self.getHistory()
148            if hist and hist[0]['_by'] == self.whoami() and time.time() - hist[0]['_timestamp'] < 3600:   # within one hour
149                prev_changes = hist[0].keys()
150                del hist[0]
151                changedFields = [x for x in set(changedFields + prev_changes) if x[0] != '_']
152            self.storeInHistory(changedFields)
153        mtool = getToolByName(self, 'portal_membership')
154        memberfolder=mtool.getHomeFolder()
155        if memberfolder!=None:
156            memberfolder.note_action(self.UID(), self.portal_type, 'post_edit')
157
158        self.recalculateAuthors()
159
160    def recalculateAuthors(self):
161        """ Recalculates author order """
162
163        def recalc(creators):
164            # FIXME does nothing if the resource is in draft state (problem: event['author'] is '')
165            new_creators = creators
166            if getToolByName(self,'portal_workflow').getInfoFor(self,'review_state',None) == 'draft':
167                #print 'draft'
168                return creators
169
170            hist_entries = self.getHistoryEntries()
171            if not hist_entries:
172                #print 'no history entries'
173                return creators
174
175            l = [(ev['timestamp'], ev['author']) for ev in hist_entries if ev['author']]
176            if not l:
177                #print 'no timestamps'
178                return creators
179            original_creator = min(l)[1]
180            assert original_creator
181
182            # XXX check if assumptions in the implementation are correct (can be deleted if it does not fall on it's face for a while)
183            for event in hist_entries[:-1]:     # no diff for the original version
184                #print event
185                diff = self.getDiffFields(event['timestamp'])
186                if diff.has_key('bodyText'):
187                    old = diff['bodyText']['old']
188                    new = diff['bodyText']['new']
189                    #print 'OLD'
190                    #print type(old)
191                    if type(old)==list:
192                        o2=[]
193                        for o in old:
194                            if type(o)==list:
195                                o='\n'.join(o)
196                            o2.append(o)
197                        old='\n'.join(o2)
198                    old=old.split('\n')
199                    #print 'NEW'
200                    #print type(new)
201                    if type(new)==list:
202                        n2=[]
203                        for n in new:
204                            if type(n)==list:
205                                n='\n'.join(n)
206                            n2.append(n)
207                        new='\n'.join(n2)
208                    new=new.split('\n')
209                    #print 'creating unified_diff..'
210                    d = difflib.unified_diff(old, new)
211                    #print 'done'
212                    #print d
213                    assert d.next().startswith('--- ')
214                    assert d.next().startswith('+++ ')
215
216                    #print 'making a list...'
217                    dl = list(d)
218                    #print 'done.'
219                    #print dl
220                    assert dl[0].startswith('@@ ')
221
222                    first_chars = [l[0] for l in dl]
223                    plus = first_chars.count('+')
224                    minus = first_chars.count('-')
225                    context = first_chars.count(' ')
226                    chunk = first_chars.count('@')
227                    assert chunk + context + minus + plus == len(first_chars)
228
229            diffsort = []
230            for event in hist_entries[:-1]:     # no diff for the original version
231                diff = self.getDiffFields(event['timestamp'])
232                if diff.has_key('bodyText'):
233                    old = diff['bodyText']['old']
234                    new = diff['bodyText']['new']
235                    if type(old)==list:
236                        o2=[]
237                        for o in old:
238                            if type(o)==list:
239                                o='\n'.join(o)
240                            o2.append(o)
241                        old='\n'.join(o2)
242                    old=old.split('\n')
243                    if type(new)==list:
244                        n2=[]
245                        for n in new:
246                            if type(n)==list:
247                                n='\n'.join(n)
248                            n2.append(n)
249                        new='\n'.join(n2)
250                    new=new.split('\n')
251                    d = difflib.unified_diff(old, new)
252                    first_chars = [l[0] for l in list(d)[2:]]
253                    plus = first_chars.count('+')
254                    minus = first_chars.count('-')
255                    context = first_chars.count(' ')
256                    chunk = first_chars.count('@')
257                    diffsort.append(((plus - minus, plus), event['author']))
258
259            diffsort.sort()
260            new_creators = remove_duplicates([creator for cmp_val, creator in diffsort])
261            new_creators = [user for user in new_creators if 'Manager' not in self.acl_users.getUser(user).getRoles()]
262            if original_creator in new_creators:
263                new_creators.remove(original_creator)
264            new_creators.insert(0, original_creator)
265
266            assert len(set(new_creators)) == len(new_creators)
267            #print 'new: %s' % new_creators
268            return new_creators
269        creators_field = self.getField('creators')
270        #print 'old: %s' % str(creators_field.get(self))
271        cr = recalc(creators_field.get(self))
272        creators_field.set(self, cr)
273
274    def Creator(self):
275        """This method is part of Plone Dublin Core.
276        Overridden to give correct values."""
277        auth = self.getAuthors()
278        if len(auth)>0:
279            return self.getAuthors()[0]
280        else:
281            return ''
282
283    def Creators(self):
284        """This is another base method that should provide good values."""
285        return self.getAuthors()
286
287    def Contributors(self):
288        """This is another base method that should provide good values
289        (everyone except first author)"""
290        auth = self.getAuthors()
291        if len(auth)>0:
292            return self.getAuthors()[1:]
293        else:
294            return []
295
296    def amIMaterial(self):
297        """ Returns True if it's a material """
298        return False
299
300    def amIOwner(self):
301        """ check owner of object """
302        roles = self.portal_membership.getAuthenticatedMember().getRolesInContext(self)
303        return 'Owner' in roles
304
305    def canIModerate(self):
306        roles = self.portal_membership.getAuthenticatedMember().getRolesInContext(self)
307        return 'Manager' in roles or 'Reviewer' in roles
308 
309     
310    def getCoverOrDefault(self):
311        wtool = getToolByName(self,'portal_workflow')
312        if self.getField('hasCoverImage').get(self)==True and \
313               wtool.getInfoFor(self,'review_state',None)!='draft':
314            return self.coverImage
315        return eval('default_%s.png' % self.meta_type.lower())
316       
317       
318    def getCoverImageURL(self, drafts=False):
319        """Returns the URL for the cover image. If drafts=True, also allow drafts to show cover image"""
320        wtool = getToolByName(self,'portal_workflow')
321        field = self.getField('hasCoverImage')
322        if field and field.get(self)==True and \
323               (wtool.getInfoFor(self,'review_state',None)!='draft' or drafts):
324            return self.absolute_url()+'/coverImage'
325        return self.getDefaultIcon()
326
327    def setCoverImage(self, value, **kwargs):
328        """ Normal mutator, but flags object to have a coverImage (hasCoverImage = True) """
329        cover=self.getField('coverImage')
330        cover.set(self,value,**kwargs)
331        has_cover=self.getField('hasCoverImage')
332        if value==None:
333            has_cover.set(self,False)
334        else:
335            has_cover.set(self,True)
336        self.reindexObject()
337
338    def delCoverImage(self):
339        """ Reverse of setCoverImage """
340        cover=self.getField('coverImage')
341        cover.set(self,None)
342        orgCover = self.getField('originalCoverImage')
343        orgCover.set(self, "DELETE_IMAGE")
344        has_cover=self.getField('hasCoverImage')
345        has_cover.set(self,False)
346        self.reindexObject()
347
348    def getAuthors(self):
349        """ used to get the list of authors """
350
351        return self.getField('creators').get(self)
352
353    def getTranslations(self):
354        wtool = getToolByName(self,'portal_workflow')
355        if self.getTranslation_of()!=None:
356            mother=self.getTranslation_of()
357            listoft= mother.getField('translations').get(mother)
358        else:
359            listoft= self.getField('translations').get(self)
360        listoft=[x for x in listoft if wtool.getInfoFor(x,'review_state',None)!='deleted']
361        return listoft
362
363    def getUserGroups(self, allow_none=True):
364        """ this is vocabulary for groups field """
365        req = self.REQUEST
366        ut = getToolByName(self, 'lemill_usertool')
367        groups = ut.getGroupsList(str(req.AUTHENTICATED_USER))
368        if allow_none:
369            result = [('no_group', 'Not assigned to any group')]
370        else:
371            result = []
372        if groups:
373            pg = getToolByName(self, 'portal_groups')
374            for g in groups:
375                group_id = g.getGroupId()
376                ga = pg.getGroupareaFolder(group_id)
377                result.append((group_id, ga.TitleOrId()))
378        result.append(('__new_group', '...or create a new group:'))
379        return result
380
381    security.declarePublic('languagesNotTranslated')
382    def languagesNotTranslated(self):
383        """ List of languages minus list of existing translations """
384        transcodes = [x.Language() for x in self.getTranslations()]
385        langs = self.availableLanguages()[1:]
386        return [x for x in langs if x[0] not in transcodes and x[0] != self.Language()]
387
388
389    security.declarePublic('getFieldsToTranslate')
390    def getFieldsToTranslate(self):
391        """ Returns fields to show from original when translating, uses list of untranslateables as guide """
392        fields= self.Schema().filterFields(isMetadata=0)
393        lif= LANGUAGE_INDEPENDENT_FIELDS+['language', 'translation_of', 'translations']
394        fields = [x for x in fields if x.__name__ not in lif]
395        return fields       
396
397    # private on purpose
398    def addMeAsAuthor(self):
399        curlist = list(self.getAuthors())
400        memberOb = self.getMember()
401        if not memberOb:
402            return
403        member = memberOb.getId()
404        if not member in curlist:
405            curlist.append(member)
406        creators=self.getField('creators')
407        creators.set(self,tuple(curlist))
408
409    def getAuthorsNames(self):
410        """ Get nice user names of authors. """
411        names = []
412        authors = self.getAuthors()
413        for author in authors:
414            auth = author.split(',')
415            for a in auth:
416                try:
417                    member=self.getMember(a)
418                    name = member.NiceName()
419                    names.append(name)
420                except AttributeError:
421                    names.append(a)
422        if not names:
423            return ""
424        return ', '.join(names)
425
426    def setBodyText(self,value,**kwargs):
427        bodyText=self.getField('bodyText')
428        bodyText.set(self,value,**kwargs)
429        self.addMeAsAuthor()
430
431    def getLanguagelist(self):
432        languagelist=self.availableLanguages()
433        return DisplayList(languagelist)
434       
435    security.declarePublic('defaultLanguage')
436    def defaultLanguage(self):
437        """ Get logged users default language """
438        member = self.getMember()
439        if member:
440            #lang=member.getLanguage_skills()
441            try:
442                lang=member.getField('language_skills').get(member)
443                if lang:
444                    return lang[0]                   
445            except AttributeError:
446                raise 'Invalid User',"%s - %s" % (self.whoami(),str(member))
447        return ''
448
449    def getHistory(self):
450        try:
451            return self.__history
452        except AttributeError:
453            self.__history=PersistentList()
454            return self.__history
455
456    def getHistoryEntries(self):
457        """Return list of history entries for viewing."""
458        history=self.getHistory()
459        entries=[]
460        wtool = getToolByName(self,'portal_workflow')
461        sort_history = [(event['_timestamp'],event) for event in history]
462        sort_history.sort()
463        history = [event for ts, event in sort_history]
464        for event, version in zip(history, count(1)):
465            entry = {}
466            entry['version'] = version
467            entry['date']=time.asctime(time.localtime(event['_timestamp']))
468            entry['timestamp'] = event['_timestamp']
469            if wtool.getInfoFor(self,'review_state',None)=='draft':
470                entry['author']=""
471            else:
472                other_member = self.getOtherMember(event['_by'])
473                entry['author']= other_member and other_member.getId() or ""
474            if '_summary' in event.keys() and event['_summary']:
475                entry['summary']=event['_summary']
476            elif version == 1:
477                entry['summary']="Resource created"
478            else:
479                # XXX: this can screw up everything.
480                fields = [self._prettyFieldName(x).decode('latin1').encode('utf-8') for x in event.keys() if not x.startswith('_')]
481                mod = ', '.join(fields)
482                entry['summary']="Modified these: %s" % mod
483            entries.append(entry)
484        entries.reverse()
485        return entries
486
487    def _prettyFieldName(self, fieldname):
488        """ return a widget's label """
489        f = self.getField(fieldname)
490        if f is None:
491            return fieldname
492        return f.widget.Label(self)
493
494    def __getLatestHistoricalValueForField(self,fieldname):
495        for entry in self.getHistory():
496            if fieldname in entry.keys():
497                return entry[fieldname]
498        return None
499
500    def storeInHistory(self,fields,summary=None,storeAuthor=True):
501        entry = {}
502        if storeAuthor:
503            entry['_by']=self.whoami()
504        else:
505            entry['_by']=''
506        entry['_timestamp']=time.time()
507        entry['_summary']=summary
508        for fieldname in fields:
509            f = self.getField(fieldname)
510            value=f.getRaw(self)
511            entry[fieldname]=value
512        history=self.getHistory()
513        # Newest first!
514        history.insert(0,entry)
515
516    def getTimeForOldHistory(self, timestamp):
517        """ retruns time for the history """
518        timestamp = float(timestamp)
519        historyTime = time.asctime(time.localtime(timestamp))
520        return historyTime
521
522    def getHistoricalFields(self, timestamp):
523        """ get historical fields. changed fields from timestamp(including) """
524        history = self.getHistory()
525        entry = {}
526        curr_timest = 0
527        rec = 0
528        for x in history:
529            if float(timestamp) >= float(x['_timestamp']):  # scroll back in time
530                rec = 1                                 # when requested time is here, start looking for fields
531            if not rec: continue
532            for k in x.keys():
533                if k.startswith('_'): continue
534                if entry.has_key(k): continue
535                entry[k] = x[k]
536        fields = self.Schema().editableFields(self)
537        for field in fields:
538            fieldname=field.getName()
539            if entry.has_key(fieldname): continue
540            f = self.getField(fieldname)
541            entry[fieldname] = f.getRaw(self)
542        return entry
543
544    def getDiffFields(self, old_version):
545        """ will return one version of document by timestamp """
546        obj_x = self.getHistoricalFields(old_version)
547        obj_y = self.getHistoricalFields(float(old_version)-1)
548        keys = []
549        diffs = {}
550        for x in obj_x.keys():                  # I don't think it's necessary to merge but you never know
551            if x not in keys: keys.append(x)
552        for x in obj_y.keys():
553            if x not in keys: keys.append(x)
554        for x in keys:
555            if not obj_x.has_key(x):
556                obj_x[x] = None
557            elif not obj_y.has_key(x):
558                obj_y[x] = None
559
560            if obj_x[x] == obj_y[x]: continue
561            if not obj_x[x] and not obj_y[x]: continue # '' vs. None
562            if x == 'modification_date': continue
563            if x == 'coverImage': continue
564            entry = {}
565            entry['name'] = x
566            entry['old'] = obj_x[x]
567            entry['new'] = obj_y[x]
568            diffs[x] = entry
569        for x in diffs.keys():
570            diffs[x]['niceName'] = self._prettyFieldName(diffs[x]['name'])
571        return diffs
572
573
574    security.declareProtected(ModerateContent, 'retract')
575    def retract(self):
576        """Retract published, but keep coverimage as it is"""
577        self.content_status_modify(workflow_action='retract', msg='Resource changed back to draft status')
578        return self
579
580    security.declareProtected(ModerateContent, 'deleteResource')
581    def deleteResource(self, reason=''):
582        """Set reason for deletion, set state to deleted and update catalog"""
583        f = self.getField('deletionReason')
584        f.set(self, reason)
585        self.content_status_modify(workflow_action='delete', msg='Resource deleted')
586        if self.meta_type in MATERIAL_TYPES:
587            self.aliases['(Default)']='base_view'
588        self.reindexObject()       
589
590    security.declareProtected(ModerateContent, 'rescue')
591    def rescue(self):
592        """Undelete a resource """
593        if self.meta_type in MATERIAL_TYPES:
594            self.aliases['(Default)']='fullscreen_view'
595        self.undeleteResource()
596
597    security.declareProtected(ModerateContent, 'undeleteResource')
598    def undeleteResource(self):
599        f=self.getField('deletionReason')
600        f.set(self, None)
601        portal_workflow=getToolByName(self, 'portal_workflow')
602        transitions = [x['id'] for x in portal_workflow.getTransitionsFor(self)]
603        if 'restore' in transitions:
604            self.content_status_modify(workflow_action='restore', msg='Resource restored1')
605        elif 'publish' in transitions:
606            self.content_status_modify(workflow_action='publish', msg='Resource restored2')
607        else:
608            plone_utils=getToolByName(self, 'plone_utils')
609            plone_utils.addPortalMessage(PMF(u'Restoration failed.'))
610        self.reindexObject()
611
612
613    security.declarePublic('setGroupsShared')
614    def setGroupsShared(self, value):
615        """ share a material/resource with a group(s) """
616        # adjust security to members of portal
617        create_new = self.REQUEST.get('new_group_name', '')
618        if create_new and value=='__new_group':
619            # create a new group here
620            from Products.CMFPlone.utils import normalizeString
621            new_group_id = normalizeString(create_new, context=self)
622            gtool = getToolByName(self, 'portal_groups')
623            try:
624                gtool.addGroup(new_group_id, (), ())
625            except KeyError:
626                # duplicate it
627                return
628            blog = gtool.getGroupareaFolder(new_group_id)
629            blog.join_group()
630            blog.setProperties(id=new_group_id, title=create_new)
631            value = new_group_id
632        if value == '__new_group':
633            return
634        f = self.getField('groups')
635        old_value = f.get(self)
636        if old_value:
637            if old_value!='no_group':
638                self.manage_delLocalRoles((old_value,))
639        if value:
640            if value!='no_group':
641                self.manage_setLocalRoles(value, ('CoAuthor',))
642            else:
643                # make sure that all CoAuthors are deleted
644                roles=dict(self.get_local_roles())
645                for (key,dvalue) in roles.items():
646                    if not 'Owner' in dvalue: self.manage_delLocalRoles((key,))               
647            f.set(self, value)
648
649    def download(self, REQUEST, RESPONSE, field):
650        """ download a file """
651        from Products.Archetypes.utils import contentDispositionHeader
652        org_filename = field.getFilename(self)
653        filename = self.Title()
654        pu = getToolByName(self, 'plone_utils')
655        filename = pu.normalizeString(filename)
656        extension = ''
657        if org_filename:        # extract .doc .pdf or something
658            extension = org_filename[org_filename.rfind('.'):]
659            if extension == -1: extension = ''
660        else:                   # try to guess extension
661            ct = field.getContentType(self)
662            mr = getToolByName(self, 'mimetypes_registry')
663            mt = mr.lookup(ct)
664            if mt:              # mt is something like (<mimetype text/plain>,) so we'll take first one
665                extension = mt[0].extensions[0]       # and take first one from here too
666                extension = '.'+extension
667        if extension:
668            filename += extension
669        header_value = contentDispositionHeader('attachment', self.getCharset(), filename=filename)
670        RESPONSE.setHeader("Content-disposition", header_value)
671        file = field.get(self)
672        return file.index_html(REQUEST, RESPONSE)
673
674
675
676InitializeClass(Resource)
677
678class LearningResource(Resource):
679    """Superclass for all learning resources (material, activity, tool)."""
680
681    security = ClassSecurityInfo()
682
683    def manage_afterAdd(self, item, container):
684        Resource.manage_afterAdd(self, item, container)
685
686    #def at_post_create_script(self):
687    #    Resource.at_post_create_script(self)
688    #    self.at_post_edit_script()
689
690    #at_post_create_script=at_post_edit_script
691
692    def addFieldReferences(self, ref, to_field):
693        """ add reference to image """
694        highest_order = -1
695        field = self.Schema().get(to_field)
696        prev = field.getRaw(self, aslist=True)
697        # get reference object and see what's the higher number
698        refsim = self.getReferenceImpl()
699        for x in refsim:
700            if hasattr(x, 'image_order'):
701                if x.image_order > highest_order:
702                    highest_order = x.image_order
703        if highest_order == -1: highest_order = 1001
704        highest_order += 10
705        new_value = []
706        from types import ListType, TupleType
707        if type(ref) == ListType or type(ref) == TupleType:
708            new_value = prev
709            for x in ref:
710                new_value.append(x)
711        else:
712            new_value = prev+[ref,]
713        field.set(self, new_value, image_order=highest_order)
714        self.__updateCoverImage()
715
716    def __updateCoverImage(self):
717        # Set cover image
718        refs = None
719        try:
720            refs = self.getField('refsToImages').get(self)
721        except AttributeError: # just in case...
722            return
723        cover=self.getField('coverImage')
724        has_cover=self.getField('hasCoverImage')
725        if refs:
726            first=refs[0]
727            value = first.getField('file').get(first)
728            cover.set(self,value)
729            has_cover.set(self,True)
730        else:
731            cover.set(self,None)
732            has_cover.set(self,False)
733
734    def getGroupsEditing(self):
735        """ return a list of groups that are this editing material """
736        local_roles = self.get_local_roles()
737        grouptool = getToolByName(self, 'portal_groups')
738        groups = []
739        for role in local_roles:
740            gr = grouptool.getGroupById(role[0])
741            if gr:
742                groups.append(gr)
743        if groups:
744            return groups[0]
745       
746
747InitializeClass(LearningResource)
748
749class Redirector(BaseContent,CommonMixIn):
750    """Redirects to new URLs of renamed resources."""
751
752    meta_type = "Redirector"
753    archetype_name = "Redirector"
754
755    aliases = {
756        '(Default)' : 'redirect',
757        'view'      : 'redirect',
758    #    'edit'      : 'redirect',
759    #    'base_view' : 'redirect',
760    #    'history_view': 'redirect',
761    }
762
763    def __bobo_traverse__(self, REQUEST, entry_name=None):
764        """ redirect to correct object """
765        obj = self.redirect()
766        if entry_name is None:
767            return obj
768        try:
769            l = getattr(obj, entry_name)
770        except AttributeError:
771            return BaseContent.__bobo_traverse__(self, REQUEST, entry_name)
772        if hasattr(l, 'im_func'):
773            return l()
774        return l
775
776    def redirect(self, REQUEST=None):
777        """Redirect to current location."""
778        rc = getToolByName(self, 'reference_catalog')
779        return rc.lookupObject(self.redirect_to, REQUEST)
780
781registerType(Redirector)
Note: See TracBrowser for help on using the repository browser.