Course maintenance: Add support for IMS Common Cartridge 1.3 import and export - refs #2747

Authored-by: Christian <christianbeeznest@users.noreply.github.com>
pull/3952/head
christianbeeznest 4 years ago committed by GitHub
parent 3ebaffa9e0
commit 47915cfb17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      composer.json
  2. 92
      main/common_cartridge/cc13_export.php
  3. 75
      main/common_cartridge/cc13_import.php
  4. 291
      main/common_cartridge/export/Cc13Convert.php
  5. 46
      main/common_cartridge/export/Cc13ExportConvert.php
  6. 49
      main/common_cartridge/export/src/CcForum.php
  7. 151
      main/common_cartridge/export/src/CcItem.php
  8. 329
      main/common_cartridge/export/src/CcManifest.php
  9. 51
      main/common_cartridge/export/src/CcMetadataGeneral.php
  10. 52
      main/common_cartridge/export/src/CcMetadataManifest.php
  11. 18
      main/common_cartridge/export/src/CcMetadataResource.php
  12. 16
      main/common_cartridge/export/src/CcMetadataResourceEducational.php
  13. 97
      main/common_cartridge/export/src/CcOrganization.php
  14. 98
      main/common_cartridge/export/src/CcPage.php
  15. 164
      main/common_cartridge/export/src/CcResources.php
  16. 102
      main/common_cartridge/export/src/CcVersion13.php
  17. 59
      main/common_cartridge/export/src/CcWebLink.php
  18. 88
      main/common_cartridge/export/src/base/CcConverters.php
  19. 53
      main/common_cartridge/export/src/base/CcGeneralFile.php
  20. 520
      main/common_cartridge/export/src/base/CcVersion1.php
  21. 122
      main/common_cartridge/export/src/base/CcVersionBase.php
  22. 269
      main/common_cartridge/export/src/base/CssParser.php
  23. 470
      main/common_cartridge/export/src/base/XMLGenericDocument.php
  24. 37
      main/common_cartridge/export/src/converter/CcConverterFolder.php
  25. 36
      main/common_cartridge/export/src/converter/CcConverterForum.php
  26. 41
      main/common_cartridge/export/src/converter/CcConverterPage.php
  27. 80
      main/common_cartridge/export/src/converter/CcConverterQuiz.php
  28. 48
      main/common_cartridge/export/src/converter/CcConverterResource.php
  29. 27
      main/common_cartridge/export/src/converter/CcConverterUrl.php
  30. 18
      main/common_cartridge/export/src/interfaces/CcIItem.php
  31. 30
      main/common_cartridge/export/src/interfaces/CcIManifest.php
  32. 10
      main/common_cartridge/export/src/interfaces/CcIMetadataFile.php
  33. 16
      main/common_cartridge/export/src/interfaces/CcIMetadataManifest.php
  34. 10
      main/common_cartridge/export/src/interfaces/CcIMetadataResource.php
  35. 16
      main/common_cartridge/export/src/interfaces/CcIOrganization.php
  36. 16
      main/common_cartridge/export/src/interfaces/CcIResource.php
  37. 7
      main/common_cartridge/export/src/lib/assesment/Assesment13ResourceFile.php
  38. 74
      main/common_cartridge/export/src/lib/assesment/Assesment1ResourceFile.php
  39. 11
      main/common_cartridge/export/src/lib/assesment/CcAssesmentAltmaterial.php
  40. 30
      main/common_cartridge/export/src/lib/assesment/CcAssesmentDecvartype.php
  41. 50
      main/common_cartridge/export/src/lib/assesment/CcAssesmentFlowLabeltype.php
  42. 46
      main/common_cartridge/export/src/lib/assesment/CcAssesmentFlowMatBase.php
  43. 52
      main/common_cartridge/export/src/lib/assesment/CcAssesmentFlowMattype.php
  44. 68
      main/common_cartridge/export/src/lib/assesment/CcAssesmentFlowtype.php
  45. 135
      main/common_cartridge/export/src/lib/assesment/CcAssesmentHelper.php
  46. 16
      main/common_cartridge/export/src/lib/assesment/CcAssesmentItemfeedbacHinttype.php
  47. 10
      main/common_cartridge/export/src/lib/assesment/CcAssesmentItemfeedbackHintmaterial.php
  48. 36
      main/common_cartridge/export/src/lib/assesment/CcAssesmentItemfeedbackShintmaterialBase.php
  49. 25
      main/common_cartridge/export/src/lib/assesment/CcAssesmentItemfeedbackShintypeBase.php
  50. 10
      main/common_cartridge/export/src/lib/assesment/CcAssesmentItemfeedbackSolutionmaterial.php
  51. 16
      main/common_cartridge/export/src/lib/assesment/CcAssesmentItemfeedbackSolutiontype.php.php
  52. 74
      main/common_cartridge/export/src/lib/assesment/CcAssesmentItemfeedbacktype.php
  53. 20
      main/common_cartridge/export/src/lib/assesment/CcAssesmentItemmetadata.php
  54. 10
      main/common_cartridge/export/src/lib/assesment/CcAssesmentMatbreak.php
  55. 32
      main/common_cartridge/export/src/lib/assesment/CcAssesmentMaterial.php
  56. 47
      main/common_cartridge/export/src/lib/assesment/CcAssesmentMaterialBase.php
  57. 18
      main/common_cartridge/export/src/lib/assesment/CcAssesmentMatref.php
  58. 62
      main/common_cartridge/export/src/lib/assesment/CcAssesmentMattext.php
  59. 60
      main/common_cartridge/export/src/lib/assesment/CcAssesmentMetadata.php
  60. 84
      main/common_cartridge/export/src/lib/assesment/CcAssesmentPresentation.php
  61. 22
      main/common_cartridge/export/src/lib/assesment/CcAssesmentPresentationMaterialBase.php
  62. 216
      main/common_cartridge/export/src/lib/assesment/CcAssesmentQuestionMultichoice.php
  63. 190
      main/common_cartridge/export/src/lib/assesment/CcAssesmentQuestionMultichoiceMultiresponse.php
  64. 155
      main/common_cartridge/export/src/lib/assesment/CcAssesmentQuestionProcBase.php
  65. 78
      main/common_cartridge/export/src/lib/assesment/CcAssesmentRenderChoicetype.php
  66. 106
      main/common_cartridge/export/src/lib/assesment/CcAssesmentRenderFibtype.php
  67. 62
      main/common_cartridge/export/src/lib/assesment/CcAssesmentRespconditiontype.php
  68. 81
      main/common_cartridge/export/src/lib/assesment/CcAssesmentResponseLabeltype.php
  69. 11
      main/common_cartridge/export/src/lib/assesment/CcAssesmentResponseMatref.php
  70. 11
      main/common_cartridge/export/src/lib/assesment/CcAssesmentResponseStrtype.php
  71. 32
      main/common_cartridge/export/src/lib/assesment/CcAssesmentResprocessingtype.php
  72. 20
      main/common_cartridge/export/src/lib/assesment/CcAssesmentRubricBase.php
  73. 48
      main/common_cartridge/export/src/lib/assesment/CcAssesmentSection.php
  74. 56
      main/common_cartridge/export/src/lib/assesment/CcAssesmentSectionItem.php
  75. 53
      main/common_cartridge/export/src/lib/assesment/CcAssignmentConditionvar.php
  76. 35
      main/common_cartridge/export/src/lib/assesment/CcAssignmentConditionvarAndtype.php
  77. 10
      main/common_cartridge/export/src/lib/assesment/CcAssignmentConditionvarOthertype.php
  78. 35
      main/common_cartridge/export/src/lib/assesment/CcAssignmentConditionvarVarequaltype.php
  79. 11
      main/common_cartridge/export/src/lib/assesment/CcAssignmentConditionvarVarsubstringtype.php
  80. 27
      main/common_cartridge/export/src/lib/assesment/CcAssignmentDisplayfeedbacktype.php
  81. 25
      main/common_cartridge/export/src/lib/assesment/CcAssignmentSetvartype.php
  82. 22
      main/common_cartridge/export/src/lib/assesment/CcQtiMetadata.php
  83. 32
      main/common_cartridge/export/src/lib/assesment/CcQtiProfiletype.php
  84. 88
      main/common_cartridge/export/src/lib/assesment/CcQtiTags.php
  85. 31
      main/common_cartridge/export/src/lib/assesment/CcQtiValues.php
  86. 44
      main/common_cartridge/export/src/lib/assesment/CcQuestionMetadata.php
  87. 86
      main/common_cartridge/export/src/lib/assesment/CcQuestionMetadataBase.php
  88. 81
      main/common_cartridge/export/src/lib/assesment/CcResponseLidtype.php
  89. 7
      main/common_cartridge/export/src/lib/assesment/CcXmlNamespace.php
  90. 236
      main/common_cartridge/export/src/lib/ccdependencyparser.php
  91. 283
      main/common_cartridge/export/src/utils/CcHelpers.php
  92. 77
      main/common_cartridge/export/src/utils/CcResourceLocation.php
  93. 39
      main/common_cartridge/export/src/utils/PkgResourceDependencies.php
  94. 54
      main/common_cartridge/export/src/utils/PkgStaticResources.php
  95. 66
      main/common_cartridge/import/Cc1p3Convert.php
  96. 92
      main/common_cartridge/import/Imscc13Import.php
  97. 1787
      main/common_cartridge/import/schemas13/LOM/ccv1p3_lomccltilink_v1p0.xsd
  98. 1889
      main/common_cartridge/import/schemas13/LOM/ccv1p3_lommanifest_v1p0.xsd
  99. 1871
      main/common_cartridge/import/schemas13/LOM/ccv1p3_lomresource_v1p0.xsd
  100. 19
      main/common_cartridge/import/schemas13/cc13libxml2validator.xsd
  101. Some files were not shown because too many files have changed in this diff Show More

@ -28,7 +28,9 @@
"main/install", "main/install",
"main/lp", "main/lp",
"main/survey", "main/survey",
"plugin" "main/common_cartridge/export",
"main/common_cartridge/import",
"plugin"
] ]
}, },
"require": { "require": {

@ -0,0 +1,92 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../inc/global.inc.php';
require_once api_get_path(SYS_CODE_PATH).'common_cartridge/export/src/lib/ccdependencyparser.php';
use Chamilo\CourseBundle\Component\CourseCopy\CourseArchiver;
use Chamilo\CourseBundle\Component\CourseCopy\CourseBuilder;
use Chamilo\CourseBundle\Component\CourseCopy\CourseSelectForm;
$current_course_tool = TOOL_COURSE_MAINTENANCE;
api_protect_course_script(true);
// Check access rights (only teachers are allowed here)
if (!api_is_allowed_to_edit()) {
api_not_allowed(true);
}
api_check_archive_dir();
api_set_more_memory_and_time_limits();
// Section for the tabs
$this_section = SECTION_COURSES;
// Breadcrumbs
$interbreadcrumb[] = [
'url' => api_get_path(WEB_CODE_PATH).'course_info/maintenance.php',
'name' => get_lang('Maintenance'),
];
// Displaying the header
$nameTools = get_lang('ExportCcVersion13');
Display::display_header($nameTools);
// Display the tool title
echo Display::page_header($nameTools);
$action = isset($_POST['action']) ? $_POST['action'] : '';
$backupOption = 'select_items';
if (Security::check_token('post') && ($action === 'course_select_form')) {
// Clear token
Security::clear_token();
$resources = Security::remove_XSS($_POST['resource']);
if (!empty($resources)) {
$cb = new CourseBuilder('partial');
$course = $cb->build(0, null, false, array_keys($resources), $resources);
$course = CourseSelectForm::get_posted_course(null, 0, '', $course);
$imsccFile = Cc13ExportConvert::export($course);
if ($imsccFile !== false) {
echo Display::return_message(get_lang('ImsccCreated'), 'confirm');
echo '<br />';
echo Display::toolbarButton(
get_lang('Download'),
api_get_path(WEB_CODE_PATH).'course_info/download.php?archive='.$imsccFile.'&'.api_get_cidreq(),
'file-zip-o',
'primary'
);
/*echo Display::url(
get_lang('Download'),
api_get_path(WEB_CODE_PATH).'course_info/download.php?archive='.$imsccFile.'&'.api_get_cidreq(),
['class' => 'btn btn-primary btn-large']
);*/
}
}
} else {
// Clear token
Security::clear_token();
$cb = new CourseBuilder('partial');
$toolsToBuild = [
RESOURCE_DOCUMENT,
RESOURCE_FORUMCATEGORY,
RESOURCE_FORUM,
RESOURCE_FORUMTOPIC,
RESOURCE_QUIZ,
RESOURCE_TEST_CATEGORY,
RESOURCE_LINK,
RESOURCE_WIKI,
];
$course = $cb->build(0, null, false, $toolsToBuild);
if ($course->has_resources()) {
// Add token to Course select form
$hiddenFields['sec_token'] = Security::get_token();
CourseSelectForm::display_form($course, $hiddenFields, false, true, true);
} else {
echo Display::return_message(get_lang('NoResourcesToBackup'), 'warning');
}
}
Display::display_footer();

@ -0,0 +1,75 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../inc/global.inc.php';
require_once api_get_path(SYS_CODE_PATH).'common_cartridge/import/src/lib/validateurlsyntax.php';
require_once api_get_path(SYS_CODE_PATH).'common_cartridge/import/src/inc/constants.php';
$current_course_tool = TOOL_COURSE_MAINTENANCE;
api_protect_course_script(true);
// Check access rights (only teachers are allowed here)
if (!api_is_allowed_to_edit()) {
api_not_allowed(true);
}
api_set_more_memory_and_time_limits();
// Section for the tabs
$this_section = SECTION_COURSES;
// Breadcrumbs
$interbreadcrumb[] = [
'url' => api_get_path(WEB_CODE_PATH).'course_info/maintenance.php?'.api_get_cidreq(),
'name' => get_lang('Maintenance'),
];
$form = new FormValidator('cc_import', 'post', api_get_self().'?'.api_get_cidreq());
$form->addFile('cc_file', get_lang('ImsccFile'));
$form->addButtonImport(get_lang('Import'));
if ($form->validate()) {
$file = $_FILES['cc_file'];
if (empty($file['tmp_name'])) {
$errorMessage = get_lang('UplUploadFailed');
echo Display::return_message($errorMessage, 'error', false);
} else {
$allowedFileMimetype = ['imscc'];
$extImportFile = substr($file['name'], (strrpos($file['name'], '.') + 1));
if (!in_array($extImportFile, $allowedFileMimetype)) {
echo Display::return_message(get_lang('YouMustImportAFileAccordingToSelectedOption'), 'error');
} else {
$baseDir = api_get_path(SYS_ARCHIVE_PATH);
$uploadPath = 'imsccImport/';
$errors = [];
if (!is_dir($baseDir.$uploadPath)) {
@mkdir($baseDir.$uploadPath);
}
$filepath = $baseDir.$uploadPath;
if (!Imscc13Import::unzip($file['tmp_name'], $filepath)) {
return false;
}
// We detect if it is cc v1.3
$detected = Imscc13Import::detectFormat($filepath);
if ($detected) {
Imscc13Import::execute($filepath);
Display::addFlash(Display::return_message(get_lang('CcFileImported'), 'normal', false));
}
}
}
}
$template = new Template(get_lang('ImportCcVersion13'));
Display::addFlash(Display::return_message(get_lang('ImportCcInstructions'), 'normal', false));
$template->assign('form', $form->returnForm());
$templateName = $template->get_template('common_cartridge/import_cc.tpl');
$content = $template->fetch($templateName);
$template->assign('header', get_lang('ImportCcVersion13'));
$template->assign('content', $content);
$template->display_one_col_template();

@ -0,0 +1,291 @@
<?php
/* For licensing terms, see /license.txt */
class Cc13Convert
{
public static function convert($packagedir, $outdir, $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'], 'quiz', $count, $objCourse->info['code'], $resources['Exercise_Question']);
$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;
}
protected static function getItemSections($itemData, $itemType, &$count, $courseCode, $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;
}
protected static function getSequence($objItems, $categoryId = null, $itemType = null, $coursecode = null, $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];
}
}
$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;
}
protected static function processSequence(CcIItem &$item, CcIManifest &$manifest, array $sequence, $packageroot, $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, $path);
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;
}
}

@ -0,0 +1,46 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CourseBundle\Component\CourseCopy\CourseArchiver;
class Cc13ExportConvert
{
public static function export($objCourse)
{
$permDirs = api_get_permissions_for_new_directories();
$backupDirectory = CourseArchiver::getBackupDir();
// Create a temp directory
$backupDir = $backupDirectory.'CourseCC13Archiver_'.api_get_unique_id();
if (mkdir($backupDir, $permDirs, true)) {
$converted = Cc13Convert::convert($backupDirectory, $backupDir, $objCourse);
if ($converted) {
$imsccFileName = self::createImscc($backupDir, $objCourse);
return $imsccFileName;
}
}
return false;
}
public static function createImscc($backupDir, $objCourse)
{
$backupDirectory = CourseArchiver::getBackupDir();
$date = new \DateTime(api_get_local_time());
$imsccFileName = $objCourse->info['code'].'_'.$date->format('Ymd-His').'.imscc';
$imsccFilePath = $backupDirectory.$imsccFileName;
// Zip the course-contents
$zip = new \PclZip($imsccFilePath);
$zip->create($backupDir, PCLZIP_OPT_REMOVE_PATH, $backupDir);
// Remove the temp-dir.
rmdirr($backupDir);
return $imsccFileName;
}
}

