See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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 * File containing processor class. 19 * 20 * @package tool_uploadcourse 21 * @copyright 2013 Frédéric Massart 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 require_once($CFG->libdir . '/csvlib.class.php'); 27 28 /** 29 * Processor class. 30 * 31 * @package tool_uploadcourse 32 * @copyright 2013 Frédéric Massart 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class tool_uploadcourse_processor { 36 37 /** 38 * Create courses that do not exist yet. 39 */ 40 const MODE_CREATE_NEW = 1; 41 42 /** 43 * Create all courses, appending a suffix to the shortname if the course exists. 44 */ 45 const MODE_CREATE_ALL = 2; 46 47 /** 48 * Create courses, and update the ones that already exist. 49 */ 50 const MODE_CREATE_OR_UPDATE = 3; 51 52 /** 53 * Only update existing courses. 54 */ 55 const MODE_UPDATE_ONLY = 4; 56 57 /** 58 * During update, do not update anything... O_o Huh?! 59 */ 60 const UPDATE_NOTHING = 0; 61 62 /** 63 * During update, only use data passed from the CSV. 64 */ 65 const UPDATE_ALL_WITH_DATA_ONLY = 1; 66 67 /** 68 * During update, use either data from the CSV, or defaults. 69 */ 70 const UPDATE_ALL_WITH_DATA_OR_DEFAUTLS = 2; 71 72 /** 73 * During update, update missing values from either data from the CSV, or defaults. 74 */ 75 const UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS = 3; 76 77 /** @var int processor mode. */ 78 protected $mode; 79 80 /** @var int upload mode. */ 81 protected $updatemode; 82 83 /** @var bool are renames allowed. */ 84 protected $allowrenames = false; 85 86 /** @var bool are deletes allowed. */ 87 protected $allowdeletes = false; 88 89 /** @var bool are resets allowed. */ 90 protected $allowresets = false; 91 92 /** @var string path to a restore file. */ 93 protected $restorefile; 94 95 /** @var string shortname of the course to be restored. */ 96 protected $templatecourse; 97 98 /** @var string reset courses after processing them. */ 99 protected $reset; 100 101 /** @var string template to generate a course shortname. */ 102 protected $shortnametemplate; 103 104 /** @var csv_import_reader */ 105 protected $cir; 106 107 /** @var array default values. */ 108 protected $defaults = array(); 109 110 /** @var array CSV columns. */ 111 protected $columns = array(); 112 113 /** @var array of errors where the key is the line number. */ 114 protected $errors = array(); 115 116 /** @var int line number. */ 117 protected $linenb = 0; 118 119 /** @var bool whether the process has been started or not. */ 120 protected $processstarted = false; 121 122 /** 123 * Constructor 124 * 125 * @param csv_import_reader $cir import reader object 126 * @param array $options options of the process 127 * @param array $defaults default data value 128 */ 129 public function __construct(csv_import_reader $cir, array $options, array $defaults = array()) { 130 131 if (!isset($options['mode']) || !in_array($options['mode'], array(self::MODE_CREATE_NEW, self::MODE_CREATE_ALL, 132 self::MODE_CREATE_OR_UPDATE, self::MODE_UPDATE_ONLY))) { 133 throw new coding_exception('Unknown process mode'); 134 } 135 136 // Force int to make sure === comparison work as expected. 137 $this->mode = (int) $options['mode']; 138 139 $this->updatemode = self::UPDATE_NOTHING; 140 if (isset($options['updatemode'])) { 141 // Force int to make sure === comparison work as expected. 142 $this->updatemode = (int) $options['updatemode']; 143 } 144 if (isset($options['allowrenames'])) { 145 $this->allowrenames = $options['allowrenames']; 146 } 147 if (isset($options['allowdeletes'])) { 148 $this->allowdeletes = $options['allowdeletes']; 149 } 150 if (isset($options['allowresets'])) { 151 $this->allowresets = $options['allowresets']; 152 } 153 154 if (isset($options['restorefile'])) { 155 $this->restorefile = $options['restorefile']; 156 } 157 if (isset($options['templatecourse'])) { 158 $this->templatecourse = $options['templatecourse']; 159 } 160 if (isset($options['reset'])) { 161 $this->reset = $options['reset']; 162 } 163 if (isset($options['shortnametemplate'])) { 164 $this->shortnametemplate = $options['shortnametemplate']; 165 } 166 167 $this->cir = $cir; 168 $this->columns = $cir->get_columns(); 169 $this->defaults = $defaults; 170 $this->validate(); 171 $this->reset(); 172 } 173 174 /** 175 * Execute the process. 176 * 177 * @param object $tracker the output tracker to use. 178 * @return void 179 */ 180 public function execute($tracker = null) { 181 if ($this->processstarted) { 182 throw new coding_exception('Process has already been started'); 183 } 184 $this->processstarted = true; 185 186 if (empty($tracker)) { 187 $tracker = new tool_uploadcourse_tracker(tool_uploadcourse_tracker::NO_OUTPUT); 188 } 189 $tracker->start(); 190 191 $total = 0; 192 $created = 0; 193 $updated = 0; 194 $deleted = 0; 195 $errors = 0; 196 197 // We will most certainly need extra time and memory to process big files. 198 core_php_time_limit::raise(); 199 raise_memory_limit(MEMORY_EXTRA); 200 201 // Loop over the CSV lines. 202 while ($line = $this->cir->next()) { 203 $this->linenb++; 204 $total++; 205 206 $data = $this->parse_line($line); 207 $course = $this->get_course($data); 208 if ($course->prepare()) { 209 $course->proceed(); 210 211 $status = $course->get_statuses(); 212 if (array_key_exists('coursecreated', $status)) { 213 $created++; 214 } else if (array_key_exists('courseupdated', $status)) { 215 $updated++; 216 } else if (array_key_exists('coursedeleted', $status)) { 217 $deleted++; 218 } 219 220 $data = array_merge($data, $course->get_data(), array('id' => $course->get_id())); 221 $tracker->output($this->linenb, true, $status, $data); 222 if ($course->has_errors()) { 223 $errors++; 224 $tracker->output($this->linenb, false, $course->get_errors(), $data); 225 } 226 } else { 227 $errors++; 228 $tracker->output($this->linenb, false, $course->get_errors(), $data); 229 } 230 } 231 232 $tracker->finish(); 233 $tracker->results($total, $created, $updated, $deleted, $errors); 234 } 235 236 /** 237 * Return a course import object. 238 * 239 * @param array $data data to import the course with. 240 * @return tool_uploadcourse_course 241 */ 242 protected function get_course($data) { 243 $importoptions = array( 244 'candelete' => $this->allowdeletes, 245 'canrename' => $this->allowrenames, 246 'canreset' => $this->allowresets, 247 'reset' => $this->reset, 248 'restoredir' => $this->get_restore_content_dir(), 249 'shortnametemplate' => $this->shortnametemplate 250 ); 251 return new tool_uploadcourse_course($this->mode, $this->updatemode, $data, $this->defaults, $importoptions); 252 } 253 254 /** 255 * Return the errors. 256 * 257 * @return array 258 */ 259 public function get_errors() { 260 return $this->errors; 261 } 262 263 /** 264 * Get the directory of the object to restore. 265 * 266 * @return string subdirectory in $CFG->backuptempdir/... 267 */ 268 protected function get_restore_content_dir() { 269 $backupfile = null; 270 $shortname = null; 271 272 if (!empty($this->restorefile)) { 273 $backupfile = $this->restorefile; 274 } else if (!empty($this->templatecourse) || is_numeric($this->templatecourse)) { 275 $shortname = $this->templatecourse; 276 } 277 278 $dir = tool_uploadcourse_helper::get_restore_content_dir($backupfile, $shortname); 279 return $dir; 280 } 281 282 /** 283 * Log errors on the current line. 284 * 285 * @param array $errors array of errors 286 * @return void 287 */ 288 protected function log_error($errors) { 289 if (empty($errors)) { 290 return; 291 } 292 293 foreach ($errors as $code => $langstring) { 294 if (!isset($this->errors[$this->linenb])) { 295 $this->errors[$this->linenb] = array(); 296 } 297 $this->errors[$this->linenb][$code] = $langstring; 298 } 299 } 300 301 /** 302 * Parse a line to return an array(column => value) 303 * 304 * @param array $line returned by csv_import_reader 305 * @return array 306 */ 307 protected function parse_line($line) { 308 $data = array(); 309 foreach ($line as $keynum => $value) { 310 if (!isset($this->columns[$keynum])) { 311 // This should not happen. 312 continue; 313 } 314 315 $key = $this->columns[$keynum]; 316 $data[$key] = $value; 317 } 318 return $data; 319 } 320 321 /** 322 * Return a preview of the import. 323 * 324 * This only returns passed data, along with the errors. 325 * 326 * @param integer $rows number of rows to preview. 327 * @param object $tracker the output tracker to use. 328 * @return array of preview data. 329 */ 330 public function preview($rows = 10, $tracker = null) { 331 if ($this->processstarted) { 332 throw new coding_exception('Process has already been started'); 333 } 334 $this->processstarted = true; 335 336 if (empty($tracker)) { 337 $tracker = new tool_uploadcourse_tracker(tool_uploadcourse_tracker::NO_OUTPUT); 338 } 339 $tracker->start(); 340 341 // We might need extra time and memory depending on the number of rows to preview. 342 core_php_time_limit::raise(); 343 raise_memory_limit(MEMORY_EXTRA); 344 345 // Loop over the CSV lines. 346 $preview = array(); 347 while (($line = $this->cir->next()) && $rows > $this->linenb) { 348 $this->linenb++; 349 $data = $this->parse_line($line); 350 $course = $this->get_course($data); 351 $result = $course->prepare(); 352 if (!$result) { 353 $tracker->output($this->linenb, $result, $course->get_errors(), $data); 354 } else { 355 $tracker->output($this->linenb, $result, $course->get_statuses(), $data); 356 } 357 $row = $data; 358 $preview[$this->linenb] = $row; 359 } 360 361 $tracker->finish(); 362 363 return $preview; 364 } 365 366 /** 367 * Reset the current process. 368 * 369 * @return void. 370 */ 371 public function reset() { 372 $this->processstarted = false; 373 $this->linenb = 0; 374 $this->cir->init(); 375 $this->errors = array(); 376 } 377 378 /** 379 * Validation. 380 * 381 * @return void 382 */ 383 protected function validate() { 384 if (empty($this->columns)) { 385 throw new moodle_exception('cannotreadtmpfile', 'error'); 386 } else if (count($this->columns) < 2) { 387 throw new moodle_exception('csvfewcolumns', 'error'); 388 } 389 } 390 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body