Actualización
This commit is contained in:
360
main/common_cartridge/export/Cc13Convert.php
Normal file
360
main/common_cartridge/export/Cc13Convert.php
Normal file
@@ -0,0 +1,360 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
class Cc13Convert
|
||||
{
|
||||
/**
|
||||
* Converts a course object into a CC13 package and writes it to disk.
|
||||
*
|
||||
* @param \Chamilo\CourseBundle\Component\CourseCopy\Course $objCourse The Course object is "augmented" with info from the api_get_course_info() function, into the "$objCourse->info" array attribute.
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function convert(string $packagedir, string $outdir, Chamilo\CourseBundle\Component\CourseCopy\Course $objCourse)
|
||||
{
|
||||
$dir = realpath($packagedir);
|
||||
if (empty($dir)) {
|
||||
throw new InvalidArgumentException('Directory does not exist!');
|
||||
}
|
||||
$odir = realpath($outdir);
|
||||
if (empty($odir)) {
|
||||
throw new InvalidArgumentException('Directory does not exist!');
|
||||
}
|
||||
|
||||
if (!empty($objCourse)) {
|
||||
//Initialize the manifest metadata class
|
||||
$meta = new CcMetadataManifest();
|
||||
|
||||
//Package metadata
|
||||
$metageneral = new CcMetadataGeneral();
|
||||
$metageneral->setLanguage($objCourse->info['language']);
|
||||
$metageneral->setTitle($objCourse->info['title'], $objCourse->info['language']);
|
||||
$metageneral->setDescription('', $objCourse->info['language']);
|
||||
$metageneral->setCatalog('category');
|
||||
$metageneral->setEntry($objCourse->info['categoryName']);
|
||||
$meta->addMetadataGeneral($metageneral);
|
||||
|
||||
// Create the manifest
|
||||
$manifest = new CcManifest();
|
||||
|
||||
$manifest->addMetadataManifest($meta);
|
||||
|
||||
$organization = null;
|
||||
|
||||
//Get the course structure - this will be transformed into organization
|
||||
//Step 1 - Get the list and order of sections/topics
|
||||
|
||||
$count = 1;
|
||||
$sections = [];
|
||||
$resources = $objCourse->resources;
|
||||
|
||||
// We check the quiz sections
|
||||
if (isset($resources['quiz'])) {
|
||||
$quizSections = self::getItemSections(
|
||||
$resources['quiz'], // array of quiz IDs generated by the course backup process
|
||||
'quiz',
|
||||
$count,
|
||||
$objCourse->info['code'],
|
||||
$resources[RESOURCE_QUIZQUESTION] // selected question IDs from the course export form
|
||||
);
|
||||
$sections = array_merge($sections, $quizSections);
|
||||
}
|
||||
|
||||
// We check the document sections
|
||||
if (isset($resources['document'])) {
|
||||
$documentSections = self::getItemSections(
|
||||
$resources['document'],
|
||||
'document',
|
||||
$count,
|
||||
$objCourse->info['code']
|
||||
);
|
||||
$sections = array_merge($sections, $documentSections);
|
||||
}
|
||||
|
||||
// We check the wiki sections
|
||||
if (isset($resources['wiki'])) {
|
||||
$wikiSections = self::getItemSections(
|
||||
$resources['wiki'],
|
||||
'wiki',
|
||||
$count,
|
||||
$objCourse->info['code']
|
||||
);
|
||||
$sections = array_merge($sections, $wikiSections);
|
||||
}
|
||||
|
||||
// We check the forum sections
|
||||
if (isset($resources['forum'])) {
|
||||
$forumSections = self::getItemSections(
|
||||
$resources['forum'],
|
||||
'forum',
|
||||
$count,
|
||||
$objCourse->info['code'],
|
||||
$resources['Forum_Category']
|
||||
);
|
||||
$sections = array_merge($sections, $forumSections);
|
||||
}
|
||||
|
||||
// We check the link sections
|
||||
if (isset($resources['link'])) {
|
||||
$linkSections = self::getItemSections(
|
||||
$resources['link'],
|
||||
'link',
|
||||
$count,
|
||||
$objCourse->info['code'],
|
||||
$resources['Link_Category']
|
||||
);
|
||||
$sections = array_merge($sections, $linkSections);
|
||||
}
|
||||
|
||||
//organization title
|
||||
$organization = new CcOrganization();
|
||||
foreach ($sections as $sectionid => $values) {
|
||||
$item = new CcItem();
|
||||
$item->title = $values[0];
|
||||
self::processSequence($item, $manifest, $values[1], $dir, $odir);
|
||||
$organization->addItem($item);
|
||||
}
|
||||
$manifest->putNodes();
|
||||
|
||||
if (!empty($organization)) {
|
||||
$manifest->addNewOrganization($organization);
|
||||
}
|
||||
|
||||
$manifestpath = $outdir.DIRECTORY_SEPARATOR.'imsmanifest.xml';
|
||||
$saved = $manifest->saveTo($manifestpath);
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of type $sections[1]['quiz', [sequence]] where sequence is the result of a call to getSequence().
|
||||
*
|
||||
* @param string $courseCode The course code litteral
|
||||
* @param ?array $itmesExtraData If defined, represents the items IDs selected in the course export form
|
||||
*
|
||||
* @return array
|
||||
* @reference self::getSequence()
|
||||
*/
|
||||
protected static function getItemSections(array $itemData, string $itemType, int &$count, string $courseCode, ?array $itmesExtraData = null)
|
||||
{
|
||||
$sections = [];
|
||||
switch ($itemType) {
|
||||
case 'quiz':
|
||||
case 'document':
|
||||
case 'wiki':
|
||||
$convertType = $itemType;
|
||||
if ($itemType == 'wiki') {
|
||||
$convertType = 'Page';
|
||||
}
|
||||
$sectionid = $count;
|
||||
$sectiontitle = ucfirst($itemType);
|
||||
$sequence = self::getSequence($itemData, 0, $convertType, $courseCode, $itmesExtraData);
|
||||
$sections[$sectionid] = [$sectiontitle, $sequence];
|
||||
$count++;
|
||||
break;
|
||||
case 'link':
|
||||
$links = self::getSequence($itemData, null, $itemType);
|
||||
foreach ($links as $categoryId => $sequence) {
|
||||
$sectionid = $count;
|
||||
if (isset($itmesExtraData[$categoryId])) {
|
||||
$sectiontitle = $itmesExtraData[$categoryId]->title;
|
||||
} else {
|
||||
$sectiontitle = 'General';
|
||||
}
|
||||
$sections[$sectionid] = [$sectiontitle, $sequence];
|
||||
$count++;
|
||||
}
|
||||
break;
|
||||
case 'forum':
|
||||
if (isset($itmesExtraData)) {
|
||||
foreach ($itmesExtraData as $fcategory) {
|
||||
if (isset($fcategory->obj)) {
|
||||
$objCategory = $fcategory->obj;
|
||||
$sectionid = $count;
|
||||
$sectiontitle = $objCategory->cat_title;
|
||||
$sequence = self::getSequence($itemData, $objCategory->iid, $itemType);
|
||||
$sections[$sectionid] = [$sectiontitle, $sequence];
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of items by category and item ID ("test" level, not "question" level) with details like title,
|
||||
* comment (description), type, questions, max_attempt, etc.
|
||||
*
|
||||
* @param array $objItems Array of item objects as returned by the course backup code
|
||||
* @param ?int $categoryId
|
||||
* @param ?string $itemType
|
||||
* @param ?string $coursecode
|
||||
* @param ?array $itemQuestions
|
||||
*
|
||||
* @return array|mixed
|
||||
*/
|
||||
protected static function getSequence(array $objItems, ?int $categoryId = null, ?string $itemType = null, ?string $coursecode = null, ?array $itemQuestions = null)
|
||||
{
|
||||
$sequences = [];
|
||||
switch ($itemType) {
|
||||
case 'quiz':
|
||||
$sequence = [];
|
||||
foreach ($objItems as $objItem) {
|
||||
if ($categoryId === 0) {
|
||||
$questions = [];
|
||||
foreach ($objItem->obj->question_ids as $questionId) {
|
||||
if (isset($itemQuestions[$questionId])) {
|
||||
$questions[$questionId] = $itemQuestions[$questionId];
|
||||
}
|
||||
}
|
||||
// As weird as this may sound, the constructors for the different object types (found in
|
||||
// src/CourseBundle/Component/CourseCopy/Resources/[objecttype].php) store the object property
|
||||
// in the "obj" attribute. Old code...
|
||||
// So here we recover properties from the quiz object, including questions (array built above).
|
||||
$sequence[$categoryId][$objItem->obj->iid] = [
|
||||
'title' => $objItem->obj->title,
|
||||
'comment' => $objItem->obj->description,
|
||||
'cc_type' => 'quiz',
|
||||
'source_id' => $objItem->obj->iid,
|
||||
'questions' => $questions,
|
||||
'max_attempt' => $objItem->obj->max_attempt,
|
||||
'expired_time' => $objItem->obj->expired_time,
|
||||
'pass_percentage' => $objItem->obj->pass_percentage,
|
||||
'random_answers' => $objItem->obj->random_answers,
|
||||
'course_code' => $coursecode,
|
||||
];
|
||||
}
|
||||
}
|
||||
$sequences = $sequence[$categoryId];
|
||||
break;
|
||||
case 'document':
|
||||
$sequence = [];
|
||||
foreach ($objItems as $objItem) {
|
||||
if ($categoryId === 0) {
|
||||
$sequence[$categoryId][$objItem->source_id] = [
|
||||
'title' => $objItem->title,
|
||||
'comment' => $objItem->comment,
|
||||
'cc_type' => ($objItem->file_type == 'folder' ? 'folder' : 'resource'),
|
||||
'source_id' => $objItem->source_id,
|
||||
'path' => $objItem->path,
|
||||
'file_type' => $objItem->file_type,
|
||||
'course_code' => $coursecode,
|
||||
];
|
||||
}
|
||||
}
|
||||
$sequences = $sequence[$categoryId];
|
||||
break;
|
||||
case 'forum':
|
||||
foreach ($objItems as $objItem) {
|
||||
if ($categoryId == $objItem->obj->forum_category) {
|
||||
$sequence[$categoryId][$objItem->obj->forum_id] = [
|
||||
'title' => $objItem->obj->forum_title,
|
||||
'comment' => $objItem->obj->forum_comment,
|
||||
'cc_type' => 'forum',
|
||||
'source_id' => $objItem->obj->iid,
|
||||
];
|
||||
}
|
||||
}
|
||||
$sequences = $sequence[$categoryId];
|
||||
break;
|
||||
case 'page':
|
||||
foreach ($objItems as $objItem) {
|
||||
if ($categoryId === 0) {
|
||||
$sequence[$categoryId][$objItem->page_id] = [
|
||||
'title' => $objItem->title,
|
||||
'comment' => $objItem->content,
|
||||
'cc_type' => 'page',
|
||||
'source_id' => $objItem->page_id,
|
||||
'reflink' => $objItem->reflink,
|
||||
];
|
||||
}
|
||||
}
|
||||
$sequences = $sequence[$categoryId];
|
||||
break;
|
||||
case 'link':
|
||||
if (!isset($categoryId)) {
|
||||
$categories = [];
|
||||
foreach ($objItems as $objItem) {
|
||||
$categories[$objItem->category_id] = self::getSequence($objItems, $objItem->category_id, $itemType);
|
||||
}
|
||||
$sequences = $categories;
|
||||
} else {
|
||||
foreach ($objItems as $objItem) {
|
||||
if ($categoryId == $objItem->category_id) {
|
||||
$sequence[$categoryId][$objItem->source_id] = [
|
||||
'title' => $objItem->title,
|
||||
'comment' => $objItem->description,
|
||||
'cc_type' => 'url',
|
||||
'source_id' => $objItem->source_id,
|
||||
'url' => $objItem->url,
|
||||
'target' => $objItem->target,
|
||||
];
|
||||
}
|
||||
}
|
||||
$sequences = $sequence[$categoryId];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $sequences;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the different types of activities exported from the course.
|
||||
* When dealing with tests, the "sequence" provided here is a test, not a question.
|
||||
* For each sequence, call the corresponding CcConverter[Type] object instantiation method in export/src/converter/.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function processSequence(CcIItem &$item, CcIManifest &$manifest, array $sequence, string $packageroot, string $outdir)
|
||||
{
|
||||
if (!empty($sequence)) {
|
||||
foreach ($sequence as $seq) {
|
||||
$activity_type = ucfirst($seq['cc_type']);
|
||||
$activity_indentation = 0;
|
||||
$aitem = self::itemIndenter($item, $activity_indentation);
|
||||
$caller = "CcConverter{$activity_type}";
|
||||
if (class_exists($caller)) {
|
||||
$obj = new $caller($aitem, $manifest, $packageroot, $outdir);
|
||||
if (!$obj->convert($outdir, $seq)) {
|
||||
throw new RuntimeException("failed to convert {$activity_type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static function itemIndenter(CcIItem &$item, $level = 0)
|
||||
{
|
||||
$indent = (int) $level;
|
||||
$indent = ($indent) <= 0 ? 0 : $indent;
|
||||
$nprev = null;
|
||||
$nfirst = null;
|
||||
for ($pos = 0, $size = $indent; $pos < $size; $pos++) {
|
||||
$nitem = new CcItem();
|
||||
$nitem->title = '';
|
||||
if (empty($nfirst)) {
|
||||
$nfirst = $nitem;
|
||||
}
|
||||
if (!empty($nprev)) {
|
||||
$nprev->addChildItem($nitem);
|
||||
}
|
||||
$nprev = $nitem;
|
||||
}
|
||||
$result = $item;
|
||||
if (!empty($nfirst)) {
|
||||
$item->addChildItem($nfirst);
|
||||
$result = $nprev;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user