def __init__(self, config, outputDir, package, pages): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = ""
class TestUniqueId(unittest.TestCase): def setUp(self): self.generator = UniqueIdGenerator("David's Gen", sys.argv[0]) def testGenerate(self): howMany = 10000 values = {} for x in range(howMany): id = self.generator.generate() self.assert_(id.isalnum()) values[id] = 1 self.assertEqual(howMany, len(values))
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, scormType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" self.scormType = scormType self.dependencies = {} def createMetaData(self, template): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ lrm = self.package.dublinCore.__dict__.copy() if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author if lrm['date'] == '': lrm['date'] = time.strftime('%Y-%m-%d') # if they don't look like VCARD entries, coerce to fn: for f in ('creator', 'publisher', 'contributors'): if re.match('.*[:;]', lrm[f]) == None: lrm[f] = u'FN:' + lrm[f] xml = template % lrm return xml def save(self, filename): """ Save a imsmanifest file to self.outputDir """ out = open(self.outputDir/filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode('utf8')) out.close() if self.scormType == "scorm1.2": templateFilename = self.config.xulDir/'templates'/'imslrm.xml' template = open(templateFilename, 'rb').read() xml = self.createMetaData(template) out = open(self.outputDir/'imslrm.xml', 'wb') out.write(xml.encode('utf8')) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = unicode(self.idGenerator.generate()) orgId = unicode(self.idGenerator.generate()) # Add the namespaces if self.scormType == "scorm1.2": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- generated by eXe - http://exelearning.org -->\n' xmlStr += u'<manifest identifier="'+manifestId+'" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>1.2</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "scorm2004": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- generated by eXe - http://exelearning.org -->\n' xmlStr += u'<manifest identifier="'+manifestId+'" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" elif self.scormType == "commoncartridge": xmlStr = u'''<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.org --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscc/imscp_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscc/imscp_v1p1 imscp_v1p1.xsd">\n''' % manifestId templateFilename = self.config.xulDir/'templates'/'cc.xml' template = open(templateFilename, 'rb').read() xmlStr += self.createMetaData(template) # Metadata if self.scormType == "commoncartridge": xmlStr += u'''<organizations> <organization identifier="%s" structure="rooted-hierarchy"> <item identifier="eXeCC-%s">\n''' % (orgId, unicode(self.idGenerator.generate())) else: xmlStr += u"<organizations default=\""+orgId+"\"> \n" xmlStr += u' <organization identifier="%s" structure="hierarchical">\n' % orgId if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u"<title>"+title+"</title>\n" if self.scormType == "commoncartridge": # FIXME flatten hierarchy for page in self.pages: self.genItemResStr(page) self.itemStr += "</item>\n" else: depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr if self.scormType == "commoncartridge": xmlStr += " </item>\n" xmlStr += " </organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-"+unicode(self.idGenerator.generate()) resId = "RES-"+unicode(self.idGenerator.generate()) filename = page.name+".html" self.itemStr += '<item identifier="'+itemId+'" ' if self.scormType != "commoncartridge": self.itemStr += 'isvisible="true" ' self.itemStr += 'identifierref="'+resId+'">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += " <resource identifier=\""+resId+"\" " self.resStr += "type=\"webcontent\" " # FIXME force dependency on popup_bg.gif on every page # because it isn't a "resource" so we can't tell which # pages will use it from content.css if self.scormType == "commoncartridge": self.resStr += """href="%s"> <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="popup_bg.gif"/>""" % (filename, filename) if page.node.package.backgroundImg: self.resStr += '\n <file href="%s"/>' % \ page.node.package.backgroundImg.basename() self.dependencies["base.css"] = True self.dependencies["content.css"] = True self.dependencies["popup_bg.gif"] = True else: self.resStr += "adlcp:scormtype=\"sco\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="popup_bg.gif"/> <file href="APIWrapper.js"/> <file href="SCOFunctions.js"/>""" % filename self.resStr += "\n" fileStr = "" for resource in page.node.getResources(): fileStr += " <file href=\""+escape(resource)+"\"/>\n" self.dependencies[resource] = True self.resStr += fileStr self.resStr += " </resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, metadataType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.metadataType = metadataType self.itemStr = "" self.resStr = "" def createMetaData(self): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ xml = '' namespace = 'lom:' # depending on (user desired) the metadata type: if self.metadataType == 'LOMES': output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lomEs) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.name)) metadata.get_general().set_title(title) if self.package.exportSource: technical = metadata.get_technical() if not technical: technical = lomsubs.technicalSub('technical') metadata.set_technical(technical) opr = technical.get_otherPlatformRequirements() if not opr: opr = lomsubs.otherPlatformRequirementsSub() technical.set_otherPlatformRequirements(opr) found = False for platform in opr.get_string(): if platform.get_valueOf_() == self.package.lomESPlatformMark: found = True if not found: opr.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.lomESPlatformMark)) metadata.export(output, 0, namespace_=namespace, pretty_print=False) xml = output.getvalue() if self.metadataType == 'LOM': output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lom) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.name)) metadata.get_general().set_title(title) metadata.export(output, 0, namespace_=namespace, pretty_print=False) xml = output.getvalue() if self.metadataType == 'DC': lrm = self.package.dublinCore.__dict__.copy() # use package values in absentia: if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author if lrm['date'] == '': lrm['date'] = time.strftime('%Y-%m-%d') # if they don't look like VCARD entries, coerce to fn: for f in ('creator', 'publisher', 'contributors'): if re.match('.*[:;]', lrm[f]) == None: lrm[f] = u'FN:' + lrm[f] templateFilename = self.config.webDir/'templates'/'imslrm.xml' template = open(templateFilename, 'rb').read() xml = template % lrm out = open(self.outputDir/'imslrm.xml', 'wb') out.write(xml.encode('utf8')) out.close() xml = '<adlcp:location>imslrm.xml</adlcp:location>' return xml def save(self, filename): """ Save a imsmanifest file to self.outputDir """ out = open(self.outputDir/filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode('utf8')) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = self.idGenerator.generate() orgId = self.idGenerator.generate() xmlStr = u"""<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by eXe - http://exelearning.net --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lom="http://ltsc.ieee.org/xsd/LOM" """ % manifestId xmlStr += "\n " xmlStr += "xsi:schemaLocation=\"http://www.imsglobal.org/xsd/" xmlStr += "imscp_v1p1 imscp_v1p1.xsd " xmlStr += "http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd\"" xmlStr += "> \n" xmlStr += "<metadata> \n" xmlStr += " <schema>IMS Content</schema> \n" xmlStr += " <schemaversion>1.1.3</schemaversion> \n" xmlStr += " %(metadata)s\n" xmlStr += "</metadata> \n" xmlStr += "<organizations default=\""+orgId+"\"> \n" xmlStr += "<organization identifier=\""+orgId xmlStr += "\" structure=\"hierarchical\"> \n" xmlStr = xmlStr % {'metadata': self.createMetaData()} if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u"<title>"+title+"</title>\n" depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr xmlStr += "</organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-"+unicode(self.idGenerator.generate()) resId = "RES-"+unicode(self.idGenerator.generate()) # If ISO9660 compatible mode is active, we want '.htm' as the extension ext = 'html' if G.application.config.cutFileName == '1': ext = 'htm' filename = page.name + '.' + ext self.itemStr += "<item identifier=\""+itemId+"\" isvisible=\"true\" " self.itemStr += "identifierref=\""+resId+"\">\n" self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += "<resource identifier=\""+resId+"\" " self.resStr += "type=\"webcontent\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/>""" % filename self.resStr += "\n" fileStr = "" dT = common.getExportDocType() if dT == "HTML5" or common.nodeHasMediaelement(page.node): self.resStr += ' <file href="exe_html5.js"/>\n' resources = page.node.getResources() my_style = G.application.config.styleStore.getStyle(page.node.package.style) if common.nodeHasMediaelement(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'mediaelement').files()] if common.nodeHasTooltips(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_tooltips').files()] if common.hasGalleryIdevice(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_lightbox').files()] if common.hasFX(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_effects').files()] if common.hasSH(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_highlighter').files()] if common.hasGames(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_games').files()] if common.hasABCMusic(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'tinymce_4'/'js'/'tinymce'/'plugins'/'abcmusic'/'export').files()] if my_style.hasValidConfig(): if my_style.get_jquery() == True: self.resStr += ' <file href="exe_jquery.js"/>\n' else: self.resStr += ' <file href="exe_jquery.js"/>\n' for resource in resources: fileStr += " <file href=\""+escape(resource)+"\"/>\n" if common.hasElpLink(page.node): fileStr += " <file href=\""+page.node.package.name+".elp\"/>\n" # Get all JS iDevices resources fileStr += common.getJavascriptIdevicesResources(page, xmlOutput = True) self.resStr += fileStr self.resStr += "</resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, metadataType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.metadataType = metadataType self.itemStr = "" self.resStr = "" def createMetaData(self): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ xml = '' namespace = 'lom:' # depending on (user desired) the metadata type: if self.metadataType == 'LOMES': output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lomEs) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.name)) metadata.get_general().set_title(title) metadata.export(output, 0, namespace_=namespace, pretty_print=False) xml = output.getvalue() if self.metadataType == 'LOM': output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lom) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.name)) metadata.get_general().set_title(title) metadata.export(output, 0, namespace_=namespace, pretty_print=False) xml = output.getvalue() if self.metadataType == 'DC': lrm = self.package.dublinCore.__dict__.copy() # use package values in absentia: if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author if lrm['date'] == '': lrm['date'] = time.strftime('%Y-%m-%d') # if they don't look like VCARD entries, coerce to fn: for f in ('creator', 'publisher', 'contributors'): if re.match('.*[:;]', lrm[f]) == None: lrm[f] = u'FN:' + lrm[f] templateFilename = self.config.webDir/'templates'/'imslrm.xml' template = open(templateFilename, 'rb').read() xml = template % lrm out = open(self.outputDir/'imslrm.xml', 'wb') out.write(xml.encode('utf8')) out.close() xml = '<adlcp:location>imslrm.xml</adlcp:location>' return xml def save(self, filename): """ Save a imsmanifest file to self.outputDir """ out = open(self.outputDir/filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode('utf8')) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = self.idGenerator.generate() orgId = self.idGenerator.generate() xmlStr = u"""<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by eXe - http://exelearning.net --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lom="http://ltsc.ieee.org/xsd/LOM" """ % manifestId xmlStr += "\n " xmlStr += "xsi:schemaLocation=\"http://www.imsglobal.org/xsd/" xmlStr += "imscp_v1p1 imscp_v1p1.xsd " xmlStr += "http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd\"" xmlStr += "> \n" xmlStr += "<metadata> \n" xmlStr += " <schema>IMS Content</schema> \n" xmlStr += " <schemaversion>1.1.3</schemaversion> \n" xmlStr += " %(metadata)s\n" xmlStr += "</metadata> \n" xmlStr += "<organizations default=\""+orgId+"\"> \n" xmlStr += "<organization identifier=\""+orgId xmlStr += "\" structure=\"hierarchical\"> \n" xmlStr = xmlStr % {'metadata': self.createMetaData()} if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u"<title>"+title+"</title>\n" depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr xmlStr += "</organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-"+unicode(self.idGenerator.generate()) resId = "RES-"+unicode(self.idGenerator.generate()) filename = page.name+".html" self.itemStr += "<item identifier=\""+itemId+"\" isvisible=\"true\" " self.itemStr += "identifierref=\""+resId+"\">\n" self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += "<resource identifier=\""+resId+"\" " self.resStr += "type=\"webcontent\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/>""" % filename self.resStr += "\n" fileStr = "" dT = common.getExportDocType() if dT == "HTML5" or common.nodeHasMediaelement(page.node): self.resStr += ' <file href="exe_html5.js"/>\n' resources = page.node.getResources() my_style = G.application.config.styleStore.getStyle(page.node.package.style) if common.nodeHasMediaelement(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'mediaelement').files()] if common.nodeHasTooltips(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_tooltips').files()] if common.hasGalleryIdevice(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_lightbox').files()] if common.hasFX(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_effects').files()] if common.hasSH(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_highlighter').files()] if common.hasGames(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_games').files()] if my_style.hasValidConfig: if my_style.get_jquery() == True: self.resStr += ' <file href="exe_jquery.js"/>\n' else: self.resStr += ' <file href="exe_jquery.js"/>\n' for resource in resources: fileStr += " <file href=\""+escape(resource)+"\"/>\n" self.resStr += fileStr self.resStr += "</resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, scormType, metadataType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" self.scormType = scormType self.metadataType = metadataType self.dependencies = {} def createMetaData(self, template): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ xml = '<?xml version="1.0" encoding="UTF-8"?>\n' namespace = 'xmlns="http://ltsc.ieee.org/xsd/LOM" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ltsc.ieee.org/xsd/LOM lomCustom.xsd"' # depending on (user desired) the metadata type: if self.metadataType == "LOMES": output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lomEs) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode("utf-8"), self.package.name)) metadata.get_general().set_title(title) metadata.export(output, 0, namespacedef_=namespace, pretty_print=False) xml += output.getvalue() if self.metadataType == "LOM": output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lom) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode("utf-8"), self.package.name)) metadata.get_general().set_title(title) metadata.export(output, 0, namespacedef_=namespace, pretty_print=False) xml += output.getvalue() if self.metadataType == "DC": lrm = self.package.dublinCore.__dict__.copy() # use package values in absentia: if lrm.get("title", "") == "": lrm["title"] = self.package.title if lrm["title"] == "": lrm["title"] = self.package.name if lrm.get("description", "") == "": lrm["description"] = self.package.description if lrm["description"] == "": lrm["description"] = self.package.name if lrm.get("creator", "") == "": lrm["creator"] = self.package.author if lrm["date"] == "": lrm["date"] = time.strftime("%Y-%m-%d") # if they don't look like VCARD entries, coerce to fn: for f in ("creator", "publisher", "contributors"): if re.match(".*[:;]", lrm[f]) == None: lrm[f] = u"FN:" + lrm[f] xml = template % lrm return xml def save(self, filename): """ Save a imsmanifest file to self.outputDir Two works: createXML and createMetaData """ out = open(self.outputDir / filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode("utf8")) out.close() # now depending on metadataType, <metadata> content is diferent: if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if self.metadataType == "DC": # if old template is desired, select imslrm.xml file:\r # anything else, yoy should select: templateFilename = self.config.webDir / "templates" / "imslrm.xml" template = open(templateFilename, "rb").read() elif self.metadataType == "LOMES": template = None elif self.metadataType == "LOM": template = None # Now the file with metadatas. # Notice that its name is independent of metadataType: xml = self.createMetaData(template) out = open(self.outputDir / "imslrm.xml", "wb") out.write(xml.encode("utf8")) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = unicode(self.idGenerator.generate()) orgId = unicode(self.idGenerator.generate()) # Add the namespaces if self.scormType == "scorm1.2": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u"<!-- Generated by eXe - http://exelearning.net -->\n" xmlStr += u'<manifest identifier="' + manifestId + '" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u'xsi:schemaLocation="http://www.imsproject.org/xsd/' xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u'adlcp_rootv1p2.xsd" ' xmlStr += u"> \n" xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>1.2</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "scorm2004": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u"<!-- Generated by eXe - http://exelearning.net -->\n" xmlStr += u'<manifest identifier="' + manifestId + '" \n' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \n' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3" \n' xmlStr += u'xmlns:adlseq="http://www.adlnet.org/xsd/adlseq_v1p3" \n' xmlStr += u'xmlns:adlnav="http://www.adlnet.org/xsd/adlnav_v1p3" \n' xmlStr += u'xmlns:imsss="http://www.imsglobal.org/xsd/imsss" \n' xmlStr += u'xmlns:lom="http://ltsc.ieee.org/xsd/LOM" \n' xmlStr += u'xmlns:lomes="http://ltsc.ieee.org/xsd/LOM" \n' xmlStr += u'xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" \n' xmlStr += u'xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd ' xmlStr += u"http://ltsc.ieee.org/xsd/LOM lomCustom.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_v1p3 adlcp_v1p3.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsss imsss_v1p0.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlseq_v1p3 adlseq_v1p3.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlnav_v1p3 adlnav_v1p3.xsd" xmlStr += u'"> \n' xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>2004 3rd Edition</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "commoncartridge": xmlStr = ( u"""<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.net --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscc/imscp_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscc/imscp_v1p1 imscp_v1p1.xsd">\n""" % manifestId ) templateFilename = self.config.webDir / "templates" / "cc.xml" template = open(templateFilename, "rb").read() xmlStr += self.createMetaData(template) # ORGANIZATION if self.scormType == "commoncartridge": xmlStr += u"""<organizations> <organization identifier="%s" structure="rooted-hierarchy"> <item identifier="eXeCC-%s">\n""" % ( orgId, unicode(self.idGenerator.generate()), ) else: xmlStr += u'<organizations default="' + orgId + '"> \n' xmlStr += u' <organization identifier="%s" structure="hierarchical">\n' % orgId if self.package.title != "": title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u" <title>" + title + "</title>\n" if self.scormType == "commoncartridge": # FIXME flatten hierarchy for page in self.pages: self.genItemResStr(page) self.itemStr += " </item>\n" self.itemStr += " </item>\n" else: depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" if depth > page.depth and self.scormType == "scorm2004": self.itemStr += """ <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>""" depth -= 1 if page.node.children and self.scormType == "scorm2004": # Add fake node with original title itemId = "ITEM-" + unicode(self.idGenerator.generate()) self.itemStr += '<item identifier="' + itemId + '" ' self.itemStr += 'isvisible="true">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" # Increase actual depth because fake node added. Next iteration closes the fake node depth = page.depth + 1 else: depth = page.depth self.genItemResStr(page) while depth >= 1: self.itemStr += "</item>\n" if depth > 1 and self.scormType == "scorm2004": self.itemStr += """ <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>""" depth -= 1 xmlStr += self.itemStr if self.scormType == "scorm2004": xmlStr += """ <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>""" xmlStr += " </organization>\n" xmlStr += "</organizations>\n" # RESOURCES xmlStr += "<resources>\n" xmlStr += self.resStr # If NOT commoncartridge, finally, special resource with # all the common files, as binded with de active style ones: if self.scormType != "commoncartridge": if self.scormType == "scorm1.2": xmlStr += """ <resource identifier="COMMON_FILES" type="webcontent" adlcp:scormtype="asset">\n""" else: xmlStr += """ <resource identifier="COMMON_FILES" type="webcontent" adlcp:scormType="asset">\n""" my_style = G.application.config.styleStore.getStyle(page.node.package.style) for x in my_style.get_style_dir().files("*.*"): xmlStr += """ <file href="%s"/>\n""" % x.basename() # we do want base.css and (for short time), popup_bg.gif, also: xmlStr += """ <file href="base.css"/>\n""" xmlStr += """ <file href="popup_bg.gif"/>\n""" # now the javascript files: xmlStr += """ <file href="SCORM_API_wrapper.js"/>\n""" xmlStr += """ <file href="SCOFunctions.js"/>\n""" if my_style.hasValidConfig: if my_style.get_jquery() == True: xmlStr += """ <file href="exe_jquery.js"/>\n""" else: xmlStr += """ <file href="exe_jquery.js"/>\n""" xmlStr += " </resource>\n" # no more resources: xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-" + unicode(self.idGenerator.generate()) resId = "RES-" + unicode(self.idGenerator.generate()) filename = page.name + ".html" self.itemStr += '<item identifier="' + itemId + '" ' if self.scormType != "commoncartridge": self.itemStr += 'isvisible="true" ' self.itemStr += 'identifierref="' + resId + '">\n' self.itemStr += " <title>" if self.scormType == "scorm2004" and page.node.children: self.itemStr += escape("<-") else: self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" ## RESOURCES self.resStr += ' <resource identifier="' + resId + '" ' self.resStr += 'type="webcontent" ' # FIXME force dependency on popup_bg.gif on every page # because it isn't a "resource" so we can't tell which # pages will use it from content.css if self.scormType == "commoncartridge": fileStr = "" self.resStr += """href="%s"> <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="popup_bg.gif"/> <file href="exe_jquery.js"/> <file href="common.js"/>""" % ( filename, filename, ) # CC export require content.* any place inside the manifest: if page.node.package.exportSource and page.depth == 1: self.resStr += ' <file href="content.xsd"/>\n' self.resStr += ' <file href="content.data"/>\n' self.resStr += ' <file href="contentv3.xml"/>\n' if page.node.package.backgroundImg: self.resStr += '\n <file href="%s"/>' % page.node.package.backgroundImg.basename() self.dependencies["base.css"] = True self.dependencies["content.css"] = True self.dependencies["popup_bg.gif"] = True else: if self.scormType == "scorm2004": self.resStr += 'adlcp:scormType="sco" ' self.resStr += 'href="' + filename + '"> \n' self.resStr += ' <file href="' + filename + '"/> \n' fileStr = "" if self.scormType == "scorm1.2": self.resStr += 'adlcp:scormtype="sco" ' self.resStr += 'href="' + filename + '"> \n' self.resStr += ' <file href="' + filename + '"/> \n' fileStr = "" dT = common.getExportDocType() if dT == "HTML5" or common.nodeHasMediaelement(page.node): self.resStr += ' <file href="exe_html5.js"/>\n' resources = page.node.getResources() if common.nodeHasMediaelement(page.node): resources = resources + [f.basename() for f in (self.config.webDir / "scripts" / "mediaelement").files()] if dT != "HTML5": self.scriptsDir = self.config.webDir / "scripts" jsFile = self.scriptsDir / "exe_html5.js" jsFile.copyfile(self.outputDir / "exe_html5.js") if common.hasGalleryIdevice(page.node): self.resStr += "\n" self.resStr += ' <file href="exe_lightbox.js"/>\n' self.resStr += ' <file href="exe_lightbox.css"/>\n' self.resStr += ' <file href="exe_lightbox_close.png"/>\n' self.resStr += ' <file href="exe_lightbox_loading.gif"/>\n' self.resStr += ' <file href="exe_lightbox_next.png"/>\n' self.resStr += ' <file href="exe_lightbox_prev.png"/>\n' for resource in resources: fileStr += ' <file href="' + escape(resource) + '"/>\n' self.dependencies[resource] = True self.resStr += fileStr # adding the dependency with the common files collected: if self.scormType != "commoncartridge": self.resStr += """ <dependency identifierref="COMMON_FILES"/>""" # and no more: self.resStr += "\n" self.resStr += " </resource>\n"
def setUp(self): self.generator = UniqueIdGenerator("David's Gen", sys.argv[0])
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, scormType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" self.scormType = scormType def createMetaData(self, template): lrm = self.package.dublinCore.__dict__.copy() if lrm.get("title", "") == "": lrm["title"] = self.package.title if lrm["title"] == "": lrm["title"] = self.package.name if lrm.get("description", "") == "": lrm["description"] = self.package.description if lrm["description"] == "": lrm["description"] = self.package.name if lrm.get("creator", "") == "": lrm["creator"] = self.package.author for f in ("creator", "publisher", "contributors"): if re.match(".*[:;]", lrm[f]) == None: lrm[f] = u"FN:" + lrm[f] xml = template % lrm return xml def save(self, filename): """ Save a imsmanifest file to self.outputDir """ out = open(self.outputDir / filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode("utf8")) out.close() if self.scormType == "scorm1.2": templateFilename = self.config.xulDir / "templates" / "imslrm.xml" template = open(templateFilename, "rb").read() xml = self.createMetaData(template) out = open(self.outputDir / "imslrm.xml", "wb") out.write(xml.encode("utf8")) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = unicode(self.idGenerator.generate()) orgId = unicode(self.idGenerator.generate()) if self.scormType == "scorm1.2": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u"<!-- generated by eXe - http://exelearning.org -->\n" xmlStr += u'<manifest identifier="' + manifestId + '" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u'xsi:schemaLocation="http://www.imsproject.org/xsd/' xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u'adlcp_rootv1p2.xsd" ' xmlStr += u"> \n" xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>1.2</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "scorm2004": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u"<!-- generated by eXe - http://exelearning.org -->\n" xmlStr += u'<manifest identifier="' + manifestId + '" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u'xsi:schemaLocation="http://www.imsproject.org/xsd/' xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u'adlcp_rootv1p2.xsd" ' xmlStr += u"> \n" elif self.scormType == "commoncartridge": xmlStr = ( u"""<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.org --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 ..\imscc_cam0p04\derived_schema\imscp_v1p1.xsd">\n""" % manifestId ) templateFilename = self.config.xulDir / "templates" / "cc.xml" template = open(templateFilename, "rb").read() xmlStr += self.createMetaData(template) if self.scormType == "commoncartridge": xmlStr += u"<organizations>\n" xmlStr += u' <organization identifier="%s" structure="rooted-heirarchy">' % orgId else: xmlStr += u'<organizations default="' + orgId + '"> \n' xmlStr += u' <organization identifier="%s" structure="hierarchical">' % orgId if self.package.title != "": title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u"<title>" + title + "</title>\n" depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr xmlStr += "</organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xlm string for items and resources """ itemId = "ITEM-" + unicode(self.idGenerator.generate()) resId = "RES-" + unicode(self.idGenerator.generate()) filename = page.name + ".html" self.itemStr += '<item identifier="' + itemId + '" isvisible="true" ' self.itemStr += 'identifierref="' + resId + '">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += ' <resource identifier="' + resId + '" ' self.resStr += 'type="webcontent" ' if self.scormType == "commoncartridge": self.resStr += ( """> <file href="%s"/> <file href="base.css"/> <file href="content.css"/>""" % filename ) else: self.resStr += 'adlcp:scormtype="sco" ' self.resStr += 'href="' + filename + '"> \n' self.resStr += ( """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="APIWrapper.js"/> <file href="SCOFunctions.js"/>""" % filename ) self.resStr += "\n" fileStr = "" for resource in page.node.getResources(): fileStr += ' <file href="' + escape(resource) + '"/>\n' self.resStr += fileStr self.resStr += "</resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, metadataType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.metadataType = metadataType self.itemStr = "" self.resStr = "" def createMetaData(self): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ xml = "" namespace = "lom:" # depending on (user desired) the metadata type: if self.metadataType == "LOMES": output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lomEs) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode("utf-8"), self.package.name)) metadata.get_general().set_title(title) metadata.export(output, 0, namespace_=namespace, pretty_print=False) xml = output.getvalue() if self.metadataType == "LOM": output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lom) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode("utf-8"), self.package.name)) metadata.get_general().set_title(title) metadata.export(output, 0, namespace_=namespace, pretty_print=False) xml = output.getvalue() if self.metadataType == "DC": lrm = self.package.dublinCore.__dict__.copy() # use package values in absentia: if lrm.get("title", "") == "": lrm["title"] = self.package.title if lrm["title"] == "": lrm["title"] = self.package.name if lrm.get("description", "") == "": lrm["description"] = self.package.description if lrm["description"] == "": lrm["description"] = self.package.name if lrm.get("creator", "") == "": lrm["creator"] = self.package.author if lrm["date"] == "": lrm["date"] = time.strftime("%Y-%m-%d") # if they don't look like VCARD entries, coerce to fn: for f in ("creator", "publisher", "contributors"): if re.match(".*[:;]", lrm[f]) == None: lrm[f] = u"FN:" + lrm[f] templateFilename = self.config.webDir / "templates" / "imslrm.xml" template = open(templateFilename, "rb").read() xml = template % lrm out = open(self.outputDir / "imslrm.xml", "wb") out.write(xml.encode("utf8")) out.close() xml = "<adlcp:location>imslrm.xml</adlcp:location>" return xml def save(self, filename): """ Save a imsmanifest file to self.outputDir """ out = open(self.outputDir / filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode("utf8")) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = self.idGenerator.generate() orgId = self.idGenerator.generate() xmlStr = ( u"""<?xml version="1.0" encoding="UTF-8"?> <!-- Generated by eXe - http://exelearning.net --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lom="http://ltsc.ieee.org/xsd/LOM" """ % manifestId ) xmlStr += "\n " xmlStr += 'xsi:schemaLocation="http://www.imsglobal.org/xsd/' xmlStr += "imscp_v1p1 imscp_v1p1.xsd " xmlStr += 'http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd"' xmlStr += "> \n" xmlStr += "<metadata> \n" xmlStr += " <schema>IMS Content</schema> \n" xmlStr += " <schemaversion>1.1.3</schemaversion> \n" xmlStr += " %(metadata)s\n" xmlStr += "</metadata> \n" xmlStr += '<organizations default="' + orgId + '"> \n' xmlStr += '<organization identifier="' + orgId xmlStr += '" structure="hierarchical"> \n' xmlStr = xmlStr % {"metadata": self.createMetaData()} if self.package.title != "": title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u"<title>" + title + "</title>\n" depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr xmlStr += "</organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-" + unicode(self.idGenerator.generate()) resId = "RES-" + unicode(self.idGenerator.generate()) filename = page.name + ".html" self.itemStr += '<item identifier="' + itemId + '" isvisible="true" ' self.itemStr += 'identifierref="' + resId + '">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += '<resource identifier="' + resId + '" ' self.resStr += 'type="webcontent" ' self.resStr += 'href="' + filename + '"> \n' self.resStr += ( """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/>""" % filename ) self.resStr += "\n" fileStr = "" dT = common.getExportDocType() if dT == "HTML5" or common.nodeHasMediaelement(page.node): self.resStr += ' <file href="exe_html5.js"/>\n' resources = page.node.getResources() my_style = G.application.config.styleStore.getStyle(page.node.package.style) if common.nodeHasMediaelement(page.node): resources = resources + [f.basename() for f in (self.config.webDir / "scripts" / "mediaelement").files()] if common.hasGalleryIdevice(page.node): self.resStr += ' <file href="exe_lightbox.js"/>\n' self.resStr += ' <file href="exe_lightbox.css"/>\n' self.resStr += ' <file href="exe_lightbox_close.png"/>\n' self.resStr += ' <file href="exe_lightbox_loading.gif"/>\n' self.resStr += ' <file href="exe_lightbox_next.png"/>\n' self.resStr += ' <file href="exe_lightbox_prev.png"/>\n' if my_style.hasValidConfig: if my_style.get_jquery() == True: self.resStr += ' <file href="exe_jquery.js"/>\n' else: self.resStr += ' <file href="exe_jquery.js"/>\n' for resource in resources: fileStr += ' <file href="' + escape(resource) + '"/>\n' self.resStr += fileStr self.resStr += "</resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, scormType, metadataType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" self.scormType = scormType self.metadataType = metadataType self.dependencies = {} def createMetaData(self, template): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ xml = '<?xml version="1.0" encoding="UTF-8"?>\n' namespace = 'xmlns="http://ltsc.ieee.org/xsd/LOM" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ltsc.ieee.org/xsd/LOM lomCustom.xsd"' # depending on (user desired) the metadata type: if self.metadataType == 'LOMES': output = StringIO.StringIO() self.package.lomEs.export(output, 0, namespacedef_=namespace, pretty_print=False) xml += output.getvalue() if self.metadataType == 'LOM': output = StringIO.StringIO() self.package.lom.export(output, 0, namespacedef_=namespace, pretty_print=False) xml += output.getvalue() if self.metadataType == 'DC': lrm = self.package.dublinCore.__dict__.copy() # use package values in absentia: if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author if lrm['date'] == '': lrm['date'] = time.strftime('%Y-%m-%d') # if they don't look like VCARD entries, coerce to fn: for f in ('creator', 'publisher', 'contributors'): if re.match('.*[:;]', lrm[f]) == None: lrm[f] = u'FN:' + lrm[f] xml = template % lrm return xml def save(self, filename): """ Save a imsmanifest file to self.outputDir Two works: createXML and createMetaData """ out = open(self.outputDir/filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode('utf8')) out.close() # now depending on metadataType, <metadata> content is diferent: if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if self.metadataType == 'DC': # if old template is desired, select imslrm.xml file:\r # anything else, yoy should select: templateFilename = self.config.webDir/'templates'/'imslrm.xml' template = open(templateFilename, 'rb').read() elif self.metadataType == 'LOMES': template = None elif self.metadataType == 'LOM': template = None # Now the file with metadatas. # Notice that its name is independent of metadataType: xml = self.createMetaData(template) out = open(self.outputDir/'imslrm.xml', 'wb') out.write(xml.encode('utf8')) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = unicode(self.idGenerator.generate()) orgId = unicode(self.idGenerator.generate()) # Add the namespaces if self.scormType == "scorm1.2": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- Generated by eXe - http://exelearning.net -->\n' xmlStr += u'<manifest identifier="'+manifestId+'" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>1.2</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "scorm2004": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- Generated by eXe - http://exelearning.net -->\n' xmlStr += u'<manifest identifier="'+manifestId+'" \n' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \n' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3" \n' xmlStr += u'xmlns:adlseq="http://www.adlnet.org/xsd/adlseq_v1p3" \n' xmlStr += u'xmlns:adlnav="http://www.adlnet.org/xsd/adlnav_v1p3" \n' xmlStr += u'xmlns:imsss="http://www.imsglobal.org/xsd/imsss" \n' xmlStr += u'xmlns:lom="http://ltsc.ieee.org/xsd/LOM" \n' xmlStr += u'xmlns:lomes="http://ltsc.ieee.org/xsd/LOM" \n' xmlStr += u'xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" \n' xmlStr += u"xsi:schemaLocation=\"http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd " xmlStr += u"http://ltsc.ieee.org/xsd/LOM lomCustom.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_v1p3 adlcp_v1p3.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsss imsss_v1p0.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlseq_v1p3 adlseq_v1p3.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlnav_v1p3 adlnav_v1p3.xsd" xmlStr += u'"> \n' xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>2004 3rd Edition</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "commoncartridge": xmlStr = u'''<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.net --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscc/imscp_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscc/imscp_v1p1 imscp_v1p1.xsd">\n''' % manifestId templateFilename = self.config.webDir/'templates'/'cc.xml' template = open(templateFilename, 'rb').read() xmlStr += self.createMetaData(template) # ORGANIZATION if self.scormType == "commoncartridge": xmlStr += u'''<organizations> <organization identifier="%s" structure="rooted-hierarchy"> <item identifier="eXeCC-%s">\n''' % (orgId, unicode(self.idGenerator.generate())) else: xmlStr += u"<organizations default=\""+orgId+"\"> \n" xmlStr += u' <organization identifier="%s" structure="hierarchical">\n' % orgId if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u" <title>"+title+"</title>\n" if self.scormType == "commoncartridge": # FIXME flatten hierarchy for page in self.pages: self.genItemResStr(page) self.itemStr += " </item>\n" self.itemStr += " </item>\n" else: # initial depth: depth = 0 depthold = 0 oldnodeleaf = 0 for page in self.pages: while depth > page.depth: if self.scormType == "scorm2004": self.itemStr += u" <imsss:sequencing>\n" self.itemStr += u" <imsss:controlMode flow=\"true\"/>\n" self.itemStr += u" </imsss:sequencing>\n" if page.node.children: self.itemStr += " </item>\n" else: if oldnodeleaf and depthold - 1 > page.depth: self.itemStr += " </item>\n" if self.scormType == "scorm1.2": if page.node.children: self.itemStr += " </item>\n" else: if oldnodeleaf and depthold - 1 > page.depth: self.itemStr += " </item>\n" depth -= 1 oldnodeleaf = not page.node.children else: if self.scormType == "scorm1.2" and depthold - 1 >= page.depth: if not page.node.children: self.itemStr += " </item>\n" # we will compare depth with the actual page.depth... # we look for decreasing depths -it means we are ending a branch: if self.scormType == "scorm2004" and depthold - 1 >= page.depth: if not page.node.children: self.itemStr += " </item>\n" # go on with the items: self.genItemResStr(page) # do not forget update depth before going on with the list: depthold = page.depth depth = page.depth if self.scormType != "scorm2004": while depth > 1: self.itemStr += " </item>\n" depth -= 1 # the LAST </item> of navigation block must disappear in CC exports: if self.scormType == "commoncartridge" and self.itemStr.endswith(" </item>\n"): self.itemStr = self.itemStr[:-12] # finally the last page (leaf) must also include sequencing: if self.scormType == "scorm2004" and not page.node.children: for x in range(1,page.depth): self.itemStr += u" <imsss:sequencing>\n" self.itemStr += u" <imsss:controlMode flow=\"true\"/>\n" self.itemStr += u" </imsss:sequencing>\n" self.itemStr += " </item>\n" # the LAST </item> of navigation block must disappear: if self.itemStr.endswith(" </item>\n") and page.depth > 1: self.itemStr = self.itemStr[:-12] self.itemStr += "\n" # that's all for itemStr xmlStr += self.itemStr xmlStr += " </organization>\n" xmlStr += "</organizations>\n" # RESOURCES xmlStr += "<resources>\n" xmlStr += self.resStr # If NOT commoncartridge, finally, special resource with # all the common files, as binded with de active style ones: if self.scormType != "commoncartridge": if self.scormType == "scorm1.2": xmlStr += """ <resource identifier="COMMON_FILES" type="webcontent" adlcp:scormtype="asset">\n""" else: xmlStr += """ <resource identifier="COMMON_FILES" type="webcontent" adlcp:scormType="asset">\n""" directory = Path(self.config.webDir).joinpath('style', self.package.style.encode('utf-8')) liststylesfiles = os.listdir(directory) for x in liststylesfiles: if os.path.isfile(directory + '/' + x): xmlStr += """ <file href="%s"/>\n""" % x # we do want base.css and (for short time), popup_bg.gif, also: xmlStr += """ <file href="base.css"/>\n""" xmlStr += """ <file href="popup_bg.gif"/>\n""" # now the javascript files: xmlStr += """ <file href="SCORM_API_wrapper.js"/>\n""" xmlStr += """ <file href="SCOFunctions.js"/>\n""" resources = page.node.getResources() my_style = G.application.config.styleStore.getStyle(page.node.package.style) if my_style.hasValidConfig: if my_style.get_jquery() == True: xmlStr += """ <file href="exe_jquery.js"/>\n""" else: xmlStr += """ <file href="exe_jquery.js"/>\n""" xmlStr += " </resource>\n" # no more resources: xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources Notice, please: items AND resources """ itemId = "ITEM-"+unicode(self.idGenerator.generate()) control = itemId resId = "RES-"+unicode(self.idGenerator.generate()) filename = page.name+".html" ## ITEMS INSIDE ORGANIZATIONS # CC do not requires if each section is visible or not, but SCORM yes: if self.scormType == "scorm1.2": self.itemStr += ' <item identifier="'+itemId+'" ' self.itemStr += 'isvisible="true" ' if self.scormType == "scorm2004" and page.depth > 1: self.itemStr += ' <item identifier="'+itemId+'" ' self.itemStr += 'isvisible="true" ' else: if self.scormType == "scorm2004" and page.depth == 1 and not page.node.children: self.itemStr += ' <item identifier="'+itemId+'" ' self.itemStr += 'isvisible="true" ' if self.scormType == "commoncartridge": self.itemStr += ' <item identifier="'+itemId+'" ' self.itemStr += 'identifierref="'+resId+'">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" # If self.scormType == "scorm2004" the identifierref shall not # be used on <item> elements that contain other <item> elements, # so: if self.scormType == "scorm2004": if not page.node.children: self.itemStr += 'identifierref="'+resId+'">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.itemStr += " </item>\n" else: if page.depth > 1: self.itemStr += ">\n" self.itemStr += " <title>" # an innocent title by the moment: self.itemStr += "->" self.itemStr += "</title>\n" # create a leaf item with this resource one level beyond, if control == itemId: itemIdleaf = "ITEM-"+unicode(self.idGenerator.generate()) self.itemStr += ' <item identifier="'+itemIdleaf+'" ' self.itemStr += 'identifierref="'+resId+'">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.itemStr += " </item>\n" control = 0 # at SCORM1.2, ALL the items at organizations # must include identifierref attribute: if self.scormType == "scorm1.2": self.itemStr += 'identifierref="'+resId+'">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" if not page.node.children: self.itemStr += " </item>\n" ## RESOURCES self.resStr += " <resource identifier=\""+resId+"\" " self.resStr += "type=\"webcontent\" " # FIXME force dependency on popup_bg.gif on every page # because it isn't a "resource" so we can't tell which # pages will use it from content.css if self.scormType == "commoncartridge": fileStr = "" self.resStr += """href="%s"> <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="popup_bg.gif"/> <file href="exe_jquery.js"/> <file href="common.js"/>""" % (filename, filename) # CC export require content.* any place inside the manifest: if page.node.package.exportSource and page.depth == 1: self.resStr += ' <file href="content.xsd"/>\n' self.resStr += ' <file href="content.data"/>\n' self.resStr += ' <file href="contentv3.xml"/>\n' if page.node.package.backgroundImg: self.resStr += '\n <file href="%s"/>' % \ page.node.package.backgroundImg.basename() self.dependencies["base.css"] = True self.dependencies["content.css"] = True self.dependencies["popup_bg.gif"] = True else: if self.scormType == "scorm2004": self.resStr += "adlcp:scormType=\"sco\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += " <file href=\""+filename+"\"/> \n" fileStr = "" if self.scormType == "scorm1.2": self.resStr += "adlcp:scormtype=\"sco\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += " <file href=\""+filename+"\"/> \n" fileStr = "" dT = common.getExportDocType() if dT == "HTML5" or common.nodeHasMediaelement(page.node): self.resStr += ' <file href="exe_html5.js"/>\n' resources = page.node.getResources() my_style = G.application.config.styleStore.getStyle(page.node.package.style) if common.nodeHasMediaelement(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'mediaelement').files()] if dT != "HTML5": self.scriptsDir = self.config.webDir/"scripts" jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(self.outputDir/'exe_html5.js') if common.hasGalleryIdevice(page.node): self.resStr += '\n' self.resStr += ' <file href="exe_lightbox.js"/>\n' self.resStr += ' <file href="exe_lightbox.css"/>\n' self.resStr += ' <file href="exe_lightbox_close.png"/>\n' self.resStr += ' <file href="exe_lightbox_loading.gif"/>\n' self.resStr += ' <file href="exe_lightbox_next.png"/>\n' self.resStr += ' <file href="exe_lightbox_prev.png"/>\n' for resource in resources: fileStr += " <file href=\""+escape(resource)+"\"/>\n" self.dependencies[resource] = True self.resStr += fileStr # adding the dependency with the common files collected: if self.scormType != "commoncartridge": self.resStr += """ <dependency identifierref="COMMON_FILES"/>""" # and no more: self.resStr += '\n' self.resStr += " </resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, scormType, metadataType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" self.scormType = scormType self.metadataType = metadataType self.dependencies = {} def _validateMetaData(self, metadata): modifiedMetaData = False fieldsModified = [] if metadata.get_general().get_description(): for description in metadata.get_general().get_description(): strings = description.get_string() for string in strings: value = string.get_valueOf_() # general description: The field must be 1000 characters maximum, standard SCORM 2.1 if len(value) > 1000: string.set_valueOf_(value[:1000]) modifiedMetaData = True fieldsModified.append(_('general description')) if metadata.get_educational(): for educational in metadata.get_educational(): if educational.get_description(): for description in educational.get_description(): strings = description.get_string() for string in strings: value = string.get_valueOf_() # educational description: The field must be 1000 characters maximum, standard SCORM 2.1 if len(value) > 1000: string.set_valueOf_(value[:1000]) modifiedMetaData = True fieldsModified.append(_('educational description')) return {'modifiedMetaData': modifiedMetaData, 'fieldsModified': fieldsModified} def createMetaData(self, template): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ xml = '<?xml version="1.0" encoding="UTF-8"?>\n' namespace = 'xmlns="http://ltsc.ieee.org/xsd/LOM" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ltsc.ieee.org/xsd/LOM lomCustom.xsd"' modifiedMetaData = False # depending on (user desired) the metadata type: if self.metadataType == 'LOMES': output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lomEs) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.name)) metadata.get_general().set_title(title) if self.scormType == "scorm1.2": modifiedMetaData = self._validateMetaData(metadata) if self.package.exportSource: technical = metadata.get_technical() if not technical: technical = lomsubs.technicalSub('technical') metadata.set_technical(technical) opr = technical.get_otherPlatformRequirements() if not opr: opr = lomsubs.otherPlatformRequirementsSub() technical.set_otherPlatformRequirements(opr) opr.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), 'editor: eXe Learning')) found = False for platform in opr.get_string(): if platform.get_valueOf_() == self.package.lomESPlatformMark: found = True if not found: opr.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.lomESPlatformMark)) metadata.export(output, 0, namespacedef_=namespace, pretty_print=False) xml += output.getvalue() if self.metadataType == 'LOM': output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lom) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string(lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.name)) metadata.get_general().set_title(title) if self.scormType == "scorm1.2": modifiedMetaData = self._validateMetaData(metadata) metadata.export(output, 0, namespacedef_=namespace, pretty_print=False) xml += output.getvalue() if self.metadataType == 'DC': lrm = self.package.dublinCore.__dict__.copy() # use package values in absentia: if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author if lrm['date'] == '': lrm['date'] = time.strftime('%Y-%m-%d') # if they don't look like VCARD entries, coerce to fn: for f in ('creator', 'publisher', 'contributors'): if re.match('.*[:;]', lrm[f]) == None: lrm[f] = u'FN:' + lrm[f] xml = template % lrm return {'xml': xml, 'modifiedMetaData' : modifiedMetaData} def save(self, filename): """ Save a imsmanifest file to self.outputDir Two works: createXML and createMetaData """ modifiedMetaData = False out = open(self.outputDir/filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode('utf8')) out.close() # now depending on metadataType, <metadata> content is diferent: if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if self.metadataType == 'DC': # if old template is desired, select imslrm.xml file:\r # anything else, yoy should select: templateFilename = self.config.webDir/'templates'/'imslrm.xml' template = open(templateFilename, 'rb').read() elif self.metadataType == 'LOMES': template = None elif self.metadataType == 'LOM': template = None # Now the file with metadatas. # Notice that its name is independent of metadataType: metaData = self.createMetaData(template) xml = metaData['xml'] modifiedMetaData = metaData['modifiedMetaData'] out = open(self.outputDir/'imslrm.xml', 'wb') out.write(xml.encode('utf8')) out.close() return modifiedMetaData def createXML(self): """ returning XLM string for manifest file """ manifestId = unicode(self.idGenerator.generate()) orgId = unicode(self.idGenerator.generate()) # Add the namespaces if self.scormType == "scorm1.2": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- Generated by eXe - http://exelearning.net -->\n' xmlStr += u'<manifest identifier="'+manifestId+'" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>1.2</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "scorm2004": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- Generated by eXe - http://exelearning.net -->\n' xmlStr += u'<manifest identifier="'+manifestId+'" \n' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \n' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3" \n' xmlStr += u'xmlns:adlseq="http://www.adlnet.org/xsd/adlseq_v1p3" \n' xmlStr += u'xmlns:adlnav="http://www.adlnet.org/xsd/adlnav_v1p3" \n' xmlStr += u'xmlns:imsss="http://www.imsglobal.org/xsd/imsss" \n' xmlStr += u'xmlns:lom="http://ltsc.ieee.org/xsd/LOM" \n' xmlStr += u'xmlns:lomes="http://ltsc.ieee.org/xsd/LOM" \n' xmlStr += u'xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" \n' xmlStr += u"xsi:schemaLocation=\"http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd " xmlStr += u"http://ltsc.ieee.org/xsd/LOM lomCustom.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_v1p3 adlcp_v1p3.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsss imsss_v1p0.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlseq_v1p3 adlseq_v1p3.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlnav_v1p3 adlnav_v1p3.xsd" xmlStr += u'"> \n' xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>2004 3rd Edition</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "commoncartridge": xmlStr = u'''<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.net --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscc/imscp_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscc/imscp_v1p1 imscp_v1p1.xsd">\n''' % manifestId templateFilename = self.config.webDir/'templates'/'cc.xml' template = open(templateFilename, 'rb').read() metaData = self.createMetaData(template) xmlStr += metaData['xml'] # ORGANIZATION if self.scormType == "commoncartridge": xmlStr += u'''<organizations> <organization identifier="%s" structure="rooted-hierarchy"> <item identifier="eXeCC-%s">\n''' % (orgId, unicode(self.idGenerator.generate())) else: xmlStr += u"<organizations default=\""+orgId+"\"> \n" xmlStr += u' <organization identifier="%s" structure="hierarchical">\n' % orgId if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u" <title>"+title+"</title>\n" if self.scormType == "commoncartridge": # FIXME flatten hierarchy for page in self.pages: self.genItemResStr(page) self.itemStr += " </item>\n" self.itemStr += " </item>\n" else: depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" if depth > page.depth and self.scormType == "scorm2004": self.itemStr += ''' <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>''' depth -= 1 if page.node.children and self.scormType == "scorm2004": # Add fake node with original title itemId = "ITEM-"+unicode(self.idGenerator.generate()) self.itemStr += '<item identifier="'+itemId+'" ' self.itemStr += 'isvisible="true">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" # Increase actual depth because fake node added. Next iteration closes the fake node depth = page.depth + 1 else: depth = page.depth self.genItemResStr(page) while depth >= 1: self.itemStr += "</item>\n" if depth > 1 and self.scormType == "scorm2004": self.itemStr += ''' <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>''' depth -= 1 xmlStr += self.itemStr if self.scormType == "scorm2004": xmlStr += ''' <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>''' xmlStr += " </organization>\n" xmlStr += "</organizations>\n" # RESOURCES xmlStr += "<resources>\n" xmlStr += self.resStr # If NOT commoncartridge, finally, special resource with # all the common files, as binded with de active style ones: if self.scormType != "commoncartridge": if self.scormType == "scorm1.2": xmlStr += """ <resource identifier="COMMON_FILES" type="webcontent" adlcp:scormtype="asset">\n""" else: xmlStr += """ <resource identifier="COMMON_FILES" type="webcontent" adlcp:scormType="asset">\n""" my_style = G.application.config.styleStore.getStyle(page.node.package.style) for x in my_style.get_style_dir().files('*.*'): xmlStr += """ <file href="%s"/>\n""" % x.basename() # we do want base.css and (for short time), popup_bg.gif, also: xmlStr += """ <file href="base.css"/>\n""" xmlStr += """ <file href="popup_bg.gif"/>\n""" # now the javascript files: xmlStr += """ <file href="SCORM_API_wrapper.js"/>\n""" xmlStr += """ <file href="SCOFunctions.js"/>\n""" xmlStr += """ <file href="common.js"/>\n""" xmlStr += """ <file href="common_i18n.js"/>\n""" if my_style.hasValidConfig: if my_style.get_jquery() == True: xmlStr += """ <file href="exe_jquery.js"/>\n""" else: xmlStr += """ <file href="exe_jquery.js"/>\n""" xmlStr += " </resource>\n" # no more resources: xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-"+unicode(self.idGenerator.generate()) resId = "RES-"+unicode(self.idGenerator.generate()) ext = 'html' if G.application.config.cutFileName == "1": ext = 'htm' filename = page.name + '.' + ext self.itemStr += '<item identifier="'+itemId+'" ' if self.scormType != "commoncartridge": self.itemStr += 'isvisible="true" ' self.itemStr += 'identifierref="'+resId+'">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" ## SCORM 12 specific metadata: Mastery Score is an ADL extension to the IMS Content Packaging Information Model ## Added for FR [#2501] Add masteryscore to manifest in evaluable nodes if self.scormType == "scorm1.2" and common.hasQuizTest(page.node): self.itemStr += " <adlcp:masteryscore>%s</adlcp:masteryscore>\n" % common.getQuizTestPassRate(page.node) ## RESOURCES self.resStr += " <resource identifier=\""+resId+"\" " self.resStr += "type=\"webcontent\" " # FIXME force dependency on popup_bg.gif on every page # because it isn't a "resource" so we can't tell which # pages will use it from content.css if self.scormType == "commoncartridge": fileStr = "" self.resStr += """href="%s"> <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="popup_bg.gif"/> <file href="exe_jquery.js"/> <file href="common_i18n.js"/> <file href="common.js"/>\n""" % (filename, filename) # CC export require content.* any place inside the manifest: if page.node.package.exportSource and page.depth == 1: self.resStr += ' <file href="content.xsd"/>\n' self.resStr += ' <file href="content.data"/>\n' self.resStr += ' <file href="contentv3.xml"/>\n' if page.node.package.backgroundImg: self.resStr += '\n <file href="%s"/>' % \ page.node.package.backgroundImg.basename() self.dependencies["base.css"] = True self.dependencies["content.css"] = True self.dependencies["popup_bg.gif"] = True else: if self.scormType == "scorm2004": self.resStr += "adlcp:scormType=\"sco\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += " <file href=\""+filename+"\"/> \n" fileStr = "" if self.scormType == "scorm1.2": self.resStr += "adlcp:scormtype=\"sco\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += " <file href=\""+filename+"\"/> \n" fileStr = "" dT = common.getExportDocType() if dT == "HTML5" or common.nodeHasMediaelement(page.node): self.resStr += ' <file href="exe_html5.js"/>\n' resources = page.node.getResources() if common.nodeHasMediaelement(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'mediaelement').files()] if dT != "HTML5": self.scriptsDir = self.config.webDir/"scripts" jsFile = (self.scriptsDir/'exe_html5.js') jsFile.copyfile(self.outputDir/'exe_html5.js') if common.nodeHasTooltips(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_tooltips').files()] if common.hasGalleryIdevice(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_lightbox').files()] if common.hasFX(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_effects').files()] if common.hasSH(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_highlighter').files()] if common.hasGames(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'exe_games').files()] if common.hasABCMusic(page.node): resources = resources + [f.basename() for f in (self.config.webDir/"scripts"/'tinymce_4'/'js'/'tinymce'/'plugins'/'abcmusic'/'export').files()] for resource in resources: fileStr += " <file href=\""+escape(resource)+"\"/>\n" self.dependencies[resource] = True if common.hasElpLink(page.node): fileStr += " <file href=\""+page.node.package.name+".elp\"/>\n" self.resStr += fileStr self.resStr += common.getJavascriptIdevicesResources(page, xmlOutput = True) # adding the dependency with the common files collected: if self.scormType != "commoncartridge": self.resStr += """ <dependency identifierref="COMMON_FILES"/>""" # and no more: self.resStr += '\n' self.resStr += " </resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, scormType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" self.scormType = scormType self.dependencies = {} def createMetaData(self, template): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ lrm = self.package.dublinCore.__dict__.copy() if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author if lrm['date'] == '': lrm['date'] = time.strftime('%Y-%m-%d') # if they don't look like VCARD entries, coerce to fn: for f in ('creator', 'publisher', 'contributors'): if re.match('.*[:;]', lrm[f]) == None: lrm[f] = u'FN:' + lrm[f] xml = template % lrm return xml def save(self, filename): """ Save a imsmanifest file to self.outputDir """ out = open(self.outputDir / filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode('utf8')) out.close() if self.scormType == "scorm1.2": templateFilename = self.config.xulDir / 'templates' / 'imslrm.xml' template = open(templateFilename, 'rb').read() xml = self.createMetaData(template) out = open(self.outputDir / 'imslrm.xml', 'wb') out.write(xml.encode('utf8')) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = unicode(self.idGenerator.generate()) orgId = unicode(self.idGenerator.generate()) # Add the namespaces if self.scormType == "scorm1.2": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- generated by eXe - http://exelearning.org -->\n' xmlStr += u'<manifest identifier="' + manifestId + '" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>1.2</schemaversion> \n" # commented out to work with tum online plattform # kinda dirty, but it HAVE to work with it, so uncomment # if you want a full SCORM1.2 compliance #xmlStr += u" <adlcp:location>imslrm.xml" #xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "scorm2004": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- generated by eXe - http://exelearning.org -->\n' xmlStr += u'<manifest identifier="' + manifestId + '" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" elif self.scormType == "commoncartridge": xmlStr = u'''<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.org --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscc/imscp_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscc/imscp_v1p1 imscp_v1p1.xsd">\n''' % manifestId templateFilename = self.config.xulDir / 'templates' / 'cc.xml' template = open(templateFilename, 'rb').read() xmlStr += self.createMetaData(template) # Metadata if self.scormType == "commoncartridge": xmlStr += u'''<organizations> <organization identifier="%s" structure="rooted-hierarchy"> <item identifier="eXeCC-%s">\n''' % (orgId, unicode(self.idGenerator.generate())) else: xmlStr += u"<organizations default=\"" + orgId + "\"> \n" xmlStr += u' <organization identifier="%s" structure="hierarchical">\n' % orgId if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u"<title>" + title + "</title>\n" if self.scormType == "commoncartridge": # FIXME flatten hierarchy for page in self.pages: self.genItemResStr(page) self.itemStr += "</item>\n" else: depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr if self.scormType == "commoncartridge": xmlStr += " </item>\n" xmlStr += " </organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-" + unicode(self.idGenerator.generate()) resId = "RES-" + unicode(self.idGenerator.generate()) filename = page.name + ".html" self.itemStr += '<item identifier="' + itemId + '" ' if self.scormType != "commoncartridge": self.itemStr += 'isvisible="true" ' self.itemStr += 'identifierref="' + resId + '">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += " <resource identifier=\"" + resId + "\" " self.resStr += "type=\"webcontent\" " # FIXME force dependency on popup_bg.gif on every page # because it isn't a "resource" so we can't tell which # pages will use it from content.css if self.scormType == "commoncartridge": self.resStr += """href="%s"> <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="popup_bg.gif"/>""" % (filename, filename) if page.node.package.backgroundImg: self.resStr += '\n <file href="%s"/>' % \ page.node.package.backgroundImg.basename() self.dependencies["base.css"] = True self.dependencies["content.css"] = True self.dependencies["popup_bg.gif"] = True else: self.resStr += "adlcp:scormtype=\"sco\" " self.resStr += "href=\"" + filename + "\"> \n" self.resStr += """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="popup_bg.gif"/> <file href="APIWrapper.js"/> <file href="SCOFunctions.js"/>""" % filename self.resStr += "\n" fileStr = "" for resource in page.node.getResources(): fileStr += " <file href=\"" + escape(resource) + "\"/>\n" self.dependencies[resource] = True self.resStr += fileStr self.resStr += " </resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" def save(self): """ Save a imsmanifest file and metadata to self.outputDir """ filename = "imsmanifest.xml" out = open(self.outputDir/filename, "wb") out.write(self.createXML().encode('utf8')) out.close() lrm = self.package.dublinCore.__dict__.copy() if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author templateFilename = self.config.xulDir/'templates'/'dublincore.xml' template = open(templateFilename, 'rb').read() xml = template % lrm out = open(self.outputDir/'dublincore.xml', 'wb') out.write(xml.encode('utf8')) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = self.idGenerator.generate() orgId = self.idGenerator.generate() xmlStr = u"""<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.org --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" """ % manifestId xmlStr += "\n " xmlStr += "xsi:schemaLocation=\"http://www.imsglobal.org/xsd/" xmlStr += "imscp_v1p1 imscp_v1p1.xsd " xmlStr += "http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd\"" xmlStr += "> \n" xmlStr += "<metadata> \n" xmlStr += " <schema>IMS Content</schema> \n" xmlStr += " <schemaversion>1.1.3</schemaversion> \n" xmlStr += " <adlcp:location>dublincore.xml" xmlStr += "</adlcp:location> \n" xmlStr += "</metadata> \n" xmlStr += "<organizations default=\""+orgId+"\"> \n" xmlStr += "<organization identifier=\""+orgId xmlStr += "\" structure=\"hierarchical\"> \n" if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u"<title>"+title+"</title>\n" depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr xmlStr += "</organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-"+unicode(self.idGenerator.generate()) resId = "RES-"+unicode(self.idGenerator.generate()) filename = page.name+".html" self.itemStr += "<item identifier=\""+itemId+"\" isvisible=\"true\" " self.itemStr += "identifierref=\""+resId+"\">\n" self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += "<resource identifier=\""+resId+"\" " self.resStr += "type=\"webcontent\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/>""" % filename self.resStr += "\n" fileStr = "" for resource in page.node.getResources(): fileStr += " <file href=\""+escape(resource)+"\"/>\n" self.resStr += fileStr self.resStr += "</resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, scormType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" self.scormType = scormType def save(self, filename): """ Save a imsmanifest file to self.outputDir """ out = open(self.outputDir/filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode('utf8')) if filename == "discussionforum.xml": out.write(self.createForumXML().encode('utf8')) out.close() templateFilename = self.config.xulDir/'templates'/'dublincore.xml' template = open(templateFilename, 'rb').read() xml = template % self.package.dublinCore.__dict__ out = open(self.outputDir/'dublincore.xml', 'wb') out.write(xml.encode('utf8')) out.close() def createForumXML(self): """ returning forum XLM string for manifest file """ xmlStr = "<?xml version = \"1.0\"?>\n" xmlStr += "<forums>\n" for page in self.pages: for idevice in page.node.idevices: if hasattr(idevice, "isForum"): forums = idevice.forumsCache.getForums() xmlStr += self.moodleForums(forums) break xmlStr += "</forums>\n" return xmlStr def moodleForums(self, forums): """ returning moodle forum XLM string for manifest file """ forumStr = "" discussionStr = "" for forum in forums: if forum.lms.lms == "moodle": forumStr += u"<forum><name>%s</name>" % forum.forumName forumStr += u"<id>%s</id>" % escape(forum.forumName) introduction = forum.introduction forumStr += u"<introduction>%s</introduction>" % \ escape(introduction).replace( '<','<' ).replace( '>', '>' ) forumStr += u"<type>%s</type>" % forum.lms.type.encode() forumStr += u"<studentpost>%s</studentpost>\n" % \ forum.lms.studentpost forumStr += u"<subscription>%s</subscription>\n" % \ forum.lms.subscription forumStr += u"<tracking>1</tracking>\n" forumStr += u"<attachmentsize></attachmentsize>\n" forumStr += u"<ratings>0</ratings>\n" forumStr += u"<groupmode>%s</groupmode>\n" % forum.lms.groupmode forumStr += u"<visible>%s</visible>\n" % forum.lms.visible forumStr += u"</forum>" for discussion in forum.discussions: discussionStr += u"<discussion>" discussionStr += u"<discussionId>%s</discussionId>" % \ escape(forum.forumName) discussionStr += u"<subject>%s</subject>" \ % escape(discussion.topic) discussionStr += u"<message>%s</message>" \ % escape(discussion.intro).replace('<', '<').replace('>','>') discussionStr += u"<subscription>send me</subscription>\n" discussionStr += u"</discussion>" xml = forumStr + discussionStr return xml def createXML(self): """ returning XLM string for manifest file """ manifestId = unicode(self.idGenerator.generate()) orgId = unicode(self.idGenerator.generate()) if self.scormType == "scorm1.2": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- generated by eXe - http://exelearning.org -->\n' xmlStr += u'<manifest identifier="'+manifestId+'" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>1.2</schemaversion> \n" xmlStr += u" <adlcp:location>dublincore.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" else: xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- generated by eXe - http://exelearning.org -->\n' xmlStr += u'<manifest identifier="'+manifestId+'" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" xmlStr += u"<organizations default=\""+orgId+"\"> \n" xmlStr += u"<organization identifier=\""+orgId xmlStr += u"\" structure=\"hierarchical\"> \n" title = escape(self.package.root.titleShort) xmlStr += u"<title>"+title+"</title>\n" depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr xmlStr += "</organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xlm string for items and resources """ itemId = "ITEM-"+unicode(self.idGenerator.generate()) resId = "RES-"+unicode(self.idGenerator.generate()) filename = page.name+".html" self.itemStr += "<item identifier=\""+itemId+"\" isvisible=\"true\" " self.itemStr += "identifierref=\""+resId+"\">\n" self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += "<resource identifier=\""+resId+"\" " self.resStr += "type=\"webcontent\" " self.resStr += "adlcp:scormtype=\"sco\" " self.resStr += "href=\""+filename+"\"> \n" self.resStr += """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/> <file href="APIWrapper.js"/> <file href="SCOFunctions.js"/>""" %filename self.resStr += "\n" fileStr = "" for resource in page.node.getResources(): fileStr += " <file href=\""+resource+"\"/>\n" self.resStr += fileStr self.resStr += "</resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages, scormType, metadataType): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" self.scormType = scormType self.metadataType = metadataType self.dependencies = {} def _validateMetaData(self, metadata): modifiedMetaData = False fieldsModified = [] if metadata.get_general().get_description(): for description in metadata.get_general().get_description(): strings = description.get_string() for string in strings: value = string.get_valueOf_() # general description: The field must be 1000 characters maximum, standard SCORM 2.1 if len(value) > 1000: string.set_valueOf_(value[:1000]) modifiedMetaData = True fieldsModified.append(_('general description')) if metadata.get_educational(): for educational in metadata.get_educational(): if educational.get_description(): for description in educational.get_description(): strings = description.get_string() for string in strings: value = string.get_valueOf_() # educational description: The field must be 1000 characters maximum, standard SCORM 2.1 if len(value) > 1000: string.set_valueOf_(value[:1000]) modifiedMetaData = True fieldsModified.append( _('educational description')) return { 'modifiedMetaData': modifiedMetaData, 'fieldsModified': fieldsModified } def createMetaData(self, template): """ if user did not supply metadata title, description or creator then use package title, description, or creator in imslrm if they did not supply a package title, use the package name if they did not supply a date, use today """ xml = '<?xml version="1.0" encoding="UTF-8"?>\n' namespace = 'xmlns="http://ltsc.ieee.org/xsd/LOM" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ltsc.ieee.org/xsd/LOM lomCustom.xsd"' modifiedMetaData = False # depending on (user desired) the metadata type: if self.metadataType == 'LOMES': output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lomEs) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string( lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.name)) metadata.get_general().set_title(title) if self.scormType == "scorm1.2": modifiedMetaData = self._validateMetaData(metadata) if self.package.exportSource: technical = metadata.get_technical() if not technical: technical = lomsubs.technicalSub('technical') metadata.set_technical(technical) opr = technical.get_otherPlatformRequirements() if not opr: opr = lomsubs.otherPlatformRequirementsSub() technical.set_otherPlatformRequirements(opr) opr.add_string( lomsubs.LangStringSub(self.package.lang.encode('utf-8'), 'editor: eXe Learning')) found = False for platform in opr.get_string(): if platform.get_valueOf_( ) == self.package.lomESPlatformMark: found = True if not found: opr.add_string( lomsubs.LangStringSub( self.package.lang.encode('utf-8'), self.package.lomESPlatformMark)) metadata.export(output, 0, namespacedef_=namespace, pretty_print=False) xml += output.getvalue() if self.metadataType == 'LOM': output = StringIO.StringIO() metadata = copy.deepcopy(self.package.lom) title = metadata.get_general().get_title() or lomsubs.titleSub([]) if not title.get_string(): title.add_string( lomsubs.LangStringSub(self.package.lang.encode('utf-8'), self.package.name)) metadata.get_general().set_title(title) if self.scormType == "scorm1.2": modifiedMetaData = self._validateMetaData(metadata) metadata.export(output, 0, namespacedef_=namespace, pretty_print=False) xml += output.getvalue() if self.metadataType == 'DC': lrm = self.package.dublinCore.__dict__.copy() # use package values in absentia: if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author if lrm['date'] == '': lrm['date'] = time.strftime('%Y-%m-%d') # if they don't look like VCARD entries, coerce to fn: for f in ('creator', 'publisher', 'contributors'): if re.match('.*[:;]', lrm[f]) == None: lrm[f] = u'FN:' + lrm[f] xml = template % lrm return {'xml': xml, 'modifiedMetaData': modifiedMetaData} def save(self, filename): """ Save a imsmanifest file to self.outputDir Two works: createXML and createMetaData """ modifiedMetaData = False out = open(self.outputDir / filename, "w") if filename == "imsmanifest.xml": out.write(self.createXML().encode('utf8')) out.close() # now depending on metadataType, <metadata> content is diferent: if self.scormType == "scorm1.2" or self.scormType == "scorm2004": if self.metadataType == 'DC': # if old template is desired, select imslrm.xml file:\r # anything else, yoy should select: templateFilename = self.config.webDir / 'templates' / 'imslrm.xml' template = open(templateFilename, 'rb').read() elif self.metadataType == 'LOMES': template = None elif self.metadataType == 'LOM': template = None # Now the file with metadatas. # Notice that its name is independent of metadataType: metaData = self.createMetaData(template) xml = metaData['xml'] modifiedMetaData = metaData['modifiedMetaData'] out = open(self.outputDir / 'imslrm.xml', 'wb') out.write(xml.encode('utf8')) out.close() return modifiedMetaData def createXML(self): """ returning XLM string for manifest file """ manifestId = unicode(self.idGenerator.generate()) orgId = unicode(self.idGenerator.generate()) # Add the namespaces if self.scormType == "scorm1.2": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- Generated by eXe - http://exelearning.net -->\n' xmlStr += u'<manifest identifier="' + manifestId + '" ' xmlStr += u'xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" ' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" ' xmlStr += u'xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" ' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' xmlStr += u"xsi:schemaLocation=\"http://www.imsproject.org/xsd/" xmlStr += u"imscp_rootv1p1p2 imscp_rootv1p1p2.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 " xmlStr += u"imsmd_rootv1p2p1.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_rootv1p2 " xmlStr += u"adlcp_rootv1p2.xsd\" " xmlStr += u"> \n" xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>1.2</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "scorm2004": xmlStr = u'<?xml version="1.0" encoding="UTF-8"?>\n' xmlStr += u'<!-- Generated by eXe - http://exelearning.net -->\n' xmlStr += u'<manifest identifier="' + manifestId + '" \n' xmlStr += u'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \n' xmlStr += u'xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3" \n' xmlStr += u'xmlns:adlseq="http://www.adlnet.org/xsd/adlseq_v1p3" \n' xmlStr += u'xmlns:adlnav="http://www.adlnet.org/xsd/adlnav_v1p3" \n' xmlStr += u'xmlns:imsss="http://www.imsglobal.org/xsd/imsss" \n' xmlStr += u'xmlns:lom="http://ltsc.ieee.org/xsd/LOM" \n' xmlStr += u'xmlns:lomes="http://ltsc.ieee.org/xsd/LOM" \n' xmlStr += u'xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" \n' xmlStr += u"xsi:schemaLocation=\"http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd " xmlStr += u"http://ltsc.ieee.org/xsd/LOM lomCustom.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlcp_v1p3 adlcp_v1p3.xsd " xmlStr += u"http://www.imsglobal.org/xsd/imsss imsss_v1p0.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlseq_v1p3 adlseq_v1p3.xsd " xmlStr += u"http://www.adlnet.org/xsd/adlnav_v1p3 adlnav_v1p3.xsd" xmlStr += u'"> \n' xmlStr += u"<metadata> \n" xmlStr += u" <schema>ADL SCORM</schema> \n" xmlStr += u" <schemaversion>2004 4th Edition</schemaversion> \n" xmlStr += u" <adlcp:location>imslrm.xml" xmlStr += u"</adlcp:location> \n" xmlStr += u"</metadata> \n" elif self.scormType == "commoncartridge": xmlStr = u'''<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.net --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscc/imscp_v1p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscc/imscp_v1p1 imscp_v1p1.xsd">\n''' % manifestId templateFilename = self.config.webDir / 'templates' / 'cc.xml' template = open(templateFilename, 'rb').read() metaData = self.createMetaData(template) xmlStr += metaData['xml'] # ORGANIZATION if self.scormType == "commoncartridge": xmlStr += u'''<organizations> <organization identifier="%s" structure="rooted-hierarchy"> <item identifier="eXeCC-%s">\n''' % (orgId, unicode(self.idGenerator.generate())) else: xmlStr += u"<organizations default=\"" + orgId + "\"> \n" xmlStr += u' <organization identifier="%s" structure="hierarchical">\n' % orgId if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u" <title>" + title + "</title>\n" if self.scormType == "commoncartridge": # FIXME flatten hierarchy for page in self.pages: self.genItemResStr(page) self.itemStr += " </item>\n" self.itemStr += " </item>\n" else: depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" if depth > page.depth and self.scormType == "scorm2004": self.itemStr += ''' <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>''' depth -= 1 if page.node.children and self.scormType == "scorm2004": # Add fake node with original title itemId = "ITEM-" + unicode(self.idGenerator.generate()) self.itemStr += '<item identifier="' + itemId + '" ' self.itemStr += 'isvisible="true">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" # Increase actual depth because fake node added. Next iteration closes the fake node depth = page.depth + 1 else: depth = page.depth self.genItemResStr(page) while depth >= 1: self.itemStr += "</item>\n" if depth > 1 and self.scormType == "scorm2004": self.itemStr += ''' <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>''' depth -= 1 xmlStr += self.itemStr if self.scormType == "scorm2004": xmlStr += ''' <imsss:sequencing> <imsss:controlMode choice="true" choiceExit="true" flow="true" forwardOnly="false"/> </imsss:sequencing>''' xmlStr += " </organization>\n" xmlStr += "</organizations>\n" # RESOURCES xmlStr += "<resources>\n" xmlStr += self.resStr # If NOT commoncartridge, finally, special resource with # all the common files, as binded with de active style ones: if self.scormType != "commoncartridge": if self.scormType == "scorm1.2": xmlStr += """ <resource identifier="COMMON_FILES" type="webcontent" adlcp:scormtype="asset">\n""" else: xmlStr += """ <resource identifier="COMMON_FILES" type="webcontent" adlcp:scormType="asset">\n""" my_style = G.application.config.styleStore.getStyle( page.node.package.style) for x in my_style.get_style_dir().files('*.*'): xmlStr += """ <file href="%s"/>\n""" % x.basename() # we do want base.css and (for short time), popup_bg.gif, also: xmlStr += """ <file href="base.css"/>\n""" xmlStr += """ <file href="popup_bg.gif"/>\n""" # now the javascript files: xmlStr += """ <file href="SCORM_API_wrapper.js"/>\n""" xmlStr += """ <file href="SCOFunctions.js"/>\n""" xmlStr += """ <file href="common.js"/>\n""" xmlStr += """ <file href="common_i18n.js"/>\n""" if my_style.hasValidConfig(): if my_style.get_jquery() == True: xmlStr += """ <file href="exe_jquery.js"/>\n""" else: xmlStr += """ <file href="exe_jquery.js"/>\n""" # SCORM 1.2 and SCORM 2004: # So that certain platforms do not delete the necessary files so that the resources can be editable if page.node.package.exportSource: xmlStr += """ <file href="content.xsd"/>\n""" xmlStr += """ <file href="content.data"/>\n""" xmlStr += """ <file href="contentv3.xml"/>\n""" xmlStr += """ <file href="imslrm.xml"/>\n""" xmlStr += " </resource>\n" # no more resources: xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-" + unicode(self.idGenerator.generate()) resId = "RES-" + unicode(self.idGenerator.generate()) ext = 'html' if G.application.config.cutFileName == "1": ext = 'htm' filename = page.name + '.' + ext self.itemStr += '<item identifier="' + itemId + '" ' if self.scormType != "commoncartridge": self.itemStr += 'isvisible="true" ' self.itemStr += 'identifierref="' + resId + '">\n' self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" ## SCORM 12 specific metadata: Mastery Score is an ADL extension to the IMS Content Packaging Information Model ## Added for FR [#2501] Add masteryscore to manifest in evaluable nodes if self.scormType == "scorm1.2" and common.hasQuizTest(page.node): self.itemStr += " <adlcp:masteryscore>%s</adlcp:masteryscore>\n" % common.getQuizTestPassRate( page.node) ## RESOURCES self.resStr += " <resource identifier=\"" + resId + "\" " self.resStr += "type=\"webcontent\" " # FIXME force dependency on popup_bg.gif on every page # because it isn't a "resource" so we can't tell which # pages will use it from content.css if self.scormType == "commoncartridge": fileStr = "" self.resStr += """href="%s"> <file href="%s"/> <file href="base.css"/> <file href="popup_bg.gif"/> <file href="exe_jquery.js"/> <file href="common_i18n.js"/> <file href="common.js"/>\n""" % (filename, filename) my_style = G.application.config.styleStore.getStyle( page.node.package.style) for x in my_style.get_style_dir().files('*.*'): fileStr += """ <file href="%s"/>\n""" % x.basename() self.dependencies[x.basename()] = True # CC export require content.* any place inside the manifest: if page.node.package.exportSource and page.depth == 1: self.resStr += ' <file href="content.xsd"/>\n' self.resStr += ' <file href="content.data"/>\n' self.resStr += ' <file href="contentv3.xml"/>\n' if page.node.package.backgroundImg: self.resStr += '\n <file href="%s"/>' % \ page.node.package.backgroundImg.basename() self.dependencies["base.css"] = True self.dependencies["content.css"] = True self.dependencies["popup_bg.gif"] = True else: if self.scormType == "scorm2004": self.resStr += "adlcp:scormType=\"sco\" " self.resStr += "href=\"" + filename + "\"> \n" self.resStr += " <file href=\"" + filename + "\"/> \n" fileStr = "" if self.scormType == "scorm1.2": self.resStr += "adlcp:scormtype=\"sco\" " self.resStr += "href=\"" + filename + "\"> \n" self.resStr += " <file href=\"" + filename + "\"/> \n" fileStr = "" dT = common.getExportDocType() if dT == "HTML5" or common.nodeHasMediaelement(page.node): self.resStr += ' <file href="exe_html5.js"/>\n' resources = page.node.getResources() if common.nodeHasMediaelement(page.node): resources = resources + [ f.basename() for f in (self.config.webDir / "scripts" / 'mediaelement').files() ] if dT != "HTML5": self.scriptsDir = self.config.webDir / "scripts" jsFile = (self.scriptsDir / 'exe_html5.js') jsFile.copyfile(self.outputDir / 'exe_html5.js') if common.nodeHasTooltips(page.node): resources = resources + [ f.basename() for f in (self.config.webDir / "scripts" / 'exe_tooltips').files() ] if common.hasGalleryIdevice(page.node): resources = resources + [ f.basename() for f in (self.config.webDir / "scripts" / 'exe_lightbox').files() ] if common.hasFX(page.node): resources = resources + [ f.basename() for f in (self.config.webDir / "scripts" / 'exe_effects').files() ] if common.hasSH(page.node): resources = resources + [ f.basename() for f in (self.config.webDir / "scripts" / 'exe_highlighter').files() ] if common.hasGames(page.node): resources = resources + [ f.basename() for f in (self.config.webDir / "scripts" / 'exe_games').files() ] if common.hasABCMusic(page.node): resources = resources + [ f.basename() for f in (self.config.webDir / "scripts" / 'tinymce_4' / 'js' / 'tinymce' / 'plugins' / 'abcmusic' / 'export').files() ] for resource in resources: fileStr += " <file href=\"" + escape(resource) + "\"/>\n" self.dependencies[resource] = True if common.hasElpLink(page.node): fileStr += " <file href=\"" + page.node.package.name + ".elp\"/>\n" self.resStr += fileStr self.resStr += common.getJavascriptIdevicesResources(page, xmlOutput=True) # adding the dependency with the common files collected: if self.scormType != "commoncartridge": self.resStr += """ <dependency identifierref="COMMON_FILES"/>""" # and no more: self.resStr += '\n' self.resStr += " </resource>\n"
class Manifest(object): """ Represents an imsmanifest xml file """ def __init__(self, config, outputDir, package, pages): """ Initialize 'outputDir' is the directory that we read the html from and also output the mainfest.xml """ self.config = config self.outputDir = outputDir self.package = package self.idGenerator = UniqueIdGenerator(package.name, config.exePath) self.pages = pages self.itemStr = "" self.resStr = "" def save(self): """ Save a imsmanifest file and metadata to self.outputDir """ filename = "imsmanifest.xml" out = open(self.outputDir / filename, "wb") out.write(self.createXML().encode('utf8')) out.close() # if user did not supply metadata title, description or creator # then use package title, description, or creator in imslrm # if they did not supply a package title, use the package name lrm = self.package.dublinCore.__dict__.copy() if lrm.get('title', '') == '': lrm['title'] = self.package.title if lrm['title'] == '': lrm['title'] = self.package.name if lrm.get('description', '') == '': lrm['description'] = self.package.description if lrm['description'] == '': lrm['description'] = self.package.name if lrm.get('creator', '') == '': lrm['creator'] = self.package.author # Metadata templateFilename = self.config.xulDir / 'templates' / 'dublincore.xml' template = open(templateFilename, 'rb').read() xml = template % lrm out = open(self.outputDir / 'dublincore.xml', 'wb') out.write(xml.encode('utf8')) out.close() def createXML(self): """ returning XLM string for manifest file """ manifestId = self.idGenerator.generate() orgId = self.idGenerator.generate() xmlStr = u"""<?xml version="1.0" encoding="UTF-8"?> <!-- generated by eXe - http://exelearning.org --> <manifest identifier="%s" xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" """ % manifestId xmlStr += "\n " xmlStr += "xsi:schemaLocation=\"http://www.imsglobal.org/xsd/" xmlStr += "imscp_v1p1 imscp_v1p1.xsd " xmlStr += "http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd\"" xmlStr += "> \n" xmlStr += "<metadata> \n" xmlStr += " <schema>IMS Content</schema> \n" xmlStr += " <schemaversion>1.1.3</schemaversion> \n" xmlStr += " <adlcp:location>dublincore.xml" xmlStr += "</adlcp:location> \n" xmlStr += "</metadata> \n" xmlStr += "<organizations default=\"" + orgId + "\"> \n" xmlStr += "<organization identifier=\"" + orgId xmlStr += "\" structure=\"hierarchical\"> \n" if self.package.title != '': title = escape(self.package.title) else: title = escape(self.package.root.titleShort) xmlStr += u"<title>" + title + "</title>\n" depth = 0 for page in self.pages: while depth >= page.depth: self.itemStr += "</item>\n" depth -= 1 self.genItemResStr(page) depth = page.depth while depth >= 1: self.itemStr += "</item>\n" depth -= 1 xmlStr += self.itemStr xmlStr += "</organization>\n" xmlStr += "</organizations>\n" xmlStr += "<resources>\n" xmlStr += self.resStr xmlStr += "</resources>\n" xmlStr += "</manifest>\n" return xmlStr def genItemResStr(self, page): """ Returning xml string for items and resources """ itemId = "ITEM-" + unicode(self.idGenerator.generate()) resId = "RES-" + unicode(self.idGenerator.generate()) filename = page.name + ".html" self.itemStr += "<item identifier=\"" + itemId + "\" isvisible=\"true\" " self.itemStr += "identifierref=\"" + resId + "\">\n" self.itemStr += " <title>" self.itemStr += escape(page.node.titleShort) self.itemStr += "</title>\n" self.resStr += "<resource identifier=\"" + resId + "\" " self.resStr += "type=\"webcontent\" " self.resStr += "href=\"" + filename + "\"> \n" self.resStr += """\ <file href="%s"/> <file href="base.css"/> <file href="content.css"/>""" % filename self.resStr += "\n" fileStr = "" for resource in page.node.getResources(): fileStr += " <file href=\"" + escape(resource) + "\"/>\n" self.resStr += fileStr self.resStr += "</resource>\n"