/** * Create the imsmanifest.xml file. * * @return False on error, true otherwise. * @author Thanos Kyritsis <*****@*****.**> * @author Amand Tihon <*****@*****.**> */ function createManifest() { /** * Create a simple <metadata> * * * @param $title The resource title * @param $description The resource description * @return A string containing the metadata block. * @author Thanos Kyritsis <*****@*****.**> * @author Amand Tihon <*****@*****.**> */ function makeMetaData($title, $description, $identifier) { if (empty($title) and empty($description)) { return ' '; } $out = "<metadata>" . "\n" . "<lom:lom>" . "\n" . "<lom:general>" . "\n" . "<lom:identifier>" . "\n" . "<lom:entry>" . $identifier . "</lom:entry>" . "\n" . "</lom:identifier>" . "\n"; if (empty($title)) { $title = "null"; } $out .= "<lom:title>" . "\n" . "<lom:string>" . htmlspecialchars($title) . "</lom:string>" . "\n" . "</lom:title>" . "\n"; if (empty($description)) { $description = "null"; $keyword = "null"; } $out .= "<lom:description>" . "\n" . "<lom:string>" . htmlspecialchars($description) . "</lom:string>" . "\n" . "</lom:description>" . "\n"; $out .= "<lom:keyword>" . "\n" . "<lom:string>" . htmlspecialchars($description) . "</lom:string>" . "\n" . "</lom:keyword>" . "\n"; $out .= "</lom:general>" . "\n" . "<lom:lifeCycle>" . "\n" . "<lom:version>" . "\n" . "<lom:string>1.0</lom:string>" . "\n" . "</lom:version>" . "\n" . "<lom:status>" . "\n" . "<lom:source>LOMv1.0</lom:source>" . "\n" . "<lom:value>final</lom:value>" . "\n" . "</lom:status>" . "\n" . "</lom:lifeCycle>" . "\n" . "<lom:metaMetadata>" . "\n" . "<lom:identifier>" . "\n" . "<lom:entry>" . $identifier . "</lom:entry>" . "\n" . "</lom:identifier>" . "\n" . "<lom:metadataSchema>LOMv1.0</lom:metadataSchema>" . "\n" . "<lom:metadataSchema>SCORM_CAM_v1.3</lom:metadataSchema>" . "\n" . "</lom:metaMetadata>" . "\n" . "<lom:technical>" . "\n" . "<lom:format>text/html</lom:format>" . "\n" // TODO . "</lom:technical>" . "\n" . "<lom:rights>" . "\n" . "<lom:cost>" . "\n" . "<lom:source>LOMv1.0</lom:source>" . "\n" . "<lom:value>no</lom:value>" . "\n" . "</lom:cost>" . "\n" . "<lom:copyrightAndOtherRestrictions>" . "\n" . "<lom:source>LOMv1.0</lom:source>" . "\n" . "<lom:value>no</lom:value>" . "\n" . "</lom:copyrightAndOtherRestrictions>" . "\n" . "</lom:rights>" . "\n" . "</lom:lom>" . "\n" . "</metadata>" . "\n"; return $out; } /** * Create a <metadata> for the whole CAM model package * * * @param $title The resource title * @param $description The resource description * @return A string containing the metadata block. * * @author Thanos Kyritsis <*****@*****.**> */ function makeCAMetaData($title, $description) { if (empty($title) and empty($description)) { return ' '; } $out = "<metadata>" . "\n" . "<schema>ADL SCORM</schema>" . "\n" . "<schemaversion>CAM 1.3</schemaversion>" . "\n" . "<lom:lom>" . "\n" . "<lom:general>" . "\n"; if (empty($title)) { $title = "null"; } $out .= "<lom:title>" . "\n" . "<lom:string>" . htmlspecialchars($title) . "</lom:string>" . "\n" . "</lom:title>" . "\n"; if (empty($description)) { $description = "null"; } $out .= "<lom:description>" . "\n" . "<lom:string>" . htmlspecialchars($description) . "</lom:string>" . "\n" . "</lom:description>" . "\n"; $out .= "</lom:general>" . "\n" . "<lom:metaMetadata>" . "\n" . "<lom:metadataSchema>LOMv1.0</lom:metadataSchema>" . "\n" . "<lom:metadataSchema>SCORM_CAM_v1.3</lom:metadataSchema>" . "\n" . "</lom:metaMetadata>" . "\n" . "</lom:lom>" . "\n" . "</metadata>" . "\n"; return $out; } /** * Recursive function to deal with the tree representation of the items * * @param $itemlist the subtree to build * @param $depth indentation level. Is it really useful ? * @return the (sub-)tree representation * * @author Thanos Kyritsis <*****@*****.**> * @author Amand Tihon <*****@*****.**> */ $blocking = ""; function createItemList($itemlist, $depth = 0) { global $blocking; $out = ""; $ident = ""; for ($i = 0; $i < $depth; $i++) { $ident .= " "; } foreach ($itemlist as $item) { $identifier = "I_" . $item['ID']; $out .= $ident . '<item identifier="' . $identifier . '" isvisible="true" '; if ($item['contentType'] != 'LABEL') { $out .= 'identifierref="R_' . $item['ID'] . '" '; } $out .= '>' . "\n"; $out .= $ident . ' <title>' . htmlspecialchars($item['name']) . '</title>' . "\n"; // Check if previous was blocking // not valid for scorm 2004 /* if (!empty($blocking) && ($item['contentType'] != 'LABEL')) { $out .= ' <adlcp:prerequisites type="aicc_script"><![CDATA[I_'.$blocking.']]></adlcp:prerequisites>'."\n"; } */ // Add metadata, except for LABELS if ($item['contentType'] != 'LABEL') { $out .= makeMetaData($item['name'], $item['itemComment'], $identifier); } if (!isset($item['children'])) { // change only if we do not recurse. $blocking = ($item['lock'] == 'CLOSE') ? $item['ID'] : ''; } else { $out .= createItemList($item['children'], $depth + 1); } $out .= $ident . '</item>' . "\n"; } return $out; } /** * Create the frame file that'll hold the document. This frame is supposed to * set the SCO's status * @param $filename string: the name of the file to create, absolute. * @param $targetPath string: The actual document path, relative to the scorm * @return False on error, true otherwise. * * @author Thanos Kyritsis <*****@*****.**> * @author Amand Tihon <*****@*****.**> */ function createFrameFile($fileName, $targetPath) { global $langErrorCreatingFrame, $langErrorCreatingManifest, $charset; if (!($f = fopen($fileName, 'w'))) { $this->error[] = $langErrorCreatingFrame; return false; } fwrite($f, '<html><head> <meta http-equiv="Content-Type" content="text/html; charset=' . $charset . '"> <script src="APIWrapper.js" type="text/javascript" language="JavaScript"></script> <title>Default Title</title> </head> <frameset border="0" rows="100%" onload="immediateComplete()"> <frame src="' . $targetPath . '" scrolling="auto"> </frameset> </html>'); fclose($f); return true; } /** * Create the frame file that'll hold the course description. * This frame sets the SCO's status * @param $filename string: the name of the file to create, absolute. * @return False on error, true otherwise. * * @author Thanos Kyritsis <*****@*****.**> */ function createDescFrameFile($fileName) { global $langErrorCreatingFrame, $course_id, $langThisCourseDescriptionIsEmpty, $charset; if (!($f = fopen($fileName, 'w'))) { $this->error[] = $langErrorCreatingFrame; return false; } $course_description = ""; $blocs = Database::get()->queryArray("SELECT `id`, `title`, `comments` FROM `course_description` WHERE course_id = ?d ORDER BY `order`", $course_id); if (count($blocs) > 0) { $course_description .= " <hr noshade size=\"1\">"; foreach ($blocs as $bloc) { $course_description .= " <H4> " . $bloc->title . " </H4> <font size=2 face='arial, helvetica'> " . make_clickable(nl2br($bloc->comments)) . " </font>"; } } else { $course_description .= "<br><h4>$langThisCourseDescriptionIsEmpty</h4>"; } fwrite($f, '<html>' . "\n" . '<head>' . "\n" . '<meta http-equiv="Content-Type" content="text/html; charset=' . $charset . '">' . "\n" . '<script src="APIWrapper.js" type="text/javascript" language="JavaScript"></script>' . "\n" . '</head>' . "\n" . '<body onload="immediateComplete()">' . "\n" . '<table width="99%" border="0">' . "\n" . '<tr>' . "\n" . '<td colspan="2">' . "\n" . $course_description . "\n" . '</td>' . "\n" . '</tr>' . "\n" . '<tr name="bottomLine">' . "\n" . '<td colspan="2">' . "\n" . '<br>' . "\n" . '<hr noshade size="1">' . "\n" . '</td>' . "\n" . '</tr>' . "\n" . '</table>' . "\n" . '</body>' . "\n" . '</html>' . "\n" ); fclose($f); return true; } // Start creating sections for items and resources // First the items... $manifest_itemTree = '<organizations default="A1"><organization identifier="A1">' . "\n" . '<title>' . $this->name . '</title>' . "\n" . createItemList($this->itemTree) . '</organization></organizations>' . "\n"; // ...Then the resources $manifest_resources = "<resources>\n"; foreach ($this->resourceMap as $module) { if ($module['contentType'] == 'LABEL') { continue; } switch ($module['contentType']) { case 'DOCUMENT': case 'MEDIA': $framefile = $this->destDir . '/frame_for_' . $module['ID'] . '.html'; $targetfile = 'Documents' . $module['path']; // Create an html file with a frame for the document. if (!createFrameFile($framefile, $targetfile)) { return false; } // Add the resource to the manifest $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" adlcp:scormType="sco" ' . ' href="' . basename($framefile) . '">' . "\n" . ' <file href="' . basename($framefile) . '" />' . "\n" . ' <file href="' . $targetfile . '">' . "\n" . makeMetaData($module['name'], $module['resourceComment'], $ridentifier) . "</file>\n" . "</resource>\n"; break; case 'EXERCISE': $targetfile = $module['fileName']; // Add the resource to the manifest $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" adlcp:scormType="sco" ' . ' href="' . $targetfile . '" >' . "\n" . ' <file href="' . $targetfile . '">' . "\n" . makeMetaData($module['name'], $module['resourceComment'], $ridentifier) . "</file>\n" . "</resource>\n"; break; case 'SCORM_ASSET' : // Add the resource to the manifest $path = 'OrigScorm'; $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" ' . ' href="OrigScorm' . $module['path'] . '">' . "\n" . ' <file href="OrigScorm' . $module['path'] . '">' . "\n" . makeMetaData($module['name'], $module['resourceComment'], $ridentifier) . "</file>\n" . "</resource>\n"; break; case 'SCORM' : // Add the resource to the manifest $path = 'OrigScorm'; $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" adlcp:scormType="sco" ' . ' href="OrigScorm' . $module['path'] . '">' . "\n" . ' <file href="OrigScorm' . $module['path'] . '">' . "\n" . makeMetaData($module['name'], $module['resourceComment'], $ridentifier) . "</file>\n" . "</resource>\n"; break; case 'COURSE_DESCRIPTION': $framefile = $this->destDir . '/frame_for_' . $module['ID'] . '.html'; // Create an html file with a frame for the document. if (!createDescFrameFile($framefile)) { return false; } // Add the resource to the manifest $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" adlcp:scormType="sco" ' . ' href="' . basename($framefile) . '">' . "\n" . ' <file href="' . basename($framefile) . '">' . "\n" . makeMetaData($module['name'], $module['resourceComment'], $ridentifier) . "</file>\n" . "</resource>\n"; break; case 'LINK': case 'MEDIALINK': $framefile = $this->destDir . '/frame_for_' . $module['ID'] . '.html'; if ($module['contentType'] == 'MEDIALINK') { $targetfile = urldecode(MultimediaHelper::makeEmbeddableMedialink($module['path'])); } else { $targetfile = $module['path']; } // Create an html file with a frame for the document. if (!createFrameFile($framefile, $targetfile)) { return false; } // Add the resource to the manifest $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" adlcp:scormType="sco" ' . ' href="' . basename($framefile) . '">' . "\n" . ' <file href="' . basename($framefile) . '">' . "\n" . makeMetaData($module['name'], $module['resourceComment'], $ridentifier) . "</file>\n" . "</resource>\n"; break; default : break; } } $manifest_resources .= '</resources>' . "\n"; $manifestPath = $this->destDir . '/imsmanifest.xml'; if (!$f = fopen($manifestPath, 'w')) { $this->error[] = $GLOBALS['langErrorCreatingManifest']; return false; } // Prepare Metadata $metadata = makeCAMetaData($this->name, $this->comment); // Write header global $charset; fwrite($f, '<?xml version="1.0" encoding="' . $charset . '" ?> <manifest identifier="SingleCourseManifest" version="1.1" xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:lom="http://ltsc.ieee.org/xsd/LOM" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_v1p3" xmlns:imsss="http://www.imsglobal.org/xsd/imsss" xmlns:adlseq="http://www.adlnet.org/xsd/adlseq_v1p3" xmlns:adlnav="http://www.adlnet.org/xsd/adlnav_v1p3" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 imscp_v1p1.xsd http://ltsc.ieee.org/xsd/LOM lom.xsd http://www.adlnet.org/xsd/adlcp_v1p3 adlcp_v1p3.xsd http://www.imsglobal.org/xsd/imsss imsss_v1p0.xsd http://www.adlnet.org/xsd/adlseq_v1p3 adlseq_v1p3.xsd http://www.adlnet.org/xsd/adlnav_v1p3 adlnav_v1p3.xsd">' . "\n"); fwrite($f, $metadata); fwrite($f, $manifest_itemTree); fwrite($f, $manifest_resources); fwrite($f, "</manifest>\n"); fclose($f); return true; }
/** * Create the imsmanifest.xml file. * * @return False on error, true otherwise. * @author Thanos Kyritsis <*****@*****.**> * @author Amand Tihon <*****@*****.**> */ function createManifest() { /** * Create a <metadata> for the whole CAM model package * * * @param $title The resource title * @param $description The resource description * @param $language The resource language (required in IMS CP 1.1.4). Defaults to "en" * @return A string containing the metadata block. * * @author Sakis Agorastos <*****@*****.**> * @author Thanos Kyritsis <*****@*****.**> */ function makeCAMetaData($title, $description, $language) { if (empty($language)) { $language = "en"; } if (empty($title) and empty($description)) { return ' '; } $out = "<metadata>" . "\n" . "<schema>IMS Content</schema>" . "\n" . "<schemaversion>1.1.4</schemaversion>" . "\n" . "<imsmd:lom>" . "\n" . "<imsmd:general>" . "\n"; if (empty($title)) { $title = "null"; } $out .= "<imsmd:title>" . "\n" . "<imsmd:langstring xml:lang=\"" . $language . "\">" . htmlspecialchars($title) . "</imsmd:langstring>" . "\n" . "</imsmd:title>" . "\n"; $out .= "<imsmd:language>" . $language . "</imsmd:language>" . "\n"; if (empty($description)) { $description = "null"; } $out .= "<imsmd:description>" . "\n" . "<imsmd:langstring xml:lang=\"" . $language . "\">" . htmlspecialchars($description) . "</imsmd:langstring>" . "\n" . "</imsmd:description>" . "\n"; $out .= "<imsmd:structure>" . "\n" . "<imsmd:source>" . "\n" . "<imsmd:langstring xml:lang=\"x-none\">LOMv1.0</imsmd:langstring>" . "\n" . "</imsmd:source>" . "\n" . "<imsmd:value>" . "\n" . "<imsmd:langstring xml:lang=\"x-none\">Hierarchical</imsmd:langstring>" . "\n" . "</imsmd:value>" . "\n" . "</imsmd:structure>"; $out .= "</imsmd:general>" . "\n" . "</imsmd:lom>" . "\n" . "</metadata>" . "\n"; return $out; } /** * Recursive function to deal with the tree representation of the items * * @param $itemlist the subtree to build * @param $depth indentation level. Is it really useful ? * @return the (sub-)tree representation * * @author Thanos Kyritsis <*****@*****.**> * @author Amand Tihon <*****@*****.**> */ $blocking = ""; function createItemList($itemlist, $depth = 0) { global $blocking; $out = ""; $ident = ""; for ($i = 0; $i < $depth; $i++) { $ident .= " "; } foreach ($itemlist as $item) { $identifier = "I_" . $item['ID']; $out .= $ident . '<item identifier="' . $identifier . '" isvisible="true" '; if ($item['contentType'] != 'LABEL') { $out .= 'identifierref="R_' . $item['ID'] . '" '; } $out .= '>' . "\n"; $out .= $ident . ' <title>' . htmlspecialchars($item['name']) . '</title>' . "\n"; if (!isset($item['children'])) { // change only if we do not recurse. $blocking = $item['lock'] == 'CLOSE' ? $item['ID'] : ''; } else { $out .= createItemList($item['children'], $depth + 1); } $out .= $ident . '</item>' . "\n"; } return $out; } /** * Create the frame file that'll hold the document. This frame is supposed to * set the SCO's status * @param $filename string: the name of the file to create, absolute. * @param $targetPath string: The actual document path, relative to the scorm * @return False on error, true otherwise. * * @author Thanos Kyritsis <*****@*****.**> * @author Amand Tihon <*****@*****.**> */ function createFrameFile($fileName, $targetPath) { global $langErrorCreatingFrame, $langErrorCreatingManifest, $charset; if (!($f = fopen($fileName, 'w'))) { $this->error[] = $langErrorCreatingFrame; return false; } fwrite($f, '<html><head> <meta http-equiv="Content-Type" content="text/html; charset=' . $charset . '"> <script src="APIWrapper.js" type="text/javascript" language="JavaScript"></script> <title>Default Title</title> </head> <frameset border="0" rows="100%" onload="immediateComplete()"> <frame src="' . $targetPath . '" scrolling="auto"> </frameset> </html>'); fclose($f); return true; } /** * Create the frame file that'll hold the course description. * This frame sets the SCO's status * @param $filename string: the name of the file to create, absolute. * @return False on error, true otherwise. * * @author Thanos Kyritsis <*****@*****.**> */ function createDescFrameFile($fileName) { global $langErrorCreatingFrame, $course_id, $langThisCourseDescriptionIsEmpty, $charset; if (!($f = fopen($fileName, 'w'))) { $this->error[] = $langErrorCreatingFrame; return false; } $course_description = ""; $blocs = Database::get()->queryArray("SELECT `id`, `title`, `comments` FROM `course_description` WHERE course_id = ?d ORDER BY id", $course_id); if (count($blocs) > 0) { $course_description .= "\n <hr noshade size=\"1\">"; foreach ($blocs as $bloc) { $course_description .= "\n <H4>\n " . $bloc->title . "\n </H4>\n <font size=2 face='arial, helvetica'>\n " . make_clickable(nl2br($bloc->comments)) . "\n </font>"; } } else { $course_description .= "<br><h4>{$langThisCourseDescriptionIsEmpty}</h4>"; } fwrite($f, '<html>' . "\n" . '<head>' . "\n" . '<meta http-equiv="Content-Type" content="text/html; charset=' . $charset . '">' . "\n" . '<script src="APIWrapper.js" type="text/javascript" language="JavaScript"></script>' . "\n" . '</head>' . "\n" . '<body onload="immediateComplete()">' . "\n" . '<table width="99%" border="0">' . "\n" . '<tr>' . "\n" . '<td colspan="2">' . "\n" . $course_description . "\n" . '</td>' . "\n" . '</tr>' . "\n" . '<tr name="bottomLine">' . "\n" . '<td colspan="2">' . "\n" . '<br>' . "\n" . '<hr noshade size="1">' . "\n" . '</td>' . "\n" . '</tr>' . "\n" . '</table>' . "\n" . '</body>' . "\n" . '</html>' . "\n"); fclose($f); return true; } // Start creating sections for items and resources // First the items... $manifest_itemTree = '<organizations default="A1">' . '<organization identifier="A1" structure = "hierarchical">' . '<title>' . $this->name . '</title>' . createItemList($this->itemTree) . '</organization>' . "" . '</organizations>' . ""; // ...Then the resources $manifest_resources = "<resources>\n"; foreach ($this->resourceMap as $module) { if ($module['contentType'] == 'LABEL') { continue; } switch ($module['contentType']) { case 'DOCUMENT': case 'MEDIA': $framefile = $this->destDir . '/frame_for_' . $module['ID'] . '.html'; $targetfile = 'Documents' . $module['path']; // Create an html file with a frame for the document. if (!createFrameFile($framefile, $targetfile)) { return false; } // Add the resource to the manifest $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" ' . ' href="' . basename($framefile) . '">' . "\n" . ' <file href="' . basename($framefile) . '" />' . "\n" . ' <file href="' . $targetfile . '">' . "\n" . "</file>\n" . "</resource>\n"; break; case 'EXERCISE': $targetfile = $module['fileName']; // Add the resource to the manifest $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" ' . ' href="' . $targetfile . '" >' . "\n" . ' <file href="' . $targetfile . '">' . "\n" . "</file>\n" . "</resource>\n"; break; case 'SCORM_ASSET': // Add the resource to the manifest $path = 'OrigScorm'; $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" ' . ' href="OrigScorm' . $module['path'] . '">' . "\n" . ' <file href="OrigScorm' . $module['path'] . '">' . "\n" . "</file>\n" . "</resource>\n"; break; case 'SCORM': // Add the resource to the manifest $path = 'OrigScorm'; $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" ' . ' href="OrigScorm' . $module['path'] . '">' . "\n" . ' <file href="OrigScorm' . $module['path'] . '">' . "\n" . "</file>\n" . "</resource>\n"; break; case 'COURSE_DESCRIPTION': $framefile = $this->destDir . '/frame_for_' . $module['ID'] . '.html'; // Create an html file with a frame for the document. if (!createDescFrameFile($framefile)) { return false; } // Add the resource to the manifest $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" ' . ' href="' . basename($framefile) . '">' . "\n" . ' <file href="' . basename($framefile) . '">' . "\n" . "</file>\n" . "</resource>\n"; break; case 'LINK': case 'MEDIALINK': $framefile = $this->destDir . '/frame_for_' . $module['ID'] . '.html'; $targetfile = $module['path']; // Create an html file with a frame for the document. if (!createFrameFile($framefile, $targetfile)) { return false; } // Add the resource to the manifest $ridentifier = "R_" . $module['ID']; $manifest_resources .= '<resource identifier="' . $ridentifier . '" type="webcontent" ' . ' href="' . basename($framefile) . '">' . "\n" . ' <file href="' . basename($framefile) . '" />' . "\n" . "</resource>\n"; break; default: break; } } $manifest_resources .= '</resources>' . "\n"; $manifestPath = $this->destDir . '/imsmanifest.xml'; if (!($f = fopen($manifestPath, 'w'))) { $this->error[] = $GLOBALS['langErrorCreatingManifest']; return false; } // Prepare Metadata $metadata = makeCAMetaData($this->name, $this->comment, $this->language); // Write header fwrite($f, '<?xml version = "1.0" encoding = "UTF-8"?> <manifest 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" xsi:schemaLocation = "http://www.imsglobal.org/xsd/imscp_v1p1 http://www.imsglobal.org/xsd/imscp_v1p1.xsd http://www.imsglobal.org/xsd/imsmd_v1p2 http://www.imsglobal.org/xsd/imsmd_v1p2.xsd " identifier="Manifest1" version="IMS CP 1.1.4">' . "\n"); fwrite($f, $metadata); fwrite($f, $manifest_itemTree); fwrite($f, $manifest_resources); fwrite($f, "</manifest>\n"); fclose($f); return true; }