def save(self, filename=None, tempFile=False): """ Save package to disk pass an optional filename """ self.tempFile = tempFile if filename: filename = Path(filename) name = filename.splitpath()[1] if not tempFile: self.name = name.basename().splitext()[0] elif self.filename: filename = Path(self.filename) else: raise AssertionError(u'No name passed when saving a new package') log.debug(u"Will save %s to: %s" % (self.name, filename)) if tempFile: self.nonpersistant.remove('filename') oldFilename, self.filename = self.filename, unicode(self.filename) try: filename.safeSave(self.doSave, _('SAVE FAILED!\nLast succesful save is %s.')) finally: self.nonpersistant.append('filename') self.filename = oldFilename else: self.filename = filename filename.safeSave(self.doSave, _('SAVE FAILED!\nLast succesful save is %s.')) self.isChanged = False self.updateRecentDocuments(filename)
def save(self, filename=None): """ Save package to disk pass an optional filename """ if filename: filename = Path(filename) name = filename.splitpath()[1] self.name = name.basename().splitext()[0] elif self.filename: filename = Path(self.filename) else: raise AssertionError(u'No name passed when saving a new package') self.filename = filename log.debug(u"Will save %s to: %s" % (self.name, filename)) filename.safeSave(self.doSave, _('SAVE FAILED!\nLast succesful save is %s.')) self.isChanged = 0
def save(self, filename=None, tempFile=False): """ Save package to disk pass an optional filename """ self.tempFile = tempFile # Get the filename if filename: filename = Path(filename) # If we are being given a new filename... # Change our name to match our new filename name = filename.splitpath()[1] if not tempFile: self.name = name.basename().splitext()[0] elif self.filename: # Otherwise use our last saved/loaded from filename filename = Path(self.filename) else: # If we don't have a last saved/loaded from filename, # raise an exception because, we need to have a new # file passed when a brand new package is saved raise AssertionError(u'No name passed when saving a new package') # Store our new filename for next file|save, and save the package log.debug(u"Will save %s to: %s" % (self.name, filename)) if tempFile: self.nonpersistant.remove('filename') oldFilename, self.filename = self.filename, unicode(self.filename) try: filename.safeSave( self.doSave, _('SAVE FAILED!\nLast succesful save is %s.')) finally: self.nonpersistant.append('filename') self.filename = oldFilename else: # Update our new filename for future saves self.filename = filename filename.safeSave(self.doSave, _('SAVE FAILED!\nLast succesful save is %s.')) self.isChanged = False self.updateRecentDocuments(filename)
def save(self, filename=None, tempFile=False): if filename: filename = Path(filename) # If we are being given a new filename... # Change our name to match our new filename name = filename.splitpath()[1] if not tempFile: self.name = name.basename().splitext()[0] elif self.filename: # Otherwise use our last saved/loaded from filename filename = Path(self.filename) else: # If we don't have a last saved/loaded from filename, # raise an exception because, we need to have a new # file passed when a brand new package is saved raise AssertionError(u'No name passed when saving a new package') if not tempFile: # Update our new filename for future saves self.filename = filename filename.safeSave(self.doSave, _('SAVE FAILED!\nLast succesful save is %s.')) self.isChanged = False
def save(self, filename=None, tempFile=False): """ Save package to disk pass an optional filename """ self.tempFile = tempFile # Get the filename if filename: filename = Path(filename) # If we are being given a new filename... # Change our name to match our new filename name = filename.splitpath()[1] if not tempFile: self.name = name.basename().splitext()[0] elif self.filename: # Otherwise use our last saved/loaded from filename filename = Path(self.filename) else: # If we don't have a last saved/loaded from filename, # raise an exception because, we need to have a new # file passed when a brand new package is saved raise AssertionError(u"No name passed when saving a new package") # Store our new filename for next file|save, and save the package log.debug(u"Will save %s to: %s" % (self.name, filename)) if tempFile: self.nonpersistant.remove("filename") oldFilename, self.filename = self.filename, unicode(self.filename) try: filename.safeSave(self.doSave, _("SAVE FAILED!\nLast succesful save is %s.")) finally: self.nonpersistant.append("filename") self.filename = oldFilename else: # Update our new filename for future saves self.filename = filename filename.safeSave(self.doSave, _("SAVE FAILED!\nLast succesful save is %s.")) self.isChanged = False self.updateRecentDocuments(filename)
class ScormExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename, scormType): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.hasForum = False self.scormType = scormType self.styleSecureMode = config.styleSecureMode def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() self.metadataType = package.exportMetadataType # copy the package's resource files package.resourceDir.copyfiles(outputDir) # copy the package's resource files, only non existant in outputDir # outputDirFiles = outputDir.files() # for rfile in package.resourceDir.files(): # if rfile not in outputDirFiles: # rfile.copy(outputDir) # copy the package's resource files, only indexed in package.resources # for md5 in package.resources.values(): # for resource in md5: # resource.path.copy(outputDir) # Export the package content # Import the Scorm Page class , if the secure mode is off. If the style has it's own page class # use that, else use the default one. if self.styleSecureMode == "0": if (self.styleDir / "scormpage.py").exists(): global ScormPage module = imp.load_source("ScormPage", self.styleDir / "scormpage.py") ScormPage = module.ScormPage self.pages = [ScormPage("index", 1, package.root, scormType=self.scormType, metadataType=self.metadataType)] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) if not self.hasForum: for idevice in page.node.idevices: if hasattr(idevice, "isForum"): if idevice.forum.lms.lms == "moodle": self.hasForum = True break # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.scormType, self.metadataType) manifest.save("imsmanifest.xml") if self.hasForum: manifest.save("discussionforum.xml") # Copy the style sheet files to the output dir styleFiles = [self.styleDir / ".." / "base.css"] styleFiles += [self.styleDir / ".." / "popup_bg.gif"] # And with all the files of the style we avoid problems: styleFiles += self.styleDir.files("*.*") if self.scormType == "commoncartridge": for sf in styleFiles[:]: if sf.basename() not in manifest.dependencies: styleFiles.remove(sf) self.styleDir.copylist(styleFiles, outputDir) # Copy the scripts dT = common.getExportDocType() if dT == "HTML5": jsFile = self.scriptsDir / "exe_html5.js" jsFile.copyfile(outputDir / "exe_html5.js") # jQuery my_style = G.application.config.styleStore.getStyle(page.node.package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = self.scriptsDir / "exe_jquery.js" jsFile.copyfile(outputDir / "exe_jquery.js") else: jsFile = self.scriptsDir / "exe_jquery.js" jsFile.copyfile(outputDir / "exe_jquery.js") if self.scormType == "commoncartridge": jsFile = self.scriptsDir / "common.js" jsFile.copyfile(outputDir / "common.js") if self.scormType == "scorm2004" or self.scormType == "scorm1.2": self.scriptsDir.copylist(("SCORM_API_wrapper.js", "SCOFunctions.js", "common.js"), outputDir) # about SCHEMAS: schemasDir = "" if self.scormType == "scorm1.2": schemasDir = self.schemasDir / "scorm1.2" schemasDir.copylist( ( "imscp_rootv1p1p2.xsd", "imsmd_rootv1p2p1.xsd", "adlcp_rootv1p2.xsd", "lom.xsd", "lomCustom.xsd", "ims_xml.xsd", ), outputDir, ) elif self.scormType == "scorm2004": schemasDir = self.schemasDir / "scorm2004" schemasDir.copylist( ( "adlcp_v1p3.xsd", "adlnav_v1p3.xsd", "adlseq_v1p3.xsd", "datatypes.dtd", "imscp_v1p1.xsd", "imsssp_v1p0.xsd", "imsss_v1p0.xsd", "imsss_v1p0auxresource.xsd", "imsss_v1p0control.xsd", "imsss_v1p0delivery.xsd", "imsmd_rootv1p2p1.xsd", "imsss_v1p0limit.xsd", "imsss_v1p0objective.xsd", "imsss_v1p0random.xsd", "imsss_v1p0rollup.xsd", "imsss_v1p0seqrule.xsd", "imsss_v1p0util.xsd", "ims_xml.xsd", "lom.xsd", "lomCustom.xsd", "xml.xsd", "XMLSchema.dtd", ), outputDir, ) try: import shutil, errno shutil.copytree(schemasDir / "common", outputDir / "common") shutil.copytree(schemasDir / "extend", outputDir / "extend") shutil.copytree(schemasDir / "unique", outputDir / "unique") shutil.copytree(schemasDir / "vocab", outputDir / "vocab") except OSError as exc: if exc.errno == errno.ENOTDIR: shutil.copy(schemasDir, outputDir) else: raise # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if ( hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasWikipedia and hasInstructions and hasMediaelement ): isBreak = True break if not hasFlowplayer: if "flowPlayer.swf" in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if "mojomagnify.js" in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if "xspf_player.swf" in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasWikipedia: if "WikipediaIdevice" == idevice.klass: hasWikipedia = True if not hasInstructions: if ( "TrueFalseIdevice" == idevice.klass or "MultichoiceIdevice" == idevice.klass or "VerdaderofalsofpdIdevice" == idevice.klass or "EleccionmultiplefpdIdevice" == idevice.klass ): hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if hasFlowplayer: videofile = self.templatesDir / "flowPlayer.swf" videofile.copyfile(outputDir / "flowPlayer.swf") controlsfile = self.templatesDir / "flowplayer.controls.swf" controlsfile.copyfile(outputDir / "flowplayer.controls.swf") if hasMagnifier: videofile = self.templatesDir / "mojomagnify.js" videofile.copyfile(outputDir / "mojomagnify.js") if hasXspfplayer: videofile = self.templatesDir / "xspf_player.swf" videofile.copyfile(outputDir / "xspf_player.swf") if hasGallery: imageGalleryCSS = self.cssDir / "exe_lightbox.css" imageGalleryCSS.copyfile(outputDir / "exe_lightbox.css") imageGalleryJS = self.scriptsDir / "exe_lightbox.js" imageGalleryJS.copyfile(outputDir / "exe_lightbox.js") self.imagesDir.copylist( ( "exe_lightbox_close.png", "exe_lightbox_loading.gif", "exe_lightbox_next.png", "exe_lightbox_prev.png", ), outputDir, ) if hasWikipedia: wikipediaCSS = self.cssDir / "exe_wikipedia.css" wikipediaCSS.copyfile(outputDir / "exe_wikipedia.css") if hasInstructions: common.copyFileIfNotInStyle("panel-amusements.png", self, outputDir) common.copyFileIfNotInStyle("stock-stop.png", self, outputDir) if hasMediaelement: mediaelement = self.scriptsDir / "mediaelement" mediaelement.copyfiles(outputDir) if dT != "HTML5": jsFile = self.scriptsDir / "exe_html5.js" if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / "fdl.html").copyfile(outputDir / "fdl.html") if hasattr(package, "scowsinglepage") and package.scowsinglepage: page = SinglePage("singlepage_index", 1, package.root) page.save(outputDir / "singlepage_index.html") # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir / package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = self.imagesDir / "favicon.ico" faviconFile.copyfile(outputDir / "favicon.ico") if hasattr(package, "scowwebsite") and package.scowwebsite: website = WebsiteExport(self.config, self.styleDir, outputDir, "website_") website.export(package) (self.styleDir / "nav.css").copyfile(outputDir / "nav.css") # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir / package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = self.imagesDir / "favicon.ico" faviconFile.copyfile(outputDir / "favicon.ico") if hasattr(package, "exportSource") and package.exportSource: (G.application.config.webDir / "templates" / "content.xsd").copyfile(outputDir / "content.xsd") (outputDir / "content.data").write_bytes(encodeObject(package)) (outputDir / "contentv3.xml").write_bytes(encodeObjectToXML(package)) # Zip it up! self.filename.safeSave(self.doZip, _("EXPORT FAILED!\nLast succesful export is %s."), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ # Zip up the scorm package zipped = ZipFile(fileObj, "w") ## old method: only files could be copied: # for scormFile in outputDir.files(): # zipped.write(scormFile, # scormFile.basename().encode('utf8'), # ZIP_DEFLATED) ## but some folders must be included also, so: outputlen = len(outputDir) + 1 for base, dirs, files in os.walk(outputDir): for file in files: fn = os.path.join(base, file) zipped.write(fn, fn[outputlen:].encode("utf8"), ZIP_DEFLATED) # zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = ScormPage(pageName, depth, child, scormType=self.scormType, metadataType=self.metadataType) self.pages.append(page) self.generatePages(child, depth + 1)
class WebsiteExport(object): #Track these variables so we can generate TinCan IDs as we go #Initialising the current Course's page's idevice id for the current package. current_page = "" current_package_name = "" current_idevice_id = "" current_xapi_prefix = "" current_package_title = "" """ WebsiteExport will export a package as a website of HTML pages """ def __init__(self, config, styleDir, filename, prefix="", report=False, skipNavigation=False, ustadMobileMode=False, ustadMobileTestMode=False): #Added ustadMobileTestMode for Course Test Mode. """ 'stylesDir' is the directory where we can copy the stylesheets from 'outputDir' is the directory that will be [over]written with the website """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.stylesDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.prefix = prefix self.report = report self.skipNavigation = skipNavigation self.ustadMobileMode = ustadMobileMode self.ustadMobileTestMode = ustadMobileTestMode #Added for Course Test Mode self.styleSecureMode = config.styleSecureMode self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.stylesDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.prefix = prefix self.report = report @staticmethod def getTinCanId(suffix = "", id_type="idevice", pagename = None): """Make a tin can activity ID - use a server prefix, the package name, the page name, then the idevice id, and if needed another / and a question id or other depending on the idevice suffix: will be added to the tincan string id_type: can be idevice (default), page or pagename: the page file name (without .html etc). """ server_tincan_prefix = "http://www.ustadmobile.com/tincan" if pagename is None: pagename = WebsiteExport.current_package_name tin_can_prefix = "%(urlprefix)s/%(packagename)s" % { "urlprefix" : server_tincan_prefix, "packagename" : WebsiteExport.current_package_name } if id_type == EXETinCan.PAGE or id_type == EXETinCan.IDEVICE: tin_can_prefix += "/" + pagename if id_type == EXETinCan.IDEVICE: tin_can_prefix += "/" + WebsiteExport.current_idevice_id if suffix != "": tin_can_prefix += "/" + suffix return tin_can_prefix def exportZip(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = TempDirPath() # Import the Website Page class , if the secure mode is off. If the style has it's own page class # use that, else use the default one. if self.styleSecureMode=="0": if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage("index", 0, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) self.copyFiles(package, outputDir) # Zip up the website package self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually saves the zip data. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def appendPageReport(self, page, package): if not page.node.idevices:self.report += u'"%s","%s",%d,"%s",,,,,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html') for idevice in page.node.idevices: if not idevice.userResources:self.report += u'"%s","%s",%d,"%s","%s","%s",,,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title) for resource in idevice.userResources: if type(resource) == Resource: self.report += u'"%s","%s",%d,"%s","%s","%s","%s","%s","%s","%s"\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource.storageName, resource.userName, resource.path, resource.checksum) else: self.report += u'"%s",%d,"%s","%s","%s","%s",,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource) def export(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ if not self.report: outputDir = self.filename if not outputDir.exists(): outputDir.mkdir() WebsiteExport.current_package_name = package.name WebsiteExport.current_xapi_prefix = \ EXETinCan.get_tincan_prefix_for_pkg(package) WebsiteExport.current_package_title = \ package.title # Import the Website Page class. If the style has it's own page class # use that, else use the default one. if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage(self.prefix + "index", 0, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] if self.report: self.report = u'"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"\n' % ('File','Page Name', 'Level', 'Page File Name', 'Idevice Type', 'Idevice Title', 'Resource File Name', 'Resource User Name', 'Resource Path', 'Resource Checksum') self.appendPageReport(thisPage,package) for nextPage in self.pages[1:]: if self.report: self.appendPageReport(nextPage,package) else: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage if not self.report: thisPage.save(outputDir, prevPage, None, self.pages) if self.prefix == "": self.copyFiles(package, outputDir) else: self.filename.write_text(self.report, 'utf-8') def copyFiles(self, package, outputDir, um_mode = False): """ Copy all the files used by the website. Parameters ---------- package : Package package that is being exported outputDir : Path The end directory being exported to """ package.resourceDir.copyfiles2(outputDir) copy_list = package.make_system_copy_list(self.stylesDir, self.scriptsDir, self.templatesDir, self.imagesDir, self.cssDir, outputDir, ustad_mobile_mode = um_mode) WebsiteExport.run_copy_list(copy_list) if hasattr(package, 'exportSource') and package.exportSource: (outputDir/'content.data').write_bytes(encodeObject(package)) (outputDir/'contentv3.xml').write_bytes(encodeObjectToXML(package)) @classmethod def run_copy_list(cls, copy_list): """ Copy a list of files - preserve modification times Parameters copy_list : list List of arrays. Item 0 of each array should be a source,1 should be the dest. Source can be a list of paths or a path object itself. For a list the dest should be a path representing a directory For an individual path the destination should be a file path """ for copy_item in copy_list: src = copy_item[0] dst = copy_item[1] if isinstance(src, Path): #this is a straight file to file copy src.copyfile2(dst) else: #this is a list of files to copy to dst which should be #a directory for src_file in src: if src_file.basename() == "content.css": life = 42 src_file.copyfile2(dst/src_file.basename()) def generatePages(self, node, depth): """ Recursively generate pages and store in pages member variable for retrieving later """ for child in node.children: # assure lower pagename, without whitespaces or alphanumeric characters: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" self.pages.append(WebsitePage(self.prefix + pageName, depth, child)) self.generatePages(child, depth + 1)
class WebsiteExport(object): """ WebsiteExport will export a package as a website of HTML pages """ def __init__(self, config, styleDir, filename, prefix="", report=False): """ 'stylesDir' is the directory where we can copy the stylesheets from 'outputDir' is the directory that will be [over]written with the website """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.stylesDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.prefix = prefix self.report = report self.styleSecureMode = config.styleSecureMode def exportZip(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = TempDirPath() # Import the Website Page class , if the secure mode is off. If the style has it's own page class # use that, else use the default one. if self.styleSecureMode=="0": if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage("index", 0, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) self.copyFiles(package, outputDir) # Zip up the website package self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually saves the zip data. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def appendPageReport(self, page, package): if not page.node.idevices:self.report += u'"%s","%s",%d,"%s",,,,,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html') for idevice in page.node.idevices: if not idevice.userResources:self.report += u'"%s","%s",%d,"%s","%s","%s",,,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title) for resource in idevice.userResources: if type(resource) == Resource: self.report += u'"%s","%s",%d,"%s","%s","%s","%s","%s","%s","%s"\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource.storageName, resource.userName, resource.path, resource.checksum) else: self.report += u'"%s",%d,"%s","%s","%s","%s",,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource) def export(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ if not self.report: outputDir = self.filename if not outputDir.exists(): outputDir.mkdir() # Import the Website Page class. If the style has it's own page class # use that, else use the default one. if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage(self.prefix + "index", 0, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] if self.report: self.report = u'"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"\n' % ('File','Page Name', 'Level', 'Page File Name', 'Idevice Type', 'Idevice Title', 'Resource File Name', 'Resource User Name', 'Resource Path', 'Resource Checksum') self.appendPageReport(thisPage,package) for nextPage in self.pages[1:]: if self.report: self.appendPageReport(nextPage,package) else: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage if not self.report: thisPage.save(outputDir, prevPage, None, self.pages) if self.prefix == "": self.copyFiles(package, outputDir) return outputDir else: self.filename.write_text(self.report, 'utf-8') def copyFiles(self, package, outputDir): """ Copy all the files used by the website. """ if os.path.isdir(self.stylesDir): # Copy the style sheet files to the output dir styleFiles = [self.stylesDir/'..'/'base.css'] styleFiles += [self.stylesDir/'..'/'popup_bg.gif'] styleFiles += self.stylesDir.files("*.css") styleFiles += self.stylesDir.files("*.jpg") styleFiles += self.stylesDir.files("*.gif") styleFiles += self.stylesDir.files("*.png") styleFiles += self.stylesDir.files("*.js") styleFiles += self.stylesDir.files("*.html") styleFiles += self.stylesDir.files("*.ico") styleFiles += self.stylesDir.files("*.ttf") styleFiles += self.stylesDir.files("*.eot") styleFiles += self.stylesDir.files("*.otf") styleFiles += self.stylesDir.files("*.woff") self.stylesDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # copy script files. my_style = G.application.config.styleStore.getStyle(package.style) # jQuery if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') else: jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') jsFile = (self.scriptsDir/'common.js') jsFile.copyfile(outputDir/'common.js') #dT = common.getExportDocType() dT=common.getExportDocType(); if dT == "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir/package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = (self.imagesDir/'favicon.ico') faviconFile.copyfile(outputDir/'favicon.ico') # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') controlsfile = (self.templatesDir/'flowplayer.controls.swf') controlsfile.copyfile(outputDir/'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir/'mojomagnify.js') videofile.copyfile(outputDir/'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir/'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir/'exe_effects') exeEffects.copyfiles(outputDir) if hasSH: exeSH = (self.scriptsDir/'exe_highlighter') exeSH.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir/'exe_games') exeGames.copyfiles(outputDir) if hasWikipedia: wikipediaCSS = (self.cssDir/'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir/'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir/'mediaelement') mediaelement.copyfiles(outputDir) dT = common.getExportDocType() if dT != "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir/'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir/'templates'/'content.xsd').copyfile(outputDir/'content.xsd') (outputDir/'content.data').write_bytes(encodeObject(package)) (outputDir/'contentv3.xml').write_bytes(encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') def generatePages(self, node, depth): """ Recursively generate pages and store in pages member variable for retrieving later """ for child in node.children: # assure lower pagename, without whitespaces or alphanumeric characters: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" self.pages.append(WebsitePage(self.prefix + pageName, depth, child)) self.generatePages(child, depth + 1)
class WebsiteExport(object): """ WebsiteExport will export a package as a website of HTML pages """ def __init__(self, config, styleDir, filename, prefix="", report=False): """ 'stylesDir' is the directory where we can copy the stylesheets from 'outputDir' is the directory that will be [over]written with the website """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.stylesDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.prefix = prefix self.report = report self.styleSecureMode = config.styleSecureMode def gDriveNotificationStatus(self, client, mesg): client.sendScript("eXe.controller.eXeViewport.prototype.gDriveNotificationStatus('%s');" % (mesg), filter_func=allSessionClients) def gDriveNotificationNotice(self, client, mesg, type): client.sendScript("eXe.controller.eXeViewport.prototype.gDriveNotificationNotice('%s', '%s');" % (mesg, type), filter_func=allSessionClients) # def exportGoogleDrive(self, package, client, auth_token, user_agent): # """ # Creates an authorized HTTP conexion, exports the current package as # 'webSite' to a temporary directory and uploads the exported files to # Google Drive # """ # num_total = 0; # file_num = 0; # # def finishExport(public_folder, outputDir): # self.gDriveNotificationStatus(client, _(u'Deleting temporary directory')) # rmtree(outputDir) # # link_url = 'http://googledrive.com/host/%s'%(public_folder['id']) # link_text = public_folder['title'] # self.gDriveNotificationStatus(client, _(u'Package exported to <a href="%s" target="_blank" title="Click to visit exported site">%s</a>') %(link_url,link_text)) # return public_folder # # def insertFile(public_folder, drive, upload_file, file_num): # """ # Creates the deferred that will upload files to the public folder # once it is created # """ # upload_content_d = threads.deferToThread(uploadContent, public_folder, drive, upload_file, file_num) # upload_content_d.addCallbacks(uploadContent_onSuccess, uploadContent_onFail) # # return upload_content_d # # def uploadContent(public_folder, drive, upload_file, file_num): # """ # Uploads one file to the given GDrive folder # """ # try : # filepath = os.path.join(self.filename, upload_file) # filetype = mimetypes.guess_type(filepath, False) # # if filetype[0] is None : # # Hard-coded types for special cases not detected by mimetypes.guess_type() # if upload_file == 'content.data' : # filetype = ('application/octet-stream', None) # # # if filetype[0] is not None : # link_url = 'http://googledrive.com/host/%s'%(public_folder['id']) # link_text = public_folder['title'] # #self.gDriveNotificationStatus(client, _(u'Package exported to <a href="%s" target="_blank" title="Click to visit exported site">%s</<a>') %(link_url,link_text)) # self.gDriveNotificationStatus(client, _(u'Uploading <em>%s</em> to public folder <a href="%s" target="_blank" title="Click to visit exported site">%s</a> (%d/%d)') # %(upload_file, link_url, link_text, file_num, num_total)) # mimetype = filetype[0] # meta = { # 'title': upload_file, # 'mimeType': mimetype, # 'parents' : [{'id' : public_folder['id']}] # } # media_body = MediaFileUpload(filepath, mimetype, resumable=True) # drive.files().insert(body=meta, media_body=media_body).execute() # else : # self.gDriveNotificationNotice(client, _(u'File <em>%s</em> skipped, unknown filetype (%d/%d)') %(upload_file, file_num, num_total), 'warning') # except Exception, e : # log.error(str(e)) # self.gDriveNotificationNotice(client, _(u'Failed upload of file %s (%d/%d)') % (upload_file, file_num, num_total), 'error') # # return public_folder # # def uploadContent_onFail(err): # log.error(str(err)) # self.gDriveNotificationNotice(client, _(u'Failed exporting to GoogleDrive'), 'error') # return err # # def uploadContent_onSuccess(public_folder): # # self.gDriveNotificationStatus(client, _(u'Exported to GoogleDrive: %s') % public_folder['title']) # return public_folder # # def publicFolder(drive, folder_name): # """ # Creates a Public Web folder, that can be read as a web site with any # web browser, and populates it with the content of the given directory # """ # # # Create public folder # self.gDriveNotificationStatus(client, _(u'Creating public folder to host published web site')) # body = { # 'title': folder_name, # 'mimeType': 'application/vnd.google-apps.folder' # } # public_folder = drive.files().insert(body=body).execute() # # permission = { # 'value': '', # 'type': 'anyone', # 'role': 'reader' # } # drive.permissions().insert(fileId=public_folder['id'], body=permission).execute() # # return public_folder # # def publicFolder_onFail(err): # log.error(str(err)) # self.gDriveNotificationNotice(client, _(u'Failed exporting to GoogleDrive'), 'error') # return err # # def publicFolder_onSuccess(public_folder): # self.gDriveNotificationStatus(client, _(u'Created public folder to host published web site: %s') % (public_folder['title'])) # return public_folder # # try: # # Creates a new temporary dir to export the package to, it will be deleted # # once the export process is finished # self.filename = Path(mkdtemp()) # self.gDriveNotificationStatus(client, _(u'Exporting package as web site in: %s') % (jquote(self.filename))) # outputDir = self.export(package) # # self.gDriveNotificationStatus(client, _(u'Starting authorized connection to Google Drive API')) # credentials = AccessTokenCredentials(auth_token, user_agent) # ca_certs = None # if hasattr(sys, 'frozen'): # ca_certs = 'cacerts.txt' # http = httplib2.Http(ca_certs=ca_certs) # http = credentials.authorize(http) # drive_service = build('drive', 'v2', http=http) # # publicFolder_d = threads.deferToThread(publicFolder, drive_service, package.name) # publicFolder_d.addCallbacks(publicFolder_onSuccess, publicFolder_onFail) # # # Loop through files in exportDir, each upload will be called from # # a chained callback function. These callbacks will return a # # deferred so the file N upload will start when file N-1 has # # successfully called its own callback # # (Deferred chain, see: http://krondo.com/?p=2159#attachment_2196) # for upload_file in os.listdir(outputDir): # file_num = file_num + 1 # publicFolder_d.addCallback(insertFile, drive_service, upload_file, file_num) # # num_total = file_num # publicFolder_d.addCallback(finishExport, outputDir) # # # TODO clean exportDir after uploading has finished # # except Exception, e: # log.error(str(e)) # self.gDriveNotificationNotice(client, _('EXPORT FAILED!'), 'error') # raise # #client.alert(_(u'Exported to %s') % filename) def exportZip(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = TempDirPath() # Import the Website Page class , if the secure mode is off. If the style has it's own page class # use that, else use the default one. if self.styleSecureMode=="0": if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage("index", 0, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) self.copyFiles(package, outputDir) # Zip up the website package self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually saves the zip data. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def appendPageReport(self, page, package): if not page.node.idevices:self.report += u'"%s","%s",%d,"%s",,,,,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html') for idevice in page.node.idevices: if not idevice.userResources:self.report += u'"%s","%s",%d,"%s","%s","%s",,,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title) for resource in idevice.userResources: if type(resource) == Resource: self.report += u'"%s","%s",%d,"%s","%s","%s","%s","%s","%s","%s"\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource.storageName, resource.userName, resource.path, resource.checksum) else: self.report += u'"%s",%d,"%s","%s","%s","%s",,,\n' % (package.filename,page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource) def export(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ if not self.report: outputDir = self.filename if not outputDir.exists(): outputDir.mkdir() # Import the Website Page class. If the style has it's own page class # use that, else use the default one. if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage(self.prefix + "index", 0, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] if self.report: self.report = u'"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"\n' % ('File','Page Name', 'Level', 'Page File Name', 'Idevice Type', 'Idevice Title', 'Resource File Name', 'Resource User Name', 'Resource Path', 'Resource Checksum') self.appendPageReport(thisPage,package) for nextPage in self.pages[1:]: if self.report: self.appendPageReport(nextPage,package) else: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage if not self.report: thisPage.save(outputDir, prevPage, None, self.pages) if self.prefix == "": self.copyFiles(package, outputDir) return outputDir else: self.filename.write_text(self.report, 'utf-8') def copyFiles(self, package, outputDir): """ Copy all the files used by the website. """ if os.path.isdir(self.stylesDir): # Copy the style sheet files to the output dir styleFiles = [self.stylesDir/'..'/'base.css'] styleFiles += [self.stylesDir/'..'/'popup_bg.gif'] styleFiles += self.stylesDir.files("*.css") styleFiles += self.stylesDir.files("*.jpg") styleFiles += self.stylesDir.files("*.gif") styleFiles += self.stylesDir.files("*.png") styleFiles += self.stylesDir.files("*.js") styleFiles += self.stylesDir.files("*.html") styleFiles += self.stylesDir.files("*.ico") styleFiles += self.stylesDir.files("*.ttf") styleFiles += self.stylesDir.files("*.eot") styleFiles += self.stylesDir.files("*.otf") styleFiles += self.stylesDir.files("*.woff") self.stylesDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # copy script files. my_style = G.application.config.styleStore.getStyle(package.style) # jQuery if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') else: jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') jsFile = (self.scriptsDir/'common.js') jsFile.copyfile(outputDir/'common.js') #dT = common.getExportDocType() dT=common.getExportDocType(); if dT == "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir/package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = (self.imagesDir/'favicon.ico') faviconFile.copyfile(outputDir/'favicon.ico') # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasGames = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasGames and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') controlsfile = (self.templatesDir/'flowplayer.controls.swf') controlsfile.copyfile(outputDir/'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir/'mojomagnify.js') videofile.copyfile(outputDir/'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir/'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir/'exe_effects') exeEffects.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir/'exe_games') exeGames.copyfiles(outputDir) if hasWikipedia: wikipediaCSS = (self.cssDir/'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir/'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir/'mediaelement') mediaelement.copyfiles(outputDir) dT = common.getExportDocType() if dT != "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir/'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir/'templates'/'content.xsd').copyfile(outputDir/'content.xsd') (outputDir/'content.data').write_bytes(encodeObject(package)) (outputDir/'contentv3.xml').write_bytes(encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') def generatePages(self, node, depth): """ Recursively generate pages and store in pages member variable for retrieving later """ for child in node.children: # assure lower pagename, without whitespaces or alphanumeric characters: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" self.pages.append(WebsitePage(self.prefix + pageName, depth, child)) self.generatePages(child, depth + 1)
class Epub3Export(object): """ Exports an eXe package as a epub 3 package The 'Hello World' of a epub 3 publication might contain files: mimetype META-INF/container.xml Content/HelloWorld.opf Content/HelloWorld.xhtml """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export epub 3 package """ # First do the export to a temporary directory outputDir = TempDirPath() ''' fileDir = outputDir/"META-INF" fileDir.mkdir() fileDir = outputDir/"Content" fileDir.mkdir() ''' metainfPages = Path(outputDir.abspath() + '/META-INF') # metainfPages = outputDir/'META-INF' metainfPages.mkdir() contentPages = Path(outputDir.abspath() + '/EPUB') # contentPages = outputDir/'Content' contentPages.mkdir() # print contentPages.abspath() # print outputDir.abspath() # Export the package content self.pages = [self.make_cover_page(package)] self.generatePages(package.root, 2) uniquifyNames(self.pages, suffix = ".xhtml") cover = None for page in self.pages: page.save(contentPages) if hasattr(page, 'cover'): cover = page.cover # Create mimetype file mimetypeFile = open(outputDir.abspath() + '/mimetype', "w") mimetypeFile.write('application/epub+zip') mimetypeFile.close() # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir / '..' / 'base.css'] styleFiles += [self.styleDir / '..' / 'popup_bg.gif'] styleFiles += [f for f in self.styleDir.files("*.css") if f.basename() != "nav.css"] styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") styleFiles += self.styleDir.files("*.ttf") styleFiles += self.styleDir.files("*.eot") styleFiles += self.styleDir.files("*.otf") styleFiles += self.styleDir.files("*.woff") # FIXME for now, only copy files referenced in Common Cartridge # this really should apply to all exports, but without a manifest # of the files needed by an included stylesheet it is too restrictive package.resourceDir.copyfiles(contentPages) self.styleDir.copylist(styleFiles, contentPages) self.scriptsDir.copylist(('common.js',), contentPages) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasWikipedia = False isBreak = False hasInstructions = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasWikipedia): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(contentPages / 'flowPlayer.swf') controlsfile = (self.templatesDir / 'flowplayer.controls.swf') controlsfile.copyfile(contentPages / 'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir / 'mojomagnify.js') videofile.copyfile(contentPages / 'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(contentPages / 'xspf_player.swf') if hasGallery: imageGalleryCSS = (self.cssDir / 'exe_lightbox.css') imageGalleryCSS.copyfile(contentPages / 'exe_lightbox.css') imageGalleryJS = (self.scriptsDir / 'exe_lightbox.js') imageGalleryJS.copyfile(contentPages / 'exe_lightbox.js') self.imagesDir.copylist(('exe_lightbox_close.png', 'exe_lightbox_loading.gif', 'exe_lightbox_next.png', 'exe_lightbox_prev.png'), contentPages) if hasWikipedia: wikipediaCSS = (self.cssDir / 'exe_wikipedia.css') wikipediaCSS.copyfile(contentPages / 'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, contentPages) common.copyFileIfNotInStyle('stock-stop.png', self, contentPages) my_style = G.application.config.styleStore.getStyle(package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(contentPages / 'exe_jquery.js') else: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(contentPages / 'exe_jquery.js') # if hasattr(package, 'exportSource') and package.exportSource: # (G.application.config.webDir / 'templates' / 'content.xsd').copyfile(outputDir / 'content.xsd') # (outputDir / 'content.data').write_bytes(encodeObject(package)) # (outputDir / 'contentv3.xml').write_bytes(encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl.html').copyfile(contentPages / 'fdl.html') # Create the nav.xhtml file container = NavEpub3(self.pages, contentPages) container.save() publication = self.make_publication_epub3(contentPages, package, cover) publication.save("package.opf") # Create the container file container = ContainerEpub3(metainfPages) container.save("container.xml") # Zip it up! self.filename.safeSave(self.doZip, _(u'EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() @classmethod def check_package_identifier(cls, package): """Make sure the package has a dublin core identifier Parameters ---------- package : Package The package to check for an identifier on """ if not package.dublinCore.identifier: package.dublinCore.identifier = str(uuid.uuid4()) def make_cover_page(self, package): """Return a Epub3Cover for the given package Parameters ---------- package : Package Package to create a cover page for """ return Epub3Cover("cover", 1, package.root) def make_publication_epub3(self, contentPagesDir, package, cover): """Return a PublicationEpub3 for this export Call only after self.pages has been generated Parameters ---------- contentPagesDir : Path The directory where the contents of the package have been exported to (e.g. the 'EPUB' dir) package : Package The package being exported cover : Epub3Cover The cover page """ publication = PublicationEpub3(self.config, contentPagesDir, package, self.pages, cover) return publication def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") mimetype = outputDir / "mimetype" zipped.write(mimetype, "mimetype", ZIP_STORED) for epubFile in outputDir.walkfiles(): if epubFile.basename() == 'mimetype': continue relativePath = epubFile.basename() parentdir = epubFile.splitpath()[0] while (outputDir.basename() != parentdir.basename()): relativePath = parentdir.basename() / relativePath parentdir = parentdir.splitpath()[0] zipped.write(epubFile, relativePath.encode('utf8'), compress_type=ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ pageName = node.titleShort.lower().replace(" ", u"_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = u"__" if pageName[0] in [unicode(i) for i in range(0, 10)]: pageName = u'_' + pageName page = Epub3Page(pageName, depth, node) self.pages.append(page) for child in node.children: self.generatePages(child, depth + 1)
class ScormExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename, scormType): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.hasForum = False self.scormType = scormType def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() # Export the package content self.pages = [ ScormPage("index", 1, package.root, scormType=self.scormType) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) if not self.hasForum: for idevice in page.node.idevices: if hasattr(idevice, "isForum"): if idevice.forum.lms.lms == "moodle": self.hasForum = True break # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.scormType) manifest.save("imsmanifest.xml") if self.hasForum: manifest.save("discussionforum.xml") # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir / '..' / 'base.css'] styleFiles += [self.styleDir / '..' / 'popup_bg.gif'] styleFiles += [ f for f in self.styleDir.files("*.css") if f.basename() <> "nav.css" ] styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") # FIXME for now, only copy files referenced in Common Cartridge # this really should apply to all exports, but without a manifest # of the files needed by an included stylesheet it is too restrictive if self.scormType == "commoncartridge": for sf in styleFiles[:]: if sf.basename() not in manifest.dependencies: styleFiles.remove(sf) self.styleDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # Copy the scripts if self.scormType == "commoncartridge": self.scriptsDir.copylist(('libot_drag.js', 'common.js'), outputDir) else: self.scriptsDir.copylist(('APIWrapper.js', 'SCOFunctions.js', 'libot_drag.js', 'common.js'), outputDir) schemasDir = "" if self.scormType == "scorm1.2": schemasDir = self.schemasDir / "scorm1.2" schemasDir.copylist( ('imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd', 'adlcp_rootv1p2.xsd', 'ims_xml.xsd'), outputDir) elif self.scormType == "scorm2004": schemasDir = self.schemasDir / "scorm2004" schemasDir.copylist( ('imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd', 'adlcp_rootv1p2.xsd', 'ims_xml.xsd'), outputDir) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False isBreak = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'magnifier.swf' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(outputDir / 'flowPlayer.swf') if hasMagnifier: videofile = (self.templatesDir / 'magnifier.swf') videofile.copyfile(outputDir / 'magnifier.swf') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(outputDir / 'xspf_player.swf') if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if package.license == "GNU Free Documentation License": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl.html').copyfile(outputDir / 'fdl.html') # Zip it up! self.filename.safeSave( self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ # Zip up the scorm package zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = ScormPage(pageName, depth, child, scormType=self.scormType) self.pages.append(page) self.generatePages(child, depth + 1)
class ScormExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename, scormType): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.templatesDir = config.webDir/"templates" self.schemasDir = config.webDir/"schemas" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.hasForum = False self.scormType = scormType def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() # Export the package content self.pages = [ ScormPage("index", 1, package.root, scormType=self.scormType) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) if not self.hasForum: for idevice in page.node.idevices: if hasattr(idevice, "isForum"): if idevice.forum.lms.lms == "moodle": self.hasForum = True break # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.scormType) manifest.save("imsmanifest.xml") if self.hasForum: manifest.save("discussionforum.xml") # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir/'..'/'base.css'] styleFiles += [self.styleDir/'..'/'popup_bg.gif'] styleFiles += [f for f in self.styleDir.files("*.css") if f.basename() <> "nav.css"] styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") # FIXME for now, only copy files referenced in Common Cartridge # this really should apply to all exports, but without a manifest # of the files needed by an included stylesheet it is too restrictive if self.scormType == "commoncartridge": for sf in styleFiles[:]: if sf.basename() not in manifest.dependencies: styleFiles.remove(sf) self.styleDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # Copy the scripts if self.scormType == "commoncartridge": self.scriptsDir.copylist(('libot_drag.js', 'common.js'), outputDir) else: self.scriptsDir.copylist(('APIWrapper.js', 'SCOFunctions.js', 'libot_drag.js', 'common.js'), outputDir) schemasDir = "" if self.scormType == "scorm1.2": schemasDir = self.schemasDir/"scorm1.2" schemasDir.copylist(('imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd', 'adlcp_rootv1p2.xsd', 'ims_xml.xsd'), outputDir) elif self.scormType == "scorm2004": schemasDir = self.schemasDir/"scorm2004" schemasDir.copylist(('imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd', 'adlcp_rootv1p2.xsd', 'ims_xml.xsd'), outputDir) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False isBreak = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'magnifier.swf' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') if hasMagnifier: videofile = (self.templatesDir/'magnifier.swf') videofile.copyfile(outputDir/'magnifier.swf') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if package.license == "GNU Free Documentation License": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') # Zip it up! self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ # Zip up the scorm package zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = ScormPage(pageName, depth, child, scormType=self.scormType) self.pages.append(page) self.generatePages(child, depth + 1)
class Epub3Export(object): """ Exports an eXe package as a epub 3 package The 'Hello World' of a epub 3 publication might contain files: mimetype META-INF/container.xml Content/HelloWorld.opf Content/HelloWorld.xhtml """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export epub 3 package """ # First do the export to a temporary directory outputDir = TempDirPath() ''' fileDir = outputDir/"META-INF" fileDir.mkdir() fileDir = outputDir/"Content" fileDir.mkdir() ''' metainfPages = Path(outputDir.abspath() + '/META-INF') # metainfPages = outputDir/'META-INF' metainfPages.mkdir() contentPages = Path(outputDir.abspath() + '/EPUB') # contentPages = outputDir/'Content' contentPages.mkdir() # print contentPages.abspath() # print outputDir.abspath() # Export the package content self.pages = [Epub3Cover("cover", 1, package.root)] self.generatePages(package.root, 2) uniquifyNames(self.pages) cover = None for page in self.pages: page.save(contentPages, self.pages) if hasattr(page, 'cover'): cover = page.cover # Create mimetype file mimetypeFile = open(outputDir.abspath() + '/mimetype', "w") mimetypeFile.write('application/epub+zip') mimetypeFile.close() # Create common_i18n file langFile = open(contentPages + '/common_i18n.js', "w") langFile.write(common.getJavaScriptStrings(False)) langFile.close() # Copy the style files to the output dir # But not nav.css styleFiles = [self.styleDir /'..'/ 'popup_bg.gif'] styleFiles += [f for f in self.styleDir.files("*.*") if f.basename() not in ['nav.css']] # FIXME for now, only copy files referenced in Common Cartridge # this really should apply to all exports, but without a manifest # of the files needed by an included stylesheet it is too restrictive # Add fallback document for possible image links if Path(self.styleDir/'fallback.xhtml').exists(): styleFiles += [self.styleDir /'fallback.xhtml'] else: styleFiles += [self.styleDir/'..'/'fallback.xhtml'] # copy the package's resource files for resourceFile in package.resourceDir.walkfiles(): fn = package.resourceDir.relpathto(resourceFile) if ("/" in fn): Dir = Path(contentPages/fn[:fn.rindex("/")]) if not Dir.exists(): Dir.makedirs() resourceFile.copy(contentPages/Dir) else: resourceFile.copy(contentPages) self.styleDir.copylist(styleFiles, contentPages) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasElpLink = False hasWikipedia = False isBreak = False hasInstructions = False hasTooltips = False hasABCMusic = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasElpLink and hasWikipedia and hasInstructions and hasTooltips and hasABCMusic): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasElpLink: hasElpLink = common.ideviceHasElpLink(idevice,package) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if not hasABCMusic: hasABCMusic = common.ideviceHasABCMusic(idevice) common.exportJavaScriptIdevicesFiles(page.node.idevices, contentPages); if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(contentPages / 'flowPlayer.swf') controlsfile = (self.templatesDir / 'flowplayer.controls.swf') controlsfile.copyfile(contentPages / 'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir / 'mojomagnify.js') videofile.copyfile(contentPages / 'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(contentPages / 'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir / 'exe_lightbox') exeLightbox.copyfiles(contentPages) if hasFX: exeEffects = (self.scriptsDir / 'exe_effects') exeEffects.copyfiles(contentPages) if hasSH: exeSH = (self.scriptsDir / 'exe_highlighter') exeSH.copyfiles(contentPages) if hasGames: exeGames = (self.scriptsDir / 'exe_games') exeGames.copyfiles(contentPages) # Add game js string to common_i18n langGameFile = open(contentPages + '/common_i18n.js', "a") langGameFile.write(common.getGamesJavaScriptStrings(False)) langGameFile.close() if hasElpLink or package.get_exportElp(): # Export the elp file currentPackagePath = Path(package.filename) currentPackagePath.copyfile(contentPages/package.name+'.elp') if hasWikipedia: wikipediaCSS = (self.cssDir / 'exe_wikipedia.css') wikipediaCSS.copyfile(contentPages / 'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, contentPages) common.copyFileIfNotInStyle('stock-stop.png', self, contentPages) if hasTooltips: exe_tooltips = (self.scriptsDir / 'exe_tooltips') exe_tooltips.copyfiles(contentPages) if hasABCMusic: pluginScripts = (self.scriptsDir/'tinymce_4/js/tinymce/plugins/abcmusic/export') pluginScripts.copyfiles(contentPages) my_style = G.application.config.styleStore.getStyle(package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(contentPages / 'exe_jquery.js') else: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(contentPages / 'exe_jquery.js') # Copy and minify CSS files css_files = getFilesCSSToMinify('epub3', self.styleDir) exportMinFileCSS(css_files, contentPages) # Copy and minify JS files js_files = getFilesJSToMinify('epub3', self.scriptsDir) exportMinFileJS(js_files, contentPages) # if hasattr(package, 'exportSource') and package.exportSource: # (G.application.config.webDir / 'templates' / 'content.xsd').copyfile(outputDir / 'content.xsd') # (outputDir / 'content.data').write_bytes(encodeObject(package)) # (outputDir / 'contentv3.xml').write_bytes(encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl.html').copyfile(contentPages / 'fdl.html') # Create the nav.xhtml file container = NavEpub3(self.pages, contentPages) container.save() # Create the publication file publication = PublicationEpub3(self.config, contentPages, package, self.pages, cover) publication.save("package.opf") # Create the container file container = ContainerEpub3(metainfPages) container.save("container.xml") # Zip it up! self.filename.safeSave(self.doZip, _(u'EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") mimetype = outputDir / "mimetype" zipped.write(mimetype, "mimetype", ZIP_STORED) for epubFile in outputDir.walkfiles(): if epubFile.basename() == 'mimetype': continue relativePath = epubFile.basename() parentdir = epubFile.splitpath()[0] while (outputDir.basename() != parentdir.basename()): relativePath = parentdir.basename() / relativePath parentdir = parentdir.splitpath()[0] zipped.write(epubFile, relativePath.encode('utf8'), compress_type=ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ pageName = node.titleShort.lower().replace(" ", u"_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = u"__" if pageName[0] in [unicode(i) for i in range(0, 10)]: pageName = u'_' + pageName page = Epub3Page(pageName, depth, node) self.pages.append(page) for child in node.children: self.generatePages(child, depth + 1)
class IMSExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() self.metadataType = package.exportMetadataType # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir / '..' / 'base.css'] styleFiles += [self.styleDir / '..' / 'popup_bg.gif'] styleFiles += self.styleDir.files("*.css") if "nav.css" in styleFiles: styleFiles.remove("nav.css") styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") styleFiles += self.styleDir.files("*.ttf") styleFiles += self.styleDir.files("*.eot") styleFiles += self.styleDir.files("*.otf") styleFiles += self.styleDir.files("*.woff") self.styleDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # Export the package content self.pages = [ IMSPage("index", 1, package.root, metadataType=self.metadataType) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.metadataType) manifest.save("imsmanifest.xml") # Copy the scripts # jQuery my_style = G.application.config.styleStore.getStyle( page.node.package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(outputDir / 'exe_jquery.js') else: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(outputDir / 'exe_jquery.js') jsFile = (self.scriptsDir / 'common.js') jsFile.copyfile(outputDir / 'common.js') dT = common.getExportDocType() if dT == "HTML5": jsFile = (self.scriptsDir / 'exe_html5.js') jsFile.copyfile(outputDir / 'exe_html5.js') self.schemasDir.copylist(('imscp_v1p1.xsd', 'imsmd_v1p2p2.xsd', 'lom.xsd', 'lomCustom.xsd', 'ims_xml.xsd'), outputDir) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(outputDir / 'flowPlayer.swf') controlsfile = (self.templatesDir / 'flowplayer.controls.swf') controlsfile.copyfile(outputDir / 'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir / 'mojomagnify.js') videofile.copyfile(outputDir / 'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(outputDir / 'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir / 'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir / 'exe_effects') exeEffects.copyfiles(outputDir) if hasSH: exeSH = (self.scriptsDir / 'exe_highlighter') exeSH.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir / 'exe_games') exeGames.copyfiles(outputDir) if hasWikipedia: wikipediaCSS = (self.cssDir / 'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir / 'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir / 'mediaelement') mediaelement.copyfiles(outputDir) if dT != "HTML5": jsFile = (self.scriptsDir / 'exe_html5.js') jsFile.copyfile(outputDir / 'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir / 'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir / 'templates' / 'content.xsd').copyfile(outputDir / 'content.xsd') (outputDir / 'content.data').write_bytes(encodeObject(package)) (outputDir / 'contentv3.xml').write_bytes( encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl.html').copyfile(outputDir / 'fdl.html') # Zip it up! self.filename.safeSave( self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'outputDir' is the temporary directory that we are exporting to before creating zip file """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = IMSPage(pageName, depth, child, metadataType=self.metadataType) self.pages.append(page) self.generatePages(child, depth + 1)
class WebsiteExport(object): """ WebsiteExport will export a package as a website of HTML pages """ def __init__(self, config, styleDir, filename): """ 'stylesDir' is the directory where we can copy the stylesheets from 'outputDir' is the directory that will be [over]written with the website """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.templatesDir = config.webDir/"templates" self.stylesDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def exportZip(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = TempDirPath() self.copyFiles(package, outputDir) if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage("index", 1, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually saves the zip data. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def export(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = self.filename if not outputDir.exists(): outputDir.mkdir() self.copyFiles(package, outputDir) if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage("index", 1, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) def copyFiles(self, package, outputDir): """ Copy all the files used by the website. """ styleFiles = [self.stylesDir/'..'/'base.css'] styleFiles += [self.stylesDir/'..'/'popup_bg.gif'] styleFiles += self.stylesDir.files("*.css") styleFiles += self.stylesDir.files("*.jpg") styleFiles += self.stylesDir.files("*.gif") styleFiles += self.stylesDir.files("*.png") styleFiles += self.stylesDir.files("*.js") styleFiles += self.stylesDir.files("*.html") self.stylesDir.copylist(styleFiles, outputDir) package.resourceDir.copyfiles(outputDir) self.scriptsDir.copylist(('libot_drag.js', 'common.js'), outputDir) self.templatesDir.copylist(('videoContainer.swf', 'magnifier.swf', 'xspf_player.swf'), outputDir) (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') def generatePages(self, node, depth): """ Recursively generate pages and store in pages member variable for retrieving later """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" self.pages.append(WebsitePage(pageName, depth, child)) self.generatePages(child, depth + 1)
class ScormExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename, scormType): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.schemasDir = config.webDir/"schemas" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.hasForum = False self.scormType = scormType self.styleSecureMode = config.styleSecureMode def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() self.metadataType = package.exportMetadataType # copy the package's resource files for resourceFile in package.resourceDir.walkfiles(): file = package.resourceDir.relpathto(resourceFile) if ("/" in file): Dir = Path(outputDir/file[:file.rindex("/")]) if not Dir.exists(): Dir.makedirs() resourceFile.copy(outputDir/Dir) else: resourceFile.copy(outputDir) # copy the package's resource files, only non existant in outputDir # outputDirFiles = outputDir.files() # for rfile in package.resourceDir.files(): # if rfile not in outputDirFiles: # rfile.copy(outputDir) # copy the package's resource files, only indexed in package.resources # for md5 in package.resources.values(): # for resource in md5: # resource.path.copy(outputDir) # Export the package content # Import the Scorm Page class , if the secure mode is off. If the style has it's own page class # use that, else use the default one. if self.styleSecureMode=="0": if (self.styleDir/"scormpage.py").exists(): global ScormPage module = imp.load_source("ScormPage",self.styleDir/"scormpage.py") ScormPage = module.ScormPage self.pages = [ ScormPage("index", 1, package.root, scormType=self.scormType, metadataType=self.metadataType) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir, self.pages) if not self.hasForum: for idevice in page.node.idevices: if hasattr(idevice, "isForum"): if idevice.forum.lms.lms == "moodle": self.hasForum = True break # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.scormType, self.metadataType) modifiedMetaData = manifest.save("imsmanifest.xml") # Create lang file langFile = open(outputDir + '/common_i18n.js', "w") langFile.write(common.getJavaScriptStrings(False)) langFile.close() if self.hasForum: manifest.save("discussionforum.xml") # Copy the style files to the output dir styleFiles = [self.styleDir/'..'/'popup_bg.gif'] # And with all the files of the style we avoid problems: styleFiles += self.styleDir.files("*.*") if self.scormType == "commoncartridge": for sf in styleFiles[:]: if sf.basename() not in manifest.dependencies: styleFiles.remove(sf) self.styleDir.copylist(styleFiles, outputDir) listCSSFiles=getFilesCSSToMinify('scorm', self.styleDir) exportMinFileCSS(listCSSFiles, outputDir) # Copy the scripts dT = common.getExportDocType() if dT == "HTML5": #listFiles+=[self.scriptsDir/'exe_html5.js'] #listOutFiles+=[outputDir/'exe_html5.js'] jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') # jQuery my_style = G.application.config.styleStore.getStyle(page.node.package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: #listFiles+=[self.scriptsDir/'exe_jquery.js'] #listOutFiles+=[outputDir/'exe_jquery.js'] jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') else: #listFiles+=[self.scriptsDir/'exe_jquery.js'] #listOutFiles+=[outputDir/'exe_jquery.js'] jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') if self.scormType == "commoncartridge" or self.scormType == "scorm2004" or self.scormType == "scorm1.2": listFiles=getFilesJSToMinify('scorm', self.scriptsDir) exportMinFileJS(listFiles, outputDir) if self.scormType == "scorm2004" or self.scormType == "scorm1.2": self.scriptsDir.copylist(('SCORM_API_wrapper.js', 'SCOFunctions.js'), outputDir) # about SCHEMAS: schemasDir = "" if self.scormType == "scorm1.2": schemasDir = self.schemasDir/"scorm1.2" schemasDir.copylist(('imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd', 'adlcp_rootv1p2.xsd', 'lom.xsd', 'lomCustom.xsd', 'ims_xml.xsd'), outputDir) elif self.scormType == "scorm2004": schemasDir = self.schemasDir/"scorm2004" schemasDir.copylist(('adlcp_v1p3.xsd', 'adlnav_v1p3.xsd', 'adlseq_v1p3.xsd', 'datatypes.dtd', 'imscp_v1p1.xsd', 'imsssp_v1p0.xsd', 'imsss_v1p0.xsd', 'imsss_v1p0auxresource.xsd', 'imsss_v1p0control.xsd', 'imsss_v1p0delivery.xsd', 'imsmd_rootv1p2p1.xsd', 'imsss_v1p0limit.xsd', 'imsss_v1p0objective.xsd', 'imsss_v1p0random.xsd', 'imsss_v1p0rollup.xsd', 'imsss_v1p0seqrule.xsd', 'imsss_v1p0util.xsd', 'ims_xml.xsd', 'lom.xsd', 'lomCustom.xsd', 'xml.xsd', 'XMLSchema.dtd'), outputDir) try: import shutil, errno shutil.copytree(schemasDir/"common", outputDir/"common") shutil.copytree(schemasDir/"extend", outputDir/"extend") shutil.copytree(schemasDir/"unique", outputDir/"unique") shutil.copytree(schemasDir/"vocab", outputDir/"vocab") except OSError as exc: if exc.errno == errno.ENOTDIR: shutil.copy(schemasDir, outputDir) else: raise # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasElpLink = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False hasABCMusic = False listIdevicesFiles = [] for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasElpLink and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips and hasABCMusic): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasElpLink: hasElpLink = common.ideviceHasElpLink(idevice,package) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if not hasABCMusic: hasABCMusic = common.ideviceHasABCMusic(idevice) if hasattr(idevice, "_iDeviceDir"): listIdevicesFiles.append((Path(idevice._iDeviceDir)/'export')) common.exportJavaScriptIdevicesFiles(page.node.idevices, outputDir); if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') controlsfile = (self.templatesDir/'flowplayer.controls.swf') controlsfile.copyfile(outputDir/'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir/'mojomagnify.js') videofile.copyfile(outputDir/'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir/'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir/'exe_effects') exeEffects.copyfiles(outputDir) if hasSH: exeSH = (self.scriptsDir/'exe_highlighter') exeSH.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir/'exe_games') exeGames.copyfiles(outputDir) # Add game js string to common_i18n langGameFile = open(outputDir + '/common_i18n.js', "a") langGameFile.write(common.getGamesJavaScriptStrings(False)) langGameFile.close() if hasElpLink or package.get_exportElp(): # Export the elp file currentPackagePath = Path(package.filename) currentPackagePath.copyfile(outputDir/package.name+'.elp') if hasWikipedia: wikipediaCSS = (self.cssDir/'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir/'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir/'mediaelement') mediaelement.copyfiles(outputDir) if dT != "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir/'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasABCMusic: pluginScripts = (self.scriptsDir/'tinymce_4/js/tinymce/plugins/abcmusic/export') pluginScripts.copyfiles(outputDir) ext = ".html" if G.application.config.cutFileName == "1": ext = ".htm" if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl' + ext).copyfile(outputDir/'fdl' + ext) if hasattr(package, 'scowsinglepage') and package.scowsinglepage: page = SinglePage("singlepage_index", 1, package.root) page.save(outputDir/"singlepage_index" + ext) # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir/package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = (self.imagesDir/'favicon.ico') faviconFile.copyfile(outputDir/'favicon.ico') if hasattr(package, 'scowwebsite') and package.scowwebsite: website = WebsiteExport(self.config, self.styleDir, outputDir, "website_") website.export(package) (self.styleDir/'nav.css').copyfile(outputDir/'nav.css') # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir/package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = (self.imagesDir/'favicon.ico') faviconFile.copyfile(outputDir/'favicon.ico') if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir/'templates'/'content.xsd').copyfile(outputDir/'content.xsd') (outputDir/'content.data').write_bytes(encodeObject(package)) (outputDir/'contentv3.xml').write_bytes(encodeObjectToXML(package)) # Zip it up! self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() return modifiedMetaData def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ # Zip up the scorm package zipped = ZipFile(fileObj, "w") ## old method: only files could be copied: # for scormFile in outputDir.files(): # zipped.write(scormFile, # scormFile.basename().encode('utf8'), # ZIP_DEFLATED) ## but some folders must be included also, so: outputlen = len(outputDir) + 1 for base, dirs, files in os.walk(outputDir): for file in files: fn = os.path.join(base, file) zipped.write(fn, fn[outputlen:].encode('utf8'), ZIP_DEFLATED) # zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = ScormPage(pageName, depth, child, scormType=self.scormType, metadataType=self.metadataType) self.pages.append(page) self.generatePages(child, depth + 1) def hasUncutResources(self): """ Check if any of the resources in the exported package has an uncut filename """ for page in self.pages: for idevice in page.node.idevices: for resource in idevice.userResources: if type(resource) == Resource and len(resource.storageName) > 12: return True return False
class ScormExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename, scormType, metadataType='DC'): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.schemasDir = config.webDir/"schemas" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.hasForum = False self.scormType = scormType self.metadataType = metadataType def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() # copy the package's resource files package.resourceDir.copyfiles(outputDir) # copy the package's resource files, only non existant in outputDir # outputDirFiles = outputDir.files() # for rfile in package.resourceDir.files(): # if rfile not in outputDirFiles: # rfile.copy(outputDir) # copy the package's resource files, only indexed in package.resources # for md5 in package.resources.values(): # for resource in md5: # resource.path.copy(outputDir) # Export the package content self.pages = [ ScormPage("index", 1, package.root, scormType=self.scormType, metadataType=self.metadataType) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) if not self.hasForum: for idevice in page.node.idevices: if hasattr(idevice, "isForum"): if idevice.forum.lms.lms == "moodle": self.hasForum = True break # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.scormType, self.metadataType) manifest.save("imsmanifest.xml") if self.hasForum: manifest.save("discussionforum.xml") # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir/'..'/'base.css'] styleFiles += [self.styleDir/'..'/'popup_bg.gif'] styleFiles += [f for f in self.styleDir.files("*.css") if f.basename() <> "nav.css"] styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") styleFiles += self.styleDir.files("*.ttf") styleFiles += self.styleDir.files("*.eot") styleFiles += self.styleDir.files("*.otf") styleFiles += self.styleDir.files("*.woff") # FIXME for now, only copy files referenced in Common Cartridge # this really should apply to all exports, but without a manifest # of the files needed by an included stylesheet it is too restrictive if self.scormType == "commoncartridge": for sf in styleFiles[:]: if sf.basename() not in manifest.dependencies: styleFiles.remove(sf) self.styleDir.copylist(styleFiles, outputDir) # Copy the scripts if self.scormType == "commoncartridge": self.scriptsDir.copylist(('libot_drag.js', 'common.js'), outputDir) if self.scormType == "scorm2004": self.scriptsDir.copylist(('AC_RunActiveContent.js', 'SCORM_API_wrapper.js', 'SCOFunctions.js', 'libot_drag.js', 'common.js'), outputDir) if self.scormType != "commoncartridge" and self.scormType != "scorm2004": self.scriptsDir.copylist(('APIWrapper.js', 'SCOFunctions.js', 'libot_drag.js', 'common.js'), outputDir) schemasDir = "" if self.scormType == "scorm1.2": schemasDir = self.schemasDir/"scorm1.2" schemasDir.copylist(('imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd', 'adlcp_rootv1p2.xsd', 'ims_xml.xsd'), outputDir) elif self.scormType == "scorm2004": schemasDir = self.schemasDir/"scorm2004" schemasDir.copylist(('adlcp_v1p3.xsd', 'adlnav_v1p3.xsd', 'adlseq_v1p3.xsd', 'datatypes.dtd', 'imscp_v1p1.xsd', 'imsssp_v1p0.xsd', 'imsss_v1p0.xsd', 'imsss_v1p0auxresource.xsd', 'imsss_v1p0control.xsd', 'imsss_v1p0delivery.xsd', 'imsss_v1p0limit.xsd', 'imsss_v1p0objective.xsd', 'imsss_v1p0random.xsd', 'imsss_v1p0rollup.xsd', 'imsss_v1p0seqrule.xsd', 'imsss_v1p0util.xsd', 'ims_xml.xsd', 'lom.xsd', 'lomCustom.xsd', 'xml.xsd', 'XMLSchema.dtd'), outputDir) try: import shutil, errno shutil.copytree(schemasDir/"lom", outputDir/"lom") except OSError as exc: if exc.errno == errno.ENOTDIR: shutil.copy(schemasDir/"lom", outputDir/"lom") else: raise # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False isBreak = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'magnifier.swf' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: if 'GalleryIdevice' == idevice.klass: hasGallery = True if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') controlsfile = (self.templatesDir/'flowplayer.controls.swf') controlsfile.copyfile(outputDir/'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir/'magnifier.swf') videofile.copyfile(outputDir/'magnifier.swf') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if hasGallery: imageGalleryCSS = (self.cssDir/'exe_lightbox.css') imageGalleryCSS.copyfile(outputDir/'exe_lightbox.css') imageGalleryJS = (self.scriptsDir/'exe_lightbox.js') imageGalleryJS.copyfile(outputDir/'exe_lightbox.js') self.imagesDir.copylist(('exeGallery_actions.png', 'exeGallery_loading.gif', 'stock-insert-image.png'), outputDir) if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if package.license == "GNU Free Documentation License": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') if hasattr(package, 'scowsinglepage') and package.scowsinglepage: page = SinglePage("singlepage_index", 1, package.root) page.save(outputDir/"singlepage_index.html") if hasattr(package, 'scowwebsite') and package.scowwebsite: website = WebsiteExport(self.config, self.styleDir, outputDir, "website_") website.export(package) (self.styleDir/'nav.css').copyfile(outputDir/'nav.css') if hasattr(package, 'scowsource') and package.scowsource: (G.application.config.webDir/'templates'/'content.xsd').copyfile(outputDir/'content.xsd') (outputDir/'content.data').write_bytes(encodeObject(package)) (outputDir/'contentv2.xml').write_bytes(encodeObjectToXML(package)) # Zip it up! self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ # Zip up the scorm package zipped = ZipFile(fileObj, "w") ## old method: only files could be copied: # for scormFile in outputDir.files(): # zipped.write(scormFile, # scormFile.basename().encode('utf8'), # ZIP_DEFLATED) ## but some folders must be included also, so: outputlen = len(outputDir) + 1 for base, dirs, files in os.walk(outputDir): for file in files: fn = os.path.join(base, file) zipped.write(fn, fn[outputlen:].encode('utf8'), ZIP_DEFLATED) # zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = ScormPage(pageName, depth, child, scormType=self.scormType, metadataType=self.metadataType) self.pages.append(page) self.generatePages(child, depth + 1)
class IMSExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.schemasDir = config.webDir/"schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() self.metadataType = package.exportMetadataType # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir/'..'/'popup_bg.gif'] styleFiles += self.styleDir.files("*.css") if "nav.css" in styleFiles: styleFiles.remove("nav.css") styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") styleFiles += self.styleDir.files("*.ttf") styleFiles += self.styleDir.files("*.eot") styleFiles += self.styleDir.files("*.otf") styleFiles += self.styleDir.files("*.woff") self.styleDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) listCSSFiles=getFilesCSSToMinify('ims', self.styleDir) exportMinFileCSS(listCSSFiles, outputDir) # Export the package content self.pages = [ IMSPage("index", 1, package.root, metadataType=self.metadataType) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.metadataType) manifest.save("imsmanifest.xml") # Copy the scripts # jQuery my_style = G.application.config.styleStore.getStyle(page.node.package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') else: jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') dT = common.getExportDocType() if dT == "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') listFiles=getFilesJSToMinify('ims', self.scriptsDir) exportMinFileJS(listFiles, outputDir) self.schemasDir.copylist(('imscp_v1p1.xsd', 'imsmd_v1p2p2.xsd', 'lom.xsd', 'lomCustom.xsd', 'ims_xml.xsd'), outputDir) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') controlsfile = (self.templatesDir/'flowplayer.controls.swf') controlsfile.copyfile(outputDir/'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir/'mojomagnify.js') videofile.copyfile(outputDir/'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir/'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir/'exe_effects') exeEffects.copyfiles(outputDir) if hasSH: exeSH = (self.scriptsDir/'exe_highlighter') exeSH.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir/'exe_games') exeGames.copyfiles(outputDir) if hasWikipedia: wikipediaCSS = (self.cssDir/'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir/'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir/'mediaelement') mediaelement.copyfiles(outputDir) if dT != "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir/'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir/'templates'/'content.xsd').copyfile(outputDir/'content.xsd') (outputDir/'content.data').write_bytes(encodeObject(package)) (outputDir/'contentv3.xml').write_bytes(encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') # Zip it up! self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'outputDir' is the temporary directory that we are exporting to before creating zip file """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = IMSPage(pageName, depth, child, metadataType=self.metadataType) self.pages.append(page) self.generatePages(child, depth + 1)
class IMSExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.templatesDir = config.webDir/"templates" self.schemasDir = config.webDir/"schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export SCORM package """ outputDir = TempDirPath() styleFiles = [self.styleDir/'..'/'base.css'] styleFiles += [self.styleDir/'..'/'popup_bg.gif'] styleFiles += self.styleDir.files("*.css") if "nav.css" in styleFiles: styleFiles.remove("nav.css") styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") self.styleDir.copylist(styleFiles, outputDir) package.resourceDir.copyfiles(outputDir) self.pages = [ IMSPage("index", 1, package.root) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) manifest = Manifest(self.config, outputDir, package, self.pages) manifest.save() self.scriptsDir.copylist(('libot_drag.js', 'common.js'), outputDir) self.schemasDir.copylist(('imscp_v1p1.xsd', 'imsmd_v1p2p2.xsd', 'ims_xml.xsd'), outputDir) hasFlowplayer = False hasMagnifier = False hasXspfplayer = False isBreak = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'magnifier.swf' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') if hasMagnifier: videofile = (self.templatesDir/'magnifier.swf') videofile.copyfile(outputDir/'magnifier.swf') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'outputDir' is the temporary directory that we are exporting to before creating zip file """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = IMSPage(pageName, depth, child) self.pages.append(page) self.generatePages(child, depth + 1)
class IMSExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.schemasDir = config.webDir/"schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() self.metadataType = package.exportMetadataType # Copy the style files to the output dir # But not nav.css styleFiles = [self.styleDir/'..'/'popup_bg.gif'] styleFiles += self.styleDir.files("*.*") if "nav.css" in styleFiles: styleFiles.remove("nav.css") self.styleDir.copylist(styleFiles, outputDir) # copy the package's resource files for resourceFile in package.resourceDir.walkfiles(): file = package.resourceDir.relpathto(resourceFile) if ("/" in file): Dir = Path(outputDir/file[:file.rindex("/")]) if not Dir.exists(): Dir.makedirs() resourceFile.copy(outputDir/Dir) else: resourceFile.copy(outputDir) listCSSFiles=getFilesCSSToMinify('ims', self.styleDir) exportMinFileCSS(listCSSFiles, outputDir) # Export the package content self.pages = [ IMSPage("index", 1, package.root, metadataType=self.metadataType) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir, self.pages) # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.metadataType) manifest.save("imsmanifest.xml") # Create lang file langGameFile = open(outputDir + '/common_i18n.js', "w") langGameFile.write(common.getJavaScriptStrings(False)) langGameFile.close() # jQuery my_style = G.application.config.styleStore.getStyle(page.node.package.style) if my_style.hasValidConfig(): if my_style.get_jquery() == True: jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') else: jsFile = (self.scriptsDir/'exe_jquery.js') jsFile.copyfile(outputDir/'exe_jquery.js') dT = common.getExportDocType() if dT == "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') listFiles=getFilesJSToMinify('ims', self.scriptsDir) exportMinFileJS(listFiles, outputDir) self.schemasDir.copylist(('imscp_v1p1.xsd', 'imsmd_v1p2p2.xsd', 'lom.xsd', 'lomCustom.xsd', 'ims_xml.xsd'), outputDir) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasElpLink = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False hasABCMusic = False listIdevicesFiles = [] for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasElpLink and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips and hasABCMusic): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasElpLink: hasElpLink = common.ideviceHasElpLink(idevice,package) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if not hasABCMusic: hasABCMusic = common.ideviceHasABCMusic(idevice) if hasattr(idevice, "_iDeviceDir"): listIdevicesFiles.append((idevice.get_jsidevice_dir()/'export')) common.exportJavaScriptIdevicesFiles(page.node.idevices, outputDir); if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') controlsfile = (self.templatesDir/'flowplayer.controls.swf') controlsfile.copyfile(outputDir/'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir/'mojomagnify.js') videofile.copyfile(outputDir/'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir/'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir/'exe_effects') exeEffects.copyfiles(outputDir) if hasSH: exeSH = (self.scriptsDir/'exe_highlighter') exeSH.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir/'exe_games') exeGames.copyfiles(outputDir) # Add game js string to common_i18n langGameFile = open(outputDir + '/common_i18n.js', "a") langGameFile.write(common.getGamesJavaScriptStrings(False)) langGameFile.close() if hasElpLink or package.get_exportElp(): # Export the elp file currentPackagePath = Path(package.filename) currentPackagePath.copyfile(outputDir/package.name+'.elp') if hasWikipedia: wikipediaCSS = (self.cssDir/'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir/'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir/'mediaelement') mediaelement.copyfiles(outputDir) if dT != "HTML5": jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(outputDir/'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir/'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasABCMusic: pluginScripts = (self.scriptsDir/'tinymce_4/js/tinymce/plugins/abcmusic/export') pluginScripts.copyfiles(outputDir) if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir/'templates'/'content.xsd').copyfile(outputDir/'content.xsd') (outputDir/'content.data').write_bytes(encodeObject(package)) (outputDir/'contentv3.xml').write_bytes(encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') # Zip it up! self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.walkfiles(): zipped.write(scormFile, outputDir.relpathto(scormFile), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'outputDir' is the temporary directory that we are exporting to before creating zip file """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if G.application.config.cutFileName == "1": pageName = pageName[0:8] if not pageName: pageName = "__" page = IMSPage(pageName, depth, child, metadataType=self.metadataType) self.pages.append(page) self.generatePages(child, depth + 1) def hasUncutResources(self): """ Check if any of the resources in the exported package has an uncut filename """ for page in self.pages: for idevice in page.node.idevices: for resource in idevice.userResources: if type(resource) == Resource and len(resource.storageName) > 12: return True return False
class ScormExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename, scormType): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.hasForum = False self.scormType = scormType self.styleSecureMode = config.styleSecureMode def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() self.metadataType = package.exportMetadataType # copy the package's resource files for resourceFile in package.resourceDir.walkfiles(): file = package.resourceDir.relpathto(resourceFile) if ("/" in file): Dir = Path(outputDir / file[:file.rindex("/")]) if not Dir.exists(): Dir.makedirs() resourceFile.copy(outputDir / Dir) else: resourceFile.copy(outputDir) # copy the package's resource files, only non existant in outputDir # outputDirFiles = outputDir.files() # for rfile in package.resourceDir.files(): # if rfile not in outputDirFiles: # rfile.copy(outputDir) # copy the package's resource files, only indexed in package.resources # for md5 in package.resources.values(): # for resource in md5: # resource.path.copy(outputDir) # Export the package content # Import the Scorm Page class , if the secure mode is off. If the style has it's own page class # use that, else use the default one. if self.styleSecureMode == "0": if (self.styleDir / "scormpage.py").exists(): global ScormPage module = imp.load_source("ScormPage", self.styleDir / "scormpage.py") ScormPage = module.ScormPage self.pages = [ ScormPage("index", 1, package.root, scormType=self.scormType, metadataType=self.metadataType) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir, self.pages) if not self.hasForum: for idevice in page.node.idevices: if hasattr(idevice, "isForum"): if idevice.forum.lms.lms == "moodle": self.hasForum = True break # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages, self.scormType, self.metadataType) modifiedMetaData = manifest.save("imsmanifest.xml") # Create lang file langFile = open(outputDir + '/common_i18n.js', "w") langFile.write(common.getJavaScriptStrings(False)) langFile.close() if self.hasForum: manifest.save("discussionforum.xml") # Copy the style files to the output dir styleFiles = [self.styleDir / '..' / 'popup_bg.gif'] # And with all the files of the style we avoid problems: styleFiles += self.styleDir.files("*.*") if self.scormType == "commoncartridge": for sf in styleFiles[:]: if sf.basename() not in manifest.dependencies: styleFiles.remove(sf) self.styleDir.copylist(styleFiles, outputDir) listCSSFiles = getFilesCSSToMinify('scorm', self.styleDir) exportMinFileCSS(listCSSFiles, outputDir) # Copy the scripts dT = common.getExportDocType() if dT == "HTML5": #listFiles+=[self.scriptsDir/'exe_html5.js'] #listOutFiles+=[outputDir/'exe_html5.js'] jsFile = (self.scriptsDir / 'exe_html5.js') jsFile.copyfile(outputDir / 'exe_html5.js') # jQuery my_style = G.application.config.styleStore.getStyle( page.node.package.style) if my_style.hasValidConfig(): if my_style.get_jquery() == True: #listFiles+=[self.scriptsDir/'exe_jquery.js'] #listOutFiles+=[outputDir/'exe_jquery.js'] jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(outputDir / 'exe_jquery.js') else: #listFiles+=[self.scriptsDir/'exe_jquery.js'] #listOutFiles+=[outputDir/'exe_jquery.js'] jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(outputDir / 'exe_jquery.js') if self.scormType == "commoncartridge" or self.scormType == "scorm2004" or self.scormType == "scorm1.2": listFiles = getFilesJSToMinify('scorm', self.scriptsDir) exportMinFileJS(listFiles, outputDir) if self.scormType == "scorm2004" or self.scormType == "scorm1.2": self.scriptsDir.copylist( ('SCORM_API_wrapper.js', 'SCOFunctions.js'), outputDir) # about SCHEMAS: schemasDir = "" if self.scormType == "scorm1.2": schemasDir = self.schemasDir / "scorm1.2" schemasDir.copylist(('imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd', 'adlcp_rootv1p2.xsd', 'lom.xsd', 'lomCustom.xsd', 'ims_xml.xsd'), outputDir) elif self.scormType == "scorm2004": schemasDir = self.schemasDir / "scorm2004" schemasDir.copylist( ('adlcp_v1p3.xsd', 'adlnav_v1p3.xsd', 'adlseq_v1p3.xsd', 'datatypes.dtd', 'imscp_v1p1.xsd', 'imsssp_v1p0.xsd', 'imsss_v1p0.xsd', 'imsss_v1p0auxresource.xsd', 'imsss_v1p0control.xsd', 'imsss_v1p0delivery.xsd', 'imsmd_rootv1p2p1.xsd', 'imsss_v1p0limit.xsd', 'imsss_v1p0objective.xsd', 'imsss_v1p0random.xsd', 'imsss_v1p0rollup.xsd', 'imsss_v1p0seqrule.xsd', 'imsss_v1p0util.xsd', 'ims_xml.xsd', 'lom.xsd', 'lomCustom.xsd', 'xml.xsd', 'XMLSchema.dtd'), outputDir) try: import shutil, errno shutil.copytree(schemasDir / "common", outputDir / "common") shutil.copytree(schemasDir / "extend", outputDir / "extend") shutil.copytree(schemasDir / "unique", outputDir / "unique") shutil.copytree(schemasDir / "vocab", outputDir / "vocab") except OSError as exc: if exc.errno == errno.ENOTDIR: shutil.copy(schemasDir, outputDir) else: raise # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasElpLink = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False hasABCMusic = False listIdevicesFiles = [] for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasElpLink and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips and hasABCMusic): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasElpLink: hasElpLink = common.ideviceHasElpLink(idevice, package) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if not hasABCMusic: hasABCMusic = common.ideviceHasABCMusic(idevice) if hasattr(idevice, "_iDeviceDir"): listIdevicesFiles.append( (idevice.get_jsidevice_dir() / 'export')) common.exportJavaScriptIdevicesFiles(page.node.idevices, outputDir) if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(outputDir / 'flowPlayer.swf') controlsfile = (self.templatesDir / 'flowplayer.controls.swf') controlsfile.copyfile(outputDir / 'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir / 'mojomagnify.js') videofile.copyfile(outputDir / 'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(outputDir / 'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir / 'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir / 'exe_effects') exeEffects.copyfiles(outputDir) if hasSH: exeSH = (self.scriptsDir / 'exe_highlighter') exeSH.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir / 'exe_games') exeGames.copyfiles(outputDir) # Add game js string to common_i18n langGameFile = open(outputDir + '/common_i18n.js', "a") langGameFile.write(common.getGamesJavaScriptStrings(False)) langGameFile.close() if hasElpLink or package.get_exportElp(): # Export the elp file currentPackagePath = Path(package.filename) currentPackagePath.copyfile(outputDir / package.name + '.elp') if hasWikipedia: wikipediaCSS = (self.cssDir / 'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir / 'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir / 'mediaelement') mediaelement.copyfiles(outputDir) if dT != "HTML5": jsFile = (self.scriptsDir / 'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir / 'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasABCMusic: pluginScripts = (self.scriptsDir / 'tinymce_4/js/tinymce/plugins/abcmusic/export') pluginScripts.copyfiles(outputDir) ext = ".html" if G.application.config.cutFileName == "1": ext = ".htm" if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl' + ext).copyfile(outputDir / 'fdl' + ext) if hasattr(package, 'scowsinglepage') and package.scowsinglepage: page = SinglePage("singlepage_index", 1, package.root) page.save(outputDir / "singlepage_index" + ext) # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir / package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = (self.imagesDir / 'favicon.ico') faviconFile.copyfile(outputDir / 'favicon.ico') if hasattr(package, 'scowwebsite') and package.scowwebsite: website = WebsiteExport(self.config, self.styleDir, outputDir, "website_") website.export(package) (self.styleDir / 'nav.css').copyfile(outputDir / 'nav.css') # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir / package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = (self.imagesDir / 'favicon.ico') faviconFile.copyfile(outputDir / 'favicon.ico') if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir / 'templates' / 'content.xsd').copyfile(outputDir / 'content.xsd') (outputDir / 'content.data').write_bytes(encodeObject(package)) (outputDir / 'contentv3.xml').write_bytes( encodeObjectToXML(package)) # Zip it up! self.filename.safeSave( self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() return modifiedMetaData def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ # Zip up the scorm package zipped = ZipFile(fileObj, "w") ## old method: only files could be copied: # for scormFile in outputDir.files(): # zipped.write(scormFile, # scormFile.basename().encode('utf8'), # ZIP_DEFLATED) ## but some folders must be included also, so: outputlen = len(outputDir) + 1 for base, dirs, files in os.walk(outputDir): for file in files: fn = os.path.join(base, file) zipped.write(fn, fn[outputlen:].encode('utf8'), ZIP_DEFLATED) # zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = ScormPage(pageName, depth, child, scormType=self.scormType, metadataType=self.metadataType) self.pages.append(page) self.generatePages(child, depth + 1) def hasUncutResources(self): """ Check if any of the resources in the exported package has an uncut filename """ for page in self.pages: for idevice in page.node.idevices: for resource in idevice.userResources: if type(resource) == Resource and len( resource.storageName) > 12: return True return False
class IMSExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.schemasDir = config.webDir/"schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir/'..'/'base.css'] styleFiles += [self.styleDir/'..'/'popup_bg.gif'] styleFiles += self.styleDir.files("*.css") if "nav.css" in styleFiles: styleFiles.remove("nav.css") styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") styleFiles += self.styleDir.files("*.ttf") styleFiles += self.styleDir.files("*.eot") styleFiles += self.styleDir.files("*.otf") styleFiles += self.styleDir.files("*.woff") self.styleDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # Export the package content self.pages = [ IMSPage("index", 1, package.root) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages) manifest.save() # Copy the scripts self.scriptsDir.copylist(('libot_drag.js', 'common.js'), outputDir) self.schemasDir.copylist(('imscp_v1p1.xsd', 'imsmd_v1p2p2.xsd', 'ims_xml.xsd'), outputDir) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False isBreak = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'magnifier.swf' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: if 'GalleryIdevice' == idevice.klass: hasGallery = True if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') controlsfile = (self.templatesDir/'flowplayer.controls.swf') controlsfile.copyfile(outputDir/'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir/'magnifier.swf') videofile.copyfile(outputDir/'magnifier.swf') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if hasGallery: imageGalleryCSS = (self.cssDir/'exe_lightbox.css') imageGalleryCSS.copyfile(outputDir/'exe_lightbox.css') imageGalleryJS = (self.scriptsDir/'exe_lightbox.js') imageGalleryJS.copyfile(outputDir/'exe_lightbox.js') self.imagesDir.copylist(('exeGallery_actions.png', 'exeGallery_loading.gif', 'stock-insert-image.png'), outputDir) if package.license == "GNU Free Documentation License": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') # Zip it up! self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'outputDir' is the temporary directory that we are exporting to before creating zip file """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = IMSPage(pageName, depth, child) self.pages.append(page) self.generatePages(child, depth + 1)
class WebsiteExport(object): """ WebsiteExport will export a package as a website of HTML pages """ def __init__(self, config, styleDir, filename, prefix="", report=False): """ 'stylesDir' is the directory where we can copy the stylesheets from 'outputDir' is the directory that will be [over]written with the website """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.stylesDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.prefix = prefix self.report = report self.styleSecureMode = config.styleSecureMode def gDriveNotificationStatus(self, client, mesg): client.sendScript( "eXe.controller.eXeViewport.prototype.gDriveNotificationStatus('%s');" % (mesg), filter_func=allSessionClients) def gDriveNotificationNotice(self, client, mesg, type): client.sendScript( "eXe.controller.eXeViewport.prototype.gDriveNotificationNotice('%s', '%s');" % (mesg, type), filter_func=allSessionClients) # def exportGoogleDrive(self, package, client, auth_token, user_agent): # """ # Creates an authorized HTTP conexion, exports the current package as # 'webSite' to a temporary directory and uploads the exported files to # Google Drive # """ # num_total = 0; # file_num = 0; # # def finishExport(public_folder, outputDir): # self.gDriveNotificationStatus(client, _(u'Deleting temporary directory')) # rmtree(outputDir) # # link_url = 'http://googledrive.com/host/%s'%(public_folder['id']) # link_text = public_folder['title'] # self.gDriveNotificationStatus(client, _(u'Package exported to <a href="%s" target="_blank" title="Click to visit exported site">%s</a>') %(link_url,link_text)) # return public_folder # # def insertFile(public_folder, drive, upload_file, file_num): # """ # Creates the deferred that will upload files to the public folder # once it is created # """ # upload_content_d = threads.deferToThread(uploadContent, public_folder, drive, upload_file, file_num) # upload_content_d.addCallbacks(uploadContent_onSuccess, uploadContent_onFail) # # return upload_content_d # # def uploadContent(public_folder, drive, upload_file, file_num): # """ # Uploads one file to the given GDrive folder # """ # try : # filepath = os.path.join(self.filename, upload_file) # filetype = mimetypes.guess_type(filepath, False) # # if filetype[0] is None : # # Hard-coded types for special cases not detected by mimetypes.guess_type() # if upload_file == 'content.data' : # filetype = ('application/octet-stream', None) # # # if filetype[0] is not None : # link_url = 'http://googledrive.com/host/%s'%(public_folder['id']) # link_text = public_folder['title'] # #self.gDriveNotificationStatus(client, _(u'Package exported to <a href="%s" target="_blank" title="Click to visit exported site">%s</<a>') %(link_url,link_text)) # self.gDriveNotificationStatus(client, _(u'Uploading <em>%s</em> to public folder <a href="%s" target="_blank" title="Click to visit exported site">%s</a> (%d/%d)') # %(upload_file, link_url, link_text, file_num, num_total)) # mimetype = filetype[0] # meta = { # 'title': upload_file, # 'mimeType': mimetype, # 'parents' : [{'id' : public_folder['id']}] # } # media_body = MediaFileUpload(filepath, mimetype, resumable=True) # drive.files().insert(body=meta, media_body=media_body).execute() # else : # self.gDriveNotificationNotice(client, _(u'File <em>%s</em> skipped, unknown filetype (%d/%d)') %(upload_file, file_num, num_total), 'warning') # except Exception, e : # log.error(str(e)) # self.gDriveNotificationNotice(client, _(u'Failed upload of file %s (%d/%d)') % (upload_file, file_num, num_total), 'error') # # return public_folder # # def uploadContent_onFail(err): # log.error(str(err)) # self.gDriveNotificationNotice(client, _(u'Failed exporting to GoogleDrive'), 'error') # return err # # def uploadContent_onSuccess(public_folder): # # self.gDriveNotificationStatus(client, _(u'Exported to GoogleDrive: %s') % public_folder['title']) # return public_folder # # def publicFolder(drive, folder_name): # """ # Creates a Public Web folder, that can be read as a web site with any # web browser, and populates it with the content of the given directory # """ # # # Create public folder # self.gDriveNotificationStatus(client, _(u'Creating public folder to host published web site')) # body = { # 'title': folder_name, # 'mimeType': 'application/vnd.google-apps.folder' # } # public_folder = drive.files().insert(body=body).execute() # # permission = { # 'value': '', # 'type': 'anyone', # 'role': 'reader' # } # drive.permissions().insert(fileId=public_folder['id'], body=permission).execute() # # return public_folder # # def publicFolder_onFail(err): # log.error(str(err)) # self.gDriveNotificationNotice(client, _(u'Failed exporting to GoogleDrive'), 'error') # return err # # def publicFolder_onSuccess(public_folder): # self.gDriveNotificationStatus(client, _(u'Created public folder to host published web site: %s') % (public_folder['title'])) # return public_folder # # try: # # Creates a new temporary dir to export the package to, it will be deleted # # once the export process is finished # self.filename = Path(mkdtemp()) # self.gDriveNotificationStatus(client, _(u'Exporting package as web site in: %s') % (jquote(self.filename))) # outputDir = self.export(package) # # self.gDriveNotificationStatus(client, _(u'Starting authorized connection to Google Drive API')) # credentials = AccessTokenCredentials(auth_token, user_agent) # ca_certs = None # if hasattr(sys, 'frozen'): # ca_certs = 'cacerts.txt' # http = httplib2.Http(ca_certs=ca_certs) # http = credentials.authorize(http) # drive_service = build('drive', 'v2', http=http) # # publicFolder_d = threads.deferToThread(publicFolder, drive_service, package.name) # publicFolder_d.addCallbacks(publicFolder_onSuccess, publicFolder_onFail) # # # Loop through files in exportDir, each upload will be called from # # a chained callback function. These callbacks will return a # # deferred so the file N upload will start when file N-1 has # # successfully called its own callback # # (Deferred chain, see: http://krondo.com/?p=2159#attachment_2196) # for upload_file in os.listdir(outputDir): # file_num = file_num + 1 # publicFolder_d.addCallback(insertFile, drive_service, upload_file, file_num) # # num_total = file_num # publicFolder_d.addCallback(finishExport, outputDir) # # # TODO clean exportDir after uploading has finished # # except Exception, e: # log.error(str(e)) # self.gDriveNotificationNotice(client, _('EXPORT FAILED!'), 'error') # raise # #client.alert(_(u'Exported to %s') % filename) def exportZip(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = TempDirPath() # Import the Website Page class , if the secure mode is off. If the style has it's own page class # use that, else use the default one. if self.styleSecureMode == "0": if (self.stylesDir / "websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir / "websitepage.py") WebsitePage = module.WebsitePage self.pages = [WebsitePage("index", 0, package.root)] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) self.copyFiles(package, outputDir) # Zip up the website package self.filename.safeSave( self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually saves the zip data. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def appendPageReport(self, page, package): if not page.node.idevices: self.report += u'"%s","%s",%d,"%s",,,,,,\n' % ( package.filename, page.node.title, page.depth, page.name + '.html') for idevice in page.node.idevices: if not idevice.userResources: self.report += u'"%s","%s",%d,"%s","%s","%s",,,,\n' % ( package.filename, page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title) for resource in idevice.userResources: if type(resource) == Resource: self.report += u'"%s","%s",%d,"%s","%s","%s","%s","%s","%s","%s"\n' % ( package.filename, page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource.storageName, resource.userName, resource.path, resource.checksum) else: self.report += u'"%s",%d,"%s","%s","%s","%s",,,\n' % ( package.filename, page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource) def export(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ if not self.report: outputDir = self.filename if not outputDir.exists(): outputDir.mkdir() # Import the Website Page class. If the style has it's own page class # use that, else use the default one. if (self.stylesDir / "websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir / "websitepage.py") WebsitePage = module.WebsitePage self.pages = [WebsitePage(self.prefix + "index", 0, package.root)] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] if self.report: self.report = u'"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"\n' % ( 'File', 'Page Name', 'Level', 'Page File Name', 'Idevice Type', 'Idevice Title', 'Resource File Name', 'Resource User Name', 'Resource Path', 'Resource Checksum') self.appendPageReport(thisPage, package) for nextPage in self.pages[1:]: if self.report: self.appendPageReport(nextPage, package) else: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage if not self.report: thisPage.save(outputDir, prevPage, None, self.pages) if self.prefix == "": self.copyFiles(package, outputDir) return outputDir else: self.filename.write_text(self.report, 'utf-8') def copyFiles(self, package, outputDir): """ Copy all the files used by the website. """ if os.path.isdir(self.stylesDir): # Copy the style sheet files to the output dir styleFiles = [self.stylesDir / '..' / 'base.css'] styleFiles += [self.stylesDir / '..' / 'popup_bg.gif'] styleFiles += self.stylesDir.files("*.css") styleFiles += self.stylesDir.files("*.jpg") styleFiles += self.stylesDir.files("*.gif") styleFiles += self.stylesDir.files("*.png") styleFiles += self.stylesDir.files("*.js") styleFiles += self.stylesDir.files("*.html") styleFiles += self.stylesDir.files("*.ico") styleFiles += self.stylesDir.files("*.ttf") styleFiles += self.stylesDir.files("*.eot") styleFiles += self.stylesDir.files("*.otf") styleFiles += self.stylesDir.files("*.woff") self.stylesDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # copy script files. my_style = G.application.config.styleStore.getStyle(package.style) # jQuery if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(outputDir / 'exe_jquery.js') else: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(outputDir / 'exe_jquery.js') jsFile = (self.scriptsDir / 'common.js') jsFile.copyfile(outputDir / 'common.js') #dT = common.getExportDocType() dT = common.getExportDocType() if dT == "HTML5": jsFile = (self.scriptsDir / 'exe_html5.js') jsFile.copyfile(outputDir / 'exe_html5.js') # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir / package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = (self.imagesDir / 'favicon.ico') faviconFile.copyfile(outputDir / 'favicon.ico') # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasGames = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasGames and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(outputDir / 'flowPlayer.swf') controlsfile = (self.templatesDir / 'flowplayer.controls.swf') controlsfile.copyfile(outputDir / 'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir / 'mojomagnify.js') videofile.copyfile(outputDir / 'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(outputDir / 'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir / 'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir / 'exe_effects') exeEffects.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir / 'exe_games') exeGames.copyfiles(outputDir) if hasWikipedia: wikipediaCSS = (self.cssDir / 'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir / 'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir / 'mediaelement') mediaelement.copyfiles(outputDir) dT = common.getExportDocType() if dT != "HTML5": jsFile = (self.scriptsDir / 'exe_html5.js') jsFile.copyfile(outputDir / 'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir / 'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir / 'templates' / 'content.xsd').copyfile(outputDir / 'content.xsd') (outputDir / 'content.data').write_bytes(encodeObject(package)) (outputDir / 'contentv3.xml').write_bytes( encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl.html').copyfile(outputDir / 'fdl.html') def generatePages(self, node, depth): """ Recursively generate pages and store in pages member variable for retrieving later """ for child in node.children: # assure lower pagename, without whitespaces or alphanumeric characters: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" self.pages.append(WebsitePage(self.prefix + pageName, depth, child)) self.generatePages(child, depth + 1)
class Epub3Export(object): """ Exports an eXe package as a epub 3 package The 'Hello World' of a epub 3 publication might contain files: mimetype META-INF/container.xml Content/HelloWorld.opf Content/HelloWorld.xhtml """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export epub 3 package """ # First do the export to a temporary directory outputDir = TempDirPath() """ fileDir = outputDir/"META-INF" fileDir.mkdir() fileDir = outputDir/"Content" fileDir.mkdir() """ metainfPages = Path(outputDir.abspath() + "/META-INF") # metainfPages = outputDir/'META-INF' metainfPages.mkdir() contentPages = Path(outputDir.abspath() + "/EPUB") # contentPages = outputDir/'Content' contentPages.mkdir() # print contentPages.abspath() # print outputDir.abspath() # Export the package content self.pages = [Epub3Cover("cover", 1, package.root)] self.generatePages(package.root, 2) uniquifyNames(self.pages) cover = None for page in self.pages: page.save(contentPages) if hasattr(page, "cover"): cover = page.cover # Create mimetype file mimetypeFile = open(outputDir.abspath() + "/mimetype", "w") mimetypeFile.write("application/epub+zip") mimetypeFile.close() # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir / ".." / "popup_bg.gif"] styleFiles += [f for f in self.styleDir.files("*.css") if f.basename() != "nav.css"] styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") styleFiles += self.styleDir.files("*.ttf") styleFiles += self.styleDir.files("*.eot") styleFiles += self.styleDir.files("*.otf") styleFiles += self.styleDir.files("*.woff") # FIXME for now, only copy files referenced in Common Cartridge # this really should apply to all exports, but without a manifest # of the files needed by an included stylesheet it is too restrictive package.resourceDir.copyfiles(contentPages) self.styleDir.copylist(styleFiles, contentPages) listCSSFiles = getFilesCSSToMinify("epub3", self.styleDir) exportMinFileCSS(listCSSFiles, outputDir) listFiles = [] listOutFiles = [] listFiles += [self.scriptsDir / "common.js"] listOutFiles += [outputDir / "common.js"] # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasWikipedia = False isBreak = False hasInstructions = False hasTooltips = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if ( hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasWikipedia ): isBreak = True break if not hasFlowplayer: if "flowPlayer.swf" in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if "mojomagnify.js" in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if "xspf_player.swf" in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasWikipedia: if "WikipediaIdevice" == idevice.klass: hasWikipedia = True if not hasInstructions: if ( "TrueFalseIdevice" == idevice.klass or "MultichoiceIdevice" == idevice.klass or "VerdaderofalsofpdIdevice" == idevice.klass or "EleccionmultiplefpdIdevice" == idevice.klass ): hasInstructions = True if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if hasFlowplayer: videofile = self.templatesDir / "flowPlayer.swf" videofile.copyfile(contentPages / "flowPlayer.swf") controlsfile = self.templatesDir / "flowplayer.controls.swf" controlsfile.copyfile(contentPages / "flowplayer.controls.swf") if hasMagnifier: videofile = self.templatesDir / "mojomagnify.js" videofile.copyfile(contentPages / "mojomagnify.js") if hasXspfplayer: videofile = self.templatesDir / "xspf_player.swf" videofile.copyfile(contentPages / "xspf_player.swf") if hasGallery: exeLightbox = self.scriptsDir / "exe_lightbox" exeLightbox.copyfiles(contentPages) if hasFX: exeEffects = self.scriptsDir / "exe_effects" exeEffects.copyfiles(contentPages) if hasSH: exeSH = self.scriptsDir / "exe_highlighter" exeSH.copyfiles(contentPages) if hasGames: exeGames = self.scriptsDir / "exe_games" exeGames.copyfiles(contentPages) if hasWikipedia: wikipediaCSS = self.cssDir / "exe_wikipedia.css" wikipediaCSS.copyfile(contentPages / "exe_wikipedia.css") if hasInstructions: common.copyFileIfNotInStyle("panel-amusements.png", self, contentPages) common.copyFileIfNotInStyle("stock-stop.png", self, contentPages) if hasTooltips: exe_tooltips = self.scriptsDir / "exe_tooltips" exe_tooltips.copyfiles(contentPages) my_style = G.application.config.styleStore.getStyle(package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = self.scriptsDir / "exe_jquery.js" jsFile.copyfile(contentPages / "exe_jquery.js") else: jsFile = self.scriptsDir / "exe_jquery.js" jsFile.copyfile(contentPages / "exe_jquery.js") listFiles = getFilesJSToMinify("epub3", self.scriptsDir) exportMinFileJS(listFiles, outputDir) # if hasattr(package, 'exportSource') and package.exportSource: # (G.application.config.webDir / 'templates' / 'content.xsd').copyfile(outputDir / 'content.xsd') # (outputDir / 'content.data').write_bytes(encodeObject(package)) # (outputDir / 'contentv3.xml').write_bytes(encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / "fdl.html").copyfile(contentPages / "fdl.html") # Create the nav.xhtml file container = NavEpub3(self.pages, contentPages) container.save() # Create the publication file publication = PublicationEpub3(self.config, contentPages, package, self.pages, cover) publication.save("package.opf") # Create the container file container = ContainerEpub3(metainfPages) container.save("container.xml") # Zip it up! self.filename.safeSave(self.doZip, _(u"EXPORT FAILED!\nLast succesful export is %s."), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") mimetype = outputDir / "mimetype" zipped.write(mimetype, "mimetype", ZIP_STORED) for epubFile in outputDir.walkfiles(): if epubFile.basename() == "mimetype": continue relativePath = epubFile.basename() parentdir = epubFile.splitpath()[0] while outputDir.basename() != parentdir.basename(): relativePath = parentdir.basename() / relativePath parentdir = parentdir.splitpath()[0] zipped.write(epubFile, relativePath.encode("utf8"), compress_type=ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ pageName = node.titleShort.lower().replace(" ", u"_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = u"__" if pageName[0] in [unicode(i) for i in range(0, 10)]: pageName = u"_" + pageName page = Epub3Page(pageName, depth, node) self.pages.append(page) for child in node.children: self.generatePages(child, depth + 1)
class WebsiteExport(object): """ WebsiteExport will export a package as a website of HTML pages """ def __init__(self, config, styleDir, filename, prefix="", report=False): """ 'stylesDir' is the directory where we can copy the stylesheets from 'outputDir' is the directory that will be [over]written with the website """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.stylesDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.prefix = prefix self.report = report self.styleSecureMode = config.styleSecureMode def exportZip(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = TempDirPath() # Import the Website Page class , if the secure mode is off. If the style has it's own page class # use that, else use the default one. if self.styleSecureMode == "0": if (self.stylesDir / "websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir / "websitepage.py") WebsitePage = module.WebsitePage self.pages = [WebsitePage("index", 0, package.root)] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) self.copyFiles(package, outputDir) # Zip up the website package self.filename.safeSave( self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually saves the zip data. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def appendPageReport(self, page, package): if not page.node.idevices: self.report += u'"%s","%s",%d,"%s",,,,,,\n' % ( package.filename, page.node.title, page.depth, page.name + '.html') for idevice in page.node.idevices: if not idevice.userResources: self.report += u'"%s","%s",%d,"%s","%s","%s",,,,\n' % ( package.filename, page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title) for resource in idevice.userResources: if type(resource) == Resource: self.report += u'"%s","%s",%d,"%s","%s","%s","%s","%s","%s","%s"\n' % ( package.filename, page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource.storageName, resource.userName, resource.path, resource.checksum) else: self.report += u'"%s",%d,"%s","%s","%s","%s",,,\n' % ( package.filename, page.node.title, page.depth, page.name + '.html', idevice.klass, idevice.title, resource) def export(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ if not self.report: outputDir = self.filename if not outputDir.exists(): outputDir.mkdir() # Import the Website Page class. If the style has it's own page class # use that, else use the default one. if (self.stylesDir / "websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir / "websitepage.py") WebsitePage = module.WebsitePage self.pages = [WebsitePage(self.prefix + "index", 0, package.root)] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] if self.report: self.report = u'"%s","%s","%s","%s","%s","%s","%s","%s","%s","%s"\n' % ( 'File', 'Page Name', 'Level', 'Page File Name', 'Idevice Type', 'Idevice Title', 'Resource File Name', 'Resource User Name', 'Resource Path', 'Resource Checksum') self.appendPageReport(thisPage, package) for nextPage in self.pages[1:]: if self.report: self.appendPageReport(nextPage, package) else: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage if not self.report: thisPage.save(outputDir, prevPage, None, self.pages) if self.prefix == "": self.copyFiles(package, outputDir) return outputDir else: self.filename.write_text(self.report, 'utf-8') def copyFiles(self, package, outputDir): """ Copy all the files used by the website. """ if os.path.isdir(self.stylesDir): # Copy the style sheet files to the output dir styleFiles = [self.stylesDir / '..' / 'base.css'] styleFiles += [self.stylesDir / '..' / 'popup_bg.gif'] styleFiles += self.stylesDir.files("*.css") styleFiles += self.stylesDir.files("*.jpg") styleFiles += self.stylesDir.files("*.gif") styleFiles += self.stylesDir.files("*.png") styleFiles += self.stylesDir.files("*.js") styleFiles += self.stylesDir.files("*.html") styleFiles += self.stylesDir.files("*.ico") styleFiles += self.stylesDir.files("*.ttf") styleFiles += self.stylesDir.files("*.eot") styleFiles += self.stylesDir.files("*.otf") styleFiles += self.stylesDir.files("*.woff") self.stylesDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # copy script files. my_style = G.application.config.styleStore.getStyle(package.style) # jQuery if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(outputDir / 'exe_jquery.js') else: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(outputDir / 'exe_jquery.js') jsFile = (self.scriptsDir / 'common.js') jsFile.copyfile(outputDir / 'common.js') #dT = common.getExportDocType() dT = common.getExportDocType() if dT == "HTML5": jsFile = (self.scriptsDir / 'exe_html5.js') jsFile.copyfile(outputDir / 'exe_html5.js') # Incluide eXe's icon if the Style doesn't have one themePath = Path(G.application.config.stylesDir / package.style) themeFavicon = themePath.joinpath("favicon.ico") if not themeFavicon.exists(): faviconFile = (self.imagesDir / 'favicon.ico') faviconFile.copyfile(outputDir / 'favicon.ico') # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasWikipedia = False isBreak = False hasInstructions = False hasMediaelement = False hasTooltips = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasWikipedia and hasInstructions and hasMediaelement and hasTooltips): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasMediaelement: hasMediaelement = common.ideviceHasMediaelement(idevice) if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(outputDir / 'flowPlayer.swf') controlsfile = (self.templatesDir / 'flowplayer.controls.swf') controlsfile.copyfile(outputDir / 'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir / 'mojomagnify.js') videofile.copyfile(outputDir / 'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(outputDir / 'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir / 'exe_lightbox') exeLightbox.copyfiles(outputDir) if hasFX: exeEffects = (self.scriptsDir / 'exe_effects') exeEffects.copyfiles(outputDir) if hasSH: exeSH = (self.scriptsDir / 'exe_highlighter') exeSH.copyfiles(outputDir) if hasGames: exeGames = (self.scriptsDir / 'exe_games') exeGames.copyfiles(outputDir) if hasWikipedia: wikipediaCSS = (self.cssDir / 'exe_wikipedia.css') wikipediaCSS.copyfile(outputDir / 'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, outputDir) common.copyFileIfNotInStyle('stock-stop.png', self, outputDir) if hasMediaelement: mediaelement = (self.scriptsDir / 'mediaelement') mediaelement.copyfiles(outputDir) dT = common.getExportDocType() if dT != "HTML5": jsFile = (self.scriptsDir / 'exe_html5.js') jsFile.copyfile(outputDir / 'exe_html5.js') if hasTooltips: exe_tooltips = (self.scriptsDir / 'exe_tooltips') exe_tooltips.copyfiles(outputDir) if hasattr(package, 'exportSource') and package.exportSource: (G.application.config.webDir / 'templates' / 'content.xsd').copyfile(outputDir / 'content.xsd') (outputDir / 'content.data').write_bytes(encodeObject(package)) (outputDir / 'contentv3.xml').write_bytes( encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl.html').copyfile(outputDir / 'fdl.html') def generatePages(self, node, depth): """ Recursively generate pages and store in pages member variable for retrieving later """ for child in node.children: # assure lower pagename, without whitespaces or alphanumeric characters: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" self.pages.append(WebsitePage(self.prefix + pageName, depth, child)) self.generatePages(child, depth + 1)
class WebsiteExport(object): """ WebsiteExport will export a package as a website of HTML pages """ def __init__(self, config, styleDir, filename, prefix=""): """ 'stylesDir' is the directory where we can copy the stylesheets from 'outputDir' is the directory that will be [over]written with the website """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.cssDir = config.webDir/"css" self.templatesDir = config.webDir/"templates" self.stylesDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.prefix = prefix def exportZip(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = TempDirPath() # Import the Website Page class. If the style has it's own page class # use that, else use the default one. if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage("index", 1, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) self.copyFiles(package, outputDir) # Zip up the website package self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually saves the zip data. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def export(self, package): """ Export web site Cleans up the previous packages pages and performs the export """ outputDir = self.filename if not outputDir.exists(): outputDir.mkdir() # Import the Website Page class. If the style has it's own page class # use that, else use the default one. if (self.stylesDir/"websitepage.py").exists(): global WebsitePage module = imp.load_source("websitepage", self.stylesDir/"websitepage.py") WebsitePage = module.WebsitePage self.pages = [ WebsitePage(self.prefix + "index", 1, package.root) ] self.generatePages(package.root, 1) uniquifyNames(self.pages) prevPage = None thisPage = self.pages[0] for nextPage in self.pages[1:]: thisPage.save(outputDir, prevPage, nextPage, self.pages) prevPage = thisPage thisPage = nextPage thisPage.save(outputDir, prevPage, None, self.pages) if self.prefix == "": self.copyFiles(package, outputDir) def copyFiles(self, package, outputDir): """ Copy all the files used by the website. """ # Copy the style sheet files to the output dir styleFiles = [self.stylesDir/'..'/'base.css'] styleFiles += [self.stylesDir/'..'/'popup_bg.gif'] styleFiles += self.stylesDir.files("*.css") styleFiles += self.stylesDir.files("*.jpg") styleFiles += self.stylesDir.files("*.gif") styleFiles += self.stylesDir.files("*.png") styleFiles += self.stylesDir.files("*.js") styleFiles += self.stylesDir.files("*.html") styleFiles += self.stylesDir.files("*.ico") styleFiles += self.stylesDir.files("*.ttf") styleFiles += self.stylesDir.files("*.eot") styleFiles += self.stylesDir.files("*.otf") styleFiles += self.stylesDir.files("*.woff") self.stylesDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # copy script files. - with modification by lernmodule.net self.scriptsDir.copylist(('libot_drag.js', 'common.js', 'lernmodule_net.js'), outputDir) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False isBreak = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'magnifier.swf' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: if 'GalleryIdevice' == idevice.klass: hasGallery = True if hasFlowplayer: videofile = (self.templatesDir/'flowPlayer.swf') videofile.copyfile(outputDir/'flowPlayer.swf') controlsfile = (self.templatesDir/'flowplayer.controls.swf') controlsfile.copyfile(outputDir/'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir/'magnifier.swf') videofile.copyfile(outputDir/'magnifier.swf') if hasXspfplayer: videofile = (self.templatesDir/'xspf_player.swf') videofile.copyfile(outputDir/'xspf_player.swf') if hasGallery: imageGalleryCSS = (self.cssDir/'exe_lightbox.css') imageGalleryCSS.copyfile(outputDir/'exe_lightbox.css') imageGalleryJS = (self.scriptsDir/'exe_lightbox.js') imageGalleryJS.copyfile(outputDir/'exe_lightbox.js') self.imagesDir.copylist(('exeGallery_actions.png', 'exeGallery_loading.gif'), outputDir) if package.license == "GNU Free Documentation License": # include a copy of the GNU Free Documentation Licence (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') def generatePages(self, node, depth): """ Recursively generate pages and store in pages member variable for retrieving later """ for child in node.children: # assure lower pagename, without whitespaces or alphanumeric characters: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" self.pages.append(WebsitePage(self.prefix + pageName, depth, child)) self.generatePages(child, depth + 1)
class ScormExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename, scormType): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir/"images" self.scriptsDir = config.webDir/"scripts" self.templatesDir = config.webDir/"templates" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] self.hasForum = False self.scormType = scormType def export(self, package): """ Export SCORM package """ outputDir = TempDirPath() styleFiles = [self.styleDir/'..'/'base.css'] styleFiles += [self.styleDir/'..'/'popup_bg.gif'] styleFiles += self.styleDir.files("*.css") if "nav.css" in styleFiles: styleFiles.remove("nav.css") styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") self.styleDir.copylist(styleFiles, outputDir) package.resourceDir.copyfiles(outputDir) self.pages = [ ScormPage("index", 1, package.root) ] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) if not self.hasForum: for idevice in page.node.idevices: if hasattr(idevice, "isForum"): if idevice.forum.lms.lms == "moodle": self.hasForum = True break manifest = Manifest(self.config, outputDir, package, self.pages, self.scormType) manifest.save("imsmanifest.xml") if self.hasForum: manifest.save("discussionforum.xml") self.scriptsDir.copylist(('APIWrapper.js', 'imscp_rootv1p1p2.xsd', 'imsmd_rootv1p2p1.xsd', 'ims_xml.xsd', 'adlcp_rootv1p2.xsd', 'SCOFunctions.js', 'libot_drag.js', 'common.js'), outputDir) self.templatesDir.copylist(('videoContainer.swf', 'magnifier.swf', 'xspf_player.swf'),outputDir) (self.templatesDir/'fdl.html').copyfile(outputDir/'fdl.html') self.filename.safeSave(self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = ScormPage(pageName, depth, child) self.pages.append(page) self.generatePages(child, depth + 1)
class Epub3Export(object): """ Exports an eXe package as a epub 3 package The 'Hello World' of a epub 3 publication might contain files: mimetype META-INF/container.xml Content/HelloWorld.opf Content/HelloWorld.xhtml """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export epub 3 package """ # First do the export to a temporary directory outputDir = TempDirPath() ''' fileDir = outputDir/"META-INF" fileDir.mkdir() fileDir = outputDir/"Content" fileDir.mkdir() ''' metainfPages = Path(outputDir.abspath() + '/META-INF') # metainfPages = outputDir/'META-INF' metainfPages.mkdir() contentPages = Path(outputDir.abspath() + '/EPUB') # contentPages = outputDir/'Content' contentPages.mkdir() # print contentPages.abspath() # print outputDir.abspath() # Export the package content self.pages = [Epub3Cover("cover", 1, package.root)] self.generatePages(package.root, 2) uniquifyNames(self.pages) cover = None for page in self.pages: page.save(contentPages, self.pages) if hasattr(page, 'cover'): cover = page.cover # Create mimetype file mimetypeFile = open(outputDir.abspath() + '/mimetype', "w") mimetypeFile.write('application/epub+zip') mimetypeFile.close() # Create common_i18n file langFile = open(contentPages + '/common_i18n.js', "w") langFile.write(common.getJavaScriptStrings(False)) langFile.close() # Copy the style files to the output dir # But not nav.css styleFiles = [self.styleDir / '..' / 'popup_bg.gif'] styleFiles += [ f for f in self.styleDir.files("*.*") if f.basename() not in ['nav.css'] ] # FIXME for now, only copy files referenced in Common Cartridge # this really should apply to all exports, but without a manifest # of the files needed by an included stylesheet it is too restrictive # Add fallback document for possible image links if Path(self.styleDir / 'fallback.xhtml').exists(): styleFiles += [self.styleDir / 'fallback.xhtml'] else: styleFiles += [self.styleDir / '..' / 'fallback.xhtml'] # copy the package's resource files for resourceFile in package.resourceDir.walkfiles(): fn = package.resourceDir.relpathto(resourceFile) if ("/" in fn): Dir = Path(contentPages / fn[:fn.rindex("/")]) if not Dir.exists(): Dir.makedirs() resourceFile.copy(contentPages / Dir) else: resourceFile.copy(contentPages) self.styleDir.copylist(styleFiles, contentPages) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False hasFX = False hasSH = False hasGames = False hasWikipedia = False isBreak = False hasInstructions = False hasTooltips = False hasABCMusic = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery and hasFX and hasSH and hasGames and hasWikipedia and hasInstructions and hasTooltips and hasABCMusic): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'mojomagnify.js' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: hasGallery = common.ideviceHasGallery(idevice) if not hasFX: hasFX = common.ideviceHasFX(idevice) if not hasSH: hasSH = common.ideviceHasSH(idevice) if not hasGames: hasGames = common.ideviceHasGames(idevice) if not hasWikipedia: if 'WikipediaIdevice' == idevice.klass: hasWikipedia = True if not hasInstructions: if 'TrueFalseIdevice' == idevice.klass or 'MultichoiceIdevice' == idevice.klass or 'VerdaderofalsofpdIdevice' == idevice.klass or 'EleccionmultiplefpdIdevice' == idevice.klass: hasInstructions = True if not hasTooltips: hasTooltips = common.ideviceHasTooltips(idevice) if not hasABCMusic: hasABCMusic = common.ideviceHasABCMusic(idevice) common.exportJavaScriptIdevicesFiles(page.node.idevices, contentPages) if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(contentPages / 'flowPlayer.swf') controlsfile = (self.templatesDir / 'flowplayer.controls.swf') controlsfile.copyfile(contentPages / 'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir / 'mojomagnify.js') videofile.copyfile(contentPages / 'mojomagnify.js') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(contentPages / 'xspf_player.swf') if hasGallery: exeLightbox = (self.scriptsDir / 'exe_lightbox') exeLightbox.copyfiles(contentPages) if hasFX: exeEffects = (self.scriptsDir / 'exe_effects') exeEffects.copyfiles(contentPages) if hasSH: exeSH = (self.scriptsDir / 'exe_highlighter') exeSH.copyfiles(contentPages) if hasGames: exeGames = (self.scriptsDir / 'exe_games') exeGames.copyfiles(contentPages) # Add game js string to common_i18n langGameFile = open(contentPages + '/common_i18n.js', "a") langGameFile.write(common.getGamesJavaScriptStrings(False)) langGameFile.close() if hasWikipedia: wikipediaCSS = (self.cssDir / 'exe_wikipedia.css') wikipediaCSS.copyfile(contentPages / 'exe_wikipedia.css') if hasInstructions: common.copyFileIfNotInStyle('panel-amusements.png', self, contentPages) common.copyFileIfNotInStyle('stock-stop.png', self, contentPages) if hasTooltips: exe_tooltips = (self.scriptsDir / 'exe_tooltips') exe_tooltips.copyfiles(contentPages) if hasABCMusic: pluginScripts = (self.scriptsDir / 'tinymce_4/js/tinymce/plugins/abcmusic/export') pluginScripts.copyfiles(contentPages) my_style = G.application.config.styleStore.getStyle(package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(contentPages / 'exe_jquery.js') else: jsFile = (self.scriptsDir / 'exe_jquery.js') jsFile.copyfile(contentPages / 'exe_jquery.js') # Copy and minify CSS files css_files = getFilesCSSToMinify('epub3', self.styleDir) exportMinFileCSS(css_files, contentPages) # Copy and minify JS files js_files = getFilesJSToMinify('epub3', self.scriptsDir) exportMinFileJS(js_files, contentPages) # if hasattr(package, 'exportSource') and package.exportSource: # (G.application.config.webDir / 'templates' / 'content.xsd').copyfile(outputDir / 'content.xsd') # (outputDir / 'content.data').write_bytes(encodeObject(package)) # (outputDir / 'contentv3.xml').write_bytes(encodeObjectToXML(package)) if package.license == "license GFDL": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl.html').copyfile(contentPages / 'fdl.html') # Create the nav.xhtml file container = NavEpub3(self.pages, contentPages) container.save() # Create the publication file publication = PublicationEpub3(self.config, contentPages, package, self.pages, cover) publication.save("package.opf") # Create the container file container = ContainerEpub3(metainfPages) container.save("container.xml") # Zip it up! self.filename.safeSave( self.doZip, _(u'EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") mimetype = outputDir / "mimetype" zipped.write(mimetype, "mimetype", ZIP_STORED) for epubFile in outputDir.walkfiles(): if epubFile.basename() == 'mimetype': continue relativePath = epubFile.basename() parentdir = epubFile.splitpath()[0] while (outputDir.basename() != parentdir.basename()): relativePath = parentdir.basename() / relativePath parentdir = parentdir.splitpath()[0] zipped.write(epubFile, relativePath.encode('utf8'), compress_type=ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'node' is the node that we are making a page for 'depth' is the number of ancestors that the page has +1 (ie. root is 1) """ pageName = node.titleShort.lower().replace(" ", u"_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = u"__" if pageName[0] in [unicode(i) for i in range(0, 10)]: pageName = u'_' + pageName page = Epub3Page(pageName, depth, node) self.pages.append(page) for child in node.children: self.generatePages(child, depth + 1)
class IMSExport(object): """ Exports an eXe package as a SCORM package """ def __init__(self, config, styleDir, filename): """ Initialize 'styleDir' is the directory from which we will copy our style sheets (and some gifs) """ self.config = config self.imagesDir = config.webDir / "images" self.scriptsDir = config.webDir / "scripts" self.cssDir = config.webDir / "css" self.templatesDir = config.webDir / "templates" self.schemasDir = config.webDir / "schemas/ims" self.styleDir = Path(styleDir) self.filename = Path(filename) self.pages = [] def export(self, package): """ Export SCORM package """ # First do the export to a temporary directory outputDir = TempDirPath() # Copy the style sheet files to the output dir # But not nav.css styleFiles = [self.styleDir / '..' / 'base.css'] styleFiles += [self.styleDir / '..' / 'popup_bg.gif'] styleFiles += self.styleDir.files("*.css") if "nav.css" in styleFiles: styleFiles.remove("nav.css") styleFiles += self.styleDir.files("*.jpg") styleFiles += self.styleDir.files("*.gif") styleFiles += self.styleDir.files("*.png") styleFiles += self.styleDir.files("*.js") styleFiles += self.styleDir.files("*.html") styleFiles += self.styleDir.files("*.ttf") styleFiles += self.styleDir.files("*.eot") styleFiles += self.styleDir.files("*.otf") styleFiles += self.styleDir.files("*.woff") self.styleDir.copylist(styleFiles, outputDir) # copy the package's resource files package.resourceDir.copyfiles(outputDir) # Export the package content self.pages = [IMSPage("index", 1, package.root)] self.generatePages(package.root, 2) uniquifyNames(self.pages) for page in self.pages: page.save(outputDir) # Create the manifest file manifest = Manifest(self.config, outputDir, package, self.pages) manifest.save() # Copy the scripts - with modification by lernmodule.net self.scriptsDir.copylist( ('libot_drag.js', 'lernmodule_net.js', 'common.js'), outputDir) self.schemasDir.copylist( ('imscp_v1p1.xsd', 'imsmd_v1p2p2.xsd', 'ims_xml.xsd'), outputDir) # copy players for media idevices. hasFlowplayer = False hasMagnifier = False hasXspfplayer = False hasGallery = False isBreak = False for page in self.pages: if isBreak: break for idevice in page.node.idevices: if (hasFlowplayer and hasMagnifier and hasXspfplayer and hasGallery): isBreak = True break if not hasFlowplayer: if 'flowPlayer.swf' in idevice.systemResources: hasFlowplayer = True if not hasMagnifier: if 'magnifier.swf' in idevice.systemResources: hasMagnifier = True if not hasXspfplayer: if 'xspf_player.swf' in idevice.systemResources: hasXspfplayer = True if not hasGallery: if 'GalleryIdevice' == idevice.klass: hasGallery = True if hasFlowplayer: videofile = (self.templatesDir / 'flowPlayer.swf') videofile.copyfile(outputDir / 'flowPlayer.swf') controlsfile = (self.templatesDir / 'flowplayer.controls.swf') controlsfile.copyfile(outputDir / 'flowplayer.controls.swf') if hasMagnifier: videofile = (self.templatesDir / 'magnifier.swf') videofile.copyfile(outputDir / 'magnifier.swf') if hasXspfplayer: videofile = (self.templatesDir / 'xspf_player.swf') videofile.copyfile(outputDir / 'xspf_player.swf') if hasGallery: imageGalleryCSS = (self.cssDir / 'exe_lightbox.css') imageGalleryCSS.copyfile(outputDir / 'exe_lightbox.css') imageGalleryJS = (self.scriptsDir / 'exe_lightbox.js') imageGalleryJS.copyfile(outputDir / 'exe_lightbox.js') self.imagesDir.copylist( ('exeGallery_actions.png', 'exeGallery_loading.gif'), outputDir) if package.license == "GNU Free Documentation License": # include a copy of the GNU Free Documentation Licence (self.templatesDir / 'fdl.html').copyfile(outputDir / 'fdl.html') # Zip it up! self.filename.safeSave( self.doZip, _('EXPORT FAILED!\nLast succesful export is %s.'), outputDir) # Clean up the temporary dir outputDir.rmtree() def doZip(self, fileObj, outputDir): """ Actually does the zipping of the file. Called by 'Path.safeSave' """ zipped = ZipFile(fileObj, "w") for scormFile in outputDir.files(): zipped.write(scormFile, scormFile.basename().encode('utf8'), ZIP_DEFLATED) zipped.close() def generatePages(self, node, depth): """ Recursive function for exporting a node. 'outputDir' is the temporary directory that we are exporting to before creating zip file """ for child in node.children: pageName = child.titleShort.lower().replace(" ", "_") pageName = re.sub(r"\W", "", pageName) if not pageName: pageName = "__" page = IMSPage(pageName, depth, child) self.pages.append(page) self.generatePages(child, depth + 1)