@ -0,0 +1,49 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_forum.php under GNU/GPL license */
class CcForum extends CcGeneralFile
{
const DEAFULTNAME = 'discussion.xml';
protected $rootns = 'dt';
protected $rootname = 'topic';
protected $ccnamespaces = ['dt' => 'http://www.imsglobal.org/xsd/imsccv1p3/imsdt_v1p3',
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', ];
protected $ccnsnames = ['dt' => 'http://www.imsglobal.org/profile/cc/ccv1p3/ccv1p3_imsdt_v1p3.xsd'];
protected $title = null;
protected $textType = 'text/plain';
protected $text = null;
protected $attachments = [];
public function setTitle($title)
{
$this->title = self::safexml($title);
}
public function setText($text, $type = 'text/plain')
{
$this->text = self::safexml($text);
$this->textType = $type;
}
public function setAttachments(array $attachments)
{
$this->attachments = $attachments;
}
protected function onSave()
{
$rns = $this->ccnamespaces[$this->rootns];
$this->appendNewElementNs($this->root, $rns, 'title', $this->title);
$text = $this->appendNewElementNs($this->root, $rns, 'text', $this->text);
$this->appendNewAttributeNs($text, $rns, 'texttype', $this->textType);
if (!empty($this->attachments)) {
$attachments = $this->appendNewElementNs($this->root, $rns, 'attachments');
foreach ($this->attachments as $value) {
$att = $this->appendNewElementNs($attachments, $rns, 'attachment');
$this->appendNewAttributeNs($att, $rns, 'href', $value);
}
}
return true;
}
}

@ -0,0 +1,151 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Item Class.
*/
class CcItem implements CcIItem
{
public $identifier = null;
public $identifierref = null;
public $isvisible = null;
public $title = null;
public $parameters = null;
public $childitems = null;
private $parentItem = null;
private $isempty = true;
public function __construct($node = null, $doc = null)
{
if (is_object($node)) {
$clname = get_class($node);
if ($clname == 'CcResource') {
$this->initNewItem();
$this->identifierref = $node->identifier;
$this->title = is_string($doc) && (!empty($doc)) ? $doc : 'item';
} elseif ($clname == 'CcManifest') {
$this->initNewItem();
$this->identifierref = $node->manifestID();
$this->title = is_string($doc) && (!empty($doc)) ? $doc : 'item';
} elseif (is_object($doc)) {
$this->processItem($node, $doc);
} else {
$this->initNewItem();
}
} else {
$this->initNewItem();
}
}
public function attrValue(&$nod, $name, $ns = null)
{
return is_null($ns) ?
($nod->hasAttribute($name) ? $nod->getAttribute($name) : null) :
($nod->hasAttributeNS($ns, $name) ? $nod->getAttributeNS($ns, $name) : null);
}
public function processItem(&$node, &$doc)
{
$this->identifier = $this->attrValue($node, "identifier");
$this->structure = $this->attrValue($node, "structure");
$this->identifierref = $this->attrValue($node, "identifierref");
$atr = $this->attrValue($node, "isvisible");
$this->isvisible = is_null($atr) ? true : $atr;
$nlist = $node->getElementsByTagName('title');
if (is_object($nlist) && ($nlist->length > 0)) {
$this->title = $nlist->item(0)->nodeValue;
}
$nlist = $doc->nodeList("//imscc:item[@identifier='".$this->identifier."']/imscc:item");
if ($nlist->length > 0) {
$this->childitems = [];
foreach ($nlist as $item) {
$key = $this->attrValue($item, "identifier");
$this->childitems[$key] = new CcItem($item, $doc);
}
}
$this->isempty = false;
}
/**
* Add one Child Item.
*/
public function addChildItem(CcIItem &$item)
{
if (is_null($this->childitems)) {
$this->childitems = [];
}
$this->childitems[$item->identifier] = $item;
}
/**
* Add new child Item.
*
* @param string $title
*
* @return CcIItem
*/
public function add_new_child_item($title = '')
{
$sc = new CcItem();
$sc->title = $title;
$this->addChildItem($sc);
return $sc;
}
public function attachResource($resource)
{
if ($this->hasChildItems()) {
throw new Exception("Can not attach resource to item that contains other items!");
}
$resident = null;
if (is_string($resource)) {
$resident = $resource;
} elseif (is_object($resource)) {
$clname = get_class($resource);
if ($clname == 'CcResource') {
$resident = $resource->identifier;
} elseif ($clname == 'CcManifest') {
$resident = $resource->manifestID();
} else {
throw new Exception("Unable to attach resource. Invalid object.");
}
}
if (is_null($resident) || (empty($resident))) {
throw new Exception("Resource must have valid identifier!");
}
$this->identifierref = $resident;
}
public function hasChildItems()
{
return is_array($this->childitems) && (count($this->childitems) > 0);
}
public function child_item($identifier)
{
return $this->hasChildItems() ? $this->childitems[$identifier] : null;
}
public function initClean()
{
$this->identifier = null;
$this->isvisible = null;
$this->title = null;
$this->parameters = null;
$this->childitems = null;
$this->parentItem = null;
$this->isempty = true;
}
public function initNewItem()
{
$this->identifier = CcHelpers::uuidgen('I_');
$this->isvisible = true; //default is true
$this->title = null;
$this->parameters = null;
$this->childitems = null;
$this->parentItem = null;
$this->isempty = false;
}
}

@ -0,0 +1,329 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_manifest.php under GNU/GPL license */
class CcManifest extends XMLGenericDocument implements CcIManifest
{
private $ccversion = null;
private $ccobj = null;
private $rootmanifest = null;
private $activemanifest = null;
private $parentmanifest = null;
private $parentparentmanifest = null;
private $ares = [];
private $mainidentifier = null;
public function __construct($ccver = 13, $activemanifest = null,
$parentmanifest = null, $parentparentmanifest = null)
{
$this->ccversion = $ccver;
$this->ccobj = new CcVersion13();
parent::__construct('UTF-8', true);
}
/**
* Register Namespace for use XPATH.
*/
public function registerNamespacesForXpath()
{
$scnam = $this->activemanifest->getCcNamespaces();
foreach ($scnam as $key => $value) {
$this->registerNS($key, $value);
}
}
/**
* Add Metadata For Manifest.
*/
public function addMetadataManifest(CcIMetadataManifest $met)
{
$metanode = $this->node("//imscc:manifest[@identifier='".
$this->activemanifest->manifestID().
"']/imscc:metadata");
$nmeta = $this->activemanifest->createMetadataNode($met, $this->doc, $metanode);
$metanode->appendChild($nmeta);
}
/**
* Add Metadata For Resource.
*
* @param string $identifier
*/
public function addMetadataResource(CcIMetadataResource $met, $identifier)
{
$metanode = $this->node("//imscc:resource".
"[@identifier='".
$identifier.
"']");
$metanode2 = $this->node("//imscc:resource".
"[@identifier='".
$identifier.
"']/imscc:file");
$nspaces = $this->activemanifest->getCcNamespaces();
$dnode = $this->appendNewElementNs($metanode2, $nspaces['imscc'], 'metadata');
$this->activemanifest->createMetadataResourceNode($met, $this->doc, $dnode);
}
/**
* Add Metadata For File.
*
* @param string $identifier
* @param string $filename
*/
public function addMetadataFile(CcIMetadataFile $met, $identifier, $filename)
{
if (empty($met) || empty($identifier) || empty($filename)) {
throw new Exception('Try to add a metadata file with nulls values given!');
}
$metanode = $this->node("//imscc:resource".
"[@identifier='".
$identifier.
"']/imscc:file".
"[@href='".
$filename.
"']");
$nspaces = $this->activemanifest->getCcNamespaces();
$dnode = $this->doc->createElementNS($nspaces['imscc'], "metadata");
$metanode->appendChild($dnode);
$this->activemanifest->createMetadataFileNode($met, $this->doc, $dnode);
}
public function onCreate()
{
$this->activemanifest = new CcVersion13();
$this->rootmanifest = $this->activemanifest;
$result = $this->activemanifest->createManifest($this->doc);
$this->registerNamespacesForXpath();
return $result;
}
public function getRelativeBasePath()
{
return $this->activemanifest->base();
}
public function parentManifest()
{
return new CcManifest($this, $this->parentmanifest, $this->parentparentmanifest);
}
public function rootManifest()
{
return new CcManifest($this, $this->rootmanifest);
}
public function manifestID()
{
return $this->activemanifest->manifestID();
}
public function getManifestNamespaces()
{
return $this->rootmanifest->getCcNamespaces();
}
/**
* Add a new organization.
*/
public function addNewOrganization(CcIOrganization &$org)
{
$norg = $this->activemanifest->createOrganizationNode($org, $this->doc);
$orgnode = $this->node("//imscc:manifest[@identifier='".
$this->activemanifest->manifestID().
"']/imscc:organizations");
$orgnode->appendChild($norg);
}
public function getResources($searchspecific = '')
{
$reslist = $this->getResourceList($searchspecific);
$resourcelist = [];
foreach ($reslist as $resourceitem) {
$resourcelist[] = new CcResources($this, $resourceitem);
}
return $resourcelist;
}
public function getCcNamespacePath($nsname)
{
if (is_string($nsname) && (!empty($nsname))) {
$scnam = $this->activemanifest->getCcNamespaces();
return $scnam[$nsname];
}
return null;
}
public function getResourceList($searchspecific = '')
{
return $this->nodeList("//imscc:manifest[@identifier='".
$this->activemanifest->manifestID().
"']/imscc:resources/imscc:resource".$searchspecific);
}
public function onLoad()
{
$this->registerNamespacesForXpath();
$this->fillManifest();
return true;
}
public function onSave()
{
return true;
}
/**
* Add a resource to the manifest.
*
* @param string $identifier
* @param string $type
*
* @return array
*/
public function addResource(CcIResource $res, $identifier = null, $type = 'webcontent')
{
if (!$this->ccobj->valid($type)) {
throw new Exception("Type invalid...");
}
if ($res == null) {
throw new Exception('Invalid Resource or dont give it');
}
$rst = $res;
// TODO: This has to be reviewed since it does not handle multiple files properly.
// Dependencies.
if (is_object($identifier)) {
$this->activemanifest->createResourceNode($rst, $this->doc, $identifier);
} else {
$nresnode = null;
$rst->type = $type;
if (!CcHelpers::isHtml($rst->filename)) {
$rst->href = null;
}
$this->activemanifest->createResourceNode($rst, $this->doc, $nresnode);
foreach ($rst->files as $file) {
$ident = $this->getIdentifierByFilename($file);
if ($ident == null) {
$newres = new CcResources($rst->manifestroot, $file);
if (!CcHelpers::isHtml($file)) {
$newres->href = null;
}
$newres->type = 'webcontent';
$this->activemanifest->createResourceNode($newres, $this->doc, $nresnode);
}
}
}
$tmparray = [$rst->identifier, $rst->files[0]];
return $tmparray;
}
public function updateInstructoronly($identifier, $value = false)
{
if (isset($this->activemanifest->resources[$identifier])) {
$resource = $this->activemanifest->resources[$identifier];
$resource->instructoronly = $value;
}
}
/**
* Append the resources nodes in the Manifest.
*
* @return DOMNode
*/
public function putNodes()
{
$resnodestr = "//imscc:manifest[@identifier='".$this->activemanifest->manifestID().
"']/imscc:resources";
$resnode = $this->node($resnodestr);
foreach ($this->activemanifest->resources as $k => $v) {
($k);
$depen = $this->checkIfExistInOther($v->files[0], $v->identifier);
if (!empty($depen)) {
$this->replaceFileXDependency($depen, $v->files[0]);
$v->type = 'webcontent';
}
}
foreach ($this->activemanifest->resources as $node) {
$rnode = $this->activemanifest->createResourceNode($node, $this->doc, null);
$resnode->appendChild($rnode);
if ($node->instructoronly) {
$metafileceduc = new CcMetadataResourceEducational();
$metafileceduc->setValue(intended_user_role::INSTRUCTOR);
$metafile = new CcMetadataResource();
$metafile->addMetadataResourceEducational($metafileceduc);
$this->activemanifest->createMetadataEducational($metafile, $this->doc, $rnode);
}
}
return $resnode;
}
/**
* TODO - implement this method - critical
*/
private function fillManifest()
{
}
private function checkIfExistInOther($name, $identifier)
{
$status = [];
foreach ($this->activemanifest->resources as $value) {
if (($value->identifier != $identifier) && isset($value->files[$name])) {
$status[] = $value->identifier;
}
}
return $status;
}
private function replaceFileXDependency($depen, $name)
{
foreach ($depen as $key => $value) {
($key);
$ident = $this->getIdentifierByFilename($name);
$this->activemanifest->resources[$value]->files =
$this->arrayRemoveByValue($this->activemanifest->resources[$value]->files, $name);
if (!in_array($ident, $this->activemanifest->resources[$value]->dependency)) {
array_push($this->activemanifest->resources[$value]->dependency, $ident);
}
}
return true;
}
private function getIdentifierByFilename($name)
{
$result = null;
if (isset($this->activemanifest->resourcesInd[$name])) {
$result = $this->activemanifest->resourcesInd[$name];
}
return $result;
}
private function arrayRemoveByValue($arr, $value)
{
return array_values(array_diff($arr, [$value]));
}
private function arrayRemoveByKey($arr, $key)
{
return array_values(array_diff_key($arr, [$key]));
}
}

@ -0,0 +1,51 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_manifest.php under GNU/GPL license */
/**
* Metadata General Type.
*/
class CcMetadataGeneral
{
public $title = [];
public $language = [];
public $description = [];
public $keyword = [];
public $coverage = [];
public $catalog = [];
public $entry = [];
public function setCoverage($coverage, $language)
{
$this->coverage[] = [$language, $coverage];
}
public function setDescription($description, $language)
{
$this->description[] = [$language, $description];
}
public function setKeyword($keyword, $language)
{
$this->keyword[] = [$language, $keyword];
}
public function setLanguage($language)
{
$this->language[] = [$language];
}
public function setTitle($title, $language)
{
$this->title[] = [$language, $title];
}
public function setCatalog($cat)
{
$this->catalog[] = [$cat];
}
public function setEntry($entry)
{
$this->entry[] = [$entry];
}
}

@ -0,0 +1,52 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_manifest.php under GNU/GPL license */
class CcMetadataManifest implements CcIMetadataManifest
{
public $arraygeneral = [];
public $arraytech = [];
public $arrayrights = [];
public $arraylifecycle = [];
public function addMetadataGeneral($obj)
{
if (empty($obj)) {
throw new Exception('Medatada Object given is invalid or null!');
}
!is_null($obj->title) ? $this->arraygeneral['title'] = $obj->title : null;
!is_null($obj->language) ? $this->arraygeneral['language'] = $obj->language : null;
!is_null($obj->description) ? $this->arraygeneral['description'] = $obj->description : null;
!is_null($obj->keyword) ? $this->arraygeneral['keyword'] = $obj->keyword : null;
!is_null($obj->coverage) ? $this->arraygeneral['coverage'] = $obj->coverage : null;
!is_null($obj->catalog) ? $this->arraygeneral['catalog'] = $obj->catalog : null;
!is_null($obj->entry) ? $this->arraygeneral['entry'] = $obj->entry : null;
}
public function addMetadataTechnical($obj)
{
if (empty($obj)) {
throw new Exception('Medatada Object given is invalid or null!');
}
!is_null($obj->format) ? $this->arraytech['format'] = $obj->format : null;
}
public function addMetadataRights($obj)
{
if (empty($obj)) {
throw new Exception('Medatada Object given is invalid or null!');
}
!is_null($obj->copyright) ? $this->arrayrights['copyrightAndOtherRestrictions'] = $obj->copyright : null;
!is_null($obj->description) ? $this->arrayrights['description'] = $obj->description : null;
!is_null($obj->cost) ? $this->arrayrights['cost'] = $obj->cost : null;
}
public function addMetadataLifecycle($obj)
{
if (empty($obj)) {
throw new Exception('Medatada Object given is invalid or null!');
}
!is_null($obj->role) ? $this->arraylifecycle['role'] = $obj->role : null;
!is_null($obj->entity) ? $this->arraylifecycle['entity'] = $obj->entity : null;
!is_null($obj->date) ? $this->arraylifecycle['date'] = $obj->date : null;
}
}

@ -0,0 +1,18 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_metadata_resource.php under GNU/GPL license */
/**
* Metadata Resource.
*/
class CcMetadataResource implements CcIMetadataResource
{
public $arrayeducational = [];
public function addMetadataResourceEducational($obj)
{
if (empty($obj)) {
throw new Exception('Medatada Object given is invalid or null!');
}
$this->arrayeducational['value'] = (!is_null($obj->value) ? $obj->value : null);
}
}

@ -0,0 +1,16 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_metadata_resource.php under GNU/GPL license */
/**
* Metadata Resource Educational Type.
*/
class CcMetadataResourceEducational
{
public $value = [];
public function setValue($value)
{
$arr = [$value];
$this->value[] = $arr;
}
}

@ -0,0 +1,97 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_organization.php under GNU/GPL license */
/**
* Organization Class.
*/
class CcOrganization implements CcIOrganization
{
public $title = null;
public $identifier = null;
public $structure = null;
public $itemlist = null;
private $metadata = null;
private $sequencing = null;
public function __construct($node = null, $doc = null)
{
if (is_object($node) && is_object($doc)) {
$this->processOrganization($node, $doc);
} else {
$this->initNew();
}
}
/**
* Add one Item into the Organization.
*/
public function addItem(CcIItem &$item)
{
if (is_null($this->itemlist)) {
$this->itemlist = [];
}
$this->itemlist[$item->identifier] = $item;
}
/**
* Add new Item into the Organization.
*
* @param string $title
*
* @return CcIItem
*/
public function addNewItem($title = '')
{
$nitem = new CcItem();
$nitem->title = $title;
$this->addItem($nitem);
return $nitem;
}
public function hasItems()
{
return is_array($this->itemlist) && (count($this->itemlist) > 0);
}
public function attrValue(&$nod, $name, $ns = null)
{
return is_null($ns) ?
($nod->hasAttribute($name) ? $nod->getAttribute($name) : null) :
($nod->hasAttributeNS($ns, $name) ? $nod->getAttributeNS($ns, $name) : null);
}
public function processOrganization(&$node, &$doc)
{
$this->identifier = $this->attrValue($node, "identifier");
$this->structure = $this->attrValue($node, "structure");
$this->title = '';
$nlist = $node->getElementsByTagName('title');
if (is_object($nlist) && ($nlist->length > 0)) {
$this->title = $nlist->item(0)->nodeValue;
}
$nlist = $doc->nodeList("//imscc:organization[@identifier='".$this->identifier."']/imscc:item");
$this->itemlist = [];
foreach ($nlist as $item) {
$this->itemlist[$item->getAttribute("identifier")] = new CcItem($item, $doc);
}
$this->isempty = false;
}
public function initNew()
{
$this->title = null;
$this->identifier = CcHelpers::uuidgen('O_');
$this->structure = 'rooted-hierarchy';
$this->itemlist = null;
$this->metadata = null;
$this->sequencing = null;
}
public function uuidgen()
{
$uuid = sprintf('%04x%04x', mt_rand(0, 65535), mt_rand(0, 65535));
return strtoupper(trim($uuid));
}
}

@ -0,0 +1,98 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_page.php under GNU/GPL license */
class CcPage extends CcGeneralFile
{
protected $rootns = 'xmlns';
protected $rootname = 'html';
protected $ccnamespaces = ['xmlns' => 'http://www.w3.org/1999/xhtml'];
protected $content = null;
protected $title = null;
protected $intro = null;
public function setContent($value)
{
// We are not cleaning up this one on purpose.
$this->content = $value;
}
public function setTitle($value)
{
$this->title = self::safexml($value);
}
public function setIntro($value)
{
$this->intro = self::safexml(strip_tags($value));
}
public function onSave()
{
$rns = $this->ccnamespaces[$this->rootns];
// Add the basic tags.
$head = $this->appendNewElementNs($this->root, $rns, 'head');
$this->appendNewAttributeNs($head, $rns, 'profile', 'http://dublincore.org/documents/dc-html/');
// Linking Dublin Core Metadata 1.1.
$link_dc = $this->appendNewElementNs($head, $rns, 'link');
$this->appendNewAttributeNs($link_dc, $rns, 'rel', 'schema.DC');
$this->appendNewAttributeNs($link_dc, $rns, 'href', 'http://purl.org/dc/elements/1.1/');
$link_dcterms = $this->appendNewElementNs($head, $rns, 'link');
$this->appendNewAttributeNs($link_dcterms, $rns, 'rel', 'schema.DCTERMS');
$this->appendNewAttributeNs($link_dcterms, $rns, 'href', 'http://purl.org/dc/terms/');
// Content type.
$meta_type = $this->appendNewElementNs($head, $rns, 'meta');
$this->appendNewAttributeNs($meta_type, $rns, 'name', 'DC.type');
$this->appendNewAttributeNs($meta_type, $rns, 'scheme', 'DCTERMS.DCMIType');
$this->appendNewAttributeNs($meta_type, $rns, 'content', 'Text');
// Content description.
if (!empty($this->intro)) {
$meta_description = $this->appendNewElementNs($head, $rns, 'meta');
$this->appendNewAttributeNs($meta_description, $rns, 'name', 'DC.description');
$this->appendNewAttributeNs($meta_description, $rns, 'content', $this->intro);
}
$meta = $this->appendNewElementNs($head, $rns, 'meta');
$this->appendNewAttributeNs($meta, $rns, 'http-equiv', 'Content-type');
$this->appendNewAttributeNs($meta, $rns, 'content', 'text/html; charset=UTF-8');
// Set the title.
$title = $this->appendNewElementNs($head, $rns, 'title', $this->title);
$body = $this->appendNewElementNs($this->root, $rns, 'body');
// We are unable to use DOM for embedding HTML due to numerous content errors.
// Therefore we place a dummy tag that will be later replaced with the real content.
$this->appendNewElementNs($body, $rns, 'div', '##REPLACE##');
return true;
}
public function saveTo($fname)
{
$result = $this->onSave();
if ($result) {
$dret = str_replace('<?xml version="1.0"?>'."\n", '', $this->viewXML());
$dret = str_replace('<div>##REPLACE##</div>', $this->content, $dret);
$result = (file_put_contents($fname, $dret) !== false);
if ($result) {
$this->filename = $fname;
$this->processPath();
}
}
return $result;
}
protected function onCreate()
{
$impl = new DOMImplementation();
$dtd = $impl->createDocumentType('html',
'-//W3C//DTD XHTML 1.0 Strict//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd');
$doc = $impl->createDocument($this->ccnamespaces[$this->rootns], null, $dtd);
$doc->formatOutput = true;
$doc->preserveWhiteSpace = true;
$this->doc = $doc;
parent::onCreate();
}
}

@ -0,0 +1,164 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_resources.php under GNU/GPL license */
class CcResources implements CcIResource
{
public $identifier = null;
public $type = null;
public $dependency = [];
public $identifierref = null;
public $href = null;
public $base = null;
public $persiststate = null;
public $metadata = [];
public $filename = null;
public $files = [];
public $isempty = null;
public $manifestroot = null;
public $folder = null;
public $instructoronly = false;
private $throwonerror = true;
public function __construct($manifest, $file, $folder = '', $throwonerror = true)
{
$this->throwonerror = $throwonerror;
if (is_string($manifest)) {
$this->folder = $folder;
$this->processResource($manifest, $file, $folder);
$this->manifestroot = $manifest;
} elseif (is_object($manifest)) {
$this->importResource($file, $manifest);
}
}
/**
* Add resource.
*
* @param string $fname
* @param string $location
*/
public function addResource($fname, $location = '')
{
$this->processResource($fname, $location, null);
}
/**
* Import a resource.
*/
public function importResource(DOMElement &$node, CcIManifest &$doc)
{
$searchstr = "//imscc:manifest[@identifier='".$doc->manifestID().
"']/imscc:resources/imscc:resource";
$this->identifier = $this->getAttrValue($node, "identifier");
$this->type = $this->getAttrValue($node, "type");
$this->href = $this->getAttrValue($node, "href");
$this->base = $this->getAttrValue($node, "base");
$this->persiststate = null;
$nodo = $doc->nodeList($searchstr."[@identifier='".
$this->identifier."']/metadata/@href");
$this->metadata = $nodo->nodeValue;
$this->filename = $this->href;
$nlist = $doc->nodeList($searchstr."[@identifier='".
$this->identifier."']/imscc:file/@href");
$this->files = [];
foreach ($nlist as $file) {
$this->files[] = $file->nodeValue;
}
$nlist = $doc->nodeList($searchstr."[@identifier='".
$this->identifier."']/imscc:dependency/@identifierref");
$this->dependency = [];
foreach ($nlist as $dependency) {
$this->dependency[] = $dependency->nodeValue;
}
$this->isempty = false;
}
/**
* Get a attribute value.
*
* @param DOMElement $nod
* @param string $name
* @param string $ns
*
* @return string
*/
public function getAttrValue(&$nod, $name, $ns = null)
{
if (is_null($ns)) {
return $nod->hasAttribute($name) ? $nod->getAttribute($name) : null;
}
return $nod->hasAttributeNS($ns, $name) ? $nod->getAttributeNS($ns, $name) : null;
}
/**
* Process a resource.
*
* @param string $manifestroot
* @param string $fname
* @param string $folder
*/
public function processResource($manifestroot, &$fname, $folder)
{
$file = empty($folder) ? $manifestroot.'/'.$fname : $manifestroot.'/'.$folder.'/'.$fname;
if (!file_exists($file) && $this->throwonerror) {
throw new Exception('The file doesnt exist!');
}
getDepFiles($manifestroot, $fname, $this->folder, $this->files);
array_unshift($this->files, $folder.$fname);
$this->initEmptyNew();
$this->href = $folder.$fname;
$this->identifierref = $folder.$fname;
$this->filename = $fname;
$this->isempty = false;
$this->folder = $folder;
}
public function adjustPath($mroot, $fname)
{
$result = null;
if (file_exists($fname->filename)) {
$result = pathDiff($fname->filename, $mroot);
} elseif (file_exists($mroot.$fname->filename) || file_exists($mroot.DIRECTORY_SEPARATOR.$fname->filename)) {
$result = $fname->filename;
toUrlPath($result);
$result = trim($result, "/");
}
return $result;
}
public function initClean()
{
$this->identifier = null;
$this->type = null;
$this->href = null;
$this->base = null;
$this->metadata = [];
$this->dependency = [];
$this->identifierref = null;
$this->persiststate = null;
$this->filename = '';
$this->files = [];
$this->isempty = true;
}
public function initEmptyNew()
{
$this->identifier = CcHelpers::uuidgen('I_', '_R');
$this->type = null;
$this->href = null;
$this->persiststate = null;
$this->filename = null;
$this->isempty = false;
$this->identifierref = null;
}
public function getManifestroot()
{
return $this->manifestroot;
}
}

@ -0,0 +1,102 @@
<?php
/* For licensing terms, see /license.txt */
class CcVersion13 extends CcVersion1
{
const WEBCONTENT = 'webcontent';
const QUESTIONBANK = 'imsqti_xmlv1p3/imscc_xmlv1p3/question-bank';
const ASSESSMENT = 'imsqti_xmlv1p3/imscc_xmlv1p3/assessment';
const ASSOCIATEDCONTENT = 'associatedcontent/imscc_xmlv1p3/learning-application-resource';
const DISCUSSIONTOPIC = 'imsdt_xmlv1p3';
const WEBLINK = 'imswl_xmlv1p3';
const BASICLTI = 'imsbasiclti_xmlv1p3';
public static $checker = [self::WEBCONTENT,
self::ASSESSMENT,
self::ASSOCIATEDCONTENT,
self::DISCUSSIONTOPIC,
self::QUESTIONBANK,
self::WEBLINK,
self::BASICLTI, ];
public function __construct()
{
$this->ccnamespaces = ['imscc' => 'http://www.imsglobal.org/xsd/imsccv1p3/imscp_v1p1',
'lomimscc' => 'http://ltsc.ieee.org/xsd/imsccv1p3/LOM/manifest',
'lom' => 'http://ltsc.ieee.org/xsd/imsccv1p3/LOM/resource',
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'cc' => 'http://www.imsglobal.org/xsd/imsccv1p3/imsccauth_v1p1',
];
$this->ccnsnames = ['imscc' => 'http://www.imsglobal.org/profile/cc/ccv1p3/ccv1p3_imscp_v1p2_v1p0.xsd',
'lomimscc' => 'http://www.imsglobal.org/profile/cc/ccv1p3/LOM/ccv1p3_lommanifest_v1p0.xsd',
'lom' => 'http://www.imsglobal.org/profile/cc/ccv1p3/LOM/ccv1p3_lomresource_v1p0.xsd',
];
$this->ccversion = '1.3.0';
$this->camversion = '1.3.0';
$this->_generator = 'Chamilo Common Cartridge generator';
}
/**
* Validate if the type are valid or not.
*
* @param string $type
*
* @return bool
*/
public function valid($type)
{
return in_array($type, self::$checker);
}
/**
* Create Education Metadata (How To).
*
* @param object $met
* @param object $xmlnode
*
* @return DOMNode
*/
public function createMetadataEducational($met, DOMDocument &$doc, $xmlnode)
{
$metadata = $doc->createElementNS($this->ccnamespaces['imscc'], 'metadata');
$xmlnode->insertBefore($metadata, $xmlnode->firstChild);
$lom = $doc->createElementNS($this->ccnamespaces['lom'], 'lom');
$metadata->appendChild($lom);
$educational = $doc->createElementNS($this->ccnamespaces['lom'], 'educational');
$lom->appendChild($educational);
foreach ($met->arrayeducational as $value) {
!is_array($value) ? $value = [$value] : null;
foreach ($value as $v) {
$userrole = $doc->createElementNS($this->ccnamespaces['lom'], 'intendedEndUserRole');
$educational->appendChild($userrole);
$nd4 = $doc->createElementNS($this->ccnamespaces['lom'], 'source', 'IMSGLC_CC_Rolesv1p2');
$nd5 = $doc->createElementNS($this->ccnamespaces['lom'], 'value', $v[0]);
$userrole->appendChild($nd4);
$userrole->appendChild($nd5);
}
}
return $metadata;
}
protected function updateItems($items, DOMDocument &$doc, DOMElement &$xmlnode)
{
foreach ($items as $key => $item) {
$itemnode = $doc->createElementNS($this->ccnamespaces['imscc'], 'item');
$this->updateAttribute($doc, 'identifier', $key, $itemnode);
$this->updateAttribute($doc, 'identifierref', $item->identifierref, $itemnode);
if (!is_null($item->title)) {
$titlenode = $doc->createElementNS($this->ccnamespaces['imscc'], 'title');
$titlenode->appendChild(new DOMText($item->title));
$itemnode->appendChild($titlenode);
}
if ($item->hasChildItems()) {
$this->updateItems($item->childitems, $doc, $itemnode);
}
$xmlnode->appendChild($itemnode);
}
}
}

@ -0,0 +1,59 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_weblink.php under GNU/GPL license */
class CcWebLink extends CcGeneralFile
{
const DEAFULTNAME = 'weblink.xml';
protected $rootns = 'wl';
protected $rootname = 'webLink';
protected $ccnamespaces = ['wl' => 'http://www.imsglobal.org/xsd/imsccv1p3/imswl_v1p3',
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', ];
protected $ccnsnames = ['wl' => 'http://www.imsglobal.org/profile/cc/ccv1p3/ccv1p3_imswl_v1p3.xsd'];
protected $url = null;
protected $title = null;
protected $href = null;
protected $target = '_self';
protected $windowFeatures = null;
/**
* Set the url title.
*
* @param string $title
*/
public function setTitle($title)
{
$this->title = self::safexml($title);
}
/**
* Set the url specifics.
*
* @param string $url
* @param string $target
* @param string $windowFeatures
*/
public function setUrl($url, $target = '_self', $windowFeatures = null)
{
$this->url = $url;
$this->target = $target;
$this->windowFeatures = $windowFeatures;
}
protected function onSave()
{
$rns = $this->ccnamespaces[$this->rootns];
$this->appendNewElementNs($this->root, $rns, 'title', $this->title);
$url = $this->appendNewElementNs($this->root, $rns, 'url');
$this->appendNewAttributeNs($url, $rns, 'href', $this->url);
if (!empty($this->target)) {
$this->appendNewAttributeNs($url, $rns, 'target', $this->target);
}
if (!empty($this->windowFeatures)) {
$this->appendNewAttributeNs($url, $rns, 'windowFeatures', $this->windowFeatures);
}
return true;
}
}

@ -0,0 +1,88 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_converters.php under GNU/GPL license */
abstract class CcConverters
{
protected $item = null;
protected $manifest = null;
protected $rootpath = null;
protected $path = null;
protected $defaultfile = null;
protected $defaultname = null;
protected $ccType = null;
protected $doc = null;
/**
* ctor.
*
* @param CcIItem $item
* @param CcIManifest $manifest
* @param string $rootpath
* @param string $path
*
* @throws InvalidArgumentException
*/
public function __construct(CcIItem &$item, CcIManifest &$manifest, $rootpath, $path)
{
$rpath = realpath($rootpath);
if (empty($rpath)) {
throw new InvalidArgumentException('Invalid path!');
}
$rpath2 = realpath($path);
if (empty($rpath)) {
throw new InvalidArgumentException('Invalid path!');
}
$doc = new XMLGenericDocument();
$this->doc = $doc;
$this->item = $item;
$this->manifest = $manifest;
$this->rootpath = $rpath;
$this->path = $rpath2;
}
/**
* performs conversion.
*
* @param string $outdir - root directory of common cartridge
* @param object $objCourse
*
* @return bool
*/
abstract public function convert($outdir, $objCourse);
/**
* Is the element visible in the course?
*
* @throws RuntimeException
*
* @return bool
*/
protected function isVisible()
{
$tdoc = new XMLGenericDocument();
return true;
}
/**
* Stores any files that need to be stored.
*/
protected function store(CcGeneralFile $doc, $outdir, $title, $deps = null)
{
$rdir = new CcResourceLocation($outdir);
$rtp = $rdir->fullpath(true).$this->defaultname;
if ($doc->saveTo($rtp)) {
$resource = new CcResources($rdir->rootdir(), $this->defaultname, $rdir->dirname(true));
$resource->dependency = empty($deps) ? [] : $deps;
$resource->instructoronly = !$this->isVisible();
$res = $this->manifest->addResource($resource, null, $this->ccType);
$resitem = new CcItem();
$resitem->attachResource($res[0]);
$resitem->title = $title;
$this->item->addChildItem($resitem);
} else {
throw new RuntimeException("Unable to save file {$rtp}!");
}
}
}

@ -0,0 +1,53 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_general.php under GNU/GPL license */
class CcGeneralFile extends XMLGenericDocument
{
/**
* Root element.
*
* @var DOMElement
*/
protected $root = null;
protected $rootns = null;
protected $rootname = null;
protected $ccnamespaces = [];
protected $ccnsnames = [];
public function __construct()
{
parent::__construct();
foreach ($this->ccnamespaces as $key => $value) {
$this->registerNS($key, $value);
}
}
protected function onCreate()
{
$rootel = $this->appendNewElementNs($this->doc,
$this->ccnamespaces[$this->rootns],
$this->rootname);
//add all namespaces
foreach ($this->ccnamespaces as $key => $value) {
$dummy_attr = "{$key}:dummy";
$this->doc->createAttributeNS($value, $dummy_attr);
}
// add location of schemas
$schemaLocation = '';
foreach ($this->ccnsnames as $key => $value) {
$vt = empty($schemaLocation) ? '' : ' ';
$schemaLocation .= $vt.$this->ccnamespaces[$key].' '.$value;
}
if (!empty($schemaLocation) && isset($this->ccnamespaces['xsi'])) {
$this->appendNewAttributeNs($rootel,
$this->ccnamespaces['xsi'],
'xsi:schemaLocation',
$schemaLocation);
}
$this->root = $rootel;
}
}

@ -0,0 +1,520 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_version1.php under GNU/GPL license */
/**
* Version 1 class of Common Cartridge.
*/
class CcVersion1 extends CcVersionBase
{
const WEBCONTENT = 'webcontent';
const QUESTIONBANK = 'imsqti_xmlv1p2/imscc_xmlv1p0/question-bank';
const ASSESSMENT = 'imsqti_xmlv1p2/imscc_xmlv1p0/assessment';
const ASSOCIATEDCONTENT = 'associatedcontent/imscc_xmlv1p0/learning-application-resource';
const DISCUSSIONTOPIC = 'imsdt_xmlv1p0';
const WEBLINK = 'imswl_xmlv1p0';
public static $checker = [self::WEBCONTENT,
self::ASSESSMENT,
self::ASSOCIATEDCONTENT,
self::DISCUSSIONTOPIC,
self::QUESTIONBANK,
self::WEBLINK, ];
public function __construct()
{
$this->ccnamespaces = ['imscc' => 'http://www.imsglobal.org/xsd/imscc/imscp_v1p1',
'lomimscc' => 'http://ltsc.ieee.org/xsd/imscc/LOM',
'lom' => 'http://ltsc.ieee.org/xsd/LOM',
'voc' => 'http://ltsc.ieee.org/xsd/LOM/vocab',
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
];
$this->ccnsnames = [
'imscc' => 'http://www.imsglobal.org/profile/cc/ccv1p0/derived_schema/imscp_v1p2_localised.xsd',
'lom' => 'http://www.imsglobal.org/profile/cc/ccv1p0/derived_schema/domainProfile_2/lomLoose_localised.xsd',
'lomimscc' => 'http://www.imsglobal.org/profile/cc/ccv1p0/derived_schema/domainProfile_1/lomLoose_localised.xsd',
'voc' => 'http://www.imsglobal.org/profile/cc/ccv1p0/derived_schema/domainProfile_2/vocab/loose.xsd',
];
$this->ccversion = '1.0.0';
$this->camversion = '1.0.0';
$this->_generator = 'Common Cartridge generator';
}
/**
* Validate if the type are valid or not.
*
* @param string $type
*
* @return bool
*/
public function valid($type)
{
return in_array($type, self::$checker);
}
/**
* Create Education Metadata (How To).
*
* @param object $met
* @param object $xmlnode
*
* @return DOMNode
*/
public function createMetadataEducational($met, DOMDocument &$doc, $xmlnode)
{
$nd = $doc->createElementNS($this->ccnamespaces['lom'], 'educational');
$nd2 = $doc->createElementNS($this->ccnamespaces['lom'], 'intendedEndUserRole');
$nd3 = $doc->createElementNS($this->ccnamespaces['voc'], 'vocabulary');
$xmlnode->appendChild($nd);
$nd->appendChild($nd2);
$nd2->appendChild($nd3);
foreach ($met->arrayeducational as $name => $value) {
!is_array($value) ? $value = [$value] : null;
foreach ($value as $v) {
$nd4 = $doc->createElementNS($this->ccnamespaces['voc'], $name, $v[0]);
$nd3->appendChild($nd4);
}
}
return $nd;
}
protected function onCreate(DOMDocument &$doc, $rootmanifestnode = null, $nmanifestID = null)
{
$doc->formatOutput = true;
$doc->preserveWhiteSpace = true;
$this->manifestID = is_null($nmanifestID) ? CcHelpers::uuidgen('M_') : $nmanifestID;
$mUUID = $doc->createAttribute('identifier');
$mUUID->nodeValue = $this->manifestID;
if (is_null($rootmanifestnode)) {
if (!empty($this->_generator)) {
$comment = $doc->createComment($this->_generator);
$doc->appendChild($comment);
}
$rootel = $doc->createElementNS($this->ccnamespaces['imscc'], 'manifest');
$rootel->appendChild($mUUID);
$doc->appendChild($rootel);
// Add all namespaces.
foreach ($this->ccnamespaces as $key => $value) {
$dummy_attr = $key.":dummy";
$doc->createAttributeNS($value, $dummy_attr);
}
// Add location of schemas.
$schemaLocation = '';
foreach ($this->ccnsnames as $key => $value) {
$vt = empty($schemaLocation) ? '' : ' ';
$schemaLocation .= $vt.$this->ccnamespaces[$key].' '.$value;
}
$aSchemaLoc = $doc->createAttributeNS($this->ccnamespaces['xsi'], 'xsi:schemaLocation');
$aSchemaLoc->nodeValue = $schemaLocation;
$rootel->appendChild($aSchemaLoc);
} else {
$rootel = $doc->createElementNS($this->ccnamespaces['imscc'], 'imscc:manifest');
$rootel->appendChild($mUUID);
}
$metadata = $doc->createElementNS($this->ccnamespaces['imscc'], 'metadata');
$schema = $doc->createElementNS($this->ccnamespaces['imscc'], 'schema', 'IMS Common Cartridge');
$schemaversion = $doc->createElementNS($this->ccnamespaces['imscc'], 'schemaversion', $this->ccversion);
$metadata->appendChild($schema);
$metadata->appendChild($schemaversion);
$rootel->appendChild($metadata);
if (!is_null($rootmanifestnode)) {
$rootmanifestnode->appendChild($rootel);
}
$organizations = $doc->createElementNS($this->ccnamespaces['imscc'], 'organizations');
$rootel->appendChild($organizations);
$resources = $doc->createElementNS($this->ccnamespaces['imscc'], 'resources');
$rootel->appendChild($resources);
return true;
}
protected function updateAttribute(DOMDocument &$doc, $attrname, $attrvalue, DOMElement &$node)
{
$busenew = (is_object($node) && $node->hasAttribute($attrname));
$nResult = null;
if (!$busenew && is_null($attrvalue)) {
$node->removeAttribute($attrname);
} else {
$nResult = $busenew ? $node->getAttributeNode($attrname) : $doc->createAttribute($attrname);
$nResult->nodeValue = $attrvalue;
if (!$busenew) {
$node->appendChild($nResult);
}
}
return $nResult;
}
protected function updateAttributeNs(DOMDocument &$doc, $attrname, $attrnamespace, $attrvalue, DOMElement &$node)
{
$busenew = (is_object($node) && $node->hasAttributeNS($attrnamespace, $attrname));
$nResult = null;
if (!$busenew && is_null($attrvalue)) {
$node->removeAttributeNS($attrnamespace, $attrname);
} else {
$nResult = $busenew ? $node->getAttributeNodeNS($attrnamespace, $attrname) :
$doc->createAttributeNS($attrnamespace, $attrname);
$nResult->nodeValue = $attrvalue;
if (!$busenew) {
$node->appendChild($nResult);
}
}
return $nResult;
}
protected function getChildNode(DOMDocument &$doc, $itemname, DOMElement &$node)
{
$nlist = $node->getElementsByTagName($itemname);
$item = is_object($nlist) && ($nlist->length > 0) ? $nlist->item(0) : null;
return $item;
}
protected function updateChildItem(DOMDocument &$doc, $itemname, $itemvalue, DOMElement &$node, $attrtostore = null)
{
$tnode = $this->getChildNode($doc, 'title', $node);
$usenew = is_null($tnode);
$tnode = $usenew ? $doc->createElementNS($this->ccnamespaces['imscc'], $itemname) : $tnode;
if (!is_null($attrtostore)) {
foreach ($attrtostore as $key => $value) {
$this->updateAttribute($doc, $key, $value, $tnode);
}
}
$tnode->nodeValue = $itemvalue;
if ($usenew) {
$node->appendChild($tnode);
}
}
protected function updateItems($items, DOMDocument &$doc, DOMElement &$xmlnode)
{
foreach ($items as $key => $item) {
$itemnode = $doc->createElementNS($this->ccnamespaces['imscc'], 'item');
$this->updateAttribute($doc, 'identifier', $key, $itemnode);
$this->updateAttribute($doc, 'identifierref', $item->identifierref, $itemnode);
$this->updateAttribute($doc, 'parameters', $item->parameters, $itemnode);
if (!empty($item->title)) {
$titlenode = $doc->createElementNS($this->ccnamespaces['imscc'],
'title',
$item->title);
$itemnode->appendChild($titlenode);
}
if ($item->hasChildItems()) {
$this->updateItems($item->childitems, $doc, $itemnode);
}
$xmlnode->appendChild($itemnode);
}
}
/**
* Create a Resource (How to).
*
* @param CcIResource $res
* @param DOMDocument $doc
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createResource(CcIResource &$res, DOMDocument &$doc, $xmlnode = null)
{
$usenew = is_object($xmlnode);
$dnode = $usenew ? $xmlnode : $doc->createElementNS($this->ccnamespaces['imscc'], "resource");
$this->updateAttribute($doc, 'identifier', $res->identifier, $dnode);
$this->updateAttribute($doc, 'type', $res->type, $dnode);
!is_null($res->href) ? $this->updateAttribute($doc, 'href', $res->href, $dnode) : null;
$this->updateAttribute($doc, 'base', $res->base, $dnode);
foreach ($res->files as $file) {
$nd = $doc->createElementNS($this->ccnamespaces['imscc'], 'file');
$ndatt = $doc->createAttribute('href');
$ndatt->nodeValue = $file;
$nd->appendChild($ndatt);
$dnode->appendChild($nd);
}
$this->resources[$res->identifier] = $res;
$this->resourcesInd[$res->files[0]] = $res->identifier;
foreach ($res->dependency as $dependency) {
$nd = $doc->createElementNS($this->ccnamespaces['imscc'], 'dependency');
$ndatt = $doc->createAttribute('identifierref');
$ndatt->nodeValue = $dependency;
$nd->appendChild($ndatt);
$dnode->appendChild($nd);
}
return $dnode;
}
/**
* Create an Item Folder (How To).
*
* @param CcIOrganization $org
* @param DOMDocument $doc
* @param DOMElement $xmlnode
*/
protected function createItemFolder(CcIOrganization &$org, DOMDocument &$doc, DOMElement &$xmlnode = null)
{
$itemfoldernode = $doc->createElementNS($this->ccnamespaces['imscc'], 'item');
$this->updateAttribute($doc, 'identifier', "root", $itemfoldernode);
if ($org->hasItems()) {
$this->updateItems($org->itemlist, $doc, $itemfoldernode);
}
if (is_null($this->organizations)) {
$this->organizations = [];
}
$this->organizations[$org->identifier] = $org;
$xmlnode->appendChild($itemfoldernode);
}
/**
* Create an Organization (How To).
*
* @param CcIOrganization $org
* @param DOMDocument $doc
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createOrganization(CcIOrganization &$org, DOMDocument &$doc, $xmlnode = null)
{
$usenew = is_object($xmlnode);
$dnode = $usenew ? $xmlnode : $doc->createElementNS($this->ccnamespaces['imscc'], "organization");
$this->updateAttribute($doc, 'identifier', $org->identifier, $dnode);
$this->updateAttribute($doc, 'structure', $org->structure, $dnode);
$this->createItemFolder($org, $doc, $dnode);
return $dnode;
}
/**
* Create Metadata For Manifest (How To).
*
* @param CcIMetadataManifest $met
* @param DOMDocument $doc
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createMetadataManifest(CcIMetadataManifest $met, DOMDocument &$doc, $xmlnode = null)
{
$dnode = $doc->createElementNS($this->ccnamespaces['lomimscc'], "lom");
if (!empty($xmlnode)) {
$xmlnode->appendChild($dnode);
}
$dnodegeneral = empty($met->arraygeneral) ? null : $this->createMetadataGeneral($met, $doc, $xmlnode);
$dnodetechnical = empty($met->arraytech) ? null : $this->createMetadataTechnical($met, $doc, $xmlnode);
$dnoderights = empty($met->arrayrights) ? null : $this->createMetadataRights($met, $doc, $xmlnode);
$dnodelifecycle = empty($met->arraylifecycle) ? null : $this->createMetadataLifecycle($met, $doc, $xmlnode);
!is_null($dnodegeneral) ? $dnode->appendChild($dnodegeneral) : null;
!is_null($dnodetechnical) ? $dnode->appendChild($dnodetechnical) : null;
!is_null($dnoderights) ? $dnode->appendChild($dnoderights) : null;
!is_null($dnodelifecycle) ? $dnode->appendChild($dnodelifecycle) : null;
return $dnode;
}
/**
* Create Metadata For Resource (How To).
*
* @param CcIMetadataResource $met
* @param DOMDocument $doc
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createMetadataResource(CcIMetadataResource $met, DOMDocument &$doc, $xmlnode = null)
{
$dnode = $doc->createElementNS($this->ccnamespaces['lom'], "lom");
!empty($xmlnode) ? $xmlnode->appendChild($dnode) : null;
!empty($met->arrayeducational) ? $this->createMetadataEducational($met, $doc, $dnode) : null;
return $dnode;
}
/**
* Create Metadata For File (How To).
*
* @param CcIMetadataFile $met
* @param DOMDocument $doc
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createMetadataFile(CcIMetadataFile $met, DOMDocument &$doc, $xmlnode = null)
{
$dnode = $doc->createElementNS($this->ccnamespaces['lom'], "lom");
!empty($xmlnode) ? $xmlnode->appendChild($dnode) : null;
!empty($met->arrayeducational) ? $this->createMetadataEducational($met, $doc, $dnode) : null;
return $dnode;
}
/**
* Create General Metadata (How To).
*
* @param object $met
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createMetadataGeneral($met, DOMDocument &$doc, $xmlnode)
{
$nd = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'general');
foreach ($met->arraygeneral as $name => $value) {
!is_array($value) ? $value = [$value] : null;
foreach ($value as $v) {
if ($name != 'language' && $name != 'catalog' && $name != 'entry') {
$nd2 = $doc->createElementNS($this->ccnamespaces['lomimscc'], $name);
$nd3 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'string', $v[1]);
$ndatt = $doc->createAttribute('language');
$ndatt->nodeValue = $v[0];
$nd3->appendChild($ndatt);
$nd2->appendChild($nd3);
$nd->appendChild($nd2);
} else {
if ($name == 'language') {
$nd2 = $doc->createElementNS($this->ccnamespaces['lomimscc'], $name, $v[0]);
$nd->appendChild($nd2);
}
}
}
}
if (!empty($met->arraygeneral['catalog']) || !empty($met->arraygeneral['entry'])) {
$nd2 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'identifier');
$nd->appendChild($nd2);
if (!empty($met->arraygeneral['catalog'])) {
$nd3 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'catalog', $met->arraygeneral['catalog'][0][0]);
$nd2->appendChild($nd3);
}
if (!empty($met->arraygeneral['entry'])) {
$nd4 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'entry', $met->arraygeneral['entry'][0][0]);
$nd2->appendChild($nd4);
}
}
return $nd;
}
/**
* Create Technical Metadata (How To).
*
* @param object $met
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createMetadataTechnical($met, DOMDocument &$doc, $xmlnode)
{
$nd = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'technical');
$xmlnode->appendChild($nd);
foreach ($met->arraytech as $name => $value) {
!is_array($value) ? $value = [$value] : null;
foreach ($value as $v) {
$nd2 = $doc->createElementNS($this->ccnamespaces['lomimscc'], $name, $v[0]);
$nd->appendChild($nd2);
}
}
return $nd;
}
/**
* Create Rights Metadata (How To).
*
* @param object $met
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createMetadataRights($met, DOMDocument &$doc, $xmlnode)
{
$nd = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'rights');
foreach ($met->arrayrights as $name => $value) {
!is_array($value) ? $value = [$value] : null;
foreach ($value as $v) {
if ($name == 'description') {
$nd2 = $doc->createElementNS($this->ccnamespaces['lomimscc'], $name);
$nd3 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'string', $v[1]);
$ndatt = $doc->createAttribute('language');
$ndatt->nodeValue = $v[0];
$nd3->appendChild($ndatt);
$nd2->appendChild($nd3);
$nd->appendChild($nd2);
} elseif ($name == 'copyrightAndOtherRestrictions' || $name == 'cost') {
$nd2 = $doc->createElementNS($this->ccnamespaces['lomimscc'], $name);
$nd3 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'value', $v[0]);
$nd2->appendChild($nd3);
$nd->appendChild($nd2);
}
}
}
return $nd;
}
/**
* Create Lifecycle Metadata (How To).
*
* @param object $met
* @param DOMDocument $doc
* @param object $met
* @param object $xmlnode
*
* @return DOMNode
*/
protected function createMetadataLifecycle($met, DOMDocument &$doc, $xmlnode)
{
$nd = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'lifeCycle');
$nd2 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'contribute');
$nd->appendChild($nd2);
$xmlnode->appendChild($nd);
foreach ($met->arraylifecycle as $name => $value) {
!is_array($value) ? $value = [$value] : null;
foreach ($value as $v) {
if ($name == 'role') {
$nd3 = $doc->createElementNS($this->ccnamespaces['lomimscc'], $name);
$nd2->appendChild($nd3);
$nd4 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'value', $v[0]);
$nd3->appendChild($nd4);
} else {
if ($name == 'date') {
$nd3 = $doc->createElementNS($this->ccnamespaces['lomimscc'], $name);
$nd2->appendChild($nd3);
$nd4 = $doc->createElementNS($this->ccnamespaces['lomimscc'], 'dateTime', $v[0]);
$nd3->appendChild($nd4);
} else {
$nd3 = $doc->createElementNS($this->ccnamespaces['lomimscc'], $name, $v[0]);
$nd2->appendChild($nd3);
}
}
}
}
return $nd;
}
}

