| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 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 |   | 
        
           |  |  | 17 | /**
 | 
        
           |  |  | 18 |  * functions used by AICC packages.
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * @package    mod_scorm
 | 
        
           |  |  | 21 |  * @copyright 1999 onwards Roberto Pinna
 | 
        
           |  |  | 22 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 23 |  */
 | 
        
           |  |  | 24 |   | 
        
           |  |  | 25 | function scorm_add_time($a, $b) {
 | 
        
           |  |  | 26 |     $aes = explode(':', $a);
 | 
        
           |  |  | 27 |     $bes = explode(':', $b);
 | 
        
           |  |  | 28 |     $aseconds = explode('.', $aes[2]);
 | 
        
           |  |  | 29 |     $bseconds = explode('.', $bes[2]);
 | 
        
           |  |  | 30 |     $change = 0;
 | 
        
           |  |  | 31 |   | 
        
           |  |  | 32 |     $acents = 0;  // Cents.
 | 
        
           |  |  | 33 |     if (count($aseconds) > 1) {
 | 
        
           |  |  | 34 |         $acents = $aseconds[1];
 | 
        
           |  |  | 35 |     }
 | 
        
           |  |  | 36 |     $bcents = 0;
 | 
        
           |  |  | 37 |     if (count($bseconds) > 1) {
 | 
        
           |  |  | 38 |         $bcents = $bseconds[1];
 | 
        
           |  |  | 39 |     }
 | 
        
           |  |  | 40 |     $cents = $acents + $bcents;
 | 
        
           |  |  | 41 |     $change = floor($cents / 100);
 | 
        
           |  |  | 42 |     $cents = $cents - ($change * 100);
 | 
        
           |  |  | 43 |     if (floor($cents) < 10) {
 | 
        
           |  |  | 44 |         $cents = '0'. $cents;
 | 
        
           |  |  | 45 |     }
 | 
        
           |  |  | 46 |   | 
        
           |  |  | 47 |     $secs = $aseconds[0] + $bseconds[0] + $change;  // Seconds.
 | 
        
           |  |  | 48 |     $change = floor($secs / 60);
 | 
        
           |  |  | 49 |     $secs = $secs - ($change * 60);
 | 
        
           |  |  | 50 |     if (floor($secs) < 10) {
 | 
        
           |  |  | 51 |         $secs = '0'. $secs;
 | 
        
           |  |  | 52 |     }
 | 
        
           |  |  | 53 |   | 
        
           |  |  | 54 |     $mins = $aes[1] + $bes[1] + $change;   // Minutes.
 | 
        
           |  |  | 55 |     $change = floor($mins / 60);
 | 
        
           |  |  | 56 |     $mins = $mins - ($change * 60);
 | 
        
           |  |  | 57 |     if ($mins < 10) {
 | 
        
           |  |  | 58 |         $mins = '0' .  $mins;
 | 
        
           |  |  | 59 |     }
 | 
        
           |  |  | 60 |   | 
        
           |  |  | 61 |     $hours = $aes[0] + $bes[0] + $change;  // Hours.
 | 
        
           |  |  | 62 |     if ($hours < 10) {
 | 
        
           |  |  | 63 |         $hours = '0' . $hours;
 | 
        
           |  |  | 64 |     }
 | 
        
           |  |  | 65 |   | 
        
           |  |  | 66 |     if ($cents != '0') {
 | 
        
           |  |  | 67 |         return $hours . ":" . $mins . ":" . $secs . '.' . $cents;
 | 
        
           |  |  | 68 |     } else {
 | 
        
           |  |  | 69 |         return $hours . ":" . $mins . ":" . $secs;
 | 
        
           |  |  | 70 |     }
 | 
        
           |  |  | 71 | }
 | 
        
           |  |  | 72 |   | 
        
           |  |  | 73 | /**
 | 
        
           |  |  | 74 |  * Take the header row of an AICC definition file
 | 
        
           |  |  | 75 |  * and returns sequence of columns and a pointer to
 | 
        
           |  |  | 76 |  * the sco identifier column.
 | 
        
           |  |  | 77 |  *
 | 
        
           |  |  | 78 |  * @param string $row AICC header row
 | 
        
           |  |  | 79 |  * @param string $mastername AICC sco identifier column
 | 
        
           |  |  | 80 |  * @return mixed
 | 
        
           |  |  | 81 |  */
 | 
        
           |  |  | 82 | function scorm_get_aicc_columns($row, $mastername='system_id') {
 | 
        
           |  |  | 83 |     $tok = strtok(strtolower($row), "\",\n\r");
 | 
        
           |  |  | 84 |     $result = new stdClass();
 | 
        
           |  |  | 85 |     $result->columns = array();
 | 
        
           |  |  | 86 |     $result->mastercol = 0;
 | 
        
           |  |  | 87 |     $i = 0;
 | 
        
           |  |  | 88 |     while ($tok) {
 | 
        
           |  |  | 89 |         if ($tok != '') {
 | 
        
           |  |  | 90 |             $result->columns[] = $tok;
 | 
        
           |  |  | 91 |             if ($tok == $mastername) {
 | 
        
           |  |  | 92 |                 $result->mastercol = $i;
 | 
        
           |  |  | 93 |             }
 | 
        
           |  |  | 94 |             $i++;
 | 
        
           |  |  | 95 |         }
 | 
        
           |  |  | 96 |         $tok = strtok("\",\n\r");
 | 
        
           |  |  | 97 |     }
 | 
        
           |  |  | 98 |     return $result;
 | 
        
           |  |  | 99 | }
 | 
        
           |  |  | 100 |   | 
        
           |  |  | 101 | /**
 | 
        
           |  |  | 102 |  * Given a colums array return a string containing the regular
 | 
        
           |  |  | 103 |  * expression to match the columns in a text row.
 | 
        
           |  |  | 104 |  *
 | 
        
           |  |  | 105 |  * @param array $column The header columns
 | 
        
           |  |  | 106 |  * @param string $remodule The regular expression module for a single column
 | 
        
           |  |  | 107 |  * @return string
 | 
        
           |  |  | 108 |  */
 | 
        
           |  |  | 109 | function scorm_forge_cols_regexp($columns, $remodule='(".*")?,') {
 | 
        
           |  |  | 110 |     $regexp = '/^';
 | 
        
           |  |  | 111 |     foreach ($columns as $column) {
 | 
        
           |  |  | 112 |         $regexp .= $remodule;
 | 
        
           |  |  | 113 |     }
 | 
        
           |  |  | 114 |     $regexp = substr($regexp, 0, -1) . '/';
 | 
        
           |  |  | 115 |     return $regexp;
 | 
        
           |  |  | 116 | }
 | 
        
           |  |  | 117 |   | 
        
           |  |  | 118 | /**
 | 
        
           |  |  | 119 |  * Sets up AICC packages
 | 
        
           |  |  | 120 |  * Called whenever package changes
 | 
        
           |  |  | 121 |  * @param object $scorm instance - fields are updated and changes saved into database
 | 
        
           |  |  | 122 |  * @return bool
 | 
        
           |  |  | 123 |  */
 | 
        
           |  |  | 124 | function scorm_parse_aicc(&$scorm) {
 | 
        
           |  |  | 125 |     global $DB;
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 |     if ($scorm->scormtype == SCORM_TYPE_AICCURL) {
 | 
        
           |  |  | 128 |         return scorm_aicc_generate_simple_sco($scorm);
 | 
        
           |  |  | 129 |     }
 | 
        
           |  |  | 130 |     if (!isset($scorm->cmid)) {
 | 
        
           |  |  | 131 |         $cm = get_coursemodule_from_instance('scorm', $scorm->id);
 | 
        
           |  |  | 132 |         $scorm->cmid = $cm->id;
 | 
        
           |  |  | 133 |     }
 | 
        
           |  |  | 134 |     $context = context_module::instance($scorm->cmid);
 | 
        
           |  |  | 135 |   | 
        
           |  |  | 136 |     $fs = get_file_storage();
 | 
        
           |  |  | 137 |   | 
        
           |  |  | 138 |     $files = $fs->get_area_files($context->id, 'mod_scorm', 'content', 0, 'sortorder, itemid, filepath, filename', false);
 | 
        
           |  |  | 139 |   | 
        
           |  |  | 140 |     $version = 'AICC';
 | 
        
           |  |  | 141 |     $ids = array();
 | 
        
           |  |  | 142 |     $courses = array();
 | 
        
           |  |  | 143 |     $extaiccfiles = array('crs', 'des', 'au', 'cst', 'ort', 'pre', 'cmp');
 | 
        
           |  |  | 144 |   | 
        
           |  |  | 145 |     foreach ($files as $file) {
 | 
        
           |  |  | 146 |         $filename = $file->get_filename();
 | 
        
           |  |  | 147 |         $ext = substr($filename, strrpos($filename, '.'));
 | 
        
           |  |  | 148 |         $extension = strtolower(substr($ext, 1));
 | 
        
           |  |  | 149 |         if (in_array($extension, $extaiccfiles)) {
 | 
        
           |  |  | 150 |             $id = strtolower(basename($filename, $ext));
 | 
        
           |  |  | 151 |             if (!isset($ids[$id])) {
 | 
        
           |  |  | 152 |                 $ids[$id] = new stdClass();
 | 
        
           |  |  | 153 |             }
 | 
        
           |  |  | 154 |             $ids[$id]->$extension = $file;
 | 
        
           |  |  | 155 |         }
 | 
        
           |  |  | 156 |     }
 | 
        
           |  |  | 157 |   | 
        
           |  |  | 158 |     foreach ($ids as $courseid => $id) {
 | 
        
           |  |  | 159 |         if (!isset($courses[$courseid])) {
 | 
        
           |  |  | 160 |             $courses[$courseid] = new stdClass();
 | 
        
           |  |  | 161 |         }
 | 
        
           |  |  | 162 |         if (isset($id->crs)) {
 | 
        
           |  |  | 163 |             $contents = $id->crs->get_content();
 | 
        
           |  |  | 164 |             $rows = explode("\r\n", $contents);
 | 
        
           |  |  | 165 |             if (is_array($rows)) {
 | 
        
           |  |  | 166 |                 foreach ($rows as $row) {
 | 
        
           |  |  | 167 |                     if (preg_match("/^(.+)=(.+)$/", $row, $matches)) {
 | 
        
           |  |  | 168 |                         switch (strtolower(trim($matches[1]))) {
 | 
        
           |  |  | 169 |                             case 'course_id':
 | 
        
           |  |  | 170 |                                 $courses[$courseid]->id = trim($matches[2]);
 | 
        
           |  |  | 171 |                             break;
 | 
        
           |  |  | 172 |                             case 'course_title':
 | 
        
           |  |  | 173 |                                 $courses[$courseid]->title = trim($matches[2]);
 | 
        
           |  |  | 174 |                             break;
 | 
        
           |  |  | 175 |                             case 'version':
 | 
        
           |  |  | 176 |                                 $courses[$courseid]->version = 'AICC_'.trim($matches[2]);
 | 
        
           |  |  | 177 |                             break;
 | 
        
           |  |  | 178 |                         }
 | 
        
           |  |  | 179 |                     }
 | 
        
           |  |  | 180 |                 }
 | 
        
           |  |  | 181 |             }
 | 
        
           |  |  | 182 |         }
 | 
        
           |  |  | 183 |         if (isset($id->des)) {
 | 
        
           |  |  | 184 |             $contents = $id->des->get_content();
 | 
        
           |  |  | 185 |             $rows = explode("\r\n", $contents);
 | 
        
           |  |  | 186 |             $columns = scorm_get_aicc_columns($rows[0]);
 | 
        
           |  |  | 187 |             $regexp = scorm_forge_cols_regexp($columns->columns);
 | 
        
           |  |  | 188 |             for ($i = 1; $i < count($rows); $i++) {
 | 
        
           |  |  | 189 |                 if (preg_match($regexp, $rows[$i], $matches)) {
 | 
        
           |  |  | 190 |                     for ($j = 0; $j < count($columns->columns); $j++) {
 | 
        
           |  |  | 191 |                         $column = $columns->columns[$j];
 | 
        
           |  |  | 192 |                         if (!isset($courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)])) {
 | 
        
           |  |  | 193 |                             $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)] = new stdClass();
 | 
        
           |  |  | 194 |                         }
 | 
        
           |  |  | 195 |                         $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1 , -1)]->$column = substr(trim($matches[$j + 1]), 1, -1);
 | 
        
           |  |  | 196 |                     }
 | 
        
           |  |  | 197 |                 }
 | 
        
           |  |  | 198 |             }
 | 
        
           |  |  | 199 |         }
 | 
        
           |  |  | 200 |         if (isset($id->au)) {
 | 
        
           |  |  | 201 |             $contents = $id->au->get_content();
 | 
        
           |  |  | 202 |             $rows = explode("\r\n", $contents);
 | 
        
           |  |  | 203 |             $columns = scorm_get_aicc_columns($rows[0]);
 | 
        
           |  |  | 204 |             $regexp = scorm_forge_cols_regexp($columns->columns);
 | 
        
           |  |  | 205 |             for ($i = 1; $i < count($rows); $i++) {
 | 
        
           |  |  | 206 |                 if (preg_match($regexp, $rows[$i], $matches)) {
 | 
        
           |  |  | 207 |                     for ($j = 0; $j < count($columns->columns); $j++) {
 | 
        
           |  |  | 208 |                         $column = $columns->columns[$j];
 | 
        
           |  |  | 209 |                         $courses[$courseid]->elements[substr(trim($matches[$columns->mastercol + 1]), 1, -1)]->$column = substr(trim($matches[$j + 1]), 1, -1);
 | 
        
           |  |  | 210 |                     }
 | 
        
           |  |  | 211 |                 }
 | 
        
           |  |  | 212 |             }
 | 
        
           |  |  | 213 |         }
 | 
        
           |  |  | 214 |         if (isset($id->cst)) {
 | 
        
           |  |  | 215 |             $contents = $id->cst->get_content();
 | 
        
           |  |  | 216 |             $rows = explode("\r\n", $contents);
 | 
        
           |  |  | 217 |             $columns = scorm_get_aicc_columns($rows[0], 'block');
 | 
        
           |  |  | 218 |             $regexp = scorm_forge_cols_regexp($columns->columns, '(.+)?,');
 | 
        
           |  |  | 219 |             for ($i = 1; $i < count($rows); $i++) {
 | 
        
           |  |  | 220 |                 if (preg_match($regexp, $rows[$i], $matches)) {
 | 
        
           |  |  | 221 |                     for ($j = 0; $j < count($columns->columns); $j++) {
 | 
        
           |  |  | 222 |                         if ($j != $columns->mastercol) {
 | 
        
           |  |  | 223 |                             $element = substr(trim($matches[$j + 1]), 1 , -1);
 | 
        
           |  |  | 224 |                             if (!empty($element)) {
 | 
        
           |  |  | 225 |                                 $courses[$courseid]->elements[$element]->parent = substr(trim($matches[$columns->mastercol + 1]), 1, -1);
 | 
        
           |  |  | 226 |                             }
 | 
        
           |  |  | 227 |                         }
 | 
        
           |  |  | 228 |                     }
 | 
        
           |  |  | 229 |                 }
 | 
        
           |  |  | 230 |             }
 | 
        
           |  |  | 231 |         }
 | 
        
           |  |  | 232 |         if (isset($id->ort)) {
 | 
        
           |  |  | 233 |             $contents = $id->ort->get_content();
 | 
        
           |  |  | 234 |             $rows = explode("\r\n", $contents);
 | 
        
           |  |  | 235 |             $columns = scorm_get_aicc_columns($rows[0], 'course_element');
 | 
        
           |  |  | 236 |             $regexp = scorm_forge_cols_regexp($columns->columns, '(.+)?,');
 | 
        
           |  |  | 237 |             for ($i = 1; $i < count($rows); $i++) {
 | 
        
           |  |  | 238 |                 if (preg_match($regexp, $rows[$i], $matches)) {
 | 
        
           |  |  | 239 |                     for ($j = 0; $j < count($matches) - 1; $j++) {
 | 
        
           |  |  | 240 |                         if ($j != $columns->mastercol) {
 | 
        
           |  |  | 241 |                             $courses[$courseid]->elements[substr(trim($matches[$j + 1]), 1, -1)]->parent = substr(trim($matches[$columns->mastercol + 1]), 1, -1);
 | 
        
           |  |  | 242 |                         }
 | 
        
           |  |  | 243 |                     }
 | 
        
           |  |  | 244 |                 }
 | 
        
           |  |  | 245 |             }
 | 
        
           |  |  | 246 |         }
 | 
        
           |  |  | 247 |         if (isset($id->pre)) {
 | 
        
           |  |  | 248 |             $contents = $id->pre->get_content();
 | 
        
           |  |  | 249 |             $rows = explode("\r\n", $contents);
 | 
        
           |  |  | 250 |             $columns = scorm_get_aicc_columns($rows[0], 'structure_element');
 | 
        
           |  |  | 251 |             $regexp = scorm_forge_cols_regexp($columns->columns, '(.+),');
 | 
        
           |  |  | 252 |             for ($i = 1; $i < count($rows); $i++) {
 | 
        
           |  |  | 253 |                 if (preg_match($regexp, $rows[$i], $matches)) {
 | 
        
           |  |  | 254 |                     $elementid = trim($matches[$columns->mastercol + 1]);
 | 
        
           |  |  | 255 |                     $elementid = trim(trim($elementid, '"'), "'"); // Remove any quotes.
 | 
        
           |  |  | 256 |   | 
        
           |  |  | 257 |                     $prereq = trim($matches[2 - $columns->mastercol]);
 | 
        
           |  |  | 258 |                     $prereq = trim(trim($prereq, '"'), "'"); // Remove any quotes.
 | 
        
           |  |  | 259 |   | 
        
           |  |  | 260 |                     $courses[$courseid]->elements[$elementid]->prerequisites = $prereq;
 | 
        
           |  |  | 261 |                 }
 | 
        
           |  |  | 262 |             }
 | 
        
           |  |  | 263 |         }
 | 
        
           |  |  | 264 |         if (isset($id->cmp)) {
 | 
        
           |  |  | 265 |             $contents = $id->cmp->get_content();
 | 
        
           |  |  | 266 |             $rows = explode("\r\n", $contents);
 | 
        
           |  |  | 267 |         }
 | 
        
           |  |  | 268 |     }
 | 
        
           |  |  | 269 |   | 
        
           |  |  | 270 |     $oldscoes = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id));
 | 
        
           |  |  | 271 |     $sortorder = 0;
 | 
        
           |  |  | 272 |     $launch = 0;
 | 
        
           |  |  | 273 |     if (isset($courses)) {
 | 
        
           |  |  | 274 |         foreach ($courses as $course) {
 | 
        
           |  |  | 275 |             $sortorder++;
 | 
        
           |  |  | 276 |             $sco = new stdClass();
 | 
        
           |  |  | 277 |             $sco->identifier = $course->id;
 | 
        
           |  |  | 278 |             $sco->scorm = $scorm->id;
 | 
        
           |  |  | 279 |             $sco->organization = '';
 | 
        
           |  |  | 280 |             $sco->title = $course->title;
 | 
        
           |  |  | 281 |             $sco->parent = '/';
 | 
        
           |  |  | 282 |             $sco->launch = '';
 | 
        
           |  |  | 283 |             $sco->scormtype = '';
 | 
        
           |  |  | 284 |             $sco->sortorder = $sortorder;
 | 
        
           |  |  | 285 |   | 
        
           |  |  | 286 |             if ($ss = $DB->get_record('scorm_scoes', array('scorm' => $scorm->id,
 | 
        
           |  |  | 287 |                                                            'identifier' => $sco->identifier))) {
 | 
        
           |  |  | 288 |                 $id = $ss->id;
 | 
        
           |  |  | 289 |                 $sco->id = $id;
 | 
        
           |  |  | 290 |                 $DB->update_record('scorm_scoes', $sco);
 | 
        
           |  |  | 291 |                 unset($oldscoes[$id]);
 | 
        
           |  |  | 292 |             } else {
 | 
        
           |  |  | 293 |                 $id = $DB->insert_record('scorm_scoes', $sco);
 | 
        
           |  |  | 294 |             }
 | 
        
           |  |  | 295 |   | 
        
           |  |  | 296 |             if ($launch == 0) {
 | 
        
           |  |  | 297 |                 $launch = $id;
 | 
        
           |  |  | 298 |             }
 | 
        
           |  |  | 299 |             if (isset($course->elements)) {
 | 
        
           |  |  | 300 |                 foreach ($course->elements as $element) {
 | 
        
           |  |  | 301 |                     unset($sco);
 | 
        
           |  |  | 302 |                     $sco = new stdClass();
 | 
        
           |  |  | 303 |                     $sco->identifier = $element->system_id;
 | 
        
           |  |  | 304 |                     $sco->scorm = $scorm->id;
 | 
        
           |  |  | 305 |                     $sco->organization = $course->id;
 | 
        
           |  |  | 306 |                     $sco->title = $element->title;
 | 
        
           |  |  | 307 |   | 
        
           |  |  | 308 |                     if (!isset($element->parent)) {
 | 
        
           |  |  | 309 |                         $sco->parent = '/';
 | 
        
           |  |  | 310 |                     } else if (strtolower($element->parent) == 'root') {
 | 
        
           |  |  | 311 |                         $sco->parent = $course->id;
 | 
        
           |  |  | 312 |                     } else {
 | 
        
           |  |  | 313 |                         $sco->parent = $element->parent;
 | 
        
           |  |  | 314 |                     }
 | 
        
           |  |  | 315 |                     $sco->launch = '';
 | 
        
           |  |  | 316 |                     $sco->scormtype = '';
 | 
        
           |  |  | 317 |                     $sco->previous = 0;
 | 
        
           |  |  | 318 |                     $sco->next = 0;
 | 
        
           |  |  | 319 |                     $id = null;
 | 
        
           |  |  | 320 |                     // Is it an Assignable Unit (AU)?
 | 
        
           |  |  | 321 |                     if (isset($element->file_name)) {
 | 
        
           |  |  | 322 |                         $sco->launch = $element->file_name;
 | 
        
           |  |  | 323 |                         $sco->scormtype = 'sco';
 | 
        
           |  |  | 324 |                     }
 | 
        
           |  |  | 325 |                     if ($oldscoid = scorm_array_search('identifier', $sco->identifier, $oldscoes)) {
 | 
        
           |  |  | 326 |                         $sco->id = $oldscoid;
 | 
        
           |  |  | 327 |                         $DB->update_record('scorm_scoes', $sco);
 | 
        
           |  |  | 328 |                         $id = $oldscoid;
 | 
        
           |  |  | 329 |                         $DB->delete_records('scorm_scoes_data', array('scoid' => $oldscoid));
 | 
        
           |  |  | 330 |                         unset($oldscoes[$oldscoid]);
 | 
        
           |  |  | 331 |                     } else {
 | 
        
           |  |  | 332 |                         $id = $DB->insert_record('scorm_scoes', $sco);
 | 
        
           |  |  | 333 |                     }
 | 
        
           |  |  | 334 |                     if (!empty($id)) {
 | 
        
           |  |  | 335 |                         $scodata = new stdClass();
 | 
        
           |  |  | 336 |                         $scodata->scoid = $id;
 | 
        
           |  |  | 337 |                         if (isset($element->web_launch)) {
 | 
        
           |  |  | 338 |                             $scodata->name = 'parameters';
 | 
        
           |  |  | 339 |                             $scodata->value = $element->web_launch;
 | 
        
           |  |  | 340 |                             $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 | 
        
           |  |  | 341 |                         }
 | 
        
           |  |  | 342 |                         if (isset($element->prerequisites)) {
 | 
        
           |  |  | 343 |                             $scodata->name = 'prerequisites';
 | 
        
           |  |  | 344 |                             $scodata->value = $element->prerequisites;
 | 
        
           |  |  | 345 |                             $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 | 
        
           |  |  | 346 |                         }
 | 
        
           |  |  | 347 |                         if (isset($element->max_time_allowed)) {
 | 
        
           |  |  | 348 |                             $scodata->name = 'max_time_allowed';
 | 
        
           |  |  | 349 |                             $scodata->value = $element->max_time_allowed;
 | 
        
           |  |  | 350 |                             $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 | 
        
           |  |  | 351 |                         }
 | 
        
           |  |  | 352 |                         if (isset($element->time_limit_action)) {
 | 
        
           |  |  | 353 |                             $scodata->name = 'time_limit_action';
 | 
        
           |  |  | 354 |                             $scodata->value = $element->time_limit_action;
 | 
        
           |  |  | 355 |                             $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 | 
        
           |  |  | 356 |                         }
 | 
        
           |  |  | 357 |                         if (isset($element->mastery_score)) {
 | 
        
           |  |  | 358 |                             $scodata->name = 'mastery_score';
 | 
        
           |  |  | 359 |                             $scodata->value = $element->mastery_score;
 | 
        
           |  |  | 360 |                             $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 | 
        
           |  |  | 361 |                         }
 | 
        
           |  |  | 362 |                         if (isset($element->core_vendor)) {
 | 
        
           |  |  | 363 |                             $scodata->name = 'datafromlms';
 | 
        
           |  |  | 364 |                             $scodata->value = preg_replace('/<cr>/i', "\r\n", $element->core_vendor);
 | 
        
           |  |  | 365 |                             $dataid = $DB->insert_record('scorm_scoes_data', $scodata);
 | 
        
           |  |  | 366 |                         }
 | 
        
           |  |  | 367 |                     }
 | 
        
           |  |  | 368 |                     if ($launch == 0) {
 | 
        
           |  |  | 369 |                         $launch = $id;
 | 
        
           |  |  | 370 |                     }
 | 
        
           |  |  | 371 |                 }
 | 
        
           |  |  | 372 |             }
 | 
        
           |  |  | 373 |         }
 | 
        
           |  |  | 374 |     }
 | 
        
           |  |  | 375 |     if (!empty($oldscoes)) {
 | 
        
           |  |  | 376 |         foreach ($oldscoes as $oldsco) {
 | 
        
           |  |  | 377 |             scorm_delete_tracks($scorm->id, $oldsco->id);
 | 
        
           |  |  | 378 |             $DB->delete_records('scorm_scoes', ['id' => $oldsco->id]);
 | 
        
           |  |  | 379 |         }
 | 
        
           |  |  | 380 |     }
 | 
        
           |  |  | 381 |   | 
        
           |  |  | 382 |     // Find first launchable object.
 | 
        
           |  |  | 383 |     $sqlselect = 'scorm = ? AND '.$DB->sql_isnotempty('scorm_scoes', 'launch', false, true);
 | 
        
           |  |  | 384 |     // We use get_records here as we need to pass a limit in the query that works cross db.
 | 
        
           |  |  | 385 |     $scoes = $DB->get_records_select('scorm_scoes', $sqlselect, array($scorm->id), 'sortorder', 'id', 0, 1);
 | 
        
           |  |  | 386 |     if (!empty($scoes)) {
 | 
        
           |  |  | 387 |         $sco = reset($scoes); // We only care about the first record - the above query only returns one.
 | 
        
           |  |  | 388 |         $scorm->launch = $sco->id;
 | 
        
           |  |  | 389 |     } else {
 | 
        
           |  |  | 390 |         $scorm->launch = $launch;
 | 
        
           |  |  | 391 |     }
 | 
        
           |  |  | 392 |   | 
        
           |  |  | 393 |     $scorm->version = 'AICC';
 | 
        
           |  |  | 394 |   | 
        
           |  |  | 395 |     return true;
 | 
        
           |  |  | 396 | }
 | 
        
           |  |  | 397 |   | 
        
           |  |  | 398 | /**
 | 
        
           |  |  | 399 |  * Given a scormid creates an AICC Session record to allow HACP
 | 
        
           |  |  | 400 |  *
 | 
        
           |  |  | 401 |  * @param int $scormid - id from scorm table
 | 
        
           |  |  | 402 |  * @return string hacpsession
 | 
        
           |  |  | 403 |  */
 | 
        
           |  |  | 404 | function scorm_aicc_get_hacp_session($scormid) {
 | 
        
           |  |  | 405 |     global $USER, $DB, $SESSION;
 | 
        
           |  |  | 406 |     $cfgscorm = get_config('scorm');
 | 
        
           |  |  | 407 |     if (empty($cfgscorm->allowaicchacp)) {
 | 
        
           |  |  | 408 |         return false;
 | 
        
           |  |  | 409 |     }
 | 
        
           |  |  | 410 |     $now = time();
 | 
        
           |  |  | 411 |   | 
        
           |  |  | 412 |     $hacpsession = $SESSION->scorm;
 | 
        
           |  |  | 413 |     $hacpsession->scormid = $scormid;
 | 
        
           |  |  | 414 |     $hacpsession->hacpsession = random_string(20);
 | 
        
           |  |  | 415 |     $hacpsession->userid      = $USER->id;
 | 
        
           |  |  | 416 |     $hacpsession->timecreated = $now;
 | 
        
           |  |  | 417 |     $hacpsession->timemodified = $now;
 | 
        
           |  |  | 418 |     $DB->insert_record('scorm_aicc_session', $hacpsession);
 | 
        
           |  |  | 419 |   | 
        
           |  |  | 420 |     return $hacpsession->hacpsession;
 | 
        
           |  |  | 421 | }
 | 
        
           |  |  | 422 |   | 
        
           |  |  | 423 | /**
 | 
        
           |  |  | 424 |  * Check the hacp_session for whether it is valid.
 | 
        
           |  |  | 425 |  *
 | 
        
           |  |  | 426 |  * @param string $hacpsession The hacpsession value to check (optional). Normally leave this blank
 | 
        
           |  |  | 427 |  *      and this function will do required_param('sesskey', ...).
 | 
        
           |  |  | 428 |  * @return mixed - false if invalid, otherwise returns record from scorm_aicc_session table.
 | 
        
           |  |  | 429 |  */
 | 
        
           |  |  | 430 | function scorm_aicc_confirm_hacp_session($hacpsession) {
 | 
        
           |  |  | 431 |     global $DB;
 | 
        
           |  |  | 432 |     $cfgscorm = get_config('scorm');
 | 
        
           |  |  | 433 |     if (empty($cfgscorm->allowaicchacp)) {
 | 
        
           |  |  | 434 |         return false;
 | 
        
           |  |  | 435 |     }
 | 
        
           |  |  | 436 |     $time = time() - ($cfgscorm->aicchacptimeout * 60);
 | 
        
           |  |  | 437 |     $sql = "hacpsession = ? AND timemodified > ?";
 | 
        
           |  |  | 438 |     $hacpsession = $DB->get_record_select('scorm_aicc_session', $sql, array($hacpsession, $time));
 | 
        
           |  |  | 439 |     if (!empty($hacpsession)) { // Update timemodified as this is still an active session - resets the timeout.
 | 
        
           |  |  | 440 |         $hacpsession->timemodified = time();
 | 
        
           |  |  | 441 |         $DB->update_record('scorm_aicc_session', $hacpsession);
 | 
        
           |  |  | 442 |     }
 | 
        
           |  |  | 443 |     return $hacpsession;
 | 
        
           |  |  | 444 | }
 | 
        
           |  |  | 445 |   | 
        
           |  |  | 446 | /**
 | 
        
           |  |  | 447 |  * generate a simple single activity AICC object
 | 
        
           |  |  | 448 |  * structure to wrap around and externally linked
 | 
        
           |  |  | 449 |  * AICC package URL
 | 
        
           |  |  | 450 |  *
 | 
        
           |  |  | 451 |  * @param object $scorm package record
 | 
        
           |  |  | 452 |  */
 | 
        
           |  |  | 453 | function scorm_aicc_generate_simple_sco($scorm) {
 | 
        
           |  |  | 454 |     global $DB;
 | 
        
           |  |  | 455 |     // Find the oldest one.
 | 
        
           |  |  | 456 |     $scos = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id), 'id');
 | 
        
           |  |  | 457 |     if (!empty($scos)) {
 | 
        
           |  |  | 458 |         $sco = array_shift($scos);
 | 
        
           |  |  | 459 |     } else {
 | 
        
           |  |  | 460 |         $sco = new stdClass();
 | 
        
           |  |  | 461 |     }
 | 
        
           |  |  | 462 |     // Get rid of old ones.
 | 
        
           |  |  | 463 |     foreach ($scos as $oldsco) {
 | 
        
           |  |  | 464 |         scorm_delete_tracks($scorm->id, $oldsco->id);
 | 
        
           |  |  | 465 |         $DB->delete_records('scorm_scoes', ['id' => $oldsco->id]);
 | 
        
           |  |  | 466 |     }
 | 
        
           |  |  | 467 |   | 
        
           |  |  | 468 |     $sco->identifier = 'A1';
 | 
        
           |  |  | 469 |     $sco->scorm = $scorm->id;
 | 
        
           |  |  | 470 |     $sco->organization = '';
 | 
        
           |  |  | 471 |     $sco->title = $scorm->name;
 | 
        
           |  |  | 472 |     $sco->parent = '/';
 | 
        
           |  |  | 473 |     // Add the HACP signal to the activity launcher.
 | 
        
           |  |  | 474 |     if (preg_match('/\?/', $scorm->reference)) {
 | 
        
           |  |  | 475 |         $sco->launch = $scorm->reference.'&CMI=HACP';
 | 
        
           |  |  | 476 |     } else {
 | 
        
           |  |  | 477 |         $sco->launch = $scorm->reference.'?CMI=HACP';
 | 
        
           |  |  | 478 |     }
 | 
        
           |  |  | 479 |     $sco->scormtype = 'sco';
 | 
        
           |  |  | 480 |     if (isset($sco->id)) {
 | 
        
           |  |  | 481 |         $DB->update_record('scorm_scoes', $sco);
 | 
        
           |  |  | 482 |         $id = $sco->id;
 | 
        
           |  |  | 483 |     } else {
 | 
        
           |  |  | 484 |         $id = $DB->insert_record('scorm_scoes', $sco);
 | 
        
           |  |  | 485 |     }
 | 
        
           |  |  | 486 |     return $id;
 | 
        
           |  |  | 487 | }
 | 
        
           |  |  | 488 |   | 
        
           |  |  | 489 | /**
 | 
        
           |  |  | 490 |  * Sets up $userdata array and default values for AICC package.
 | 
        
           |  |  | 491 |  *
 | 
        
           |  |  | 492 |  * @param stdClass $userdata an empty stdClass variable that should be set up with user values
 | 
        
           |  |  | 493 |  * @param object $scorm package record
 | 
        
           |  |  | 494 |  * @param string $scoid SCO Id
 | 
        
           |  |  | 495 |  * @param string $attempt attempt number for the user
 | 
        
           |  |  | 496 |  * @param string $mode scorm display mode type
 | 
        
           |  |  | 497 |  * @return array The default values that should be used for AICC package
 | 
        
           |  |  | 498 |  */
 | 
        
           |  |  | 499 | function get_scorm_default (&$userdata, $scorm, $scoid, $attempt, $mode) {
 | 
        
           |  |  | 500 |     global $USER;
 | 
        
           |  |  | 501 |     $aiccuserid = get_config('scorm', 'aiccuserid');
 | 
        
           |  |  | 502 |     if (!empty($aiccuserid)) {
 | 
        
           |  |  | 503 |         $userdata->student_id = $USER->id;
 | 
        
           |  |  | 504 |     } else {
 | 
        
           |  |  | 505 |         $userdata->student_id = $USER->username;
 | 
        
           |  |  | 506 |     }
 | 
        
           |  |  | 507 |     $userdata->student_name = $USER->lastname .', '. $USER->firstname;
 | 
        
           |  |  | 508 |   | 
        
           |  |  | 509 |     if ($usertrack = scorm_get_tracks($scoid, $USER->id, $attempt)) {
 | 
        
           |  |  | 510 |         foreach ($usertrack as $key => $value) {
 | 
        
           |  |  | 511 |             $userdata->$key = $value;
 | 
        
           |  |  | 512 |         }
 | 
        
           |  |  | 513 |     } else {
 | 
        
           |  |  | 514 |         $userdata->status = '';
 | 
        
           |  |  | 515 |         $userdata->score_raw = '';
 | 
        
           |  |  | 516 |     }
 | 
        
           |  |  | 517 |   | 
        
           |  |  | 518 |     if ($scodatas = scorm_get_sco($scoid, SCO_DATA)) {
 | 
        
           |  |  | 519 |         foreach ($scodatas as $key => $value) {
 | 
        
           |  |  | 520 |             $userdata->$key = $value;
 | 
        
           |  |  | 521 |         }
 | 
        
           |  |  | 522 |     } else {
 | 
        
           |  |  | 523 |         throw new \moodle_exception('cannotfindsco', 'scorm');
 | 
        
           |  |  | 524 |     }
 | 
        
           |  |  | 525 |     if (!$sco = scorm_get_sco($scoid)) {
 | 
        
           |  |  | 526 |         throw new \moodle_exception('cannotfindsco', 'scorm');
 | 
        
           |  |  | 527 |     }
 | 
        
           |  |  | 528 |   | 
        
           |  |  | 529 |     $userdata->mode = 'normal';
 | 
        
           |  |  | 530 |     if (!empty($mode)) {
 | 
        
           |  |  | 531 |         $userdata->mode = $mode;
 | 
        
           |  |  | 532 |     }
 | 
        
           |  |  | 533 |     if ($userdata->mode == 'normal') {
 | 
        
           |  |  | 534 |         $userdata->credit = 'credit';
 | 
        
           |  |  | 535 |     } else {
 | 
        
           |  |  | 536 |         $userdata->credit = 'no-credit';
 | 
        
           |  |  | 537 |     }
 | 
        
           |  |  | 538 |   | 
        
           |  |  | 539 |     if (isset($userdata->status)) {
 | 
        
           |  |  | 540 |         if ($userdata->status == '') {
 | 
        
           |  |  | 541 |             $userdata->entry = 'ab-initio';
 | 
        
           |  |  | 542 |         } else {
 | 
        
           |  |  | 543 |             if (isset($userdata->{'cmi.core.exit'}) && ($userdata->{'cmi.core.exit'} == 'suspend')) {
 | 
        
           |  |  | 544 |                 $userdata->entry = 'resume';
 | 
        
           |  |  | 545 |             } else {
 | 
        
           |  |  | 546 |                 $userdata->entry = '';
 | 
        
           |  |  | 547 |             }
 | 
        
           |  |  | 548 |         }
 | 
        
           |  |  | 549 |     }
 | 
        
           |  |  | 550 |   | 
        
           |  |  | 551 |     $def = array();
 | 
        
           |  |  | 552 |     $def['cmi.core.student_id'] = $userdata->student_id;
 | 
        
           |  |  | 553 |     $def['cmi.core.student_name'] = $userdata->student_name;
 | 
        
           |  |  | 554 |     $def['cmi.core.credit'] = $userdata->credit;
 | 
        
           |  |  | 555 |     $def['cmi.core.entry'] = $userdata->entry;
 | 
        
           |  |  | 556 |     $def['cmi.launch_data'] = scorm_isset($userdata, 'datafromlms');
 | 
        
           |  |  | 557 |     $def['cmi.core.lesson_mode'] = $userdata->mode;
 | 
        
           |  |  | 558 |     $def['cmi.student_data.attempt_number'] = scorm_isset($userdata, 'cmi.student_data.attempt_number');
 | 
        
           |  |  | 559 |     $def['cmi.student_data.mastery_score'] = scorm_isset($userdata, 'mastery_score');
 | 
        
           |  |  | 560 |     $def['cmi.student_data.max_time_allowed'] = scorm_isset($userdata, 'max_time_allowed');
 | 
        
           |  |  | 561 |     $def['cmi.student_data.time_limit_action'] = scorm_isset($userdata, 'time_limit_action');
 | 
        
           |  |  | 562 |     $def['cmi.student_data.tries_during_lesson'] = scorm_isset($userdata, 'cmi.student_data.tries_during_lesson');
 | 
        
           |  |  | 563 |   | 
        
           |  |  | 564 |     $def['cmi.core.lesson_location'] = scorm_isset($userdata, 'cmi.core.lesson_location');
 | 
        
           |  |  | 565 |     $def['cmi.core.lesson_status'] = scorm_isset($userdata, 'cmi.core.lesson_status');
 | 
        
           |  |  | 566 |     $def['cmi.core.exit'] = scorm_isset($userdata, 'cmi.core.exit');
 | 
        
           |  |  | 567 |     $def['cmi.core.score.raw'] = scorm_isset($userdata, 'cmi.core.score.raw');
 | 
        
           |  |  | 568 |     $def['cmi.core.score.max'] = scorm_isset($userdata, 'cmi.core.score.max');
 | 
        
           |  |  | 569 |     $def['cmi.core.score.min'] = scorm_isset($userdata, 'cmi.core.score.min');
 | 
        
           |  |  | 570 |     $def['cmi.core.total_time'] = scorm_isset($userdata, 'cmi.core.total_time', '00:00:00');
 | 
        
           |  |  | 571 |     $def['cmi.suspend_data'] = scorm_isset($userdata, 'cmi.suspend_data');
 | 
        
           |  |  | 572 |     $def['cmi.comments'] = scorm_isset($userdata, 'cmi.comments');
 | 
        
           |  |  | 573 |   | 
        
           |  |  | 574 |     return $def;
 | 
        
           |  |  | 575 | }
 |