source: trunk/Collection.py @ 1916

Revision 1916, 35.8 KB checked in by gabor, 12 years ago (diff)

ref #738 spent 18h, Added support to href attributes. Links to downloaded files points to the proper downloaded file, links to the LeMill site are deleted and links to external site are kept intact. It is now possible to navigate from the collection's html file to its members' html file.

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.ATReferenceBrowserWidget.ATReferenceBrowserWidget import ReferenceBrowserWidget
21from Products.CMFCore.permissions import ModifyPortalContent
22from Products.Archetypes.public import BaseContent, registerType, BaseFolder
23from Products.Archetypes.public import StringField
24from Products.Archetypes.public import TextAreaWidget
25from Products.Archetypes.atapi import DisplayList
26from Globals import InitializeClass
27from Products.CMFCore.utils import getToolByName
28from AccessControl import ClassSecurityInfo, Unauthorized
29from SharedMetadata import score
30
31from config import PROJECTNAME, ALL_CONTENT_TYPES, CONTENT_TYPES, DEFAULT_ICONS, ACTIVITY_TYPES, TOOLS_TYPES
32from Resources import Resource
33from permissions import MODIFY_CONTENT
34
35import sre
36from cStringIO import StringIO
37import zipfile
38from urllib2 import urlopen, HTTPError, URLError
39
40schema = BaseSchema + score + Schema((
41
42    StringField('description',
43        accessor='Description',
44        widget=TextAreaWidget(
45            label="Learning and teaching story",
46            description="",
47            label_msgid='label_learning_story',
48            description_msgid='description_description',
49            i18n_domain = "lemill",
50            visible={'view':'invisible','edit':'visible'},
51        ),
52    ),
53   
54    ReferenceField('refsToResources',
55        accessor = 'getRefsToResources',
56        relationship = 'References',
57        mutator = 'addRefsToResources',
58        allowed_types = ALL_CONTENT_TYPES,
59        multiValued = True,
60        widget = ReferenceBrowserWidget(
61            visible = {'view':'invisible', 'edit':'invisible'},
62            ),
63    ),
64    ReferenceField('relatedContent',
65        relationship = 'relatesToContent',
66        multiValued = True,
67        isMetadata = True,
68        languageIndependent = False,
69        index = 'KeywordIndex',
70        write_permission = ModifyPortalContent,
71        allowed_types= ALL_CONTENT_TYPES,
72        widget = ReferenceBrowserWidget(
73            allow_search = True,
74            allow_browse = True,
75            show_indexes = False,
76            force_close_on_insert = True,
77            startup_directory = "content",
78            size = 4,
79            i18n_domain = "lemill",
80            label = "Related Content",
81            label_msgid = "label_related_content",
82            description = "",
83            description_msgid = "help_story_related_content",
84            visible = {'edit' : 'invisible', 'view' : 'invisible' }
85            )
86        ),
87    ReferenceField('relatedMethods',
88        relationship = 'relatesToMethods',
89        multiValued = True,
90        isMetadata = True,
91        languageIndependent = False,
92        index = 'KeywordIndex',
93        write_permission = ModifyPortalContent,
94        allowed_types=('Document','Activity',),
95        widget = ReferenceBrowserWidget(
96            allow_search = True,
97            allow_browse = True,
98            show_indexes = False,
99            force_close_on_insert = True,
100            startup_directory = "methods",
101            size = 4,
102            i18n_domain = "lemill",
103            label = "Related Methods",
104            label_msgid = "label_related_methods",
105            description = "",
106            description_msgid = "help_story_related_methods",
107            visible = {'edit' : 'invisible', 'view' : 'invisible' }
108            )
109        ),
110    ReferenceField('relatedTools',
111        relationship = 'relatesToTools',
112        multiValued = True,
113        isMetadata = True,
114        languageIndependent = False,
115        index = 'KeywordIndex',
116        write_permission = ModifyPortalContent,
117        allowed_types=('Document','Tool',),
118        widget = ReferenceBrowserWidget(
119            allow_search = True,
120            allow_browse = True,
121            show_indexes = False,
122            force_close_on_insert = True,
123            startup_directory = "tools",
124            size = 4,
125
126            i18n_domain = "lemill",
127            label = "Related Tools",
128            label_msgid = "label_related_tools",
129            description = "",
130            description_msgid = "help_story_related_tools",
131            visible = {'edit' : 'invisible', 'view' : 'invisible' }
132            )
133        ),
134    ComputedField('goodStory',
135        index = 'FieldIndex',
136        expression = 'here.isThisGoodStory()',
137        isMetadata = True
138        )
139))
140
141schema = schema.copy()
142
143class Collection(Resource):
144    """Collection"""
145   
146    schema = schema
147    actions= (
148    {
149    'id':'view',
150    'name':'view',
151    'action':'string:${object_url}/collection_view',
152    'permission':('View',),
153    },
154    {
155    'id':'edit',
156    'name':'Edit',
157    'action':'string:${object_url}/base_edit',
158    'permission':('View',),
159    },
160    )
161    meta_type = "Collection"
162    archetype_name = "Collection" 
163    typeDescription="Collection of resources."
164    typeDescMsgId='description_collection'
165    global_allow = 1
166    security = ClassSecurityInfo()
167    _at_rename_after_creation = True
168
169    def at_post_create_script(self):
170        self.at_post_edit_script()
171
172    def at_post_edit_script(self):
173        self._renameAfterCreation()
174        self.setGoodStory()
175
176    def after_add_rename(self):
177        self._renameAfterCreation()
178       
179    def setGoodStory(self):
180        """ Update goodStory -flag """
181        # after trying to get field and then setting the field by recalculating self.isThisGoodStory()
182        # it appears to be *always* up-to-date. (field.get()==self.isThisGoodStory())
183        # so the problem is indexing.
184        self.reindexObject()
185               
186#        field = self.getField('goodStory')
187#        wasgood=field.get(self)
188#        isgood= self.isThisGoodStory()
189#        print 'setting story %s. wasgood:%s isgood:%s' % (self.getId(), wasgood, isgood)
190#        ct = getToolByName(self,'portal_catalog')
191#        print ct({'getGoodStory':True})
192#        self.reindexObject()
193#        print ct({'getGoodStory':True})
194#        if wasgood!=isgood:
195#            field.set(self, isgood)
196#            self.reindexObject()
197#            ct = self.getToolByName('portal_catalog')
198           
199
200    def getLargestCount(self, reftype='relatedContent'):
201        field = self.Schema().get(reftype)
202        rtype=field.relationship
203        f = self.getReferenceImpl(relationship=rtype)
204
205        highest = 1001
206        for a in f:
207            try:
208                highest = a.collection_position
209            except AttributeError:
210                pass
211        highest += 100
212        return highest
213
214
215    security.declareProtected(MODIFY_CONTENT,'addRelatedContent')
216    def addRelatedContent(self, ref, empty=0):
217        """ add reference to content """
218        self.addRefsToResources(ref, empty, reftype="relatedContent")
219        self.setGoodStory()
220
221    security.declareProtected(MODIFY_CONTENT,'addRelatedMethods')
222    def addRelatedMethods(self, ref, empty=0):
223        """ add reference to methods """
224        self.addRefsToResources(ref, empty, reftype="relatedMethods")
225        self.setGoodStory()
226
227    security.declareProtected(MODIFY_CONTENT,'addRelatedTools')
228    def addRelatedTools(self, ref, empty=0):
229        """ add reference to tools """
230        self.addRefsToResources(ref, empty, reftype="relatedTools")
231        self.setGoodStory()
232
233    security.declareProtected(MODIFY_CONTENT,'addRefsToResources')
234    def addRefsToResources(self, ref, empty=0, reftype=None):
235        """ add reference to resource """
236        from types import ListType, TupleType
237        if reftype:
238            field = self.Schema().get(reftype)
239        else:
240            if type(ref) == ListType or type(ref) == TupleType:
241                for x in ref: # if entry is a list of mixed type entries, run this method again for each component.
242                    self.addRefsToResources(x)
243                return 1
244            uc=getToolByName(self, 'uid_catalog')
245            obj=uc(UID=ref)
246            objtype=obj[0].portal_type
247            if objtype in CONTENT_TYPES:
248                reftype='relatedContent'
249            elif objtype in ACTIVITY_TYPES:
250                reftype='relatedMethods'
251            elif objtype in TOOLS_TYPES:
252                reftype='relatedTools'
253            else:
254                reftype='refsToResources'
255            field = self.Schema().get(reftype)
256
257        prev = field.getRaw(self)
258        new_value = []
259        if type(ref) == ListType or type(ref) == TupleType:
260            new_value = prev
261            if empty:
262                new_value = []
263            for x in ref:
264                new_value.append(x)
265        else:
266            new_value = prev+[ref,]
267        field.set(self, new_value, collection_position=self.getLargestCount(reftype))
268        self.setGoodStory()
269
270    def isThisGoodStory(self):
271        """ Check if there is content,method,tool and description """       
272        desc=self.Description()
273        c=self.getRelatedContent()
274        m=self.getRelatedMethods()
275        t=self.getRelatedTools()
276        wtool=getToolByName(self, 'portal_workflow')
277        r= wtool.getInfoFor(self,'review_state',None)!='deleted'
278        if desc and c and m and t and r:
279            return True
280        else:
281            return False
282
283    def getItemCount(self):
284        """ return how many items are in collection """
285        return len(self.getRelatedMethods())+len(self.getRelatedTools())+len(self.getRelatedContent())
286
287
288    def getRelatedContent(self):
289        """ make sure that related content comes in correct order even when programmer forgets to ask for it"""
290        return self.getResources()
291
292    def getSortedList(self, reftype='relatedContent'):
293        #f = self.getField('refsToResources')
294        field = self.Schema().get(reftype)
295        rtype=field.relationship
296        f = self.getReferenceImpl(relationship=rtype)
297        arr = []
298        err_base = 12345123
299        for a in f:
300            try:
301                i = a.collection_position
302            except AttributeError:
303                i = err_base + 100
304                a.collection_position = i
305            arr.append([i, a])
306        arr.sort()
307        return arr
308
309    def getBlurp(self):
310        """ Some 1000 chars or something FIXME TO FIND NICE PLACES TO CUT (<p><br> etc.)"""
311        lt = getToolByName(self, 'lemill_tool')
312        return lt.split_at_p_or_br(lt.shorten_link_names(lt.htmlify(self.Description())))
313
314    def getResources(self, reftype='relatedContent'):
315        """ ... """
316        #return self.getRefsToResources()
317        sorted = self.getSortedList(reftype)
318        arr = []
319        for x in sorted:
320            arr.append(x[1].getTargetObject())
321        # Only Tools and Methods should get sorted by name
322        if reftype != 'relatedContent':
323            mod_resources = [(x.Title(), x) for x in arr]
324            mod_resources.sort()
325            arr = [x[1] for x in mod_resources]
326        return arr
327
328
329    security.declareProtected(MODIFY_CONTENT,'delContent')
330    def delContent(self, objid,REQUEST=None):
331        """ front for delResources """
332        if objid:
333            self.delResources(objid, reftype='relatedContent')
334        if REQUEST:
335            return REQUEST.RESPONSE.redirect(self.absolute_url() + "/collection_edit")
336
337    security.declareProtected(MODIFY_CONTENT,'delMethods')
338    def delMethods(self, objid,REQUEST=None):
339        """ front for delResources """
340        if objid:
341            self.delResources(objid, reftype='relatedMethods')
342        if REQUEST:
343            return REQUEST.RESPONSE.redirect(self.absolute_url() + "/collection_edit")
344
345    security.declareProtected(MODIFY_CONTENT,'delTools')
346    def delTools(self, objid,REQUEST=None):
347        """ front for delResources """
348        if objid:
349            self.delResources(objid, reftype='relatedTools')
350        if REQUEST:
351            return REQUEST.RESPONSE.redirect(self.absolute_url() + "/collection_edit")
352
353    security.declareProtected(MODIFY_CONTENT,'delResources')
354    def delResources(self, objid, reftype='relatedContent'):
355        """ delete selected resource """
356        field = self.Schema().get(reftype)
357        objlist = field.get(self)
358
359        newlist = [o.UID() for o in objlist if o.id!=objid]
360        field.set(self, newlist)
361        # if collection is empty, delete it too
362#        if new == []:
363#            self.collections.delCollection(self.getId())
364#            return True
365#        else:
366#            return False
367        self.setGoodStory()
368        return False
369
370    def getCollectionContent(self):
371        """ Returns a list of content, methods and tools, ordered by their modification date """
372        objects=self.getRelatedContent()+self.getRelatedMethods()+self.getRelatedTools()
373        objects = [(x.ModificationDate(), x) for x in objects]
374        objects.sort()
375        return [x[1] for x in objects]
376       
377
378
379    security.declareProtected(MODIFY_CONTENT,'moveUpContent')
380    def moveUpContent(self, objid,REQUEST=None):
381        """ Front for more general moveUpResources """
382        if objid:
383            self.moveUpResources(objid, reftype='relatedContent')
384        return REQUEST.RESPONSE.redirect(self.absolute_url() + "/collection_edit")
385
386    security.declareProtected(MODIFY_CONTENT,'moveUpResources')
387    def moveUpResources(self, objid, reftype='relatedContent'):
388        """ move up """
389        field = self.getField(reftype)
390        sorted = self.getSortedList(reftype)
391        cur_pos = 0
392        count = 0
393        for x in sorted:
394            if x[1].getTargetObject().id == objid:
395                cur_pos = count
396                break
397            count += 1
398        if cur_pos > 0:
399            prev_uid = sorted[cur_pos-1][1].getTargetObject().UID()
400            prev_pos = sorted[cur_pos-1][1].collection_position
401            curr_uid = sorted[cur_pos][1].getTargetObject().UID()
402            curr_pos = sorted[cur_pos][1].collection_position
403
404            sorted.remove(sorted[cur_pos])
405            sorted.remove(sorted[cur_pos-1])
406            new_list = []
407            [ new_list.append(x[1].getTargetObject()) for x in sorted ]
408           
409            field.set(self, new_list)
410            new_list.append(curr_uid)
411            field.set(self, new_list, collection_position=prev_pos)
412            new_list.append(prev_uid)
413            field.set(self, new_list, collection_position=curr_pos)
414            #new_list = []
415            #for x in sorted:
416            #    new_list.append(x[1].getTargetObject())
417            #field.set(self, new_list)
418
419
420    security.declareProtected(MODIFY_CONTENT,'moveDownContent')
421    def moveDownContent(self, objid,REQUEST=None):
422        """ Front for more general moveDownResources """
423        if objid:
424            self.moveDownResources(objid, reftype='relatedContent')
425        return REQUEST.RESPONSE.redirect(self.absolute_url() + "/collection_edit")
426
427
428    security.declareProtected(MODIFY_CONTENT,'moveDownResources')
429    def moveDownResources(self, objid, reftype='relatedContent'):
430        """ move down """
431        field = self.getField(reftype)
432        sorted = self.getSortedList(reftype=reftype)
433        cur_pos = 0
434        count = 0
435        for x in sorted:
436            if x[1].getTargetObject().id == objid:
437                cur_pos = count
438                break
439            count += 1
440        if cur_pos+1 < len(sorted):
441            next_uid = sorted[cur_pos+1][1].getTargetObject().UID()
442            next_pos = sorted[cur_pos+1][1].collection_position
443            curr_uid = sorted[cur_pos][1].getTargetObject().UID()
444            curr_pos = sorted[cur_pos][1].collection_position
445
446            sorted.remove(sorted[cur_pos+1])
447            sorted.remove(sorted[cur_pos])
448            new_list = []
449            [ new_list.append(x[1].getTargetObject()) for x in sorted ]
450           
451            field.set(self, new_list)
452            new_list.append(curr_uid)
453            field.set(self, new_list, collection_position=next_pos)
454            new_list.append(next_uid)
455            field.set(self, new_list, collection_position=curr_pos)
456
457    def amIOwner(self):
458        """ check owner of object """
459        roles = self.portal_membership.getAuthenticatedMember().getRolesInContext(self)
460        return 'Owner' in roles
461
462
463    def manage_afterAdd(self, item, container):
464        #Replaces the left side portlets with the content type's own action portlet.
465        BaseContent.manage_afterAdd(self, item, container)
466        if not hasattr(item.aq_base, 'left_slots'):
467            self._setProperty('left_slots', ['here/portlet_%s_actions/macros/portlet' % item.meta_type.lower(),], 'lines')
468
469    security.declareProtected(MODIFY_CONTENT,'cleanRefsToResources')
470    def cleanRefsToResources(self):
471        """ Check if (deprecated) RefsToResources contains stuff that should be in relatedContent etc. """
472        if not self.Schema().get('relatedContent'): # update schema if old type collection
473            #print 'schema update required for %s' % self.getId()
474            self._updateSchema()
475        objlist=self.getRefsToResources()
476        field=self.getField('refsToResources')
477        newlist=[]
478        for o in objlist:
479            objtype=o.meta_type
480            if objtype in CONTENT_TYPES:
481                reftype='relatedContent'
482            elif objtype in ACTIVITY_TYPES:
483                reftype='relatedMethods'
484            elif objtype in TOOLS_TYPES:
485                reftype='relatedTools'
486            else:
487                reftype='keeper'
488
489            if reftype=='keeper':
490                newlist.append(o.UID())
491            else:
492                self.addRefsToResources(o.UID(),reftype=reftype)
493        field.set(self, newlist)
494
495    def getLearningStoryText(self):
496        """ will return the nice version for the story text """
497        lt = getToolByName(self, 'lemill_tool')
498        return lt.shorten_link_names(lt.htmlify(self.Description()))
499
500#Zip / SCORM download stuff begin --------------------------------------------------------------------------------
501    def _splitHTML(self, htmlPage, pageURL):
502
503        def flatten(seq):
504            res = []
505            for item in seq:
506                if isinstance(item, (list, tuple)):
507                    res.extend(flatten(item))
508                else:
509                    res.append(item)
510            return res
511
512        def combineNones(inputList, infoList):
513            previousInfo = ''
514            text = ''
515            list = range(len(infoList))
516            list.reverse()
517            for i in list:
518                if infoList[i] == None:
519                    if previousInfo == None:
520                        text = inputList[i] + text
521                    else:
522                        previousInfo = None
523                        text = inputList[i]
524                    del infoList[i]
525                    del inputList[i]
526                elif previousInfo == None:
527                    previousInfo = ''
528                    infoList.insert(i + 1, None)
529                    inputList.insert(i + 1, text)
530            if previousInfo == None:
531                infoList.insert(0, None)
532                inputList.insert(0, text)
533
534        def reqursiveRegularSplit(inputList, infoList, info, reg, first, next):
535            if isinstance(inputList, (list, tuple)):
536                for i in range(len(inputList)):
537                    if not isinstance(infoList[i], str):
538                        inputList[i], infoList[i] = reqursiveRegularSplit(inputList[i], infoList[i], info, reg, first, next)
539            else:
540                reg = sre.compile(reg, sre.DOTALL | sre.IGNORECASE)
541                inputList = sre.split(reg, inputList)
542                if len(inputList) > 1:
543                    infoList = []
544                    for i in range(len(inputList)):
545                        if i % next == first:
546                            infoList.append(info)
547                        else:
548                            infoList.append(None)
549                    combineNones(inputList, infoList)
550                else:
551                    infoList = [infoList]
552            return inputList, infoList
553
554        splitHtml, infoList = reqursiveRegularSplit(htmlPage, None, 'base', '<base href="(.*?)".*?/>', 1, 2)
555        if len(splitHtml) > 1:
556            baseURL = splitHtml[-2]
557            del splitHtml[1::2]
558            del infoList[1::2]
559        else:
560            baseURL = pageURL
561        if not baseURL.endswith('/'):
562            baseURL += '/'
563
564        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'src', '(<link[^>]+href=")(.*?)("[^>]+rel="stylesheet")', 2, 4)
565        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'href', '(<a[^>]+)href="(.*?)"', 2, 3)
566        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'fvars', '(<embed[^>]*?flashvars=")(.*?)(")', 2, 4)
567        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'XMLmp3', '(<voiceover[^>]+src=")(.*?)(")', 2, 4)
568        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'src', '(<[^>]+src=")(.*?)(")', 2, 4)
569        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'src', '(<param.+?name="movie".*?value=")(.*?)(")', 2, 4)
570        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'fvars', r"(<script.*?>.*?AC_FL_RunContent\(.*?'flashvars', ')(.*?)((?<!\\)'.*?</script>)", 2, 4)
571        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'jsfname', "(<script.*?>.*?AC_FL_RunContent\(.*?'movie', ')(.*?)(')", 2, 4)
572        splitHtml, infoList = reqursiveRegularSplit(splitHtml, infoList, 'fvars', '(<param.+?name="flashvars".*?value=")(.*?)(")', 2, 4)
573
574        return flatten(splitHtml), flatten(infoList), baseURL
575
576    def _getAbsoluteURL(self, url, info, baseURL=''):
577        flash = None
578        if info in ('src', 'jsfname', 'XMLmp3', 'href'):
579            if not url.startswith('http://'):
580                url = baseURL + url
581            if info == 'jsfname':
582                url += '.swf'
583        elif info == 'fvars':
584            if url.startswith('file='):
585                url = url[5:-5]
586                flash = 'mp3'
587            elif url.startswith('xml='):
588                url = url[4:]
589                flash = 'pilot'
590            elif url.startswith("config={videoFile: '"):
591                url = url[20:-2]
592                flash = 'flv1'
593            elif url.startswith("config={videoFile: \\'"):
594                url = url[21:-3]
595                flash = 'flv2'
596        url = url.replace('\\', '/')
597        return url, flash
598
599    def _splitFileName(self, fileName):
600        splitted = fileName.split('.')
601        if len(splitted) > 1:
602            return '.'.join(splitted[:-1]), '.' + splitted[-1]
603        return fileName, ''
604
605    def _hasFileNameInSrcs(self, fileName, srcs):
606        for absURL in srcs:
607            src = srcs[absURL]
608            if src.has_key('fileName') and src['fileName'] == fileName:
609                return src
610        return None
611
612    def _findUniqueFileName(self, absURL, srcs):
613        fileName = absURL.split('?')[0].split('/')[-1]
614        src2 = self._hasFileNameInSrcs(fileName, srcs)
615        if src2 == None:
616            return fileName
617        else:
618            src2['counter'] += 1
619            fileName, extension = self._splitFileName(fileName)
620            return self._findUniqueFileName('%s(%d)%s' % (fileName, src2['counter'], extension), srcs)
621
622    def _processHtml(self, splitHtml, infoList, srcs, hrefs, baseURL, htmlIndex):
623        for i in range(len(infoList)):
624            if infoList[i] in ('src', 'jsfname', 'fvars', 'XMLmp3', 'href'):
625                absURL, flash = self._getAbsoluteURL(splitHtml[i], infoList[i], baseURL)
626                if infoList[i] != 'href' or absURL.find('at_download') != -1:
627                    if srcs.has_key(absURL):
628                        srcs[absURL]['usedBy'].add(htmlIndex)
629                    else:
630                        srcs[absURL] = {}
631                        src = srcs[absURL]
632                        src['fileName'] = self._findUniqueFileName(absURL, srcs)
633                        src['finalURL'] = ''
634                        src['usedBy'] = set([htmlIndex])
635                        src['counter'] = 0
636                        if flash == 'mp3':
637                            src['extension'] = 'mp3'
638                        elif flash == 'pilot':
639                            src['extension'] = 'xml'
640                        elif flash in ('flv1', 'flv2'):
641                            src['extension'] = 'flv'
642                        elif infoList[i] == 'XMLmp3':
643                            src['extension'] = 'mp3'
644                        else:
645                            src['extension'] = ''
646            if infoList[i] == 'href':
647                absURL, flash = self._getAbsoluteURL(splitHtml[i], infoList[i], baseURL)
648                if hrefs.has_key(splitHtml[i]):
649                    hrefs[absURL]['usedBy'].add(htmlIndex)
650                else:
651                    hrefs[absURL] = {}
652                    href = hrefs[absURL]
653                    href['finalURL'] = ''
654                    href['type'] = 0
655                    href['usedBy'] = set([htmlIndex])
656
657    def _processPilotXMLs(self, srcs, baseURLs, baseDirs):
658        xmlDatas = {}
659        xmlList = [absURL for absURL in srcs if srcs[absURL]['extension'] == 'xml']
660        for absURL in xmlList:
661            try:
662                src = srcs[absURL]
663                file = urlopen(absURL)
664                xml = file.read().decode('utf-16')
665                file.close()
666                xmlDatas[absURL]={}
667                xmlData = xmlDatas[absURL]
668                xmlData['split'], xmlData['info'], baseURL = self._splitHTML(xml, '')
669                xmlData['fileName'] = src['fileName']
670                xmlData['baseDirs'] = []
671                for htmlIndex in src['usedBy']:
672                    self._processHtml(xmlData['split'], xmlData['info'], srcs, {}, baseURLs[htmlIndex], htmlIndex)
673                    xmlData['baseDirs'].append(baseDirs[htmlIndex])
674            except (HTTPError, URLError):
675                print "File download error: " + absURL
676        return xmlDatas
677
678    def _downloadFiles(self, srcs, zip, baseDirs = []):
679        def _addExtension(fileName, extension):
680            name, ext = self._splitFileName(fileName)
681            if ext == '' and extension != '':
682                fileName = '%s.%s' % (fileName, extension)
683            return fileName
684        for absURL in srcs:
685            src = srcs[absURL]
686            data = ''
687            mediaType = ''
688            subType = ''
689            try:
690                if src['extension'] != 'xml':
691                    file = urlopen(absURL)
692                    data = file.read()
693                    mediaType, subType = file.headers.getheader('Content-Type').split('/')
694                    file.close()
695                    subType = subType.split(';')[0]
696                    if mediaType == 'image':
697                        if subType in ('jpeg', 'pjpeg'):
698                           src['extension'] = 'jpg'
699                        elif subType == 'x-ms-bmp':
700                           src['extension'] = 'bmp'
701                        elif subType in ('png', 'gif'):
702                            src['extension'] = subType
703                        elif subType == 'x-png':
704                            src['extension'] = 'png'
705                    if mediaType == 'application' and subType == 'x-shockwave-flash':
706                        src['extension'] = 'swf'
707                    if mediaType == 'video':
708                        if subType in ('x-msvideo', 'avi'):
709                            src['extension'] = 'avi'
710                        elif subType == 'x-ms-wmv':
711                            src['extension'] = 'wmv'
712                        elif subType == 'mpeg':
713                            src['extension'] = 'mpg'
714                        elif subType == 'mp4':
715                            src['extension'] = subType
716                        elif subType == 'quicktime':
717                            src['extension'] = 'mov'
718            except (HTTPError, URLError):
719                print "File download error: " + absURL
720
721            fileName = _addExtension(src['fileName'], src['extension'])
722            if len(src['usedBy']) > 1:
723                src['finalURL'] = '_SharedFiles/' + fileName
724                if src['extension'] != 'xml':
725                    zip.writestr((src['finalURL']).encode('latin-1'), data)
726            else:
727                src['finalURL'] = '_Files/' + fileName
728                if src['extension'] != 'xml':
729                    for i in src['usedBy']:
730                        zip.writestr((baseDirs[i] + src['finalURL']).encode('latin-1'), data)
731
732    def _processHrefs(self, hrefs, srcs, htmlURLs, baseDirs, portalURL):
733        downloadURLs = []
734        for absURL in srcs:
735            fileName = absURL.split('?')[0].split('/')
736            domain = '/'.join(fileName[:-1])
737            fileName = fileName[-1]
738            downloadURLs.append('%s/at_download/%s' % (domain, fileName))
739        for absURL in hrefs:
740            href = hrefs[absURL]
741            if absURL in htmlURLs:
742                href['finalURL'] = 'href="%sindex.html"' % baseDirs[htmlURLs.index(absURL)]
743            elif absURL + '/' in htmlURLs:
744                href['finalURL'] = 'href="%sindex.html"' % baseDirs[htmlURLs.index(absURL + '/')]
745            elif absURL in srcs:
746                href['finalURL'] = srcs[absURL]['finalURL']
747                href['type'] = 1
748            elif absURL in downloadURLs:
749                href['finalURL'] = srcs[absURL.replace('/at_download', '')]['finalURL']
750                href['type'] = 1
751            elif absURL.startswith(portalURL):
752                href['finalURL'] = ''
753            else:
754                href['finalURL'] = 'href="%s"' % absURL
755
756    def _updateHtmls(self, splitHtmls, infoLists, baseURLs, baseDirs, srcs, hrefs):
757        for i in range(len(splitHtmls)):
758            splitHtml = splitHtmls[i]
759            infoList = infoLists[i]
760            for j in range(len(splitHtml)):
761                if infoList[j] in ('src', 'jsfname', 'fvars'):
762                    absURL, flash = self._getAbsoluteURL(splitHtml[j], infoList[j], baseURLs[i])
763                    src = srcs[absURL]
764                    if baseDirs[i] != '' and len(src['usedBy']) > 1:
765                        finalURL = '../../' + src['finalURL']
766                    else:
767                        finalURL = src['finalURL']
768                    if infoList[j] == 'src':
769                        splitHtml[j] = finalURL
770                    elif infoList[j] == 'jsfname':
771                        splitHtml[j] = '.'.join(finalURL.split('.')[:-1])
772                    elif infoList[j] == 'fvars':
773                        if flash == 'mp3':
774                            splitHtml[j] = 'file=%s' % finalURL
775                        elif flash == 'pilot':
776                            splitHtml[j] = 'xml=%s' % finalURL
777                        elif flash == 'flv1':
778                            splitHtml[j] = "config={videoFile: '../%s'}" % finalURL
779                        elif flash == 'flv2':
780                            splitHtml[j] = "config={videoFile: \\'../%s\\'}" % finalURL
781                elif infoList[j] == 'href':
782                    absURL, flash = self._getAbsoluteURL(splitHtml[j], infoList[j], baseURLs[i])
783                    href = hrefs[absURL]
784                    if href['type'] == 1:
785                        if baseDirs[i] != '' and len(href['usedBy']) > 1:
786                            finalURL = 'href="../../%s"' % href['finalURL']
787                        else:
788                            finalURL = 'href="%s"' % href['finalURL']
789                    else:
790                        finalURL = href['finalURL']
791                    splitHtml[j] = finalURL
792
793    def _updatePilotXMLs(self, xmlDatas, baseURLs, srcs):
794        for absURL in xmlDatas:
795            splitXML = xmlDatas[absURL]['split']
796            xmlInfoList = xmlDatas[absURL]['info']
797            for j in range(len(splitXML)):
798                if xmlInfoList[j] in ('src', 'XMLmp3'):
799                    absURL2, flash = self._getAbsoluteURL(splitXML[j], xmlInfoList[j])
800                    src = srcs[absURL2]
801                    if len(src['usedBy']) > 1:
802                        splitXML[j] = '../../' + src['finalURL']
803                    else:
804                        splitXML[j] = src['finalURL']
805
806    def _addHtmlsToZip(self, splitHtmls, zip, baseDirs = None):
807        i = 0
808        baseDir = ''
809        for splitHtml in splitHtmls:
810            if isinstance(baseDirs, (list, tuple)):
811                baseDir = baseDirs[i]
812            zip.writestr(baseDir + 'index.html', ''.join(splitHtml))
813            i += 1
814
815    def _addPilotXMLsToZip(self, xmlDatas, srcs, zip):
816        for absURL in xmlDatas:
817            xmlData = xmlDatas[absURL]
818            xml = ''.join(xmlData['split']).encode('utf-16')
819            for baseDir in xmlData['baseDirs']:
820                zip.writestr(baseDir + srcs[absURL]['finalURL'], xml)
821                break
822
823    def buildZip(self):
824        """ Builds a Zip """
825        splitHtmls = []
826        infoLists = []
827        baseURLs = []
828        baseDirs = []
829        htmlURLs = []
830        srcs = {}
831        hrefs = {}
832        htmlIndex = 0
833        resourceTypes = ('Content', 'Methods', 'Tools')
834        htmlList = [(self, '')]
835        for resourceType in resourceTypes:
836            for resource in self.getResources(reftype = 'related%s' % resourceType):
837                htmlList.append((resource, '%s/%s/' % (resourceType.lower(), resource.getId())))
838
839        for html in htmlList:
840            splitHtml, infoList, baseURL = self._splitHTML(html[0].standalone_view(), self.portal_url())
841            splitHtmls.append(splitHtml)
842            infoLists.append(infoList)
843            baseURLs.append(baseURL)
844            baseDirs.append(html[1])
845            htmlURLs.append(html[0].absolute_url() + '/')
846            self._processHtml(splitHtml, infoList, srcs, hrefs, baseURL, htmlIndex)
847            htmlIndex += 1
848
849        xmlDatas = self._processPilotXMLs(srcs, baseURLs, baseDirs)
850        zipStr = StringIO()
851        zip = zipfile.ZipFile(zipStr, 'w', compression = zipfile.ZIP_DEFLATED)
852        self._downloadFiles(srcs, zip, baseDirs)
853        self._processHrefs(hrefs, srcs, htmlURLs, baseDirs, self.portal_url())
854        self._updateHtmls(splitHtmls, infoLists, baseURLs, baseDirs, srcs, hrefs)
855        self._updatePilotXMLs(xmlDatas, baseURLs, srcs)
856        self._addHtmlsToZip(splitHtmls, zip, baseDirs)
857        self._addPilotXMLsToZip(xmlDatas, srcs, zip)
858        zip.close()
859        response = self.REQUEST.RESPONSE
860        response.setHeader('Content-Type', 'application/zip')
861        response.setHeader('Content-Disposition', 'attachment; filename="%s.zip"' % self.getId())
862        return zipStr.getvalue()
863#Zip / SCORM download sruff end ----------------------------------------------------------------------------------
864
865    #XXX These additional meta-type='' and obj=None might not be needed, but I will leave them here just to be on the safe side.
866    def getDefaultIcon(self, meta_type='', obj=None):
867        """ Will return a portrait of the creator or his default icon """
868        owner = self.Creator()
869        mtool = getToolByName(self, 'portal_membership')
870        member_folder = mtool.getHomeFolder(owner)
871        given_image = member_folder.getCoverImageURL()
872        return given_image
873       
874
875registerType(Collection, PROJECTNAME)
876
877class CollectionsFolder(BaseFolder):
878    """ container for collection """
879    schema = BaseSchema
880    id = "collections"
881    meta_type = "CollectionsFolder"
882    archetype_name = "CollectionsFolder" 
883
884    actions= (
885    {
886    'id':'view',
887    'name':'view',
888    'action':'string:${object_url}/collections_list',
889    'permission':('View',),
890    },
891    )
892               
893registerType(CollectionsFolder, PROJECTNAME)
Note: See TracBrowser for help on using the repository browser.