@ -0,0 +1,122 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_version_base.php under GNU/GPL license */
/**
* Abstract Version Base class.
*/
abstract class CcVersionBase
{
public $resources = null;
public $resourcesInd = null;
public $organizations = null;
public $ccversion = null;
public $camversion = null;
protected $_generator = null;
protected $ccnamespaces = [];
protected $isrootmanifest = false;
protected $manifestID = null;
protected $organizationid = null;
protected $metadata = null;
protected $base = null;
public function getCcNamespaces()
{
return $this->ccnamespaces;
}
public function createManifest(DOMDocument &$doc, $rootmanifestnode = null)
{
return $this->onCreate($doc, $rootmanifestnode);
}
public function createResourceNode(CcIResource &$res, DOMDocument &$doc, $xmlnode = null)
{
return $this->createResource($res, $doc, $xmlnode);
}
public function createMetadataNode(&$met, DOMDocument &$doc, $xmlnode = null)
{
return $this->createMetadataManifest($met, $doc, $xmlnode);
}
public function createMetadataResourceNode(&$met, DOMDocument &$doc, $xmlnode = null)
{
return $this->createMetadataResource($met, $doc, $xmlnode);
}
public function createMetadataFileNode(&$met, DOMDocument &$doc, $xmlnode = null)
{
return $this->createMetadataFile($met, $doc, $xmlnode);
}
public function createOrganizationNode(CcIOrganization &$org, DOMDocument &$doc, $xmlnode = null)
{
return $this->createOrganization($org, $doc, $xmlnode);
}
public function manifestID()
{
return $this->manifestID;
}
public function setManifestID($id)
{
$this->manifestID = $id;
}
public function getBase()
{
return $this->base;
}
public function setBase($baseval)
{
$this->base = $baseval;
}
public function importResources(DOMElement &$node, CcIManifest &$doc)
{
if (is_null($this->resources)) {
$this->resources = [];
}
$nlist = $node->getElementsByTagNameNS($this->ccnamespaces['imscc'], 'resource');
if (is_object($nlist)) {
foreach ($nlist as $nd) {
$sc = new CcResource($doc, $nd);
$this->resources[$sc->identifier] = $sc;
}
}
}
public function importOrganizationItems(DOMElement &$node, CcIManifest &$doc)
{
if (is_null($this->organizations)) {
$this->organizations = [];
}
$nlist = $node->getElementsByTagNameNS($this->ccnamespaces['imscc'], 'organization');
if (is_object($nlist)) {
foreach ($nlist as $nd) {
$sc = new CcOrganization($nd, $doc);
$this->organizations[$sc->identifier] = $sc;
}
}
}
public function setGenerator($value)
{
$this->_generator = $value;
}
abstract protected function onCreate(DOMDocument &$doc, $rootmanifestnode = null, $nmanifestID = null);
abstract protected function createMetadataManifest(CcIMetadataManifest $met, DOMDocument &$doc, $xmlnode = null);
abstract protected function createMetadataResource(CcIMetadataResource $met, DOMDocument &$doc, $xmlnode = null);
abstract protected function createMetadataFile(CcIMetadataFile $met, DOMDocument &$doc, $xmlnode = null);
abstract protected function createResource(CcIResource &$res, DOMDocument &$doc, $xmlnode = null);
abstract protected function createOrganization(CcIOrganization &$org, DOMDocument &$doc, $xmlnode = null);
}

