MDL-39172 Import of .dat files is broken in blackboard_six import format
[moodle.git] / question / format / blackboard_six / format.php
CommitLineData
03f5a0f8 1<?php
d3603157
TH
2// This file is part of Moodle - http://moodle.org/
3//
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.
8//
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.
13//
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/>.
16
41a89a07 17/**
cc0188c2 18 * Blackboard V5 and V6 question importer.
d3603157 19 *
cc0188c2 20 * @package qformat_blackboard_six
d3603157
TH
21 * @copyright 2005 Michael Penney
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41a89a07 23 */
03f5a0f8 24
a17b297d
TH
25defined('MOODLE_INTERNAL') || die();
26
cc0188c2
JMV
27require_once($CFG->libdir . '/xmlize.php');
28require_once($CFG->dirroot . '/question/format/blackboard_six/formatbase.php');
29require_once($CFG->dirroot . '/question/format/blackboard_six/formatqti.php');
30require_once($CFG->dirroot . '/question/format/blackboard_six/formatpool.php');
31
2ffd3701
JF
32class qformat_blackboard_six_file {
33 /** @var int type of file being imported, one of the constants FILETYPE_QTI or FILETYPE_POOL. */
34 public $filetype;
35 /** @var string the xml text */
36 public $text;
37 /** @var string path to path to root of image tree in unzipped archive. */
38 public $filebase = '';
39}
40
cc0188c2
JMV
41class qformat_blackboard_six extends qformat_blackboard_six_base {
42 /** @var int Blackboard assessment qti files were always imported by the blackboard_six plugin. */
43 const FILETYPE_QTI = 1;
44 /** @var int Blackboard question pool files were previously handled by the blackboard plugin. */
45 const FILETYPE_POOL = 2;
cc0188c2
JMV
46
47 public function get_filecontent($path) {
48 $fullpath = $this->tempdir . '/' . $path;
49 if (is_file($fullpath) && is_readable($fullpath)) {
50 return file_get_contents($fullpath);
51 }
52 return false;
53 }
54
cc0188c2
JMV
55 /**
56 * Return content of all files containing questions,
57 * as an array one element for each file found,
58 * For each file, the corresponding element is an array of lines.
59 * @param string filename name of file
60 * @return mixed contents array or false on failure
61 */
62 public function readdata($filename) {
4756e9c9
PS
63 global $CFG;
64
cc0188c2
JMV
65 // Find if we are importing a .dat file.
66 if (strtolower(pathinfo($filename, PATHINFO_EXTENSION)) == 'dat') {
67 if (!is_readable($filename)) {
68 $this->error(get_string('filenotreadable', 'error'));
03f5a0f8 69 return false;
70 }
2ffd3701
JF
71
72 $fileobj = new qformat_blackboard_six_file();
73
cc0188c2
JMV
74 // As we are not importing a .zip file,
75 // there is no imsmanifest, and it is not possible
76 // to parse it to find the file type.
77 // So we need to guess the file type by looking at the content.
78 // For now we will do that searching for a required tag.
79 // This is certainly not bullet-proof but works for all usual files.
2ffd3701 80 $fileobj->text = file_get_contents($filename);
d56837de 81 if (strpos($fileobj->text, '<questestinterop>')) {
2ffd3701 82 $fileobj->filetype = self::FILETYPE_QTI;
03f5a0f8 83 }
d56837de 84 if (strpos($fileobj->text, '<POOL>')) {
2ffd3701 85 $fileobj->filetype = self::FILETYPE_POOL;
03f5a0f8 86 }
cc0188c2 87 // In all other cases we are not able to handle this question file.
03f5a0f8 88
cc0188c2 89 // Readquestions is now expecting an array of strings.
2ffd3701 90 return array($fileobj);
03f5a0f8 91 }
cc0188c2
JMV
92 // We are importing a zip file.
93 // Create name for temporary directory.
94 $unique_code = time();
95 $this->tempdir = make_temp_directory('bbquiz_import/' . $unique_code);
96 if (is_readable($filename)) {
97 if (!copy($filename, $this->tempdir . '/bboard.zip')) {
98 $this->error(get_string('cannotcopybackup', 'question'));
99 fulldelete($this->tempdir);
100 return false;
03f5a0f8 101 }
cc0188c2
JMV
102 if (unzip_file($this->tempdir . '/bboard.zip', '', false)) {
103 $dom = new DomDocument();
03f5a0f8 104
cc0188c2
JMV
105 if (!$dom->load($this->tempdir . '/imsmanifest.xml')) {
106 $this->error(get_string('errormanifest', 'qformat_blackboard_six'));
107 fulldelete($this->tempdir);
03f5a0f8 108 return false;
109 }
03f5a0f8 110
cc0188c2 111 $xpath = new DOMXPath($dom);
aeb15530 112
cc0188c2
JMV
113 // We starts from the root element.
114 $query = '//resources/resource';
cc0188c2 115 $q_file = array();
aeb15530 116
cc0188c2
JMV
117 $examfiles = $xpath->query($query);
118 foreach ($examfiles as $examfile) {
2ffd3701
JF
119 $fileobj = new qformat_blackboard_six_file();
120
cc0188c2
JMV
121 if ($examfile->getAttribute('type') == 'assessment/x-bb-qti-test'
122 || $examfile->getAttribute('type') == 'assessment/x-bb-qti-pool') {
123
124 if ($content = $this->get_filecontent($examfile->getAttribute('bb:file'))) {
2ffd3701
JF
125 $fileobj->filetype = self::FILETYPE_QTI;
126 $fileobj->filebase = $this->tempdir;
127 $fileobj->text = $content;
128 $q_file[] = $fileobj;
03f5a0f8 129 }
130 }
cc0188c2
JMV
131 if ($examfile->getAttribute('type') == 'assessment/x-bb-pool') {
132 if ($examfile->getAttribute('baseurl')) {
2ffd3701 133 $fileobj->filebase = $this->tempdir. '/' . $examfile->getAttribute('baseurl');
03f5a0f8 134 }
cc0188c2 135 if ($content = $this->get_filecontent($examfile->getAttribute('file'))) {
2ffd3701
JF
136 $fileobj->filetype = self::FILETYPE_POOL;
137 $fileobj->text = $content;
138 $q_file[] = $fileobj;
03f5a0f8 139 }
03f5a0f8 140 }
141 }
03f5a0f8 142
cc0188c2
JMV
143 if ($q_file) {
144 return $q_file;
145 } else {
146 $this->error(get_string('cannotfindquestionfile', 'question'));
147 fulldelete($this->tempdir);
03f5a0f8 148 }
cc0188c2
JMV
149 } else {
150 $this->error(get_string('cannotunzip', 'question'));
151 fulldelete($this->temp_dir);
152 }
153 } else {
154 $this->error(get_string('cannotreaduploadfile', 'error'));
155 fulldelete($this->tempdir);
156 }
157 return false;
158 }
159
160 /**
2ffd3701
JF
161 * Parse the array of objects into an array of questions.
162 * Each object is the content of a .dat questions file.
cc0188c2
JMV
163 * This *could* burn memory - but it won't happen that much
164 * so fingers crossed!
2ffd3701 165 * @param array $lines array of qformat_blackboard_six_file objects for each input file.
cc0188c2
JMV
166 * @return array (of objects) question objects.
167 */
168 public function readquestions($lines) {
169
170 // Set up array to hold all our questions.
171 $questions = array();
03f5a0f8 172
2ffd3701
JF
173 // Each element of $lines is a qformat_blackboard_six_file object.
174 foreach ($lines as $fileobj) {
175 if ($fileobj->filetype == self::FILETYPE_QTI) {
176 $importer = new qformat_blackboard_six_qti();
177 } else if ($fileobj->filetype == self::FILETYPE_POOL) {
178 $importer = new qformat_blackboard_six_pool();
179 } else {
180 // In all other cases we are not able to import the file.
181 debugging('fileobj type not recognised', DEBUG_DEVELOPER);
182 continue;
183 }
184 $importer->set_filebase($fileobj->filebase);
185 $questions = array_merge($questions, $importer->readquestions($fileobj->text));
03f5a0f8 186 }
2ffd3701 187
cc0188c2 188 return $questions;
03f5a0f8 189 }
190}