2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * @subpackage backup-imscc
19 * @copyright 2009 Mauro Rondinelli (mauro.rondinelli [AT] uvcms.com)
20 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');
25 require_once($CFG->dirroot
. '/backup/cc/entities.class.php');
26 require_once($CFG->dirroot
. '/backup/cc/entity.label.class.php');
27 require_once($CFG->dirroot
. '/backup/cc/entity.resource.class.php');
28 require_once($CFG->dirroot
. '/backup/cc/entity.forum.class.php');
29 require_once($CFG->dirroot
. '/backup/cc/entity.quiz.class.php');
33 const CC_TYPE_FORUM
= 'imsdt_xmlv1p0';
34 const CC_TYPE_QUIZ
= 'imsqti_xmlv1p2/imscc_xmlv1p0/assessment';
35 const CC_TYPE_QUESTION_BANK
= 'imsqti_xmlv1p2/imscc_xmlv1p0/question-bank';
36 const CC_TYPE_WEBLINK
= 'imswl_xmlv1p0';
37 const CC_TYPE_WEBCONTENT
= 'webcontent';
38 const CC_TYPE_ASSOCIATED_CONTENT
= 'associatedcontent/imscc_xmlv1p0/learning-application-resource';
39 const CC_TYPE_EMPTY
= '';
41 public static $restypes = array('associatedcontent/imscc_xmlv1p0/learning-application-resource', 'webcontent');
42 public static $forumns = array('dt' => 'http://www.imsglobal.org/xsd/imsdt_v1p0');
43 public static $quizns = array('xmlns' => 'http://www.imsglobal.org/xsd/ims_qtiasiv1p2');
44 public static $resourcens = array('wl' => 'http://www.imsglobal.org/xsd/imswl_v1p0');
49 public static function getquizns() {
50 return static::$quizns;
57 public static function getforumns() {
58 return static::$forumns;
65 public static function getresourcens() {
66 return static::$resourcens;
69 public static function get_manifest($folder) {
70 if (!is_dir($folder)) {
76 $dirIter = new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator
::KEY_AS_PATHNAME
);
77 $recIter = new RecursiveIteratorIterator($dirIter, RecursiveIteratorIterator
::CHILD_FIRST
);
78 foreach ($recIter as $info) {
79 if ($info->isFile() && ($info->getFilename() == 'imsmanifest.xml')) {
80 $result = $info->getPathname();
84 } catch (Exception
$e) {}
89 public static $instances = array();
90 public static $manifest;
91 public static $path_to_manifest_folder;
93 public static $namespaces = array('imscc' => 'http://www.imsglobal.org/xsd/imscc/imscp_v1p1',
94 'lomimscc' => 'http://ltsc.ieee.org/xsd/imscc/LOM',
95 'lom' => 'http://ltsc.ieee.org/xsd/LOM',
96 'voc' => 'http://ltsc.ieee.org/xsd/LOM/vocab',
97 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
98 'cc' => 'http://www.imsglobal.org/xsd/imsccauth_v1p0');
100 function __construct ($path_to_manifest) {
102 static::$manifest = new DOMDocument();
103 static::$manifest->validateOnParse
= false;
105 static::$path_to_manifest_folder = dirname($path_to_manifest);
107 static::log_action('Proccess start');
108 static::log_action('Load the manifest file: ' . $path_to_manifest);
110 if (!static::$manifest->load($path_to_manifest, LIBXML_NONET
)) {
111 static::log_action('Cannot load the manifest file: ' . $path_to_manifest, true);
115 public function is_auth () {
117 $xpath = static::newx_path(static::$manifest, static::$namespaces);
119 $count_auth = $xpath->evaluate('count(/imscc:manifest/cc:authorizations)');
121 if ($count_auth > 0) {
130 protected function get_metadata ($section, $key) {
132 $xpath = static::newx_path(static::$manifest, static::$namespaces);
134 $metadata = $xpath->query('/imscc:manifest/imscc:metadata/lomimscc:lom/lomimscc:' . $section . '/lomimscc:' . $key . '/lomimscc:string');
135 $value = !empty($metadata->item(0)->nodeValue
) ?
$metadata->item(0)->nodeValue
: '';
140 public function generate_moodle_xml () {
144 $cdir = static::$path_to_manifest_folder . DIRECTORY_SEPARATOR
. 'course_files';
146 if (!file_exists($cdir)) {
150 $sheet_base = static::loadsheet(SHEET_BASE
);
152 // MOODLE_BACKUP / INFO / DETAILS / MOD
153 $node_info_details_mod = $this->create_code_info_details_mod();
155 // MOODLE_BACKUP / BLOCKS / BLOCK
156 $node_course_blocks_block = $this->create_node_course_blocks_block();
158 // MOODLE_BACKUP / COURSES / SECTIONS / SECTION
159 $node_course_sections_section = $this->create_node_course_sections_section();
161 // MOODLE_BACKUP / COURSES / QUESTION_CATEGORIES
162 $node_course_question_categories = $this->create_node_question_categories();
164 // MOODLE_BACKUP / COURSES / MODULES / MOD
165 $node_course_modules_mod = $this->create_node_course_modules_mod();
167 // MOODLE_BACKUP / COURSE / HEADER
168 $node_course_header = $this->create_node_course_header();
171 $filename = optional_param('file', 'not_available.zip', PARAM_RAW
);
172 $filename = basename($filename);
174 $www_root = $CFG->wwwroot
;
176 $find_tags = array('[#zip_filename#]',
178 '[#node_course_header#]',
179 '[#node_info_details_mod#]',
180 '[#node_course_blocks_block#]',
181 '[#node_course_sections_section#]',
182 '[#node_course_question_categories#]',
183 '[#node_course_modules#]');
185 $replace_values = array($filename,
188 $node_info_details_mod,
189 $node_course_blocks_block,
190 $node_course_sections_section,
191 $node_course_question_categories,
192 $node_course_modules_mod);
194 $result_xml = str_replace($find_tags, $replace_values, $sheet_base);
196 // COPY RESOURSE FILES
197 $entities = new entities();
199 $entities->move_all_files();
201 if (array_key_exists("index", self
::$instances)) {
203 if (!file_put_contents(static::$path_to_manifest_folder . DIRECTORY_SEPARATOR
. 'moodle.xml', $result_xml)) {
204 static::log_action('Cannot save the moodle manifest file: ' . static::$path_to_tmp_folder . DIRECTORY_SEPARATOR
. 'moodle.xml', true);
211 notify('The course is empty');
212 static::log_action('The course is empty', false);
219 protected function get_sections_numbers ($instances) {
223 if (array_key_exists("index", $instances)) {
224 foreach ($instances["index"] as $instance) {
225 if ($instance["deep"] == ROOT_DEEP
) {
234 protected function create_node_course_header () {
236 $node_course_header = '';
237 $sheet_course_header = static::loadsheet(SHEET_COURSE_HEADER
);
239 $course_title = trim($this->get_metadata('general', 'title'));
240 $course_title = empty($course_title) ?
'Untitled Course' : $course_title;
241 $course_description = $this->get_metadata('general', 'description');
242 $section_count = $this->get_sections_numbers(static::$instances) - 1;
244 if ($section_count == -1) {
248 if (empty($course_title)) {
249 $this->log_action('The course title not found', true);
252 $course_short_name = $this->create_course_code($course_title);
254 $find_tags = array('[#course_name#]',
255 '[#course_short_name#]',
256 '[#course_description#]',
258 '[#section_count#]');
260 $replace_values = array(entities
::safexml($course_title),
261 entities
::safexml($course_short_name),
262 entities
::safexml($course_description),
266 $node_course_header = str_replace($find_tags, $replace_values, $sheet_course_header);
268 return $node_course_header;
271 protected function create_node_question_categories () {
273 $quiz = new cc_quiz();
275 static::log_action('Creating node: QUESTION_CATEGORIES');
277 $node_course_question_categories = $quiz->generate_node_question_categories();
279 return $node_course_question_categories;
282 protected function create_node_course_modules_mod () {
284 $labels = new cc_label();
285 $resources = new cc_resource();
286 $forums = new cc_forum();
287 $quiz = new cc_quiz();
289 static::log_action('Creating node: COURSE/MODULES/MOD');
292 $node_course_modules_mod_label = $labels->generate_node();
294 // RESOURCES (WEB CONTENT AND WEB LINK)
295 $node_course_modules_mod_resource = $resources->generate_node();
298 $node_course_modules_mod_forum = $forums->generate_node();
301 $node_course_modules_mod_quiz = $quiz->generate_node_course_modules_mod();
303 $node_course_modules = $node_course_modules_mod_label . $node_course_modules_mod_resource . $node_course_modules_mod_forum . $node_course_modules_mod_quiz;
305 return $node_course_modules;
309 protected function create_node_course_sections_section () {
311 static::log_action('Creating node: COURSE/SECTIONS/SECTION');
313 $node_course_sections_section = '';
314 $sheet_course_sections_section = static::loadsheet(SHEET_COURSE_SECTIONS_SECTION
);
316 $topics = $this->get_nodes_by_criteria('deep', ROOT_DEEP
);
320 if (!empty($topics)) {
322 foreach ($topics as $topic) {
325 $node_node_course_sections_section_mods_mod = $this->create_node_course_sections_section_mods_mod($topic['index']);
327 if ($topic['moodle_type'] == MOODLE_TYPE_LABEL
) {
329 $find_tags = array('[#section_id#]',
330 '[#section_number#]',
331 '[#section_summary#]',
332 '[#node_course_sections_section_mods_mod#]');
334 $replace_values = array($i,
337 $node_node_course_sections_section_mods_mod);
341 $find_tags = array('[#section_id#]',
342 '[#section_number#]',
343 '[#section_summary#]',
344 '[#node_course_sections_section_mods_mod#]');
346 $replace_values = array($i,
349 $node_node_course_sections_section_mods_mod);
353 $node_course_sections_section .= str_replace($find_tags, $replace_values, $sheet_course_sections_section);
358 return $node_course_sections_section;
361 protected function create_node_course_blocks_block () {
365 static::log_action('Creating node: COURSE/BLOCKS/BLOCK');
367 $sheet_course_blocks_block = static::loadsheet(SHEET_COURSE_BLOCKS_BLOCK
);
368 $node_course_blocks_block = '';
370 $format_config = $CFG->dirroot
. '/course/format/weeks/config.php';
372 if (@is_file
($format_config) && is_readable($format_config)) {
373 require ($format_config);
376 if (!empty($format['defaultblocks'])) {
377 $blocknames = $format['defaultblocks'];
379 if (!empty($CFG->defaultblocks
)) {
380 $blocknames = $CFG->defaultblocks
;
382 $blocknames = 'participants,activity_modules,search_forums,admin,course_list:news_items,calendar_upcoming,recent_activity';
386 $blocknames = explode(':', $blocknames);
387 $blocks_left = explode(',', $blocknames[0]);
388 $blocks_right = explode(',', $blocknames[1]);
390 $find_tags = array('[#block_id#]',
392 '[#block_position#]',
398 foreach ($blocks_left as $block) {
402 $replace_values = array($i,
407 $node_course_blocks_block .= str_replace($find_tags, $replace_values, $sheet_course_blocks_block);
412 foreach ($blocks_right as $block) {
417 $replace_values = array($i,
422 $node_course_blocks_block .= str_replace($find_tags, $replace_values, $sheet_course_blocks_block);
425 return $node_course_blocks_block;
429 protected function create_node_course_sections_section_mods_mod ($root_parent) {
431 $sheet_course_sections_section_mods_mod = static::loadsheet(SHEET_COURSE_SECTIONS_SECTION_MODS_MOD
);
432 $childs = $this->get_nodes_by_criteria('root_parent', $root_parent);
436 $node_course_sections_section_mods_mod = '';
438 foreach ($childs as $child) {
440 if ($child['moodle_type'] == MOODLE_TYPE_LABEL
) {
441 if ($child['index'] == $child['root_parent']) {
452 $indent = $child['deep'] - ROOT_DEEP
;
455 $indent = $indent - 1;
458 $find_tags = array('[#mod_id#]',
459 '[#mod_instance_id#]',
464 $replace_values = array($child['index'],
466 $child['moodle_type'],
470 $node_course_sections_section_mods_mod .= str_replace($find_tags, $replace_values, $sheet_course_sections_section_mods_mod);
474 $response = $node_course_sections_section_mods_mod;
484 public function get_nodes_by_criteria ($key, $value) {
488 if (array_key_exists('index', static::$instances)) {
489 foreach (static::$instances['index'] as $item) {
490 if ($item[$key] == $value) {
500 protected function create_code_info_details_mod () {
502 static::log_action('Creating node: INFO/DETAILS/MOD');
504 $xpath = static::newx_path(static::$manifest, static::$namespaces);
506 $items = $xpath->query('/imscc:manifest/imscc:organizations/imscc:organization/imscc:item | /imscc:manifest/imscc:resources/imscc:resource[@type="' . static::CC_TYPE_QUESTION_BANK
. '"]');
508 $this->create_instances($items);
510 $count_quiz = $this->count_instances(MOODLE_TYPE_QUIZ
);
511 $count_forum = $this->count_instances(MOODLE_TYPE_FORUM
);
512 $count_resource = $this->count_instances(MOODLE_TYPE_RESOURCE
);
513 $count_label = $this->count_instances(MOODLE_TYPE_LABEL
);
515 $sheet_info_details_mod_instances_instance = static::loadsheet(SHEET_INFO_DETAILS_MOD_INSTANCE
);
517 if ($count_resource > 0) {
518 $resource_instance = $this->create_mod_info_details_mod_instances_instance($sheet_info_details_mod_instances_instance, $count_resource, static::$instances['instances'][MOODLE_TYPE_RESOURCE
]);
520 if ($count_quiz > 0) {
521 $quiz_instance = $this->create_mod_info_details_mod_instances_instance($sheet_info_details_mod_instances_instance, $count_quiz, static::$instances['instances'][MOODLE_TYPE_QUIZ
]);
523 if ($count_forum > 0) {
524 $forum_instance = $this->create_mod_info_details_mod_instances_instance($sheet_info_details_mod_instances_instance, $count_forum, static::$instances['instances'][MOODLE_TYPE_FORUM
]);
526 if ($count_label > 0) {
527 $label_instance = $this->create_mod_info_details_mod_instances_instance($sheet_info_details_mod_instances_instance, $count_label, static::$instances['instances'][MOODLE_TYPE_LABEL
]);
530 $resource_mod = $count_resource ?
$this->create_mod_info_details_mod(MOODLE_TYPE_RESOURCE
, $resource_instance) : '';
531 $quiz_mod = $count_quiz ?
$this->create_mod_info_details_mod(MOODLE_TYPE_QUIZ
, $quiz_instance) : '';
532 $forum_mod = $count_forum ?
$this->create_mod_info_details_mod(MOODLE_TYPE_FORUM
, $forum_instance) : '';
533 $label_mod = $count_label ?
$this->create_mod_info_details_mod(MOODLE_TYPE_LABEL
, $label_instance) : '';
536 return $label_mod . $resource_mod . $quiz_mod . $forum_mod;
540 protected function create_mod_info_details_mod ($mod_type, $node_info_details_mod_instances_instance) {
542 $sheet_info_details_mod = static::loadsheet(SHEET_INFO_DETAILS_MOD
);
544 $find_tags = array('[#mod_type#]' ,'[#node_info_details_mod_instances_instance#]');
545 $replace_values = array($mod_type , $node_info_details_mod_instances_instance);
547 return str_replace($find_tags, $replace_values, $sheet_info_details_mod);
550 protected function create_mod_info_details_mod_instances_instance ($sheet, $instances_quantity, $instances) {
554 $find_tags = array('[#mod_instance_id#]',
556 '[#mod_user_info#]');
558 for ($i = 1; $i <= $instances_quantity; $i++
) {
560 $user_info = ($instances[$i - 1]['common_cartriedge_type'] == static::CC_TYPE_FORUM
) ?
'true' : 'false';
561 if ($instances[$i - 1]['common_cartriedge_type'] == static::CC_TYPE_EMPTY
) {
562 if ($instances[$i - 1]['deep'] <= ROOT_DEEP
) {
567 $replace_values = array($instances[$i - 1]['instance'],
568 entities
::safexml($instances[$i - 1]['title']),
571 $instance .= str_replace($find_tags, $replace_values, $sheet);
578 protected function create_instances ($items, $level = 0, &$array_index = 0, $index_root = 0) {
585 $xpath = self
::newx_path(static::$manifest, static::$namespaces);
587 foreach ($items as $item) {
591 if ($item->nodeName
== "item") {
593 $identifierref = $xpath->query('@identifierref', $item);
594 $identifierref = !empty($identifierref->item(0)->nodeValue
) ?
$identifierref->item(0)->nodeValue
: '';
596 $title = $xpath->query('imscc:title', $item);
597 $title = !empty($title->item(0)->nodeValue
) ?
$title->item(0)->nodeValue
: '';
599 $cc_type = $this->get_item_cc_type($identifierref);
600 $moodle_type = $this->convert_to_moodle_type($cc_type);
602 elseif ($item->nodeName
== "resource") {
604 $identifierref = $xpath->query('@identifier', $item);
605 $identifierref = !empty($identifierref->item(0)->nodeValue
) ?
$identifierref->item(0)->nodeValue
: '';
607 $cc_type = $this->get_item_cc_type($identifierref);
608 $moodle_type = $this->convert_to_moodle_type($cc_type);
610 $title = 'Quiz Bank ' . ($this->count_instances($moodle_type) +
1);
614 if ($level == ROOT_DEEP
) {
615 $index_root = $array_index;
618 static::$instances['index'][$array_index]['common_cartriedge_type'] = $cc_type;
619 static::$instances['index'][$array_index]['moodle_type'] = $moodle_type;
620 static::$instances['index'][$array_index]['title'] = $title ?
$title : '';
621 static::$instances['index'][$array_index]['root_parent'] = $index_root;
622 static::$instances['index'][$array_index]['index'] = $array_index;
623 static::$instances['index'][$array_index]['deep'] = $level;
624 static::$instances['index'][$array_index]['instance'] = $this->count_instances($moodle_type);
625 static::$instances['index'][$array_index]['resource_indentifier'] = $identifierref;
627 static::$instances['instances'][$moodle_type][] = array('title' => $title,
628 'instance' => static::$instances['index'][$array_index]['instance'],
629 'common_cartriedge_type' => $cc_type,
630 'resource_indentifier' => $identifierref,
633 $more_items = $xpath->query('imscc:item', $item);
635 if ($more_items->length
> 0) {
636 $this->create_instances($more_items, $level, $array_index, $index_root);
645 public function count_instances ($type) {
649 if (array_key_exists('index', static::$instances)) {
650 if (static::$instances['index'] && $type) {
652 foreach (static::$instances['index'] as $instance) {
653 if (!empty($instance['moodle_type'])) {
654 $types[] = $instance['moodle_type'];
658 $quantity_instances = array_count_values($types);
659 $quantity = array_key_exists($type, $quantity_instances) ?
$quantity_instances[$type] : 0;
666 public function convert_to_moodle_type ($cc_type) {
667 $type = TYPE_UNKNOWN
;
669 if ($cc_type == static::CC_TYPE_FORUM
) {
670 $type = MOODLE_TYPE_FORUM
;
673 if ($cc_type == static::CC_TYPE_QUIZ
) {
674 $type = MOODLE_TYPE_QUIZ
;
677 if ($cc_type == static::CC_TYPE_WEBLINK
) {
678 $type = MOODLE_TYPE_RESOURCE
;
681 if ($cc_type == static::CC_TYPE_WEBCONTENT
) {
682 $type = MOODLE_TYPE_RESOURCE
;
685 if ($cc_type == static::CC_TYPE_ASSOCIATED_CONTENT
) {
686 $type = MOODLE_TYPE_RESOURCE
;
689 if ($cc_type == static::CC_TYPE_QUESTION_BANK
) {
690 $type = MOODLE_TYPE_QUESTION_BANK
;
693 if ($cc_type == static::CC_TYPE_EMPTY
) {
694 $type = MOODLE_TYPE_LABEL
;
700 public function get_item_cc_type ($identifier) {
702 $xpath = static::newx_path(static::$manifest, static::$namespaces);
704 $nodes = $xpath->query('/imscc:manifest/imscc:resources/imscc:resource[@identifier="' . $identifier . '"]/@type');
706 if ($nodes && !empty($nodes->item(0)->nodeValue
)) {
707 return $nodes->item(0)->nodeValue
;
713 public static function newx_path (DOMDocument
$manifest, $namespaces = '') {
715 $xpath = new DOMXPath($manifest);
717 if (!empty($namespaces)) {
718 foreach ($namespaces as $prefix => $ns) {
719 if (!$xpath->registerNamespace($prefix, $ns)) {
720 static::log_action('Cannot register the namespace: ' . $prefix . ':' . $ns, true);
728 public static function loadsheet ($file) {
730 $content = (is_readable($file) && ($content = file_get_contents($file))) ?
$content : false;
732 static::log_action('Loading sheet: ' . $file);
735 static::log_action('Cannot load the xml sheet: ' . $file, true);
738 static::log_action('Load OK!');
743 public static function log_file() {
744 return static::$path_to_manifest_folder . DIRECTORY_SEPARATOR
. 'cc2moodle.log';
747 public static function log_action ($text, $critical_error = false) {
749 $full_message = strtoupper(date("j/n/Y g:i:s a")) . " - " . $text . "\r";
751 file_put_contents(static::log_file(), $full_message, FILE_APPEND
);
753 if ($critical_error) {
754 static::critical_error($text);
758 protected function critical_error ($text) {
760 $path_to_log = static::log_file();
765 <hr />A critical error has been found!
771 The process has been stopped. Please see the <a href="' . $path_to_log . '">log file</a> for more information.</p>
773 <p>Log: ' . $path_to_log . '</p>
783 protected function create_course_code ($title) {
784 //Making sure that text of the short name does not go over the DB limit.
785 //and leaving the space to add additional characters by the platform
786 $code = substr(strtoupper(str_replace(' ', '', trim($title))),0,94);