@ -0,0 +1,269 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/gral_lib/cssparser.php under GNU/GPL license */
class CssParser
{
private $css;
private $html;
public function __construct($html = true)
{
$this->html = ($html != false);
$this->clear();
}
public function clear()
{
unset($this->css);
$this->css = [];
if ($this->html) {
$this->add("ADDRESS", "");
$this->add("APPLET", "");
$this->add("AREA", "");
$this->add("A", "text-decoration : underline; color : Blue;");
$this->add("A:visited", "color : Purple;");
$this->add("BASE", "");
$this->add("BASEFONT", "");
$this->add("BIG", "");
$this->add("BLOCKQUOTE", "");
$this->add("BODY", "");
$this->add("BR", "");
$this->add("B", "font-weight: bold;");
$this->add("CAPTION", "");
$this->add("CENTER", "");
$this->add("CITE", "");
$this->add("CODE", "");
$this->add("DD", "");
$this->add("DFN", "");
$this->add("DIR", "");
$this->add("DIV", "");
$this->add("DL", "");
$this->add("DT", "");
$this->add("EM", "");
$this->add("FONT", "");
$this->add("FORM", "");
$this->add("H1", "");
$this->add("H2", "");
$this->add("H3", "");
$this->add("H4", "");
$this->add("H5", "");
$this->add("H6", "");
$this->add("HEAD", "");
$this->add("HR", "");
$this->add("HTML", "");
$this->add("IMG", "");
$this->add("INPUT", "");
$this->add("ISINDEX", "");
$this->add("I", "font-style: italic;");
$this->add("KBD", "");
$this->add("LINK", "");
$this->add("LI", "");
$this->add("MAP", "");
$this->add("MENU", "");
$this->add("META", "");
$this->add("OL", "");
$this->add("OPTION", "");
$this->add("PARAM", "");
$this->add("PRE", "");
$this->add("P", "");
$this->add("SAMP", "");
$this->add("SCRIPT", "");
$this->add("SELECT", "");
$this->add("SMALL", "");
$this->add("STRIKE", "");
$this->add("STRONG", "");
$this->add("STYLE", "");
$this->add("SUB", "");
$this->add("SUP", "");
$this->add("TABLE", "");
$this->add("TD", "");
$this->add("TEXTAREA", "");
$this->add("TH", "");
$this->add("TITLE", "");
$this->add("TR", "");
$this->add("TT", "");
$this->add("UL", "");
$this->add("U", "text-decoration : underline;");
$this->add("VAR", "");
}
}
public function setHTML($html)
{
$this->html = ($html != false);
}
public function add($key, $codestr)
{
$key = strtolower($key);
$codestr = strtolower($codestr);
if (!isset($this->css[$key])) {
$this->css[$key] = [];
}
$codes = explode(";", $codestr);
if (count($codes) > 0) {
$codekey = '';
$codevalue = '';
foreach ($codes as $code) {
$code = trim($code);
$this->assignValues(explode(":", $code), $codekey, $codevalue);
if (strlen($codekey) > 0) {
$this->css[$key][trim($codekey)] = trim($codevalue);
}
}
}
}
public function get($key, $property)
{
$key = strtolower($key);
$property = strtolower($property);
$tag = '';
$subtag = '';
$class = '';
$id = '';
$this->assignValues(explode(":", $key), $tag, $subtag);
$this->assignValues(explode(".", $tag), $tag, $class);
$this->assignValues(explode("#", $tag), $tag, $id);
$result = "";
$_subtag = '';
$_class = '';
$_id = '';
foreach ($this->css as $_tag => $value) {
$this->assignValues(explode(":", $_tag), $_tag, $_subtag);
$this->assignValues(explode(".", $_tag), $_tag, $_class);
$this->assignValues(explode("#", $_tag), $_tag, $_id);
$tagmatch = (strcmp($tag, $_tag) == 0) | (strlen($_tag) == 0);
$subtagmatch = (strcmp($subtag, $_subtag) == 0) | (strlen($_subtag) == 0);
$classmatch = (strcmp($class, $_class) == 0) | (strlen($_class) == 0);
$idmatch = (strcmp($id, $_id) == 0);
if ($tagmatch & $subtagmatch & $classmatch & $idmatch) {
$temp = $_tag;
if ((strlen($temp) > 0) & (strlen($_class) > 0)) {
$temp .= ".".$_class;
} elseif (strlen($temp) == 0) {
$temp = ".".$_class;
}
if ((strlen($temp) > 0) & (strlen($_subtag) > 0)) {
$temp .= ":".$_subtag;
} elseif (strlen($temp) == 0) {
$temp = ":".$_subtag;
}
if (isset($this->css[$temp][$property])) {
$result = $this->css[$temp][$property];
}
}
}
return $result;
}
public function getSection($key)
{
$key = strtolower($key);
$tag = '';
$subtag = '';
$class = '';
$id = '';
$_subtag = '';
$_class = '';
$_id = '';
$this->assignValues(explode(":", $key), $tag, $subtag);
$this->assignValues(explode(".", $tag), $tag, $class);
$this->assignValues(explode("#", $tag), $tag, $id);
$result = [];
foreach ($this->css as $_tag => $value) {
$this->assignValues(explode(":", $_tag), $_tag, $_subtag);
$this->assignValues(explode(".", $_tag), $_tag, $_class);
$this->assignValues(explode("#", $_tag), $_tag, $_id);
$tagmatch = (strcmp($tag, $_tag) == 0) | (strlen($_tag) == 0);
$subtagmatch = (strcmp($subtag, $_subtag) == 0) | (strlen($_subtag) == 0);
$classmatch = (strcmp($class, $_class) == 0) | (strlen($_class) == 0);
$idmatch = (strcmp($id, $_id) == 0);
if ($tagmatch & $subtagmatch & $classmatch & $idmatch) {
$temp = $_tag;
if ((strlen($temp) > 0) & (strlen($_class) > 0)) {
$temp .= ".".$_class;
} elseif (strlen($temp) == 0) {
$temp = ".".$_class;
}
if ((strlen($temp) > 0) & (strlen($_subtag) > 0)) {
$temp .= ":".$_subtag;
} elseif (strlen($temp) == 0) {
$temp = ":".$_subtag;
}
foreach ($this->css[$temp] as $property => $value) {
$result[$property] = $value;
}
}
}
return $result;
}
public function parseStr($str)
{
$this->clear();
// Remove comments
$str = preg_replace("/\/\*(.*)?\*\//Usi", "", $str);
// Parse this damn csscode
$parts = explode("}", $str);
if (count($parts) > 0) {
foreach ($parts as $part) {
$keystr = '';
$codestr = '';
$this->assignValues(explode("{", $part), $keystr, $codestr);
$keys = explode(",", trim($keystr));
if (count($keys) > 0) {
foreach ($keys as $key) {
if (strlen($key) > 0) {
$key = str_replace("\n", "", $key);
$key = str_replace("\\", "", $key);
$this->Add($key, trim($codestr));
}
}
}
}
}
return count($this->css) > 0;
}
public function parse($filename)
{
$this->clear();
if (file_exists($filename)) {
return $this->parseStr(file_get_contents($filename));
} else {
return false;
}
}
public function getCSS()
{
$result = "";
foreach ($this->css as $key => $values) {
$result .= $key." {\n";
foreach ($values as $key => $value) {
$result .= " $key: $value;\n";
}
$result .= "}\n\n";
}
return $result;
}
private function assignValues($arr, &$val1, &$val2)
{
$n = count($arr);
if ($n > 0) {
$val1 = $arr[0];
$val2 = ($n > 1) ? $arr[1] : '';
}
}
}

@ -0,0 +1,470 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/xmlbase.php under GNU/GPL license */
/**
* Base XML class.
*/
class XMLGenericDocument
{
/**
* Document.
*
* @var DOMDocument
*/
public $doc = null;
/**
* Xpath.
*
* @var DOMXPath
*/
protected $dxpath = null;
protected $filename;
private $charset;
private $filepath;
private $isloaded = false;
private $arrayPrefixNS = [];
private $isHtml = false;
public function __construct($ch = 'UTF-8', $validatenow = true)
{
$this->charset = $ch;
$this->documentInit();
$this->doc->validateOnParse = $validatenow;
}
public function __destruct()
{
$this->dxpath = null;
$this->doc = null;
}
/**
* @param string $value
*
* @return string
*/
public static function safexml($value)
{
$result = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'),
ENT_NOQUOTES,
'UTF-8',
false);
return $result;
}
public function viewXML()
{
return $this->doc->saveXML();
}
public function registerNS($prefix, $nsuri)
{
$this->arrayPrefixNS[$prefix] = $nsuri;
}
public function load($fname)
{
// Sine xml will remain loaded should the repeated load fail we should recreate document to be empty.
$this->documentInit(false);
$this->isloaded = $this->doc->load($fname);
if ($this->isloaded) {
$this->filename = $fname;
$this->processPath();
$this->isHtml = false;
}
return $this->onLoad();
}
public function loadUrl($url)
{
$this->documentInit();
$this->isloaded = true;
$this->doc->loadXML(file_get_contents($url));
$this->isHtml = false;
return $this->onLoad();
}
public function loadHTML($content)
{
$this->documentInit();
$this->doc->validateOnParse = false;
$this->isloaded = true;
$this->doc->loadHTML($content);
$this->isHtml = true;
return $this->onLoad();
}
public function loadXML($content)
{
$this->documentInit();
$this->doc->validateOnParse = false;
$this->isloaded = true;
$this->doc->load($content);
$this->isHtml = true;
return $this->onLoad();
}
public function loadHTMLFile($fname)
{
// Sine xml will remain loaded should the repeated load fail
// we should recreate document to be empty.
$this->documentInit();
$this->doc->validateOnParse = false;
$this->isloaded = $this->doc->loadHTMLFile($fname);
if ($this->isloaded) {
$this->filename = $fname;
$this->processPath();
$this->isHtml = true;
}
return $this->onLoad();
}
public function loadXMLFile($fname)
{
// Sine xml will remain loaded should the repeated load fail
// we should recreate document to be empty.
$this->documentInit();
$this->doc->validateOnParse = false;
$this->isloaded = $this->doc->load($fname);
if ($this->isloaded) {
$this->filename = $fname;
$this->processPath();
$this->isHtml = true;
}
return $this->onLoad();
}
public function loadString($content)
{
$this->doc = new DOMDocument("1.0", $this->charset);
$content = '<virtualtag>'.$content.'</virtualtag>';
$this->doc->loadXML($content);
return true;
}
public function save()
{
$this->saveTo($this->filename);
}
public function saveTo($fname)
{
$status = false;
if ($this->onSave()) {
if ($this->isHtml) {
$this->doc->saveHTMLFile($fname);
} else {
$this->doc->save($fname);
}
$this->filename = $fname;
$this->processPath();
$status = true;
}
return $status;
}
public function validate()
{
return $this->doc->validate();
}
public function attributeValue($path, $attrname, $node = null)
{
$this->chkxpath();
$result = null;
$resultlist = null;
if (is_null($node)) {
$resultlist = $this->dxpath->query($path);
} else {
$resultlist = $this->dxpath->query($path, $node);
}
if (is_object($resultlist) && ($resultlist->length > 0) && $resultlist->item(0)->hasAttribute($attrname)) {
$result = $resultlist->item(0)->getAttribute($attrname);
}
return $result;
}
/**
* Get's text value of the node based on xpath query.
*
* @param string $path
* @param DOMNode $node
* @param int $count
*
* @return string
*/
public function nodeValue($path, $node = null, $count = 1)
{
$nd = $this->node($path, $node, $count);
return $this->nodeTextValue($nd);
}
/**
* Get's text value of the node.
*
* @param DOMNode $node
*
* @return string
*/
public function nodeTextValue($node)
{
$result = '';
if (is_object($node)) {
if ($node->hasChildNodes()) {
$chnodesList = $node->childNodes;
$types = [XML_TEXT_NODE, XML_CDATA_SECTION_NODE];
foreach ($chnodesList as $chnode) {
if (in_array($chnode->nodeType, $types)) {
$result .= $chnode->wholeText;
}
}
}
}
return $result;
}
/**
* Get the nodes from a path
*
* @param string $path
* @param DOMNode $nd
* @param int $count
*
* @return DOMNode
*/
public function node($path, $nd = null, $count = 1)
{
$result = null;
$resultlist = $this->nodeList($path, $nd);
if (is_object($resultlist) && ($resultlist->length > 0)) {
$result = $resultlist->item($count - 1);
}
return $result;
}
/**
* Get a list of nodes from a path
*
* @param string $path
* @param DOMNode $node
*
* @return DOMNodeList
*/
public function nodeList($path, $node = null)
{
$this->chkxpath();
$resultlist = null;
if (is_null($node)) {
$resultlist = $this->dxpath->query($path);
} else {
$resultlist = $this->dxpath->query($path, $node);
}
return $resultlist;
}
/**
* Create new attribute.
*
* @param string $namespace
* @param string $name
* @param string $value
*
* @return DOMAttr
*/
public function createAttributeNs($namespace, $name, $value = null)
{
$result = $this->doc->createAttributeNS($namespace, $name);
if (!is_null($value)) {
$result->nodeValue = $value;
}
return $result;
}
/**
* Create new attribute.
*
* @param string $name
* @param string $value
*
* @return DOMAttr
*/
public function createAttribute($name, $value = null)
{
$result = $this->doc->createAttribute($name);
if (!is_null($value)) {
$result->nodeValue = $value;
}
return $result;
}
/**
* Adds new node.
*
* @param string $namespace
* @param string $name
* @param string $value
*
* @return DOMNode
*/
public function appendNewElementNs(DOMNode &$parentnode, $namespace, $name, $value = null)
{
$newnode = null;
if (is_null($value)) {
$newnode = $this->doc->createElementNS($namespace, $name);
} else {
$newnode = $this->doc->createElementNS($namespace, $name, $value);
}
return $parentnode->appendChild($newnode);
}
/**
* New node with CDATA content.
*
* @param string $namespace
* @param string $name
* @param string $value
*/
public function appendNewElementNsCdata(DOMNode &$parentnode, $namespace, $name, $value = null)
{
$newnode = $this->doc->createElementNS($namespace, $name);
if (!is_null($value)) {
$cdata = $this->doc->createCDATASection($value);
$newnode->appendChild($cdata);
}
return $parentnode->appendChild($newnode);
}
/**
* Adds new node.
*
* @param string $name
* @param string $value
*
* @return DOMNode
*/
public function appendNewElement(DOMNode &$parentnode, $name, $value = null)
{
$newnode = null;
if (is_null($value)) {
$newnode = $this->doc->createElement($name);
} else {
$newnode = $this->doc->createElement($name, $value);
}
return $parentnode->appendChild($newnode);
}
/**
* Adds new attribute.
*
* @param string $name
* @param string $value
*
* @return DOMNode
*/
public function appendNewAttribute(DOMNode &$node, $name, $value = null)
{
return $node->appendChild($this->createAttribute($name, $value));
}
/**
* Adds new attribute.
*
* @param string $namespace
* @param string $name
* @param string $value
*
* @return DOMNode
*/
public function appendNewAttributeNs(DOMNode &$node, $namespace, $name, $value = null)
{
return $node->appendChild($this->createAttributeNs($namespace, $name, $value));
}
public function fileName()
{
return $this->filename;
}
public function filePath()
{
return $this->filepath;
}
public function resetXpath()
{
$this->dxpath = null;
$this->chkxpath();
}
protected function onLoad()
{
return $this->isloaded;
}
protected function onSave()
{
return true;
}
protected function onCreate()
{
return true;
}
protected function processPath()
{
$path_parts = pathinfo($this->filename);
$this->filepath = array_key_exists('dirname', $path_parts) ? $path_parts['dirname']."/" : '';
}
private function documentInit($withonCreate = true)
{
$hg = false;
if ($this->isloaded) {
$guardstate = $this->doc->validateOnParse;
$hg = true;
unset($this->dxpath);
unset($this->doc);
$this->isloaded = false;
}
$this->doc = new DOMDocument("1.0", $this->charset);
$this->doc->strictErrorChecking = true;
if ($hg) {
$this->doc->validateOnParse = $guardstate;
}
$this->doc->formatOutput = true;
$this->doc->preserveWhiteSpace = true;
if ($withonCreate) {
$this->onCreate();
}
}
private function chkxpath()
{
if (!isset($this->dxpath) || is_null($this->dxpath)) {
$this->dxpath = new DOMXPath($this->doc);
foreach ($this->arrayPrefixNS as $nskey => $nsuri) {
$this->dxpath->registerNamespace($nskey, $nsuri);
}
}
}
}

@ -0,0 +1,37 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_converter_folder.php under GNU/GPL license */
class CcConverterFolder extends CcConverters
{
public function __construct(CcIItem &$item, CcIManifest &$manifest, $rootpath, $path)
{
$this->defaultfile = 'folder.xml';
parent::__construct($item, $manifest, $rootpath, $path);
}
public function convert($outdir, $objDocument)
{
$contextid = $objDocument['source_id'];
$folder = api_get_path(SYS_COURSE_PATH).api_get_course_path($objDocument['course_code']).'/'.$objDocument['path'];
$files = CcHelpers::handleStaticContent($this->manifest,
$this->rootpath,
$contextid,
$outdir,
true,
$folder
);
$resvalue = null;
foreach ($files as $values) {
if ($values[2]) {
$resvalue = $values[0];
break;
}
}
$resitem = new CcItem();
$resitem->identifierref = $resvalue;
$resitem->title = $objDocument['title'];
$this->item->addChildItem($resitem);
return true;
}
}

@ -0,0 +1,36 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_converter_forum.php under GNU/GPL license */
class CcConverterForum extends CcConverters
{
public function __construct(CcIItem &$item, CcIManifest &$manifest, $rootpath, $path)
{
$this->ccType = CcVersion13::DISCUSSIONTOPIC;
$this->defaultfile = 'forum.xml';
$this->defaultname = 'discussion.xml';
parent::__construct($item, $manifest, $rootpath, $path);
}
public function convert($outdir, $item)
{
$rt = new CcForum();
$title = $item['title'];
$rt->setTitle($title);
$text = $item['comment'];
$deps = null;
if (!empty($text)) {
$contextid = $item['source_id'];
$result = CcHelpers::processLinkedFiles($text,
$this->manifest,
$this->rootpath,
$contextid,
$outdir);
$textformat = 'text/html';
$rt->setText($result[0], $textformat);
$deps = $result[1];
}
$this->store($rt, $outdir, $title, $deps);
return true;
}
}

@ -0,0 +1,41 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_converter_page.php under GNU/GPL license */
class CcConverterPage extends CcConverters
{
public function __construct(CcIItem &$item, CcIManifest &$manifest, $rootpath, $path)
{
$this->ccType = CcVersion13::WEBCONTENT;
$this->defaultfile = 'page.xml';
$this->defaultname = uniqid().'.html';
parent::__construct($item, $manifest, $rootpath, $path);
}
public function convert($outdir, $objPage)
{
$rt = new CcPage();
$title = $objPage['title'];
$intro = '';
$contextid = $objPage['source_id'];
$pagecontent = $objPage['comment'];
$rt->setTitle($title);
$rawname = str_replace(' ', '_', strtolower(trim(Security::filter_filename($title))));
if (!empty($rawname)) {
$this->defaultname = $rawname.".html";
}
$result = CcHelpers::processLinkedFiles($pagecontent,
$this->manifest,
$this->rootpath,
$contextid,
$outdir,
true);
$rt->setContent($result[0]);
$rt->setIntro($intro);
//store everything
$this->store($rt, $outdir, $title, $result[1]);
return true;
}
}

@ -0,0 +1,80 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_converter_quiz.php under GNU/GPL license */
class CcConverterQuiz extends CcConverters
{
public function __construct(CcIItem &$item, CcIManifest &$manifest, $rootpath, $path)
{
$this->ccType = CcVersion13::ASSESSMENT;
$this->defaultfile = 'quiz.xml';
$this->defaultname = Assesment13ResourceFile::DEAFULTNAME;
parent::__construct($item, $manifest, $rootpath, $path);
}
public function convert($outdir, $objQuizz)
{
$rt = new Assesment13ResourceFile();
$title = $objQuizz['title'];
$rt->setTitle($title);
// Metadata.
$metadata = new CcAssesmentMetadata();
$rt->setMetadata($metadata);
$metadata->enableFeedback();
$metadata->enableHints();
$metadata->enableSolutions();
// Attempts.
$maxAttempts = $objQuizz['max_attempt'];
if ($maxAttempts > 0) {
// Qti does not support number of specific attempts bigger than 5 (??)
if ($maxAttempts > 5) {
$maxAttempts = CcQtiValues::unlimited;
}
$metadata->setMaxattempts($maxAttempts);
}
// Time limit must be converted into minutes.
$timelimit = $objQuizz['expired_time'];
if ($timelimit > 0) {
$metadata->setTimelimit($timelimit);
$metadata->enableLatesubmissions(false);
}
$contextid = $objQuizz['source_id'];
$result = CcHelpers::processLinkedFiles($objQuizz['comment'],
$this->manifest,
$this->rootpath,
$contextid,
$outdir);
CcAssesmentHelper::addAssesmentDescription($rt, $result[0], CcQtiValues::HTMLTYPE);
// Section.
$section = new CcAssesmentSection();
$rt->setSection($section);
// Process the actual questions.
$ndeps = CcAssesmentHelper::processQuestions($objQuizz,
$this->manifest,
$section,
$this->rootpath,
$contextid,
$outdir
);
if ($ndeps === false) {
// No exportable questions in quiz or quiz has no questions
// so just skip it.
return true;
}
// Store any additional dependencies.
$deps = array_merge($result[1], $ndeps);
// Store everything.
$this->store($rt, $outdir, $title, $deps);
return true;
}
}

