source: Products.ecmigration/trunk/Products/ecmigration/migrate.py @ 862

Revision 862, 29.2 KB checked in by brent, 3 years ago (diff)

Added the ability to import filenames only to fix migrations where the filenames were missing.

Line 
1# -*- coding: us-ascii -*-
2# _______________________________________________________________________
3#              __________                      .__       
4#   ____   ____\______   \____________  ___  __|__| ______
5# _/ __ \ /    \|     ___/\_  __ \__  \ \  \/  /  |/  ___/
6# \  ___/|   |  \    |     |  | \// __ \_>    <|  |\___ \
7#  \___  >___|  /____|     |__|  (____  /__/\_ \__/____  >
8#      \/     \/                      \/      \/       \/
9# _______________________________________________________________________
10#
11#    This file is part of the eduCommons software package.
12#
13#    Copyright (c) 2011 enPraxis, LLC
14#    http://enpraxis.net
15#
16#    This program is free software; you can redistribute it and/or modify
17#    it under the terms of the GNU General Public License as published by
18#    the Free Software Foundation, version 2.8 
19#
20#    This program is distributed in the hope that it will be useful,
21#    but WITHOUT ANY WARRANTY; without even the implied warranty of
22#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23#    GNU General Public License for more details.
24#
25#    You should have received a copy of the GNU General Public License
26#    along with this program; if not, write to the Free Software
27#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28# _______________________________________________________________________
29
30__author__ = 'Brent Lambert'
31__version__ = '$ Revision 0.0 $'[11:-2]
32
33
34from Products.CMFPlone.utils import _createObjectByType
35from Products.CMFCore.WorkflowCore import WorkflowException
36from cStringIO import StringIO
37from datetime import datetime
38from urlparse import urlparse, urlunparse
39from BeautifulSoup import BeautifulSoup
40import Globals
41import transaction
42import exceptions
43import cPickle as Pickle
44import Globals
45import os
46import tarfile
47import tempfile
48import logging
49
50
51# Try to import annotation interfaces
52
53try:
54    from collective.contentlicensing.DublinCoreExtensions.interfaces import ILicensable, ILicense
55except ImportError:
56    from Products.ContentLicensing.DublinCoreExtensions.interfaces import ILicensable, ILicense
57
58try:
59    from enpraxis.educommons.annotations.interfaces import IClearCopyrightable, IClearCopyright
60    has_clearcopyrightable = True
61except ImportError:
62    try:
63        from enpraxis.educommons.interfaces import IClearCopyrightable, IClearCopyright
64        has_clearcopyrightable = True
65    except ImportError:
66        has_clearcopyrightable = False
67   
68
69try:
70    from enpraxis.educommons.annotations.interfaces import IAccessibilityCompliantable, IAccessibilityCompliant
71    has_accessibilitycompliance = True
72except ImportError:
73    try:
74        from enpraxis.educommons.interfaces import IAccessibilityCompliantable, IAccessibilityCompliant
75        has_accessibilitycompliance = True
76    except ImportError:
77        has_accessibilitycompliance = False
78
79try:
80    from enpraxis.educommons.annotations.interfaces import ICourseOrderable
81    has_courseorderable = True
82except ImportError:
83    try:
84        from enpraxis.educommons.interfaces import ICourseOrderable
85        has_courseorderable = True
86    except ImportError:
87        has_courseorderable = False
88
89try:
90    from zope.annotation.interfaces import IAnnotations
91    has_annotations = True
92except ImportError:
93    has_annotations = False
94
95
96# Migration logging
97
98def getBaseDirectory():
99    """ Get the base directory for migration files """
100    try:
101        cwd = Globals.data_dir
102    except AttributeError:
103        cwd = os.getcwd()    # needed for eduCommons 4.0.0
104    basedir = os.path.join(cwd, 'migration')
105    if not os.path.exists(basedir):
106        os.mkdir(basedir)
107    return basedir
108
109def setupLogging(logger):
110    """ Set up logging for the migration """
111    base = getBaseDirectory()
112    logfn = os.path.join(base, 'ecmigration.log')
113    handler = logging.FileHandler(logfn)
114    logfmt = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
115    handler.setFormatter(logfmt)
116    logger.addHandler(handler)
117    logger.setLevel(logging.INFO)
118   
119   
120ecmlogger = logging.getLogger('ecmigration')
121setupLogging(ecmlogger)
122
123
124class MigrationException(exceptions.Exception):
125    """ Migration Error Exception """
126
127    def __init__(self, value):
128        self.value = value
129
130    def __str__(self):
131        return repr(self.value)
132
133
134class FileArchiveManager(object):
135    """ File based archive manager """
136
137    def __init__(self, base, portalid, log):
138        self.base = base
139        portal = os.path.join(self.base, portalid)
140        if not os.path.exists(portal):
141            os.mkdir(portal)
142        self.log = log
143
144    def addFile(self, fn, data):
145        """ Add a file to the archive from a string """
146        self.log.debug(fn)
147        fname = os.path.join(self.base, fn)
148        path = os.path.split(fname)[:-1][0]
149        if not os.path.exists(path):
150            self.mkdir(self.base, fn.split(os.sep)[:-1])
151        f = open(fname, 'wb')
152        f.write(data)
153        f.close()
154
155    def addFileOpen(self, fn):
156        """ Open a file for writing """
157        self.log.debug(fn)
158        fname = os.path.join(self.base, fn)
159        path = os.path.split(fname)[:-1][0]
160        if not os.path.exists(path):
161            self.mkdir(self.base, fn.split(os.sep)[:-1])
162        return open(fname, 'wb')
163
164    def addFileWrite(self, f, data):
165        """ Write data to the file """
166        f.write(data)
167
168    def addFileClose(self, f):
169        """ Close the file """
170        f.close()
171
172    def getFile(self, fn):
173        fname = os.path.join(self.base, fn)
174        f = open(fname, 'rb')
175        data = f.read()
176        f.close()
177        return data
178
179    def getFileOpen(self, fn):
180        fname = os.path.join(self.base, fn)
181        return open(fname, 'rb')
182
183    def getFileClose(self, f):
184        f.close()
185
186    def mkdir(self, path, folders):
187        if folders:
188            p = os.path.join(path, folders[0])
189            fp = os.path.join(self.base, p)
190            if not os.path.exists(fp):
191                os.mkdir(fp)
192            self.mkdir(p, folders[1:])
193                             
194       
195
196
197class TarArchiveManager(object):
198    """ Archive data using tarfile """
199
200    def __init__(self, log, archname, mode='w:bz2'):
201        """ Open the tar file """
202        self.log = log
203        self.tar_archive = tarfile.open(archname, mode)
204
205    def addFileFromString(self, fn, data, lmod):
206        """ Write a file from a string to the archive """
207        tinfo = tarfile.TarInfo(fn)
208        fdata = StringIO(data)
209        fdata.seek(0, 2)
210        tinfo.size = fdata.tell()
211        fdata.seek(0)
212        tinfo.mtime = lmod
213        self.tar_archive.addfile(tinfo, fdata)
214        # Circumvent caching for large file handling
215        self.tar_archive.members = []
216        self.log.debug(fn)
217
218    def addFileFromDisk(self, fn, arcname):
219        """ Write a file from the filesystemto the archive """
220        self.tar_archive.add(fn, arcname)
221        self.log.debug(arcname)
222
223    def getNextFileInfo(self):
224        """ Return file information or None if no files left """
225        return self.tar_archive.next()
226
227    def readFile(self, info):
228        f = self.tar_archive.extractfile(info)
229        data = f.read()
230        f.close()
231        # Circumvent caching for large file handling
232        self.tar_archive.members = []
233        return data
234
235    def getFile(self, info):
236        f = self.tar_archive.extractfile(info)
237        self.tar_archive.members = []
238        return f
239
240    def close(self):
241        """ Close the tar file """
242        self.tar_archive.close()
243       
244
245class ECMigration:
246    """ Migrate an eduCommons site """
247
248    transforms = {
249        'ECDepartment':'Division',
250        'ECCourse':'Course',
251        'ECDocument':'Document',
252        'ECFile':'File',
253        'ECFolder':'Folder',
254        'ECImage':'Image',
255        'ECLink':'Link',
256        'GFolder':'Folder',
257        'GDocument':'Document',
258        'GFile':'File',
259        'GImage':'Image',
260        'GLink':'Link',
261        'FSSFile':'File',
262        }
263
264    disallowed_types = [
265        'ECLogFolder',
266        'ECLog'
267        ]
268
269    id_transforms = {
270        'About':'about',
271        'Help':'help',
272        'terms_of_use':'terms-of-use',
273        'privacy_policy':'privacy-policy'
274        }
275
276    state_transforms = {
277        'Visible':'Published',
278        'published':'Published',
279        'Hidden':'InProgress',
280        }
281
282    def __init__(self, context):
283        self.context = context
284        self.pw = context.portal_workflow
285        self.imp = 0
286        self.exp = 0
287        self.base = getBaseDirectory()
288        self.logger = ecmlogger
289        self.crosslistings = []
290
291    def importFilenames(self):
292        portal = self.context.portal_url.getPortalObject()
293        archive = FileArchiveManager(self.base, portal.getId(), self.logger)
294        filelistfn = os.path.join(portal.getId(), 'filelist.ecmigration')
295        files = self._getMetadata(filelistfn, archive)
296        for x in files:
297            ext = x.split('.')[-1]
298            if 'ecmigration_metadata' == ext:
299                data = self._getMetadata(x, archive)
300                if data['type'] in ['File', 'Image']:
301                    if data.has_key('originfn'):
302                        oid = x.split('.' + ext)[0]
303                        oid = '/'.join(oid.split(os.sep))
304                        obj = self._getObjectByPath(oid)
305                        if obj:
306                            if not obj.getFilename():
307                                obj.setFilename(data['originfn'])
308
309    def importContent(self):
310        """ Import a migration """
311        self.crosslistings = []
312        self.logger.info('Starting Import')
313        starttime = datetime.now()
314        portal = self.context.portal_url.getPortalObject()
315        archive = FileArchiveManager(self.base, portal.getId(), self.logger)
316        filelistfn = os.path.join(portal.getId(), 'filelist.ecmigration')
317        self.logger.info('Getting file list: %s' %filelistfn)
318        files = self._getMetadata(filelistfn, archive)
319        self.imp = 0
320        for x in files:
321            ext = x.split('.')[-1]
322            if 'ecmigration_filelist' == ext:
323                pass
324            elif 'ecmigration_settings' == ext:
325                self.logger.info('Getting eduCommons settings: %s' %x)
326                data = self._getMetadata(x, archive)
327                self.importSettings(data)
328            elif 'ecmigration_userinfo' == ext:
329                self.logger.info('Getting eduCommons user info: %s' %x)
330                data = self._getMetadata(x, archive)
331                self.importUsers(data)
332            elif 'ecmigration_directory' == ext:
333                data = self._getMetadata(x, archive)
334                if data['type'] not in self.disallowed_types:
335                    obj = self.importObject(data)
336                    self.imp += 1
337            elif 'ecmigration_metadata' == ext:
338                data = self._getMetadata(x, archive)
339                if data['type'] not in self.disallowed_types:
340                    obj = self.importObject(data)
341            else:
342                #f = archvie.getFileOpen(x)
343                #data = archive.getFile(x)
344                self.importObjectData(x, archive)
345                self.imp += 1
346        self.setCrosslistings()
347        endtime = datetime.now()
348        self.logger.info('Imported %d objects' %self.imp)
349        self.logger.info('Total Time: %s' %(endtime-starttime))
350
351
352    def importSettings(self, data):
353        """ Import Settings """
354        portal = self.context.portal_url.getPortalObject()
355        props = portal.portal_properties.site_properties
356        props.manage_changeProperties(default_language=data['default_language'])
357        transaction.savepoint(optimistic=True)
358
359    def importUsers(self, data):
360        """ Import Users """
361        users = data['users']
362        portal = self.context.portal_url.getPortalObject()
363        pr = portal.portal_registration
364        for x in users:
365            if not users[x]['email']:
366                users[x]['email'] = 'a@b.com'
367            pr.addMember(id=x,
368                         password=pr.generatePassword(),
369                         roles=users[x]['roles'],
370                         properties = {'fullname':users[x]['fullname'],
371                                       'username':x,
372                                       'email':users[x]['email']})
373        for x in users:
374            portal.acl_users.source_users._user_passwords[x] = users[x]['password']
375        transaction.savepoint(optimistic=True)
376
377    def importObject(self, data):
378        """ Import an object from data out of the archive """
379        oid = '/'.join(data['filename'].split(os.sep))
380        obj = self._getObjectByPath(oid)
381        if not obj:
382            # Object does not exist, create it
383            parent = self._getObjectByPath('/'.join(oid.split('/')[:-1]))
384            if parent:
385                nid = oid.split('/')[-1]
386                if 'FSSFile' == data['type']:
387                    dt = 'File'
388                else:
389                    dt = data['type']
390                _createObjectByType(dt, parent, id=nid)
391                obj = getattr(parent, nid)
392            else:
393                pass
394        if obj:
395            if getattr(obj, 'getId', None):
396                if obj.portal_type == data['type']:
397                    self._updateObject(obj, data)
398                else:
399                    obj = None
400        self.logger.info('I%.9d %s' %(self.imp, oid))
401        return obj
402
403    def importObjectData(self, fn, archive):
404        """ Import file data into the object. """
405        oid = '/'.join(fn.split(os.sep))
406        obj = self._getObjectByPath(oid)
407        if obj:
408            if 'File' == obj.Type():
409                f = archive.getFileOpen(fn)
410                fobj = obj.getFile()
411                try:
412                    fobj.manage_upload(f)
413                except AttributeError, e:
414                    obj.setFile(f)
415                fobj.content_type = obj.content_type
416                archive.getFileClose(f)
417            else:
418                data = archive.getFile(fn)
419                pf = obj.getPrimaryField()
420                pf.set(obj, data)
421            obj.reindexObject()
422            transaction.savepoint(optimistic=True)
423        else:
424            pass
425
426    def _updateObject(self, obj, data):
427        """ Update the object with the new settings """
428        for x in data['fields']:
429            field = obj.getField(x)
430            if field:
431                if 'crosslisting' == field.getName():
432                    self.crosslistings.append((obj, data['fields'][x]))
433                else:
434                    field.set(obj, data['fields'][x])
435        if data.has_key('rightsholder'):
436            if ILicensable.providedBy(obj):
437                lic = ILicense(obj)
438                lic.setRightsHolder(data['rightsholder'])
439                lic.setRightsLicense(data['rightslicense'])
440        if data.has_key('clearedcopyright'):
441            if IClearCopyrightable.providedBy(obj):
442                cc = IClearCopyright(obj)
443                cc.setClearedCopyright(data['clearedcopyright'])
444        if data.has_key('accessibilitycompliant'):
445            if IAccessibilityCompliantable.providedBy(obj):
446                acc = IAccessibilityCompliant(obj)
447                if getattr(acc, 'setAccessible', None):
448                    acc.setAccessible(data['accessibilitycompliant'])
449                else:
450                    # Function name changed in eduCommons 3.2.1+
451                    acc.setAccessibilityCompliant(data['accessibilitycompliant'])
452        if data.has_key('courseorder'):
453            if ICourseOrderable.providedBy(obj):
454                try:
455                    from enpraxis.educommons.annotations.interfaces import ICourseOrder
456                    order = ICourseOrder(obj)
457                except ImportError, NameError:
458                    if has_annotations:
459                        ann = IAnnotations(obj)
460                        if 'Course' == data['type']:
461                            ann['eduCommons.objPositionInCourse'] = 0
462                        else:
463                            ann['eduCommons.objPositionInCourse'] = data['courseorder'] + 1
464                else:
465                    if 'Course' == data['type']:
466                        order.setPositionInCourse(0)
467                    else:
468                        order.setPositionInCourse(data['courseorder'] + 1)
469        if data.has_key('originfn'):
470            obj.setFilename(data['originfn'])
471        if data.has_key('content_type'):
472            obj.content_type = data['content_type']
473        try:
474            obj.reindexObject()
475        except AttributeError:
476            pass
477        transaction.savepoint(optimistic=True)
478        self._setWorkflow(obj, data['review_state'])
479
480    def _getMetadata(self, fn, archive):
481        """ Unpickle metadata and return it. """
482        data = archive.getFile(fn)
483        return Pickle.loads(data)
484
485    def _getObjectByPath(self, path):
486        """ Return an object via its path """
487        opath = path.split('/')[1:]
488        obj = self.context
489        for x in opath:
490            if getattr(obj.aq_base, x, None):
491                obj = obj[x]
492            else:
493                obj = None
494                break
495        return obj
496   
497    def _setWorkflow(self, obj, state):
498        pw = obj.portal_workflow
499        wf = pw.getWorkflowsFor(obj)[0]
500        if 'content_workflow' == wf.getId():
501            if state in wf.states:
502                if state != pw.getInfoFor(obj, 'review_state'):
503                    pw.doActionFor(obj, 'submit')
504                if state != pw.getInfoFor(obj, 'review_state'):
505                    pw.doActionFor(obj, 'release')
506                if state != pw.getInfoFor(obj, 'review_state'):
507                    pw.doActionFor(obj, 'publish')
508
509    def setCrosslistings(self):
510        """ Set up cross listings """
511        portal = self.context.portal_url.getPortalObject()
512        for x in self.crosslistings:
513            obj = x[0]
514            field = obj.getField('crosslisting')
515            uids = []
516            for y in x[1]:
517                robj = getattr(portal, y, None)
518                if robj:
519                    uids.append(robj.UID())
520            if uids:
521                field.set(obj, uids)
522                   
523
524    def exportContent(self, filename=None):
525        """ Export eduCommons content out of site ready for migration. """
526        # Set starting params
527        starttime = datetime.now()
528        portal = self.context.portal_url.getPortalObject()
529        archive = FileArchiveManager(self.base, portal.getId(), self.logger)
530        brains = self.context.portal_catalog(path={'query':'/', 'depth':2,},)
531        self.exp = 0
532        self.fixed = 0
533        self.files = []
534        # Do the export
535        self.logger.info('Starting Export')
536        self.exportSettings(archive)
537        self.exportUsers(archive)
538        self.exportObjects(brains, archive, 99999)
539        data = Pickle.dumps(self.files)
540        flist = os.path.join(portal.getId(), 'filelist.ecmigration')
541        archive.addFile(flist, data)
542        self.logger.info('Added file info list: %s' %flist)
543        # Record results
544        endtime = datetime.now()
545        self.logger.info('Exported %d objects' %self.exp)
546        self.logger.info('Fixed %d objects' %self.fixed)
547        self.logger.info('Total time %s' %(endtime-starttime))
548
549    def exportSettings(self, archive):
550        props = self.context.portal_properties.site_properties
551        fn = '%s/settings.ecmigration_settings' %self.context.getId()
552        objstore = {'filename':fn, 'type':'Settings_ecmigration',}
553        objstore['default_language'] = props.getProperty('default_language')
554        data = Pickle.dumps(objstore)
555        self.files.append(fn)
556        archive.addFile(fn, data)
557        self.logger.info('Added settings file: %s' %fn)
558
559    def exportUsers(self, archive):
560        fn = '%s/userinfo.ecmigration_userinfo' %self.context.getId()
561        objstore = {'filename':fn, 'type':'UserInfo_ecmigration', 'users':{},}
562        for user in self.context.acl_users.getUsers():
563            username = user.getName()
564            password = self.context.acl_users.source_users._user_passwords[username]
565            objstore['users'][username] = {
566                'password':password,
567                'fullname':user.getProperty('fullname'),
568                'email':user.getProperty('email'),
569                'roles':user.getRoles(),
570                }
571        data = Pickle.dumps(objstore)
572        self.files.append(fn)
573        archive.addFile(fn, data)
574        self.logger.info('Added user info file: %s' %fn)
575
576    def exportObjects(self, brains, tf, depth=0):
577        for x in brains:
578            if x.getId not in ['Feedback', 'feedback', 'Courses_listing', 'courselist']:
579                self.exportObject(x, tf)
580                if depth and x.is_folderish:
581                    brains = self.context.portal_catalog(path={'query':x.getPath(), 'depth':1,},)
582                    self.exportObjects(brains, tf, depth-1)
583
584    def exportObject(self, brain, archive):
585        """ Export an object """
586        if 'Download this Course' == brain.Title:
587            return
588        fn = self._getPath(brain)
589        obj = brain.getObject()
590        if obj.portal_type not in ['ECLogFolder', 'ECLog']:
591            # Get the metadata and store it
592            if obj.isPrincipiaFolderish:
593                self.exportMetadata(obj, fn, '.ecmigration_directory', archive)
594            else:
595                self.exportMetadata(obj, fn, '.ecmigration_metadata', archive)
596                self.exportFile(obj, fn, archive)
597        obj._p_deactivate()
598        transaction.savepoint()
599        minimize = self.context.restrictedTraverse('/Control_Panel/Database/main/manage_minimize')
600        minimize()
601        self.logger.info('E%.9d %s' %(self.exp, fn))
602        self.exp += 1
603       
604    def exportMetadata(self, obj, fn, ftype, archive):
605        """ Export an object's metadata """
606        objstore = {'filename':fn}
607        self._storeMetaData(obj, objstore)
608        data = Pickle.dumps(objstore)
609        self.files.append(fn+ftype)
610        archive.addFile(fn+ftype, data)
611
612    def exportFile(self, obj, fn, archive):
613        """ Export the primary field of an object as a file in the archive """
614        pfield = obj.getPrimaryField()
615        fd = pfield.get(obj)
616        if 'text' == pfield.getName():
617            # Convert ResolveUids to real links
618            try:
619                ds = self.context.portal_transforms.convert('fck_ruid_to_url', fd, context=obj)
620            except:
621                # Transform no longer in eduCommons 3.2.1+
622                data = fd
623            else:
624                data = ds.getData()
625            data = self._transformLinks(data)
626            self.files.append(fn)
627            archive.addFile(fn, data)
628        elif type(fd) == type(''):
629            data = fd
630            self.files.append(fn)
631            archive.addFile(fn, data)
632        else:
633            data = fd.data
634            if type(data) != type(''):
635                self.files.append(fn)
636                f = archive.addFileOpen(fn)
637                while data is not None:
638                    archive.addFileWrite(f, data.data)
639                    data = data.next
640                archive.addFileClose(f)   
641            else:
642                self.files.append(fn)
643                archive.addFile(fn, data)
644               
645    def _getPath(self, brain):
646        opath = brain.getPath()
647        fn = opath.split('/')
648        self._transformId(fn)
649        return os.sep.join(fn[1:])
650
651    def _storeMetaData(self, obj, objstore):
652        """ Export Object Metadata """
653        objstore['type'] = self._transformType(obj.portal_type)
654        # Get field data
655        pfield = obj.getPrimaryField()
656        if objstore['type'] in ['School', 'Division', 'Course']:
657            pfield = None
658        objstore['fields'] = {}
659        for x in obj.Schema().fields():
660            if x != pfield:
661                fid = x.getName()
662                if fid in ['id', 'locallyAllowedTypes', 'immediatelyAddableTypes', 'constrainTypesMode', ]:
663                    pass # Do not store the ID, get it from the filename instead
664                elif 'clearedCopyright' == fid:
665                    # if clearedcopyright flag is stored as a field
666                    # move it to annotations instead
667                    objstore['clearedcopyright'] = x.get(obj)
668                elif 'navPosition' == fid:
669                    objstore['courseorder'] = x.get(obj)
670                elif fid in ['file', 'image']:
671                    pass
672                elif 'text' == fid:
673                    # If html then remove all resolveUIDs
674                    data = x.get(obj)
675                    try:
676                        ds = self.context.portal_transforms.convert('fck_ruid_to_url', data, context=obj)
677                    except:
678                        # Transform is not in eduCommons 3.2.1+
679                        pass
680                    else:
681                        data = self._transformLinks(ds.getData())
682                    objstore['fields'][fid] = data
683                elif 'crosslisting' == fid:
684                    refobjs = x.get(obj)
685                    if refobjs:
686                        robjs = []
687                        for y in refobjs:
688                            if y != 'None':
689                                robjs.append(y)
690                        objstore['fields'][fid] = tuple(robjs)
691                    else:
692                        objstore['fields'][fid] = ()   
693                else:
694                    objstore['fields'][fid] = x.get(obj)
695        # Get non field related data
696        objstore['owner'] = obj.getOwner().getId()
697        objstore['workflowhistory'] = obj.workflow_history
698        objstore['review_state'] = self._transformState(obj.portal_workflow.getInfoFor(obj, 'review_state'))
699        #print objstore['review_state']
700        # Get annotation related data
701        if ILicensable.providedBy(obj):
702            lic = ILicense(obj)
703            objstore['rightsholder'] = lic.getRightsHolder()
704            objstore['rightslicense'] = lic.getRightsLicense()
705        if has_clearcopyrightable:
706            if IClearCopyrightable.providedBy(obj):
707                cc = IClearCopyright(obj)
708                objstore['clearedcopyright'] = cc.getClearedCopyright()
709        if has_accessibilitycompliance:
710            if IAccessibilityCompliantable.providedBy(obj):
711                acc = IAccessibilityCompliant(obj)
712                if getattr(acc, 'getAccessible', None):
713                    objstore['accessibilitycompliant'] = acc.getAccessible()
714                else:
715                    objstore['accessibilitycompliant'] = acc.getAccessibilityCompliant()
716        if has_courseorderable:
717            if ICourseOrderable.providedBy(obj):
718                try:
719                    from enpraxis.educommons.annotations.interfaces import ICourseOrder
720                except ImportError:
721                    from zope.annotation.interfaces import IAnnotations
722                    ann = IAnnotations(obj)
723                    if ann.has_key('eduCommons.objPositionInCourse'):
724                        co = ann['eduCommons.objPositionInCourse']
725                    else:
726                        co = 0
727                else:
728                    order = ICourseOrder(obj)
729                    co = order.getPositionInCourse()
730                if co:
731                    objstore['courseorder'] = co
732        if getattr(obj, 'content_type', None):
733            objstore['content_type'] = obj.content_type
734        if obj.Type() in ['File', 'Image']:
735            objstore['originfn'] = obj.getFilename()
736
737    def _transformType(self, ptype):
738        """ Change type """
739        if self.transforms.has_key(ptype):
740            return self.transforms[ptype]
741        else:
742            return ptype
743
744    def _transformId(self, fn):
745        """ Change IDs """
746        if len(fn) > 2 and self.id_transforms.has_key(fn[2]):
747            fn[2] = self.id_transforms[fn[2]]
748            if len(fn) > 3 and 'about' == fn[2] and 'index_html' == fn[3]:
749                fn[3] = 'abouttext_text'
750            if len(fn) > 3 and 'help' == fn[2] and 'index_html' == fn[3]:
751                fn[3] = 'help_text'
752        if len(fn) > 3 and self.id_transforms.has_key(fn[3]):
753            fn[3] = self.id_transforms[fn[3]]
754
755    def _transformState(self, state):
756        if state in self.state_transforms:
757            state = self.state_transforms[state]
758        return state
759
760    def _transformLinks(self, data):
761        soup = BeautifulSoup(data)
762        links = soup.findAll(href=True)
763        for x in links:
764            href = x['href']
765            url = self._transformLinkUrl(href)
766            if url:
767                x['href'] = url
768        images = soup.findAll(src=True)
769        for x in images:
770            src = x['src']
771            url = self._transformLinkUrl(src)
772            if url:
773                x['src'] = url
774        return soup.renderContents()
775
776    def _transformLinkUrl(self, url):
777        """ Fix bad urls """
778        purl = urlparse(url)
779        if '' == purl[0] and '' == purl[1]:
780            if '/editor/' in purl[2]:
781                purl = (purl[0], purl[1], purl[2].replace('/editor/', '/'), purl[3], purl[4], purl[5])
782            if len(purl[2]) > 0 and '/' == purl[2][0]:
783                nurl = purl[2].split('/')
784                if len(nurl) > 1 and self.context.getId() != nurl[1]:
785                    nurl.insert(1, self.context.getId())
786                    purl = (purl[0], purl[1], '/'.join(nurl), purl[3], purl[4], purl[5])
787        newurl = urlunparse(purl)
788        if newurl != url:
789            self.fixed += 1
790            return newurl
791   
792        else:
793            return None
794                   
Note: See TracBrowser for help on using the repository browser.