@ -0,0 +1,48 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_converter_resource.php under GNU/GPL license */
class CcConverterResource extends CcConverters
{
public function __construct(CcIItem &$item, CcIManifest &$manifest, $rootpath, $path)
{
$this->ccType = CcVersion13::WEBCONTENT;
$this->defaultfile = 'resource.xml';
parent::__construct($item, $manifest, $rootpath, $path);
}
public function convert($outdir, $objResource)
{
$title = $objResource['title'];
$contextid = $objResource['source_id'];
$docfilepath = null;
if (isset($objResource['path'])) {
$docfilepath = api_get_path(SYS_COURSE_PATH).api_get_course_path($objResource['course_code']).DIRECTORY_SEPARATOR.$objResource['path'];
}
$files = CcHelpers::handleResourceContent($this->manifest,
$this->rootpath,
$contextid,
$outdir,
true,
$docfilepath);
$deps = null;
$resvalue = null;
foreach ($files as $values) {
if ($values[2]) {
$resvalue = $values[0];
break;
}
}
$resitem = new CcItem();
$resitem->identifierref = $resvalue;
$resitem->title = $title;
$this->item->addChildItem($resitem);
// Checking the visibility.
$this->manifest->updateInstructoronly($resvalue, !$this->isVisible());
return true;
}
}

@ -0,0 +1,27 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_converter_url.php under GNU/GPL license */
class CcConverterUrl extends CcConverters
{
public function __construct(CcIItem &$item, CcIManifest &$manifest, $rootpath, $path)
{
$this->ccType = CcVersion13::WEBLINK;
$this->defaultfile = 'url.xml';
$this->defaultname = 'weblink.xml';
parent::__construct($item, $manifest, $rootpath, $path);
}
public function convert($outdir, $objLink)
{
$rt = new CcWebLink();
$title = $objLink['title'];
$rt->setTitle($title);
$url = $objLink['url'];
if (!empty($url)) {
$rt->setUrl($url, $objLink['target']);
}
$this->store($rt, $outdir, $title);
return true;
}
}

@ -0,0 +1,18 @@
<?php
/* For licensing terms, see /license.txt */
/**
* CC Item Interface.
*/
interface CcIItem
{
public function addChildItem(CcIItem &$item);
public function attachResource($res); // can be object or value
public function hasChildItems();
public function attrValue(&$nod, $name, $ns = null);
public function processItem(&$node, &$doc);
}

@ -0,0 +1,30 @@
<?php
/* For licensing terms, see /license.txt */
/**
* CC Manifest Interface.
*/
interface CcIManifest
{
public function onCreate();
public function onLoad();
public function onSave();
public function addNewOrganization(CcIOrganization &$org);
public function getResources();
public function getResourceList();
public function addResource(CcIResource $res, $identifier = null, $type = 'webcontent');
public function addMetadataManifest(CcIMetadataManifest $met);
public function addMetadataResource(CcIMetadataResource $met, $identifier);
public function addMetadataFile(CcIMetadataFile $met, $identifier, $filename);
public function putNodes();
}

@ -0,0 +1,10 @@
<?php
/* For licensing terms, see /license.txt */
/**
* CC Metadata File Interface.
*/
interface CcIMetadataFile
{
public function addMetadataFileEducational($obj);
}

@ -0,0 +1,16 @@
<?php
/* For licensing terms, see /license.txt */
/**
* CC Metadata Manifest Interface.
*/
interface CcIMetadataManifest
{
public function addMetadataGeneral($obj);
public function addMetadataTechnical($obj);
public function addMetadataRights($obj);
public function addMetadataLifecycle($obj);
}

@ -0,0 +1,10 @@
<?php
/* For licensing terms, see /license.txt */
/**
* CC Metadata Resource Interface.
*/
interface CcIMetadataResource
{
public function addMetadataResourceEducational($obj);
}

@ -0,0 +1,16 @@
<?php
/* For licensing terms, see /license.txt */
/**
* CC Organization Interface.
*/
interface CcIOrganization
{
public function addItem(CcIItem &$item);
public function hasItems();
public function attrValue(&$nod, $name, $ns = null);
public function processOrganization(&$node, &$doc);
}

@ -0,0 +1,16 @@
<?php
/* For licensing terms, see /license.txt */
/**
* CC Resource Interface.
*/
interface CcIResource
{
public function getAttrValue(&$nod, $name, $ns = null);
public function addResource($fname, $location = '');
public function importResource(DOMElement &$node, CcIManifest &$doc);
public function processResource($manifestroot, &$fname, $folder);
}

@ -0,0 +1,7 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class Assesment13ResourceFile extends Assesment1ResourceFile
{
protected $ccnsnames = ['xmlns' => 'http://www.imsglobal.org/profile/cc/ccv1p3/ccv1p3_qtiasiv1p2p1_v1p0.xsd'];
}

@ -0,0 +1,74 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class Assesment1ResourceFile extends CcGeneralFile
{
const DEAFULTNAME = 'assesment.xml';
protected $rootns = 'xmlns';
protected $rootname = CcQtiTags::QUESTESTINTEROP;
protected $ccnamespaces = ['xmlns' => 'http://www.imsglobal.org/xsd/ims_qtiasiv1p2',
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', ];
protected $ccnsnames = ['xmlns' => 'http://www.imsglobal.org/profile/cc/ccv1p0/derived_schema/domainProfile_4/ims_qtiasiv1p2_localised.xsd'];
protected $assessmentTitle = 'Untitled';
protected $metadata = null;
protected $rubric = null;
protected $presentationMaterial = null;
protected $section = null;
public function setMetadata(CcAssesmentMetadata $object)
{
$this->metadata = $object;
}
public function setRubric(CcAssesmentRubricBase $object)
{
$this->rubric = $object;
}
public function setPresentationMaterial(CcAssesmentPresentationMaterialBase $object)
{
$this->presentationMaterial = $object;
}
public function setSection(CcAssesmentSection $object)
{
$this->section = $object;
}
public function setTitle($value)
{
$this->assessmentTitle = self::safexml($value);
}
protected function onSave()
{
$rns = $this->ccnamespaces[$this->rootns];
//root assesment element - required
$assessment = $this->appendNewElementNs($this->root, $rns, CcQtiTags::ASSESSMENT);
$this->appendNewAttributeNs($assessment, $rns, CcQtiTags::IDENT, CcHelpers::uuidgen('QDB_'));
$this->appendNewAttributeNs($assessment, $rns, CcQtiTags::TITLE, $this->assessmentTitle);
//metadata - optional
if (!empty($this->metadata)) {
$this->metadata->generate($this, $assessment, $rns);
}
//rubric - optional
if (!empty($this->rubric)) {
$this->rubric->generate($this, $assessment, $rns);
}
//presentation_material - optional
if (!empty($this->presentationMaterial)) {
$this->presentationMaterial->generate($this, $assessment, $rns);
}
//section - required
if (!empty($this->section)) {
$this->section->generate($this, $assessment, $rns);
}
return true;
}
}

@ -0,0 +1,11 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentAltmaterial extends CcAssesmentMaterialBase
{
public function __construct($value = null)
{
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML);
$this->tagname = CcQtiTags::ALTMATERIAL;
}
}

@ -0,0 +1,30 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentDecvartype extends CcQuestionMetadataBase
{
public function __construct()
{
$this->setSetting(CcQtiTags::VARNAME, CcQtiValues::SCORE);
$this->setSetting(CcQtiTags::VARTYPE, CcQtiValues::INTEGER);
$this->setSetting(CcQtiTags::MINVALUE);
$this->setSetting(CcQtiTags::MAXVALUE);
}
public function setVartype($value)
{
$this->setSetting(CcQtiTags::VARTYPE, $value);
}
public function setLimits($min = null, $max = null)
{
$this->setSetting(CcQtiTags::MINVALUE, $min);
$this->setSetting(CcQtiTags::MAXVALUE, $max);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::DECVAR);
$this->generateAttributes($doc, $node, $namespace);
}
}

@ -0,0 +1,50 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentFlowLabeltype extends CcQuestionMetadataBase
{
protected $flowLabel = null;
protected $responseLabel = null;
public function __construct()
{
$this->setSetting(CcQtiTags::T_CLASS);
}
public function setClass($value)
{
$this->setSetting(CcQtiTags::T_CLASS, $value);
}
public function setFlowLabel(CcAssesmentFlowLabeltype $object)
{
$this->flowLabel = $object;
}
public function setResponseLabel(CcAssesmentResponseLabeltype $object)
{
$this->responseLabel = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::FLOW_LABEL);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->material)) {
$this->material->generate($doc, $node, $namespace);
}
if (!empty($this->materialRef)) {
$this->materialRef->generate($doc, $node, $namespace);
}
if (!empty($this->responseLabel)) {
$this->responseLabel->generate($doc, $node, $namespace);
}
if (!empty($this->flowLabel)) {
$this->flowLabel->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,46 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentFlowMatBase extends CcQuestionMetadataBase
{
protected $mattag = null;
public function __construct($value = null)
{
$this->setSetting(CcQtiTags::T_CLASS);
}
public function setFlowMat(CcAssesmentFlowMatBase $object)
{
$this->setTagValue($object);
}
public function setMaterial(CcAssesmentMaterial $object)
{
$this->setTagValue($object);
}
public function setMaterialRef(CcAssesmentMatref $object)
{
$this->setTagValue($object);
}
public function setClass($value)
{
$this->setSetting(CcQtiTags::T_CLASS, $value);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::FLOW_MAT);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->mattag)) {
$this->mattag->generate($doc, $node, $namespace);
}
}
protected function setTagValue($object)
{
$this->mattag = $object;
}
}

@ -0,0 +1,52 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentFlowMattype extends CcQuestionMetadataBase
{
protected $material = null;
protected $materialRef = null;
protected $flowMat = null;
public function __construct()
{
$this->setSetting(CcQtiTags::T_CLASS);
}
public function setClass($value)
{
$this->setSetting(CcQtiTags::T_CLASS, $value);
}
public function setMaterial(CcAssesmentMaterial $object)
{
$this->material = $object;
}
public function setMaterialRef(CcAssesmentResponseMatref $object)
{
$this->materialRef = $object;
}
public function setFlowMat(CcAssesmentFlowMattype $object)
{
$this->flowMat = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::FLOW_MAT);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->flowMat)) {
$this->flowMat->generate($doc, $node, $namespace);
}
if (!empty($this->material)) {
$this->material->generate($doc, $node, $namespace);
}
if (!empty($this->materialRef)) {
$this->materialRef->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,68 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentFlowtype extends CcQuestionMetadataBase
{
protected $flow = null;
protected $material = null;
protected $materialRef = null;
protected $responseLid = null;
protected $responseStr = null;
public function __construct()
{
$this->setSetting(CcQtiTags::T_CLASS);
}
public function setClass($value)
{
$this->setSetting(CcQtiTags::T_CLASS, $value);
}
public function setFlow(CcAssesmentFlowtype $object)
{
$this->flow = $object;
}
public function setMaterial(CcAssesmentMaterial $object)
{
$this->material = $object;
}
public function setMaterialRef(CcAssesmentResponseMatref $object)
{
$this->materialRef = $object;
}
public function setResponseLid(CcResponseLidtype $object)
{
$this->responseLid = $object;
}
public function setResponseStr(CcAssesmentResponseStrtype $object)
{
$this->responseStr = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::FLOW);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->flow)) {
$this->flow->generate($doc, $node, $namespace);
}
if (!empty($this->material)) {
$this->material->generate($doc, $node, $namespace);
}
if (!empty($this->responseLid)) {
$this->responseLid->generate($doc, $node, $namespace);
}
if (!empty($this->responseStr)) {
$this->responseStr->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,135 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
abstract class CcAssesmentHelper
{
public static $correctFb = null;
public static $incorrectFb = null;
public static function addFeedback($qitem, $content, $contentType, $ident)
{
if (empty($content)) {
return false;
}
$qitemfeedback = new CcAssesmentItemfeedbacktype();
$qitem->addItemfeedback($qitemfeedback);
if (!empty($ident)) {
$qitemfeedback->setIdent($ident);
}
$qflowmat = new CcAssesmentFlowMattype();
$qitemfeedback->setFlowMat($qflowmat);
$qmaterialfb = new CcAssesmentMaterial();
$qflowmat->setMaterial($qmaterialfb);
$qmattext = new CcAssesmentMattext();
$qmaterialfb->setMattext($qmattext);
$qmattext->setContent($content, $contentType);
return true;
}
public static function addAnswer($qresponseChoice, $content, $contentType)
{
$qresponseLabel = new CcAssesmentResponseLabeltype();
$qresponseChoice->addResponseLabel($qresponseLabel);
$qrespmaterial = new CcAssesmentMaterial();
$qresponseLabel->setMaterial($qrespmaterial);
$qrespmattext = new CcAssesmentMattext();
$qrespmaterial->setMattext($qrespmattext);
$qrespmattext->setContent($content, $contentType);
return $qresponseLabel;
}
public static function addResponseCondition($node, $title, $ident, $feedbackRefid, $respident)
{
$qrespcondition = new CcAssesmentRespconditiontype();
$node->addRespcondition($qrespcondition);
//define rest of the conditions
$qconditionvar = new CcAssignmentConditionvar();
$qrespcondition->setConditionvar($qconditionvar);
$qvarequal = new CcAssignmentConditionvarVarequaltype($ident);
$qvarequal->enableCase();
$qconditionvar->setVarequal($qvarequal);
$qvarequal->setRespident($respident);
$qdisplayfeedback = new CcAssignmentDisplayfeedbacktype();
$qrespcondition->addDisplayfeedback($qdisplayfeedback);
$qdisplayfeedback->setFeedbacktype(CcQtiValues::RESPONSE);
$qdisplayfeedback->setLinkrefid($feedbackRefid);
}
public static function addAssesmentDescription($rt, $content, $contenttype)
{
if (empty($rt) || empty($content)) {
return;
}
$activity_rubric = new CcAssesmentRubricBase();
$rubric_material = new CcAssesmentMaterial();
$activity_rubric->setMaterial($rubric_material);
$rubric_mattext = new CcAssesmentMattext();
$rubric_material->setLabel('Summary');
$rubric_material->setMattext($rubric_mattext);
$rubric_mattext->setContent($content, $contenttype);
$rt->setRubric($activity_rubric);
}
public static function addRespcondition($node, $title, $feedbackRefid, $gradeValue = null, $continue = false)
{
$qrespcondition = new CcAssesmentRespconditiontype();
$qrespcondition->setTitle($title);
$node->addRespcondition($qrespcondition);
$qrespcondition->enableContinue($continue);
//Add setvar if grade present
if ($gradeValue !== null) {
$qsetvar = new CcAssignmentSetvartype($gradeValue);
$qrespcondition->addSetvar($qsetvar);
}
//define the condition for success
$qconditionvar = new CcAssignmentConditionvar();
$qrespcondition->setConditionvar($qconditionvar);
$qother = new CcAssignmentConditionvarOthertype();
$qconditionvar->setOther($qother);
$qdisplayfeedback = new CcAssignmentDisplayfeedbacktype();
$qrespcondition->addDisplayfeedback($qdisplayfeedback);
$qdisplayfeedback->setFeedbacktype(CcQtiValues::RESPONSE);
$qdisplayfeedback->setLinkrefid($feedbackRefid);
}
/**
* Enter description here ...
*
* @param XMLGenericDocument $qdoc
* @param unknown_type $manifest
* @param cc_assesment_section $section
* @param unknown_type $rootpath
* @param unknown_type $contextid
* @param unknown_type $outdir
*/
public static function processQuestions(&$objQuizz, &$manifest, CcAssesmentSection &$section, $rootpath, $contextid, $outdir)
{
PkgResourceDependencies::instance()->reset();
$questioncount = 0;
foreach ($objQuizz['questions'] as $question) {
$qtype = $question->quiz_type;
/* Question type :
* 1 : Unique Answer (Multiple choice, single response)
* 2 : Multiple Answers (Multiple choice, multiple response)
*
*/
$questionProcessor = null;
switch ($qtype) {
case 1:
$questionProcessor = new CcAssesmentQuestionMultichoice($objQuizz, $objQuizz['questions'], $manifest, $section, $question, $rootpath, $contextid, $outdir);
$questionProcessor->generate();
$questioncount++;
break;
case 2:
$questionProcessor = new CcAssesmentQuestionMultichoiceMultiresponse($objQuizz, $objQuizz['questions'], $manifest, $section, $question, $rootpath, $contextid, $outdir);
$questionProcessor->generate();
$questioncount++;
break;
}
}
//return dependencies
return ($questioncount == 0) ? false : PkgResourceDependencies::instance()->getDeps();
}
}

@ -0,0 +1,16 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentItemfeedbacHinttype extends CcAssesmentItemfeedbackShintypeBase
{
public function __construct()
{
parent::__construct();
$this->tagname = CcQtiTags::HINT;
}
public function addHintmaterial(CcAssesmentItemfeedbackHintmaterial $object)
{
$this->items[] = $object;
}
}

@ -0,0 +1,10 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentItemfeedbackHintmaterial extends CcAssesmentItemfeedbackShintmaterialBase
{
public function __construct()
{
$this->tagname = CcQtiTags::HINT;
}
}

@ -0,0 +1,36 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentItemfeedbackShintmaterialBase extends CcQuestionMetadataBase
{
protected $tagname = null;
protected $flowMats = [];
protected $materials = [];
public function addFlowMat(CcAssesmentFlowMattype $object)
{
$this->flowMats[] = $object;
}
public function add_material(CcAssesmentMaterial $object)
{
$this->materials[] = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, $this->tagname);
if (!empty($this->flowMats)) {
foreach ($this->flowMats as $flowMat) {
$flowMat->generate($doc, $node, $namespace);
}
}
if (!empty($this->materials)) {
foreach ($this->materials as $material) {
$material->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,25 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentItemfeedbackShintypeBase extends CcQuestionMetadataBase
{
protected $tagname = null;
protected $items = [];
public function __construct()
{
$this->setSetting(CcQtiTags::FEEDBACKSTYLE, CcQtiValues::COMPLETE);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, $this->tagname);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->items)) {
foreach ($this->items as $telement) {
$telement->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,10 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentItemfeedbackSolutionmaterial extends CcAssesmentItemfeedbackShintmaterialBase
{
public function __construct()
{
$this->tagname = CcQtiTags::SOLUTIONMATERIAL;
}
}

@ -0,0 +1,16 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentItemfeedbackSolutiontype extends CcAssesmentItemfeedbackShintypeBase
{
public function __construct()
{
parent::__construct();
$this->tagname = CcQtiTags::SOLUTION;
}
public function add_solutionmaterial(CcAssesmentItemfeedbackSolutionmaterial $object)
{
$this->items[] = $object;
}
}

@ -0,0 +1,74 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentItemfeedbacktype extends CcQuestionMetadataBase
{
protected $flowMat = null;
protected $material = null;
protected $solution = null;
protected $hint = null;
public function __construct()
{
$this->setSetting(CcQtiTags::IDENT, CcHelpers::uuidgen('I_'));
$this->setSetting(CcQtiTags::TITLE);
}
/**
* @param string $value
*/
public function setIdent($value)
{
$this->setSetting(CcQtiTags::IDENT, $value);
}
/**
* @param string $value
*/
public function setTitle($value)
{
$this->setSetting(CcQtiTags::TITLE, $value);
}
public function setFlowMat(CcAssesmentFlowMattype $object)
{
$this->flowMat = $object;
}
public function setMaterial(CcAssesmentMaterial $object)
{
$this->material = $object;
}
public function set_solution(CcAssesmentItemfeedbackSolutiontype $object)
{
$this->solution = $object;
}
public function set_hint($object)
{
$this->hint = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::ITEMFEEDBACK);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->flowMat) && empty($this->material)) {
$this->flowMat->generate($doc, $node, $namespace);
}
if (!empty($this->material) && empty($this->flowMat)) {
$this->material->generate($doc, $node, $namespace);
}
if (!empty($this->solution)) {
$this->solution->generate($doc, $node, $namespace);
}
if (!empty($this->itemfeedback)) {
$this->itemfeedback->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,20 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentItemmetadata extends CcQuestionMetadataBase
{
public function addMetadata($object)
{
$this->metadata[] = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::ITEMMETADATA);
if (!empty($this->metadata)) {
foreach ($this->metadata as $metaitem) {
$metaitem->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,10 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentMatbreak
{
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$doc->appendNewElementNs($item, $namespace, CcQtiTags::MATBREAK);
}
}

@ -0,0 +1,32 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentMaterial extends CcAssesmentMaterialBase
{
protected $altmaterial = null;
public function __construct($value = null)
{
$this->setSetting(CcQtiTags::LABEL);
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML);
$this->tagname = CcQtiTags::MATERIAL;
}
public function setLabel($value)
{
$this->setSetting(CcQtiTags::LABEL, $value);
}
public function setAltmaterial(CcAssesmentAltmaterial $object)
{
$this->altmaterial = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$material = parent::generate($doc, $item, $namespace);
if (!empty($this->altmaterial)) {
$this->altmaterial->generate($doc, $material, $namespace);
}
}
}

@ -0,0 +1,47 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
abstract class CcAssesmentMaterialBase extends CcQuestionMetadataBase
{
/**
* @var mixed
*/
protected $mattag = null;
protected $tagname = null;
public function setMattext(CcAssesmentMattext $object)
{
$this->setTagValue($object);
}
public function setMatref(CcAssesmentMatref $object)
{
$this->setTagValue($object);
}
public function setMatbreak(CcAssesmentMatbreak $object)
{
$this->setTagValue($object);
}
public function setLang($value)
{
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML, $value);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$material = $doc->appendNewElementNs($item, $namespace, $this->tagname);
$this->generateAttributes($doc, $material, $namespace);
if (!empty($this->mattag)) {
$this->mattag->generate($doc, $material, $namespace);
}
return $material;
}
protected function setTagValue($object)
{
$this->mattag = $object;
}
}

@ -0,0 +1,18 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentMatref
{
protected $linkref = null;
public function __construct($linkref)
{
$this->linkref = $linkref;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::MATREF, $this->linkref);
$doc->appendNewAttributeNs($node, $namespace, CcQtiTags::LINKREFID, $this->linkref);
}
}

@ -0,0 +1,62 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentMattext extends CcQuestionMetadataBase
{
protected $value = null;
public function __construct($value = null)
{
$this->setSetting(CcQtiTags::TEXTTYPE, CcQtiValues::TEXTTYPE);
$this->setSetting(CcQtiTags::CHARSET); //, 'ascii-us');
$this->setSetting(CcQtiTags::LABEL);
$this->setSetting(CcQtiTags::URI);
$this->setSetting(CcQtiTags::WIDTH);
$this->setSetting(CcQtiTags::HEIGHT);
$this->setSetting(CcQtiTags::X0);
$this->setSetting(CcQtiTags::Y0);
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML);
$this->setSettingWns(CcQtiTags::XML_SPACE, CcXmlNamespace::XML); //, 'default');
$this->value = $value;
}
public function setLabel($value)
{
$this->setSetting(CcQtiTags::LABEL, $value);
}
public function setUri($value)
{
$this->setSetting(CcQtiTags::URI, $value);
}
public function setWidthHeight($width = null, $height = null)
{
$this->setSetting(CcQtiTags::WIDTH, $width);
$this->setSetting(CcQtiTags::HEIGHT, $height);
}
public function setCoor($x = null, $y = null)
{
$this->setSetting(CcQtiTags::X0, $x);
$this->setSetting(CcQtiTags::Y0, $y);
}
public function setLang($lang = null)
{
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML, $lang);
}
public function setContent($content, $type = CcQtiValues::TEXTTYPE, $charset = null)
{
$this->value = $content;
$this->setSetting(CcQtiTags::TEXTTYPE, $type);
$this->setSetting(CcQtiTags::CHARSET, $charset);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$mattext = $doc->appendNewElementNsCdata($item, $namespace, CcQtiTags::MATTEXT, $this->value);
$this->generateAttributes($doc, $mattext, $namespace);
}
}

@ -0,0 +1,60 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentMetadata extends CcQuestionMetadataBase
{
public function __construct()
{
//prepared default values
$this->setSetting(CcQtiMetadata::CC_PROFILE, CcQtiValues::EXAM_PROFILE);
$this->setSetting(CcQtiMetadata::QMD_ASSESSMENTTYPE, CcQtiValues::EXAMINATION);
$this->setSetting(CcQtiMetadata::QMD_SCORETYPE, CcQtiValues::PERCENTAGE);
//optional empty values
$this->setSetting(CcQtiMetadata::QMD_FEEDBACKPERMITTED);
$this->setSetting(CcQtiMetadata::QMD_HINTSPERMITTED);
$this->setSetting(CcQtiMetadata::QMD_SOLUTIONSPERMITTED);
$this->setSetting(CcQtiMetadata::QMD_TIMELIMIT);
$this->setSetting(CcQtiMetadata::CC_ALLOW_LATE_SUBMISSION);
$this->setSetting(CcQtiMetadata::CC_MAXATTEMPTS);
}
public function enableHints($value = true)
{
$this->enableSettingYesno(CcQtiMetadata::QMD_HINTSPERMITTED, $value);
}
public function enableSolutions($value = true)
{
$this->enableSettingYesno(CcQtiMetadata::QMD_SOLUTIONSPERMITTED, $value);
}
public function enableLatesubmissions($value = true)
{
$this->enableSettingYesno(CcQtiMetadata::CC_ALLOW_LATE_SUBMISSION, $value);
}
public function enableFeedback($value = true)
{
$this->enableSettingYesno(CcQtiMetadata::QMD_FEEDBACKPERMITTED, $value);
}
public function setTimelimit($value)
{
$ivalue = (int) $value;
if (($ivalue < 0) || ($ivalue > 527401)) {
throw new OutOfRangeException('Time limit value out of permitted range!');
}
$this->setSetting(CcQtiMetadata::QMD_TIMELIMIT, $value);
}
public function setMaxattempts($value)
{
$valid_values = [CcQtiValues::EXAMINATION, CcQtiValues::UNLIMITED, 1, 2, 3, 4, 5];
if (!in_array($value, $valid_values)) {
throw new OutOfRangeException('Max attempts has invalid value');
}
$this->setSetting(CcQtiMetadata::CC_MAXATTEMPTS, $value);
}
}

@ -0,0 +1,84 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentPresentation extends CcQuestionMetadataBase
{
protected $flow = null;
protected $material = null;
protected $responseLid = null;
protected $responseStr = null;
public function __construct()
{
$this->setSetting(CcQtiTags::LABEL);
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML);
$this->setSetting(CcQtiTags::X0);
$this->setSetting(CcQtiTags::Y0);
$this->setSetting(CcQtiTags::WIDTH);
$this->setSetting(CcQtiTags::HEIGHT);
}
public function setLabel($value)
{
$this->setSetting(CcQtiTags::LABEL, $value);
}
public function setLang($value)
{
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML, $value);
}
public function setCoor($x = null, $y = null)
{
$this->setSetting(CcQtiTags::X0, $x);
$this->setSetting(CcQtiTags::Y0, $y);
}
public function setSize($width = null, $height = null)
{
$this->setSetting(CcQtiTags::WIDTH, $width);
$this->setSetting(CcQtiTags::HEIGHT, $height);
}
public function setFlow(CcAssesmentFlowtype $object)
{
$this->flow = $object;
}
public function setMaterial(CcAssesmentMaterial $object)
{
$this->material = $object;
}
public function setResponseLid(CcResponseLidtype $object)
{
$this->responseLid = $object;
}
public function setResponseStr(CcAssesmentResponseStrtype $object)
{
$this->responseStr = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::PRESENTATION);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->flow)) {
$this->flow->generate($doc, $node, $namespace);
}
if (!empty($this->material) && empty($this->flow)) {
$this->material->generate($doc, $node, $namespace);
}
if (!empty($this->responseLid) && empty($this->flow)) {
$this->responseLid->generate($doc, $node, $namespace);
}
if (!empty($this->responseStr) && empty($this->flow)) {
$this->responseStr->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,22 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentPresentationMaterialBase extends CcQuestionMetadataBase
{
protected $flowmats = [];
public function addFlowMat($object)
{
$this->flowmats[] = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::PRESENTATION_MATERIAL);
if (!empty($this->flowmats)) {
foreach ($this->flowmats as $flowMat) {
$flowMat->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,216 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentQuestionMultichoice extends CcAssesmentQuestionProcBase
{
public function __construct($quiz, $questions, $manifest, $section, $question, $rootpath, $contextid, $outdir)
{
parent::__construct($quiz, $questions, $manifest, $section, $question, $rootpath, $contextid, $outdir);
$this->qtype = CcQtiProfiletype::MULTIPLE_CHOICE;
$correctAnswerNode = 0;
$questionScore = 0;
foreach ($question->answers as $answer) {
if ($answer['correct'] > 0) {
$correctAnswerNode = 1;
$this->correctAnswerNodeId = $answer['id'];
$questionScore = $answer['ponderation'];
break;
}
}
if (empty($correctAnswerNode)) {
throw new RuntimeException('No correct answer!');
}
//$this->total_grade_value = ($question_score).'.0000000';
$this->totalGradeValue = $questionScore;
}
public function onGenerateAnswers()
{
//add responses holder
$qresponseLid = new CcResponseLidtype();
$this->qresponseLid = $qresponseLid;
$this->qpresentation->setResponseLid($qresponseLid);
$qresponseChoice = new CcAssesmentRenderChoicetype();
$qresponseLid->setRenderChoice($qresponseChoice);
//Mark that question has only one correct answer -
//which applies for multiple choice and yes/no questions
$qresponseLid->setRcardinality(CcQtiValues::SINGLE);
//are we to shuffle the responses?
$shuffleAnswers = $this->quiz['random_answers'] > 0;
$qresponseChoice->enableShuffle($shuffleAnswers);
$answerlist = [];
$qaResponses = $this->questionNode->answers;
foreach ($qaResponses as $node) {
$answerContent = $node['answer'];
$id = $node['id'];
$result = CcHelpers::processLinkedFiles($answerContent,
$this->manifest,
$this->rootpath,
$this->contextid,
$this->outdir);
$qresponseLabel = CcAssesmentHelper::addAnswer($qresponseChoice,
$result[0],
CcQtiValues::HTMLTYPE);
PkgResourceDependencies::instance()->add($result[1]);
$answerIdent = $qresponseLabel->getIdent();
$feedbackIdent = $answerIdent.'_fb';
if (empty($this->correctAnswerIdent) && $id) {
$this->correctAnswerIdent = $answerIdent;
}
//add answer specific feedbacks if not empty
$content = $node['comment'];
if (!empty($content)) {
$result = CcHelpers::processLinkedFiles($content,
$this->manifest,
$this->rootpath,
$this->contextid,
$this->outdir);
CcAssesmentHelper::addFeedback($this->qitem,
$result[0],
CcQtiValues::HTMLTYPE,
$feedbackIdent);
PkgResourceDependencies::instance()->add($result[1]);
$answerlist[$answerIdent] = $feedbackIdent;
}
}
$this->answerlist = $answerlist;
}
public function onGenerateFeedbacks()
{
parent::onGenerateFeedbacks();
//Question combined feedbacks
$correctQuestionFb = '';
$incorrectQuestionFb = '';
if (empty($correctQuestionFb)) {
//Hardcode some text for now
$correctQuestionFb = 'Well done!';
}
if (empty($incorrectQuestionFb)) {
//Hardcode some text for now
$incorrectQuestionFb = 'Better luck next time!';
}
$proc = ['correct_fb' => $correctQuestionFb, 'general_incorrect_fb' => $incorrectQuestionFb];
foreach ($proc as $ident => $content) {
if (empty($content)) {
continue;
}
$result = CcHelpers::processLinkedFiles($content,
$this->manifest,
$this->rootpath,
$this->contextid,
$this->outdir);
CcAssesmentHelper::addFeedback($this->qitem,
$result[0],
CcQtiValues::HTMLTYPE,
$ident);
PkgResourceDependencies::instance()->add($result[1]);
if ($ident == 'correct_fb') {
$this->correctFeedbacks[] = $ident;
} else {
$this->incorrectFeedbacks[] = $ident;
}
}
}
public function onGenerateResponseProcessing()
{
parent::onGenerateResponseProcessing();
//respconditions
/**
* General unconditional feedback must be added as a first respcondition
* without any condition and just displayfeedback (if exists).
*/
if (!empty($this->generalFeedback)) {
$qrespcondition = new CcAssesmentRespconditiontype();
$qrespcondition->setTitle('General feedback');
$this->qresprocessing->addRespcondition($qrespcondition);
$qrespcondition->enableContinue();
//define the condition for success
$qconditionvar = new CcAssignmentConditionvar();
$qrespcondition->setConditionvar($qconditionvar);
$qother = new CcAssignmentConditionvarOthertype();
$qconditionvar->setOther($qother);
$qdisplayfeedback = new CcAssignmentDisplayfeedbacktype();
$qrespcondition->addDisplayfeedback($qdisplayfeedback);
$qdisplayfeedback->setFeedbacktype(CcQtiValues::RESPONSE);
$qdisplayfeedback->setLinkrefid('general_fb');
}
//success condition
$qrespcondition = new CcAssesmentRespconditiontype();
$qrespcondition->setTitle('Correct');
$this->qresprocessing->addRespcondition($qrespcondition);
$qrespcondition->enableContinue(false);
$qsetvar = new CcAssignmentSetvartype(100);
$qrespcondition->addSetvar($qsetvar);
//define the condition for success
$qconditionvar = new CcAssignmentConditionvar();
$qrespcondition->setConditionvar($qconditionvar);
$qvarequal = new CcAssignmentConditionvarVarequaltype($this->correctAnswerIdent);
$qconditionvar->setVarequal($qvarequal);
$qvarequal->setRespident($this->qresponseLid->getIdent());
if (array_key_exists($this->correctAnswerIdent, $this->answerlist)) {
$qdisplayfeedback = new CcAssignmentDisplayfeedbacktype();
$qrespcondition->addDisplayfeedback($qdisplayfeedback);
$qdisplayfeedback->setFeedbacktype(CcQtiValues::RESPONSE);
$qdisplayfeedback->setLinkrefid($this->answerlist[$this->correctAnswerIdent]);
}
foreach ($this->correctFeedbacks as $ident) {
$qdisplayfeedback = new CcAssignmentDisplayfeedbacktype();
$qrespcondition->addDisplayfeedback($qdisplayfeedback);
$qdisplayfeedback->setFeedbacktype(CcQtiValues::RESPONSE);
$qdisplayfeedback->setLinkrefid($ident);
}
//rest of the conditions
foreach ($this->answerlist as $ident => $refid) {
if ($ident == $this->correctAnswerIdent) {
continue;
}
$qrespcondition = new CcAssesmentRespconditiontype();
$this->qresprocessing->addRespcondition($qrespcondition);
$qsetvar = new CcAssignmentSetvartype(0);
$qrespcondition->addSetvar($qsetvar);
//define the condition for fail
$qconditionvar = new CcAssignmentConditionvar();
$qrespcondition->setConditionvar($qconditionvar);
$qvarequal = new CcAssignmentConditionvarVarequaltype($ident);
$qconditionvar->setVarequal($qvarequal);
$qvarequal->setRespident($this->qresponseLid->getIdent());
$qdisplayfeedback = new CcAssignmentDisplayfeedbacktype();
$qrespcondition->addDisplayfeedback($qdisplayfeedback);
$qdisplayfeedback->setFeedbacktype(CcQtiValues::RESPONSE);
$qdisplayfeedback->setLinkrefid($refid);
foreach ($this->incorrectFeedbacks as $ident) {
$qdisplayfeedback = new CcAssignmentDisplayfeedbacktype();
$qrespcondition->addDisplayfeedback($qdisplayfeedback);
$qdisplayfeedback->setFeedbacktype(CcQtiValues::RESPONSE);
$qdisplayfeedback->setLinkrefid($ident);
}
}
}
}

@ -0,0 +1,190 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentQuestionMultichoiceMultiresponse extends CcAssesmentQuestionProcBase
{
protected $correctAnswers = null;
public function __construct($quiz, $questions, $manifest, $section, $questionNode, $rootpath, $contextid, $outdir)
{
parent::__construct($quiz, $questions, $manifest, $section, $questionNode, $rootpath, $contextid, $outdir);
$this->qtype = CcQtiProfiletype::MULTIPLE_RESPONSE;
$correctAnswerNodes = [];
$questionScore = 0;
foreach ($questionNode->answers as $answer) {
if ($answer['correct'] > 0) {
$correctAnswerNodes[] = $answer;
$questionScore += $answer['ponderation'];
}
}
if (count($correctAnswerNodes) == 0) {
throw new RuntimeException('No correct answer!');
}
$this->correctAnswers = $correctAnswerNodes;
$this->totalGradeValue = $questionScore;
}
public function onGenerateAnswers()
{
//add responses holder
$qresponseLid = new CcResponseLidtype();
$this->qresponseLid = $qresponseLid;
$this->qpresentation->setResponseLid($qresponseLid);
$qresponseChoice = new CcAssesmentRenderChoicetype();
$qresponseLid->setRenderChoice($qresponseChoice);
//Mark that question has more than one correct answer
$qresponseLid->setRcardinality(CcQtiValues::MULTIPLE);
//are we to shuffle the responses?
$shuffleAnswers = $this->quiz['random_answers'] > 0;
$qresponseChoice->enableShuffle($shuffleAnswers);
$answerlist = [];
$qaResponses = $this->questionNode->answers;
foreach ($qaResponses as $node) {
$answerContent = $node['answer'];
$answerGradeFraction = $node['ponderation'];
$result = CcHelpers::processLinkedFiles($answerContent,
$this->manifest,
$this->rootpath,
$this->contextid,
$this->outdir);
$qresponseLabel = CcAssesmentHelper::addAnswer($qresponseChoice,
$result[0],
CcQtiValues::HTMLTYPE);
PkgResourceDependencies::instance()->add($result[1]);
$answerIdent = $qresponseLabel->getIdent();
$feedbackIdent = $answerIdent.'_fb';
//add answer specific feedbacks if not empty
$content = $node['comment'];
if (!empty($content)) {
$result = CcHelpers::processLinkedFiles($content,
$this->manifest,
$this->rootpath,
$this->contextid,
$this->outdir);
CcAssesmentHelper::addFeedback($this->qitem,
$result[0],
CcQtiValues::HTMLTYPE,
$feedbackIdent);
PkgResourceDependencies::instance()->add($result[1]);
}
$answerlist[$answerIdent] = [$feedbackIdent, ($answerGradeFraction > 0)];
}
$this->answerlist = $answerlist;
}
public function onGenerateFeedbacks()
{
parent::onGenerateFeedbacks();
//Question combined feedbacks
$correctQuestionFb = '';
$incorrectQuestionFb = '';
if (empty($correctQuestionFb)) {
//Hardcode some text for now
$correctQuestionFb = 'Well done!';
}
if (empty($incorrectQuestionFb)) {
//Hardcode some text for now
$incorrectQuestionFb = 'Better luck next time!';
}
$proc = ['correct_fb' => $correctQuestionFb, 'incorrect_fb' => $incorrectQuestionFb];
foreach ($proc as $ident => $content) {
if (empty($content)) {
continue;
}
$result = CcHelpers::processLinkedFiles($content,
$this->manifest,
$this->rootpath,
$this->contextid,
$this->outdir);
CcAssesmentHelper::addFeedback($this->qitem,
$result[0],
CcQtiValues::HTMLTYPE,
$ident);
PkgResourceDependencies::instance()->add($result[1]);
if ($ident == 'correct_fb') {
$this->correctFeedbacks[$ident] = $ident;
} else {
$this->incorrectFeedbacks[$ident] = $ident;
}
}
}
public function onGenerateResponseProcessing()
{
parent::onGenerateResponseProcessing();
//respconditions
/**
* General unconditional feedback must be added as a first respcondition
* without any condition and just displayfeedback (if exists).
*/
CcAssesmentHelper::addRespcondition($this->qresprocessing,
'General feedback',
$this->generalFeedback,
null,
true
);
$qrespcondition = new CcAssesmentRespconditiontype();
$qrespcondition->setTitle('Correct');
$this->qresprocessing->addRespcondition($qrespcondition);
$qrespcondition->enableContinue(false);
$qsetvar = new CcAssignmentSetvartype(100);
$qrespcondition->addSetvar($qsetvar);
//define the condition for success
$qconditionvar = new CcAssignmentConditionvar();
$qrespcondition->setConditionvar($qconditionvar);
//create root and condition
$qandcondition = new CcAssignmentConditionvarAndtype();
$qconditionvar->setAnd($qandcondition);
foreach ($this->answerlist as $ident => $refid) {
$qvarequal = new CcAssignmentConditionvarVarequaltype($ident);
$qvarequal->enableCase();
if ($refid[1]) {
$qandcondition->setVarequal($qvarequal);
} else {
$qandcondition->setNot($qvarequal);
}
$qvarequal->setRespident($this->qresponseLid->getIdent());
}
$qdisplayfeedback = new CcAssignmentDisplayfeedbacktype();
$qrespcondition->addDisplayfeedback($qdisplayfeedback);
$qdisplayfeedback->setFeedbacktype(CcQtiValues::RESPONSE);
//TODO: this needs to be fixed
reset($this->correctFeedbacks);
$ident = key($this->correctFeedbacks);
$qdisplayfeedback->setLinkrefid($ident);
//rest of the conditions
foreach ($this->answerlist as $ident => $refid) {
CcAssesmentHelper::addResponseCondition($this->qresprocessing,
'Incorrect feedback',
$refid[0],
$this->generalFeedback,
$this->qresponseLid->getIdent()
);
}
//Final element for incorrect feedback
reset($this->incorrectFeedbacks);
$ident = key($this->incorrectFeedbacks);
CcAssesmentHelper::addRespcondition($this->qresprocessing,
'Incorrect feedback',
$ident,
0
);
}
}

@ -0,0 +1,155 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentQuestionProcBase
{
protected $quiz = null;
protected $questions = null;
protected $manifest = null;
protected $section = null;
protected $questionNode = null;
protected $rootpath = null;
protected $contextid = null;
protected $outdir = null;
protected $qtype = null;
protected $qmetadata = null;
protected $qitem = null;
protected $qpresentation = null;
protected $qresponseLid = null;
protected $qresprocessing = null;
protected $correct_grade_value = null;
protected $correctAnswerNodeId = null;
protected $correctAnswerIdent = null;
protected $totalGradeValue = null;
protected $answerlist = null;
protected $generalFeedback = null;
protected $correctFeedbacks = [];
protected $incorrectFeedbacks = [];
/**
* @param XMLGenericDocument $questions
* @param cc_assesment_section $section
* @param object $question_node
* @param string $rootpath
* @param string $contextid
* @param string $outdir
*/
public function __construct(&$quiz, &$questions, CcManifest &$manifest, CcAssesmentSection &$section, &$questionNode, $rootpath, $contextid, $outdir)
{
$this->quiz = $quiz;
$this->questions = $questions;
$this->manifest = $manifest;
$this->section = $section;
$this->questionNode = $questionNode;
$this->rootpath = $rootpath;
$this->contextid = $contextid;
$this->outdir = $outdir;
$qitem = new CcAssesmentSectionItem();
$this->section->addItem($qitem);
$qitem->setTitle($questionNode->question);
$this->qitem = $qitem;
}
public function onGenerateMetadata()
{
if (empty($this->qmetadata)) {
$this->qmetadata = new CcQuestionMetadata($this->qtype);
//Get weighting value
$weightingValue = $this->questionNode->ponderation;
if ($weightingValue > 1) {
$this->qmetadata->setWeighting($weightingValue);
}
//Get category
$questionCategory = $this->questionNode->questionCategory;
if (!empty($questionCategory)) {
$this->qmetadata->setCategory($questionCategory);
}
$rts = new CcAssesmentItemmetadata();
$rts->addMetadata($this->qmetadata);
$this->qitem->setItemmetadata($rts);
}
}
public function onGeneratePresentation()
{
if (empty($this->qpresentation)) {
$qpresentation = new CcAssesmentPresentation();
$this->qitem->setPresentation($qpresentation);
//add question text
$qmaterial = new CcAssesmentMaterial();
$qmattext = new CcAssesmentMattext();
$questionText = $this->questionNode->question;
$result = CcHelpers::processLinkedFiles($questionText,
$this->manifest,
$this->rootpath,
$this->contextid,
$this->outdir);
$qmattext->setContent($result[0], CcQtiValues::HTMLTYPE);
$qmaterial->setMattext($qmattext);
$qpresentation->setMaterial($qmaterial);
$this->qpresentation = $qpresentation;
PkgResourceDependencies::instance()->add($result[1]);
}
}
public function onGenerateAnswers()
{
}
public function onGenerateFeedbacks()
{
$generalQuestionFeedback = '';
if (empty($generalQuestionFeedback)) {
return;
}
$name = 'general_fb';
//Add question general feedback - the one that should be always displayed
$result = CcHelpers::processLinkedFiles($generalQuestionFeedback,
$this->manifest,
$this->rootpath,
$this->contextid,
$this->outdir);
CcAssesmentHelper::addFeedback($this->qitem,
$result[0],
CcQtiValues::HTMLTYPE,
$name);
PkgResourceDependencies::instance()->add($result[1]);
$this->generalFeedback = $name;
}
public function onGenerateResponseProcessing()
{
$qresprocessing = new CcAssesmentResprocessingtype();
$this->qitem->addResprocessing($qresprocessing);
$qdecvar = new CcAssesmentDecvartype();
$qresprocessing->setDecvar($qdecvar);
//according to the Common Cartridge 1.1 Profile: Implementation document
//this should always be set to 0, 100 in case of question type that is not essay
$qdecvar->setLimits(0, 100);
$qdecvar->setVartype(CcQtiValues::DECIMAL);
$this->qresprocessing = $qresprocessing;
}
public function generate()
{
$this->onGenerateMetadata();
$this->onGeneratePresentation();
$this->onGenerateAnswers();
$this->onGenerateFeedbacks();
$this->onGenerateResponseProcessing();
}
}

@ -0,0 +1,78 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentRenderChoicetype extends CcQuestionMetadataBase
{
protected $materials = [];
protected $material_refs = [];
protected $responseLabels = [];
protected $flow_labels = [];
public function __construct()
{
$this->setSetting(CcQtiTags::SHUFFLE, CcQtiValues::NO);
$this->setSetting(CcQtiTags::MINNUMBER);
$this->setSetting(CcQtiTags::MAXNUMBER);
}
public function add_material(CcAssesmentMaterial $object)
{
$this->materials[] = $object;
}
public function add_material_ref(CcAssesmentResponseMatref $object)
{
$this->material_refs[] = $object;
}
public function addResponseLabel(CcAssesmentResponseLabeltype $object)
{
$this->responseLabels[] = $object;
}
public function add_flow_label($object)
{
$this->flow_labels[] = $object;
}
public function enableShuffle($value = true)
{
$this->enableSettingYesno(CcQtiTags::SHUFFLE, $value);
}
public function setLimits($min = null, $max = null)
{
$this->setSetting(CcQtiTags::MINNUMBER, $min);
$this->setSetting(CcQtiTags::MAXNUMBER, $max);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::RENDER_CHOICE);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->materials)) {
foreach ($this->materials as $mattag) {
$mattag->generate($doc, $node, $namespace);
}
}
if (!empty($this->material_refs)) {
foreach ($this->material_refs as $matreftag) {
$matreftag->generate($doc, $node, $namespace);
}
}
if (!empty($this->responseLabels)) {
foreach ($this->responseLabels as $resplabtag) {
$resplabtag->generate($doc, $node, $namespace);
}
}
if (!empty($this->flow_labels)) {
foreach ($this->flow_labels as $flowlabtag) {
$flowlabtag->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,106 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentRenderFibtype extends CcQuestionMetadataBase
{
protected $material = null;
protected $materialRef = null;
protected $responseLabel = null;
protected $flowLabel = null;
public function __construct()
{
$this->setSetting(CcQtiTags::ENCODING);
$this->setSetting(CcQtiTags::CHARSET);
$this->setSetting(CcQtiTags::ROWS);
$this->setSetting(CcQtiTags::COLUMNS);
$this->setSetting(CcQtiTags::MAXCHARS);
$this->setSetting(CcQtiTags::MINNUMBER);
$this->setSetting(CcQtiTags::MAXNUMBER);
$this->setSetting(CcQtiTags::PROMPT, CcQtiValues::BOX);
$this->setSetting(CcQtiTags::FIBTYPE, CcQtiValues::STRING);
}
public function setEncoding($value)
{
$this->setSetting(CcQtiTags::ENCODING, $value);
}
public function setCharset($value)
{
$this->setSetting(CcQtiTags::CHARSET, $value);
}
public function setRows($value)
{
$this->setSetting(CcQtiTags::ROWS, $value);
}
public function setColumns($value)
{
$this->setSetting(CcQtiTags::COLUMNS, $value);
}
public function setMaxchars($value)
{
$this->setSetting(CcQtiTags::COLUMNS, $value);
}
public function setLimits($min = null, $max = null)
{
$this->setSetting(CcQtiTags::MINNUMBER, $min);
$this->setSetting(CcQtiTags::MAXNUMBER, $max);
}
public function setPrompt($value)
{
$this->setSetting(CcQtiTags::PROMPT, $value);
}
public function setFibtype($value)
{
$this->setSetting(CcQtiTags::FIBTYPE, $value);
}
public function setMaterial(CcAssesmentMaterial $object)
{
$this->material = $object;
}
public function setMaterialRef(CcAssesmentResponseMatref $object)
{
$this->materialRef = $object;
}
public function setResponseLabel(CcAssesmentResponseLabeltype $object)
{
$this->responseLabel = $object;
}
public function setFlowLabel($object)
{
$this->flowLabel = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::RENDER_FIB);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->material) && empty($this->materialRef)) {
$this->material->generate($doc, $node, $namespace);
}
if (!empty($this->materialRef) && empty($this->material)) {
$this->materialRef->generate($doc, $node, $namespace);
}
if (!empty($this->responseLabel)) {
$this->responseLabel->generate($doc, $node, $namespace);
}
if (!empty($this->flowLabel)) {
$this->flowLabel->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,62 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentRespconditiontype extends CcQuestionMetadataBase
{
protected $conditionvar = null;
protected $setvar = [];
protected $displayfeedback = [];
public function __construct()
{
$this->setSetting(CcQtiTags::TITLE);
$this->setSetting(CcQtiTags::CONTINUE_, CcQtiValues::NO);
}
public function setTitle($value)
{
$this->setSetting(CcQtiTags::TITLE, $value);
}
public function enableContinue($value = true)
{
$this->enableSettingYesno(CcQtiTags::CONTINUE_, $value);
}
public function setConditionvar(CcAssignmentConditionvar $object)
{
$this->conditionvar = $object;
}
public function addSetvar(CcAssignmentSetvartype $object)
{
$this->setvar[] = $object;
}
public function addDisplayfeedback(CcAssignmentDisplayfeedbacktype $object)
{
$this->displayfeedback[] = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::RESPCONDITION);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->conditionvar)) {
$this->conditionvar->generate($doc, $node, $namespace);
}
if (!empty($this->setvar)) {
foreach ($this->setvar as $setvar) {
$setvar->generate($doc, $node, $namespace);
}
}
if (!empty($this->displayfeedback)) {
foreach ($this->displayfeedback as $displayfeedback) {
$displayfeedback->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,81 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentResponseLabeltype extends CcQuestionMetadataBase
{
protected $material = null;
protected $materialRef = null;
protected $flowMat = null;
public function __construct()
{
$this->setSetting(CcQtiTags::IDENT, CcHelpers::uuidgen('I_'));
$this->setSetting(CcQtiTags::LABELREFID);
$this->setSetting(CcQtiTags::RSHUFFLE);
$this->setSetting(CcQtiTags::MATCH_GROUP);
$this->setSetting(CcQtiTags::MATCH_MAX);
}
public function setIdent($value)
{
$this->setSetting(CcQtiTags::IDENT, $value);
}
public function getIdent()
{
return $this->getSetting(CcQtiTags::IDENT);
}
public function setLabelrefid($value)
{
$this->setSetting(CcQtiTags::LABELREFID, $value);
}
public function enableRshuffle($value = true)
{
$this->enableSettingYesno(CcQtiTags::RSHUFFLE, $value);
}
public function setMatchGroup($value)
{
$this->setSetting(CcQtiTags::MATCH_GROUP, $value);
}
public function setMatchMax($value)
{
$this->setSetting(CcQtiTags::MATCH_MAX, $value);
}
public function setMaterial(CcAssesmentMaterial $object)
{
$this->material = $object;
}
public function setMaterialRef(CcAssesmentResponseMatref $object)
{
$this->materialRef = $object;
}
public function setFlowMat(CcAssesmentFlowMattype $object)
{
$this->flowMat = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::RESPONSE_LABEL);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->material)) {
$this->material->generate($doc, $node, $namespace);
}
if (!empty($this->materialRef)) {
$this->materialRef->generate($doc, $node, $namespace);
}
if (!empty($this->flowMat)) {
$this->flowMat->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,11 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentResponseMatref extends CcAssesmentMatref
{
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::MATERIAL_REF);
$doc->appendNewAttributeNs($node, $namespace, CcQtiTags::LINKREFID, $this->linkref);
}
}

@ -0,0 +1,11 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentResponseStrtype extends CcResponseLidtype
{
public function __construct()
{
$rtt = parent::__construct();
$this->tagname = CcQtiTags::RESPONSE_STR;
}
}

@ -0,0 +1,32 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentResprocessingtype extends CcQuestionMetadataBase
{
protected $decvar = null;
protected $respconditions = [];
public function setDecvar(CcAssesmentDecvartype $object)
{
$this->decvar = $object;
}
public function addRespcondition(CcAssesmentRespconditiontype $object)
{
$this->respconditions[] = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::RESPROCESSING);
$outcomes = $doc->appendNewElementNs($node, $namespace, CcQtiTags::OUTCOMES);
if (!empty($this->decvar)) {
$this->decvar->generate($doc, $outcomes, $namespace);
}
if (!empty($this->respconditions)) {
foreach ($this->respconditions as $rcond) {
$rcond->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,20 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentRubricBase extends CcQuestionMetadataBase
{
protected $material = null;
public function setMaterial($object)
{
$this->material = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$rubric = $doc->appendNewElementNs($item, $namespace, CcQtiTags::RUBRIC);
if (!empty($this->material)) {
$this->material->generate($doc, $rubric, $namespace);
}
}
}

@ -0,0 +1,48 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentSection extends CcQuestionMetadataBase
{
/**
* @var array
*/
protected $items = [];
public function __construct()
{
$this->setSetting(CcQtiTags::IDENT, CcHelpers::uuidgen('I_'));
$this->setSetting(CcQtiTags::TITLE);
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML);
}
public function setIdent($value)
{
$this->setSetting(CcQtiTags::IDENT, $value);
}
public function setTitle($value)
{
$this->setSetting(CcQtiTags::TITLE, $value);
}
public function setLang($value)
{
$this->setSettingWns(CcQtiTags::XML_LANG, CcXmlNamespace::XML, $value);
}
public function addItem(CcAssesmentSectionItem $object)
{
$this->items[] = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::SECTION);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->items)) {
foreach ($this->items as $item) {
$item->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,56 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssesmentSectionItem extends CcAssesmentSection
{
protected $itemmetadata = null;
protected $presentation = null;
protected $resprocessing = [];
protected $itemfeedback = [];
public function setItemmetadata(CcAssesmentItemmetadata $object)
{
$this->itemmetadata = $object;
}
public function setPresentation(CcAssesmentPresentation $object)
{
$this->presentation = $object;
}
public function addResprocessing(CcAssesmentResprocessingtype $object)
{
$this->resprocessing[] = $object;
}
public function addItemfeedback(CcAssesmentItemfeedbacktype $object)
{
$this->itemfeedback[] = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::ITEM);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->itemmetadata)) {
$this->itemmetadata->generate($doc, $node, $namespace);
}
if (!empty($this->presentation)) {
$this->presentation->generate($doc, $node, $namespace);
}
if (!empty($this->resprocessing)) {
foreach ($this->resprocessing as $resprocessing) {
$resprocessing->generate($doc, $node, $namespace);
}
}
if (!empty($this->itemfeedback)) {
foreach ($this->itemfeedback as $itemfeedback) {
$itemfeedback->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,53 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssignmentConditionvar extends CcQuestionMetadataBase
{
protected $and = null;
protected $other = null;
protected $varequal = [];
protected $varsubstring = null;
public function setAnd(CcAssignmentConditionvarAndtype $object)
{
$this->and = $object;
}
public function setOther(CcAssignmentConditionvarOthertype $object)
{
$this->other = $object;
}
public function setVarequal(CcAssignmentConditionvarVarequaltype $object)
{
$this->varequal[] = $object;
}
public function setVarsubstring(CcAssignmentConditionvarVarsubstringtype $object)
{
$this->varsubstring = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::CONDITIONVAR);
if (!empty($this->and)) {
$this->and->generate($doc, $node, $namespace);
}
if (!empty($this->other)) {
$this->other->generate($doc, $node, $namespace);
}
if (!empty($this->varequal)) {
foreach ($this->varequal as $varequal) {
$varequal->generate($doc, $node, $namespace);
}
}
if (!empty($this->varsubstring)) {
$this->varsubstring->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,35 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssignmentConditionvarAndtype extends CcQuestionMetadataBase
{
protected $nots = [];
protected $varequals = [];
public function setNot(CcAssignmentConditionvarVarequaltype $object)
{
$this->nots[] = $object;
}
public function setVarequal(CcAssignmentConditionvarVarequaltype $object)
{
$this->varequals[] = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::AND_);
if (!empty($this->nots)) {
foreach ($this->nots as $notv) {
$not = $doc->appendNewElementNs($node, $namespace, CcQtiTags::NOT_);
$notv->generate($doc, $not, $namespace);
}
}
if (!empty($this->varequals)) {
foreach ($this->varequals as $varequal) {
$varequal->generate($doc, $node, $namespace);
}
}
}
}

@ -0,0 +1,10 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssignmentConditionvarOthertype extends CcQuestionMetadataBase
{
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$doc->appendNewElementNs($item, $namespace, CcQtiTags::OTHER);
}
}

@ -0,0 +1,35 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssignmentConditionvarVarequaltype extends CcQuestionMetadataBase
{
protected $tagname = null;
protected $answerid = null;
public function __construct($value = null)
{
if (is_null($value)) {
throw new InvalidArgumentException('Must not pass null!');
}
$this->answerid = $value;
$this->setSetting(CcQtiTags::RESPIDENT);
$this->setSetting(CcQtiTags::CASE_);
$this->tagname = CcQtiTags::VAREQUAL;
}
public function setRespident($value)
{
$this->setSetting(CcQtiTags::RESPIDENT, $value);
}
public function enableCase($value = true)
{
$this->enableSettingYesno(CcQtiTags::CASE_, $value);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, $this->tagname, $this->answerid);
$this->generateAttributes($doc, $node, $namespace);
}
}

@ -0,0 +1,11 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssignmentConditionvarVarsubstringtype extends CcAssignmentConditionvarVarequaltype
{
public function __construct($value)
{
parent::__construct($value);
$this->tagname = CcQtiTags::VARSUBSTRING;
}
}

@ -0,0 +1,27 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssignmentDisplayfeedbacktype extends CcQuestionMetadataBase
{
public function __construct()
{
$this->setSetting(CcQtiTags::FEEDBACKTYPE);
$this->setSetting(CcQtiTags::LINKREFID);
}
public function setFeedbacktype($value)
{
$this->setSetting(CcQtiTags::FEEDBACKTYPE, $value);
}
public function setLinkrefid($value)
{
$this->setSetting(CcQtiTags::LINKREFID, $value);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::DISPLAYFEEDBACK);
$this->generateAttributes($doc, $node, $namespace);
}
}

@ -0,0 +1,25 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcAssignmentSetvartype extends CcQuestionMetadataBase
{
protected $tagvalue = null;
public function __construct($tagvalue = 100)
{
$this->setSetting(CcQtiTags::VARNAME, CcQtiValues::SCORE);
$this->setSetting(CcQtiTags::ACTION, CcQtiValues::SET);
$this->tagvalue = $tagvalue;
}
public function setVarname($value)
{
$this->setSetting(CcQtiTags::VARNAME, $value);
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, CcQtiTags::SETVAR, $this->tagvalue);
$this->generateAttributes($doc, $node, $namespace);
}
}

@ -0,0 +1,22 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
abstract class CcQtiMetadata
{
// Assessment.
const QMD_ASSESSMENTTYPE = 'qmd_assessmenttype';
const QMD_SCORETYPE = 'qmd_scoretype';
const QMD_FEEDBACKPERMITTED = 'qmd_feedbackpermitted';
const QMD_HINTSPERMITTED = 'qmd_hintspermitted';
const QMD_SOLUTIONSPERMITTED = 'qmd_solutionspermitted';
const QMD_TIMELIMIT = 'qmd_timelimit';
const CC_ALLOW_LATE_SUBMISSION = 'cc_allow_late_submission';
const CC_MAXATTEMPTS = 'cc_maxattempts';
const CC_PROFILE = 'cc_profile';
// Items.
const CC_WEIGHTING = 'cc_weighting';
const QMD_SCORINGPERMITTED = 'qmd_scoringpermitted';
const QMD_COMPUTERSCORED = 'qmd_computerscored';
const CC_QUESTION_CATEGORY = 'cc_question_category';
}

@ -0,0 +1,32 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
abstract class CcQtiProfiletype
{
const MULTIPLE_CHOICE = 'cc.multiple_choice.v0p1';
const MULTIPLE_RESPONSE = 'cc.multiple_response.v0p1';
const TRUE_FALSE = 'cc.true_false.v0p1';
const FIELD_ENTRY = 'cc.fib.v0p1';
const PATTERN_MATCH = 'cc.pattern_match.v0p1';
const ESSAY = 'cc.essay.v0p1';
/**
* validates a profile value.
*
* @param string $value
*
* @return bool
*/
public static function valid($value)
{
static $verificationValues = [self::ESSAY,
self::FIELD_ENTRY,
self::MULTIPLE_CHOICE,
self::MULTIPLE_RESPONSE,
self::PATTERN_MATCH,
self::TRUE_FALSE,
];
return in_array($value, $verificationValues);
}
}

@ -0,0 +1,88 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
abstract class CcQtiTags
{
const QUESTESTINTEROP = 'questestinterop';
const ASSESSMENT = 'assessment';
const QTIMETADATA = 'qtimetadata';
const QTIMETADATAFIELD = 'qtimetadatafield';
const FIELDLABEL = 'fieldlabel';
const FIELDENTRY = 'fieldentry';
const SECTION = 'section';
const IDENT = 'ident';
const ITEM = 'item';
const TITLE = 'title';
const ITEMMETADATA = 'itemmetadata';
const PRESENTATION = 'presentation';
const MATERIAL = 'material';
const MATTEXT = 'mattext';
const MATREF = 'matref';
const MATBREAK = 'matbreak';
const TEXTTYPE = 'texttype';
const RESPONSE_LID = 'response_lid';
const RENDER_CHOICE = 'render_choice';
const RESPONSE_LABEL = 'response_label';
const RESPROCESSING = 'resprocessing';
const OUTCOMES = 'outcomes';
const DECVAR = 'decvar';
const RESPCONDITION = 'respcondition';
const CONDITIONVAR = 'conditionvar';
const OTHER = 'other';
const DISPLAYFEEDBACK = 'displayfeedback';
const MAXVALUE = 'maxvalue';
const MINVALUE = 'minvalue';
const VARNAME = 'varname';
const VARTYPE = 'vartype';
const CONTINUE_ = 'continue';
const FEEDBACKTYPE = 'feedbacktype';
const LINKREFID = 'linkrefid';
const VAREQUAL = 'varequal';
const RESPIDENT = 'respident';
const ITEMFEEDBACK = 'itemfeedback';
const FLOW_MAT = 'flow_mat';
const RCARDINALITY = 'rcardinality';
const CHARSET = 'charset';
const LABEL = 'label';
const URI = 'uri';
const WIDTH = 'width';
const HEIGHT = 'height';
const X0 = 'x0';
const Y0 = 'y0';
const XML_LANG = 'lang';
const XML_SPACE = 'space';
const RUBRIC = 'rubric';
const ALTMATERIAL = 'altmaterial';
const PRESENTATION_MATERIAL = 'presentation_material';
const T_CLASS = 'class';
const MATERIAL_REF = 'material_ref';
const RTIMING = 'rtiming';
const RENDER_FIB = 'render_fib';
const SHUFFLE = 'shuffle';
const MINNUMBER = 'minnumber';
const MAXNUMBER = 'maxnumber';
const ENCODING = 'encoding';
const MAXCHARS = 'maxchars';
const PROMPT = 'prompt';
const FIBTYPE = 'fibtype';
const ROWS = 'rows';
const COLUMNS = 'columns';
const LABELREFID = 'labelrefid';
const RSHUFFLE = 'rshuffle';
const MATCH_GROUP = 'match_group';
const MATCH_MAX = 'match_max';
const FLOW = 'flow';
const RESPONSE_STR = 'response_str';
const FLOW_LABEL = 'flow_label';
const SETVAR = 'setvar';
const ACTION = 'action';
const AND_ = 'and';
const NOT_ = 'not';
const CASE_ = 'case';
const VARSUBSTRING = 'varsubstring';
const HINT = 'hint';
const SOLUTION = 'solution';
const FEEDBACKSTYLE = 'feedbackstyle';
const SOLUTIONMATERIAL = 'solutionmaterial';
const HINTMATERIAL = 'hintmaterial';
}

@ -0,0 +1,31 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
abstract class CcQtiValues
{
const EXAM_PROFILE = 'cc.exam.v0p1';
const YES = 'Yes';
const NO = 'No';
const RESPONSE = 'Response';
const SOLUTION = 'Solution';
const HINT = 'Hint';
const EXAMINATION = 'Examination';
const PERCENTAGE = 'Percentage';
const UNLIMITED = 'unlimited';
const SINGLE = 'Single';
const MULTIPLE = 'Multiple';
const ORDERER = 'Ordered';
const ASTERISK = 'Asterisk';
const BOX = 'Box';
const DASHLINE = 'Dashline';
const UNDERLINE = 'Underline';
const DECIMAL = 'Decimal';
const INTEGER = 'Integer';
const SCIENTIFIC = 'Scientific';
const STRING = 'String';
const SCORE = 'SCORE';
const SET = 'Set';
const COMPLETE = 'Complete';
const TEXTTYPE = 'text/plain';
const HTMLTYPE = 'text/html';
}

@ -0,0 +1,44 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcQuestionMetadata extends CcQuestionMetadataBase
{
/**
* Constructs metadata.
*
* @param string $profile
*
* @throws InvalidArgumentException
*/
public function __construct($profile)
{
if (!CcQtiProfiletype::valid($profile)) {
throw new InvalidArgumentException('Invalid profile type!');
}
$this->setSetting(CcQtiMetadata::CC_PROFILE, $profile);
$this->setSetting(CcQtiMetadata::CC_QUESTION_CATEGORY);
$this->setSetting(CcQtiMetadata::CC_WEIGHTING);
$this->setSetting(CcQtiMetadata::QMD_SCORINGPERMITTED);
$this->setSetting(CcQtiMetadata::QMD_COMPUTERSCORED);
}
public function setCategory($value)
{
$this->setSetting(CcQtiMetadata::CC_QUESTION_CATEGORY, $value);
}
public function setWeighting($value)
{
$this->setSetting(CcQtiMetadata::CC_WEIGHTING, $value);
}
public function enableScoringpermitted($value = true)
{
$this->enableSettingYesno(CcQtiMetadata::QMD_SCORINGPERMITTED, $value);
}
public function enableComputerscored($value = true)
{
$this->enableSettingYesno(CcQtiMetadata::QMD_COMPUTERSCORED, $value);
}
}

@ -0,0 +1,86 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcQuestionMetadataBase
{
protected $metadata = [];
/**
* @param string $namespace
*/
public function generateAttributes(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
foreach ($this->metadata as $attribute => $value) {
if (!is_null($value)) {
if (!is_array($value)) {
$doc->appendNewAttributeNs($item, $namespace, $attribute, $value);
} else {
$ns = key($value);
$nval = current($value);
if (!is_null($nval)) {
$doc->appendNewAttributeNs($item, $ns, $attribute, $nval);
}
}
}
}
}
/**
* @param string $namespace
*/
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$qtimetadata = $doc->appendNewElementNs($item, $namespace, CcQtiTags::QTIMETADATA);
foreach ($this->metadata as $label => $entry) {
if (!is_null($entry)) {
$qtimetadatafield = $doc->appendNewElementNs($qtimetadata, $namespace, CcQtiTags::QTIMETADATAFIELD);
$doc->appendNewElementNs($qtimetadatafield, $namespace, CcQtiTags::FIELDLABEL, $label);
$doc->appendNewElementNs($qtimetadatafield, $namespace, CcQtiTags::FIELDENTRY, $entry);
}
}
}
/**
* @param string $setting
* @param mixed $value
*/
protected function setSetting($setting, $value = null)
{
$this->metadata[$setting] = $value;
}
/**
* @param string $setting
*
* @return mixed
*/
protected function getSetting($setting)
{
$result = null;
if (array_key_exists($setting, $this->metadata)) {
$result = $this->metadata[$setting];
}
return $result;
}
/**
* @param string $setting
* @param string $namespace
* @param string $value
*/
protected function setSettingWns($setting, $namespace, $value = null)
{
$this->metadata[$setting] = [$namespace => $value];
}
/**
* @param string $setting
* @param bool $value
*/
protected function enableSettingYesno($setting, $value = true)
{
$svalue = $value ? CcQtiValues::YES : CcQtiValues::NO;
$this->setSetting($setting, $svalue);
}
}

@ -0,0 +1,81 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
class CcResponseLidtype extends CcQuestionMetadataBase
{
protected $tagname = null;
protected $material = null;
protected $materialRef = null;
protected $renderChoice = null;
protected $renderFib = null;
public function __construct()
{
$this->setSetting(CcQtiTags::RCARDINALITY, CcQtiValues::SINGLE);
$this->setSetting(CcQtiTags::RTIMING);
$this->setSetting(CcQtiTags::IDENT, CcHelpers::uuidgen('I_'));
$this->tagname = CcQtiTags::RESPONSE_LID;
}
public function setRcardinality($value)
{
$this->setSetting(CcQtiTags::RCARDINALITY, $value);
}
public function enableRtiming($value = true)
{
$this->enableSettingYesno(CcQtiTags::RTIMING, $value);
}
public function setIdent($value)
{
$this->setSetting(CcQtiTags::IDENT, $value);
}
public function getIdent()
{
return $this->getSetting(CcQtiTags::IDENT);
}
public function setMaterialRef(CcAssesmentResponseMatref $object)
{
$this->materialRef = $object;
}
public function setMaterial(CcAssesmentMaterial $object)
{
$this->material = $object;
}
public function setRenderChoice(CcAssesmentRenderChoicetype $object)
{
$this->renderChoice = $object;
}
public function setRenderFib(CcAssesmentRenderFibtype $object)
{
$this->renderFib = $object;
}
public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace)
{
$node = $doc->appendNewElementNs($item, $namespace, $this->tagname);
$this->generateAttributes($doc, $node, $namespace);
if (!empty($this->material) && empty($this->materialRef)) {
$this->material->generate($doc, $node, $namespace);
}
if (!empty($this->materialRef) && empty($this->material)) {
$this->materialRef->generate($doc, $node, $namespace);
}
if (!empty($this->renderChoice) && empty($this->renderFib)) {
$this->renderChoice->generate($doc, $node, $namespace);
}
if (!empty($this->renderFib) && empty($this->renderChoice)) {
$this->renderFib->generate($doc, $node, $namespace);
}
}
}

@ -0,0 +1,7 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/cc_asssesment.php under GNU/GPL license */
abstract class CcXmlNamespace
{
const XML = 'http://www.w3.org/XML/1998/namespace';
}

@ -0,0 +1,236 @@
<?php
/* Source: https://github.com/moodle/moodle/blob/MOODLE_310_STABLE/backup/cc/cc_lib/gral_lib/ccdependencyparser.php under GNU/GPL license */
/**
* Converts \ Directory separator to the / more suitable for URL.
*
* @param string $path
*/
function toUrlPath(&$path)
{
for ($count = 0; $count < strlen($path); $count++) {
$chr = $path[$count];
if (($chr == '\\')) {
$path[$count] = '/';
}
}
}
/**
* Returns relative path from two directories with full path.
*
* @param string $path1
* @param string $path2
*
* @return string
*/
function pathDiff($path1, $path2)
{
toUrlPath($path1);
toUrlPath($path2);
$result = "";
$bl2 = strlen($path2);
$a = strpos($path1, $path2);
if ($a !== false) {
$result = trim(substr($path1, $bl2 + $a), '/');
}
return $result;
}
/**
* Converts direcotry separator in given path to / to validate in CC
* Value is passed byref hence variable itself is changed.
*
* @param string $path
*/
function toNativePath(&$path)
{
for ($count = 0; $count < strlen($path); $count++) {
$chr = $path[$count];
if (($chr == '\\') || ($chr == '/')) {
$path[$count] = '/';
}
}
}
/**
* Function strips url part from css link.
*
* @param string $path
* @param string $rootDir
*
* @return string
*/
function stripUrl($path, $rootDir = '')
{
$result = $path;
if (is_string($path) && ($path != '')) {
$start = strpos($path, '(') + 1;
$length = strpos($path, ')') - $start;
$rut = $rootDir.substr($path, $start, $length);
$result = fullPath($rut, '/');
}
return $result;
}
/**
* Get full path
*
* @param string $path
* @param string $dirsep
* @return false|string
*/
function fullPath($path, $dirsep = DIRECTORY_SEPARATOR)
{
$token = '$IMS-CC-FILEBASE$';
$path = str_replace($token, '', $path);
if (is_string($path) && ($path != '')) {
$sep = $dirsep;
$dotDir = '.';
$upDir = '..';
$length = strlen($path);
$rtemp = trim($path);
$start = strrpos($path, $sep);
$canContinue = ($start !== false);
$result = $canContinue ? '' : $path;
$rcount = 0;
while ($canContinue) {
$dirPart = ($start !== false) ? substr($rtemp, $start + 1, $length - $start) : $rtemp;
$canContinue = ($dirPart !== false);
if ($canContinue) {
if ($dirPart != $dotDir) {
if ($dirPart == $upDir) {
$rcount++;
} else {
if ($rcount > 0) {
$rcount--;
} else {
$result = ($result == '') ? $dirPart : $dirPart.$sep.$result;
}
}
}
$rtemp = substr($path, 0, $start);
$start = strrpos($rtemp, $sep);
$canContinue = (($start !== false) || (strlen($rtemp) > 0));
}
}
}
return $result;
}
/**
* validates URL.
*
* @param string $url
*
* @return bool
*/
function isUrl($url)
{
$result = filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED) !== false;
return $result;
}
/**
* Gets the dependency files of the $fname file
*
* @param string $manifestroot
* @param string $fname
* @param string $folder
* @param array $filenames
*/
function getDepFiles($manifestroot, $fname, $folder, &$filenames)
{
static $types = ['xhtml' => true, 'html' => true, 'htm' => true];
$extension = strtolower(trim(pathinfo($fname, PATHINFO_EXTENSION)));
$filenames = [];
if (isset($types[$extension])) {
$dcx = new XMLGenericDocument();
$filename = $manifestroot.$folder.$fname;
if (!file_exists($filename)) {
$filename = $manifestroot.DIRECTORY_SEPARATOR.$folder.DIRECTORY_SEPARATOR.$fname;
}
if (file_exists($filename)) {
$res = $dcx->loadHTMLFile($filename);
if ($res) {
getDepFilesHTML($manifestroot, $fname, $filenames, $dcx, $folder);
}
}
}
}
/**
* Gets the dependency of .html of the $fname file
*
* @param string $manifestroot
* @param string $fname
* @param string $filenames
* @param string $dcx
* @param string $folder
*/
function getDepFilesHTML($manifestroot, $fname, &$filenames, &$dcx, $folder)
{
$dcx->resetXpath();
$nlist = $dcx->nodeList("//img/@src | //link/@href | //script/@src | //a[not(starts-with(@href,'#'))]/@href");
$cssObjArray = [];
foreach ($nlist as $nl) {
$item = $folder.$nl->nodeValue;
$path_parts = pathinfo($item);
$fname = $path_parts['basename'];
$ext = array_key_exists('extension', $path_parts) ? $path_parts['extension'] : '';
if (!isUrl($folder.$nl->nodeValue) && !isUrl($nl->nodeValue)) {
$path = $folder.$nl->nodeValue;
$file = fullPath($path, "/");
toNativePath($file);
if (file_exists($manifestroot.DIRECTORY_SEPARATOR.$file)) {
$filenames[$file] = $file;
}
}
if ($ext == 'css') {
$css = new CssParser();
$css->parse($dcx->filePath().$nl->nodeValue);
$cssObjArray[$item] = $css;
}
}
$nlist = $dcx->nodeList("//*/@class");
foreach ($nlist as $nl) {
$item = $folder.$nl->nodeValue;
foreach ($cssObjArray as $csskey => $cssobj) {
$bimg = $cssobj->get($item, "background-image");
$limg = $cssobj->get($item, "list-style-image");
$npath = pathinfo($csskey);
if ((!empty($bimg)) && ($bimg != 'none')) {
$value = stripUrl($bimg, $npath['dirname'].'/');
$filenames[$value] = $value;
} elseif ((!empty($limg)) && ($limg != 'none')) {
$value = stripUrl($limg, $npath['dirname'].'/');
$filenames[$value] = $value;
}
}
}
$elemsToCheck = ["body", "p", "ul", "h4", "a", "th"];
$doWeHaveIt = [];
foreach ($elemsToCheck as $elem) {
$doWeHaveIt[$elem] = ($dcx->nodeList("//".$elem)->length > 0);
}
foreach ($elemsToCheck as $elem) {
if ($doWeHaveIt[$elem]) {
foreach ($cssObjArray as $csskey => $cssobj) {
$sb = $cssobj->get($elem, "background-image");
$sbl = $cssobj->get($elem, "list-style-image");
$npath = pathinfo($csskey);
if ((!empty($sb)) && ($sb != 'none')) {
$value = stripUrl($sb, $npath['dirname'].'/');
$filenames[$value] = $value;
} elseif ((!empty($sbl)) && ($sbl != 'none')) {
$value = stripUrl($sbl, $npath['dirname'].'/');
$filenames[$value] = $value;
}
}
}
}
}

@ -0,0 +1,283 @@
<?php
/* For licensing terms, see /license.txt */
abstract class CcHelpers
{
/**
* Checks extension of the supplied filename.
*
* @param string $filename
*/
public static function isHtml($filename)
{
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return in_array($extension, ['htm', 'html']);
}
/**
* Generates unique identifier.
*
* @param string $prefix
* @param string $suffix
*
* @return string
*/
public static function uuidgen($prefix = '', $suffix = '', $uppercase = true)
{
$uuid = trim(sprintf('%s%04x%04x%s', $prefix, mt_rand(0, 65535), mt_rand(0, 65535), $suffix));
$result = $uppercase ? strtoupper($uuid) : strtolower($uuid);
return $result;
}
/**
* Creates new folder with random name.
*
* @param string $where
* @param string $prefix
* @param string $suffix
*
* @return mixed - directory short name or false in case of failure
*/
public static function randomdir($where, $prefix = '', $suffix = '')
{
$permDirs = api_get_permissions_for_new_directories();
$dirname = false;
$randomname = self::uuidgen($prefix, $suffix, false);
$newdirname = $where.DIRECTORY_SEPARATOR.$randomname;
if (mkdir($newdirname)) {
chmod($newdirname, $permDirs);
$dirname = $randomname;
}
return $dirname;
}
public static function buildQuery($attributes, $search)
{
$result = '';
foreach ($attributes as $attribute) {
if ($result != '') {
$result .= ' | ';
}
$result .= "//*[starts-with(@{$attribute},'{$search}')]/@{$attribute}";
}
return $result;
}
public static function processEmbeddedFiles(&$doc, $attributes, $search, $customslash = null)
{
$result = [];
$query = self::buildQuery($attributes, $search);
$list = $doc->nodeList($query);
foreach ($list as $filelink) {
$rvalue = str_replace($search, '', $filelink->nodeValue);
if (!empty($customslash)) {
$rvalue = str_replace($customslash, '/', $rvalue);
}
$result[] = rawurldecode($rvalue);
}
return $result;
}
/**
* Get list of embedded files.
*
* @param string $html
*
* @return multitype:mixed
*/
public static function embeddedFiles($html)
{
$result = [];
$doc = new XMLGenericDocument();
$doc->doc->validateOnParse = false;
$doc->doc->strictErrorChecking = false;
if (!empty($html) && $doc->loadHTML($html)) {
$attributes = ['src', 'href'];
$result1 = self::processEmbeddedFiles($doc, $attributes, '@@PLUGINFILE@@');
$result2 = self::processEmbeddedFiles($doc, $attributes, '$@FILEPHP@$', '$@SLASH@$');
$result = array_merge($result1, $result2);
}
return $result;
}
public static function embeddedMapping($packageroot, $contextid = null, $folder = null, $docfilepath = null)
{
if (isset($folder)) {
$files = array_diff(scandir($folder), ['.', '..']);
} else {
$folder = dirname($docfilepath);
$files[] = basename($docfilepath);
}
$depfiles = [];
foreach ($files as $file) {
$mainfile = 1;
$filename = $file;
$filepath = DIRECTORY_SEPARATOR;
$source = '';
$author = '';
$license = '';
$hashedname = '';
$hashpart = '';
$location = $folder.DIRECTORY_SEPARATOR.$file;
$type = mime_content_type($file);
$depfiles[$filepath.$filename] = [$location,
($mainfile == 1),
strtolower(str_replace(' ', '_', $filename)),
$type,
$source,
$author,
$license,
strtolower(str_replace(' ', '_', $filepath)), ];
}
return $depfiles;
}
public static function addFiles(CcIManifest &$manifest, $packageroot, $outdir, $allinone = true, $folder = null, $docfilepath = null)
{
$permDirs = api_get_permissions_for_new_directories();
$files = CcHelpers::embeddedMapping($packageroot, null, $folder, $docfilepath);
$rdir = $allinone ? new CcResourceLocation($outdir) : null;
foreach ($files as $virtual => $values) {
$clean_filename = $values[2];
if (!$allinone) {
$rdir = new CcResourceLocation($outdir);
}
$rtp = $rdir->fullpath().$values[7].$clean_filename;
//Are there any relative virtual directories?
//let us try to recreate them
$justdir = $rdir->fullpath(false).$values[7];
if (!file_exists($justdir)) {
if (!mkdir($justdir, $permDirs, true)) {
throw new RuntimeException('Unable to create directories!');
}
}
$source = $values[0];
if (is_dir($source)) {
continue;
}
if (!copy($source, $rtp)) {
throw new RuntimeException('Unable to copy files!');
}
$resource = new CcResources($rdir->rootdir(),
$values[7].$clean_filename,
$rdir->dirname(false));
$res = $manifest->addResource($resource, null, CcVersion13::WEBCONTENT);
PkgStaticResources::instance()->add($virtual,
$res[0],
$rdir->dirname(false).$values[7].$clean_filename,
$values[1],
$resource);
}
PkgStaticResources::instance()->finished = true;
}
/**
* Excerpt from IMS CC 1.1 overview :
* No spaces in filenames, directory and file references should
* employ all lowercase or all uppercase - no mixed case.
*
* @param string $packageroot
* @param int $contextid
* @param string $outdir
* @param bool $allinone
*
* @throws RuntimeException
*/
public static function handleStaticContent(CcIManifest &$manifest, $packageroot, $contextid, $outdir, $allinone = true, $folder = null)
{
self::addFiles($manifest, $packageroot, $outdir, $allinone, $folder);
return PkgStaticResources::instance()->getValues();
}
public static function handleResourceContent(CcIManifest &$manifest, $packageroot, $contextid, $outdir, $allinone = true, $docfilepath = null)
{
$result = [];
self::addFiles($manifest, $packageroot, $outdir, $allinone, null, $docfilepath);
$files = self::embeddedMapping($packageroot, $contextid, null, $docfilepath);
$rootnode = null;
$rootvals = null;
$depfiles = [];
$depres = [];
$flocation = null;
foreach ($files as $virtual => $values) {
$vals = PkgStaticResources::instance()->getIdentifier($virtual);
$resource = $vals[3];
$identifier = $resource->identifier;
$flocation = $vals[1];
if ($values[1]) {
$rootnode = $resource;
$rootvals = $flocation;
continue;
}
$depres[] = $identifier;
$depfiles[] = $vals[1];
$result[$virtual] = [$identifier, $flocation, false];
}
if (!empty($rootnode)) {
$rootnode->files = array_merge($rootnode->files, $depfiles);
$result[$virtual] = [$rootnode->identifier, $rootvals, true];
}
return $result;
}
public static function processLinkedFiles($content, CcIManifest &$manifest, $packageroot,
$contextid, $outdir, $webcontent = false)
{
// Detect all embedded files
// copy all files in the cc package stripping any spaces and using only lowercase letters
// add those files as resources of the type webcontent to the manifest
// replace the links to the resource using $IMS-CC-FILEBASE$ and their new locations
// cc_resource has array of files and array of dependencies
// most likely we would need to add all files as independent resources and than
// attach them all as dependencies to the forum tag.
$lfiles = self::embeddedFiles($content);
$text = $content;
$deps = [];
if (!empty($lfiles)) {
$files = self::handleStaticContent($manifest,
$packageroot,
$contextid,
$outdir);
$replaceprefix = $webcontent ? '' : '$IMS-CC-FILEBASE$';
foreach ($lfiles as $lfile) {
if (isset($files[$lfile])) {
$filename = str_replace('%2F', '/', rawurlencode($lfile));
$content = str_replace('@@PLUGINFILE@@'.$filename,
$replaceprefix.'../'.$files[$lfile][1],
$content);
// For the legacy stuff.
$content = str_replace('$@FILEPHP@$'.str_replace('/', '$@SLASH@$', $filename),
$replaceprefix.'../'.$files[$lfile][1],
$content);
$deps[] = $files[$lfile][0];
}
}
$text = $content;
}
return [$text, $deps];
}
}

@ -0,0 +1,77 @@
<?php
/* For licensing terms, see /license.txt */
final class CcResourceLocation
{
/**
* Root directory.
*
* @var string
*/
private $rootdir = null;
/**
* new directory.
*
* @var string
*/
private $dir = null;
/**
* Full precalculated path.
*
* @var string
*/
private $fullpath = null;
/**
* ctor.
*
* @param string $rootdir - path to the containing directory
*
* @throws InvalidArgumentException
* @throws RuntimeException
*/
public function __construct($rootdir)
{
$rdir = realpath($rootdir);
if (empty($rdir)) {
throw new InvalidArgumentException('Invalid path!');
}
$dir = CcHelpers::randomdir($rdir, 'i_');
if ($dir === false) {
throw new RuntimeException('Unable to create directory!');
}
$this->rootdir = $rdir;
$this->dir = $dir;
$this->fullpath = $rdir.DIRECTORY_SEPARATOR.$dir;
}
/**
* Newly created directory.
*
* @return string
*/
public function dirname($endseparator = false)
{
return $this->dir.($endseparator ? '/' : '');
}
/**
* Full path to the new directory.
*
* @return string
*/
public function fullpath($endseparator = false)
{
return $this->fullpath.($endseparator ? DIRECTORY_SEPARATOR : '');
}
/**
* Returns containing dir.
*
* @return string
*/
public function rootdir($endseparator = false)
{
return $this->rootdir.($endseparator ? DIRECTORY_SEPARATOR : '');
}
}

@ -0,0 +1,39 @@
<?php
/* For licensing terms, see /license.txt */
class PkgResourceDependencies
{
private $values = [];
private static $instance = null;
/**
* @return PkgResourceDependencies
*/
public static function instance()
{
if (empty(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c();
}
return self::$instance;
}
public function add(array $deps)
{
$this->values = array_merge($this->values, $deps);
}
public function reset()
{
$this->values = [];
}
/**
* @return array
*/
public function getDeps()
{
return $this->values;
}
}

@ -0,0 +1,54 @@
<?php
/* For licensing terms, see /license.txt */
class PkgStaticResources
{
public $finished = false;
private $values = [];
private static $instance = null;
/**
* @return PkgStaticResources
*/
public static function instance()
{
if (empty(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c();
}
return self::$instance;
}
/**
* add new element.
*
* @param string $identifier
* @param string $file
* @param bool $main
*/
public function add($key, $identifier, $file, $main, $node = null)
{
$this->values[$key] = [$identifier, $file, $main, $node];
}
/**
* @return array
*/
public function getValues()
{
return $this->values;
}
public function getIdentifier($location)
{
return isset($this->values[$location]) ? $this->values[$location] : false;
}
public function reset()
{
$this->values = [];
$this->finished = false;
}
}

@ -0,0 +1,66 @@
<?php
/* For licensing terms, see /license.txt */
class Cc1p3Convert extends CcBase
{
const CC_TYPE_FORUM = 'imsdt_xmlv1p3';
const CC_TYPE_QUIZ = 'imsqti_xmlv1p3/imscc_xmlv1p3/assessment';
const CC_TYPE_QUESTION_BANK = 'imsqti_xmlv1p3/imscc_xmlv1p3/question-bank';
const CC_TYPE_WEBLINK = 'imswl_xmlv1p3';
const CC_TYPE_ASSOCIATED_CONTENT = 'associatedcontent/imscc_xmlv1p3/learning-application-resource';
const CC_TYPE_BASICLTI = 'imsbasiclti_xmlv1p3';
public static $namespaces = ['imscc' => 'http://www.imsglobal.org/xsd/imsccv1p3/imscp_v1p1',
'lomimscc' => 'http://ltsc.ieee.org/xsd/imsccv1p3/LOM/manifest',
'lom' => 'http://ltsc.ieee.org/xsd/imsccv1p3/LOM/resource',
'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
'cc' => 'http://www.imsglobal.org/xsd/imsccv1p3/imsccauth_v1p1', ];
public static $restypes = ['associatedcontent/imscc_xmlv1p3/learning-application-resource', 'webcontent'];
public static $forumns = ['dt' => 'http://www.imsglobal.org/xsd/imsccv1p3/imsdt_v1p3'];
public static $quizns = ['xmlns' => 'http://www.imsglobal.org/xsd/ims_qtiasiv1p2'];
public static $resourcens = ['wl' => 'http://www.imsglobal.org/xsd/imsccv1p3/imswl_v1p3'];
public static $basicltins = [
'xmlns' => 'http://www.imsglobal.org/xsd/imslticc_v1p0',
'blti' => 'http://www.imsglobal.org/xsd/imsbasiclti_v1p0',
'lticm' => 'http://www.imsglobal.org/xsd/imslticm_v1p0',
'lticp' => 'http://www.imsglobal.org/xsd/imslticp_v1p0',
];
public function __construct($path_to_manifest)
{
parent::__construct($path_to_manifest);
}
public function generateImportData()
{
$xpath = static::newxPath(static::$manifest, static::$namespaces);
$items = $xpath->query('/imscc:manifest/imscc:organizations/imscc:organization/imscc:item | /imscc:manifest/imscc:resources/imscc:resource[@type="'.static::CC_TYPE_QUESTION_BANK.'"]');
$this->createInstances($items);
$resources = new Cc13Resource();
$forums = new Cc13Forum();
$quiz = new Cc13Quiz();
$documentValues = $resources->generateData('document');
$linkValues = $resources->generateData('link');
$forumValues = $forums->generateData();
$quizValues = $quiz->generateData();
if (!empty($forums)) {
$saved = $forums->storeForums($forumValues);
}
if (!empty($quizValues)) {
$saved = $quiz->storeQuizzes($quizValues);
}
if (!empty($documentValues)) {
$saved = $resources->storeDocuments($documentValues, static::$pathToManifestFolder);
}
if (!empty($linkValues)) {
$saved = $resources->storeLinks($linkValues);
}
}
}

@ -0,0 +1,92 @@
<?php
/* For licensing terms, see /license.txt */
class Imscc13Import
{
const FORMAT_IMSCC13 = 'imscc13';
public function log($message, $level, $a = null, $depth = null, $display = false)
{
error_log("(imscc1) $message , level : $level , extra info: $a, message depth : $depth");
}
public static function detectFormat($filepath)
{
$manifest = Cc1p3Convert::getManifest($filepath);
if (file_exists($manifest)) {
// Looks promising, lets load some information.
$handle = fopen($manifest, 'r');
$xmlSnippet = fread($handle, 1024);
fclose($handle);
// Check if it has the required strings.
$xmlSnippet = strtolower($xmlSnippet);
$xmlSnippet = preg_replace('/\s*/m', '', $xmlSnippet);
$xmlSnippet = str_replace("'", '', $xmlSnippet);
$xmlSnippet = str_replace('"', '', $xmlSnippet);
$search_string = "xmlns=http://www.imsglobal.org/xsd/imsccv1p3/imscp_v1p1";
if (strpos($xmlSnippet, $search_string) !== false) {
return self::FORMAT_IMSCC13;
}
}
return null;
}
public function execute($filepath)
{
$manifest = Cc1p3Convert::getManifest($filepath);
if (empty($manifest)) {
throw new RuntimeException('No Manifest detected!');
}
$validator = new ManifestValidator('schemas13');
if (!$validator->validate($manifest)) {
throw new RuntimeException('validation error(s): '.PHP_EOL.ErrorMessages::instance());
}
$cc113Convert = new Cc1p3Convert($manifest);
if ($cc113Convert->isAuth()) {
throw new RuntimeException('protected_cc_not_supported');
}
$cc113Convert->generateImportData();
}
/**
* Unzip a file into the specified directory. Throws a RuntimeException
* if the extraction failed.
*/
public static function unzip($file, $to = 'cache/zip')
{
@ini_set('memory_limit', '256M');
if (!is_dir($to)) {
mkdir($to);
chmod($to, 0777);
}
if (class_exists('ZipArchive')) {
// use ZipArchive
$zip = new ZipArchive();
$res = $zip->open($file);
if ($res === true) {
$zip->extractTo($to);
$zip->close();
} else {
throw new RuntimeException('Could not open zip file [ZipArchive].');
}
} else {
// use PclZip
$zip = new PclZip($file);
if ($zip->extract(PCLZIP_OPT_PATH, $to) === 0) {
throw new RuntimeException('Could not extract zip file [PclZip].');
}
}
return true;
}
}

@ -0,0 +1,19 @@
<?xml version = "1.0" encoding = "UTF-8"?>
<xs:schema xmlns ="http://dummy.libxml2.validator"
targetNamespace ="http://dummy.libxml2.validator"
xmlns:xs ="http://www.w3.org/2001/XMLSchema"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:imscp ="http://www.imsglobal.org/xsd/imsccv1p3/imscp_v1p1"
xmlns:lomimscc ="http://ltsc.ieee.org/xsd/imsccv1p3/LOM/manifest"
xmlns:lom ="http://ltsc.ieee.org/xsd/imsccv1p3/LOM/resource"
xmlns:cc ="http://www.imsglobal.org/xsd/imsccv1p3/imsccauth_v1p3"
version="IMS CC 1.3 CP 1.2"
elementFormDefault ="qualified"
attributeFormDefault="unqualified"
>
<xs:import namespace="http://www.imsglobal.org/xsd/imsccv1p3/imscp_v1p1" schemaLocation="ccv1p3_imscp_v1p2_v1p0.xsd" />
<!--xs:import namespace="http://ltsc.ieee.org/xsd/imsccv1p3/LOM/manifest" schemaLocation="LOM/ccv1p3_lommanifest_v1p0.xsd"/-->
<!--xs:import namespace="http://ltsc.ieee.org/xsd/imsccv1p3/LOM/resource" schemaLocation="LOM/ccv1p3_lomresource_v1p0.xsd"/-->
<!--xs:import namespace="http://www.imsglobal.org/xsd/imsccv1p3/imsccauth_v1p3" schemaLocation="ccv1p3_imsccauth_v1p3.xsd" /-->
</xs:schema>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save