See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
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 * This file contains the base classes that are extended to create portfolio export functionality. 19 * 20 * For places in moodle that want to add export functionality to subclass. 21 * 22 * @package core_portfolio 23 * @copyright 2008 Penny Leach <penny@catalyst.net.nz>, Martin Dougiamas 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** 30 * Base class for callers 31 * 32 * @see also portfolio_module_caller_base 33 * 34 * @package core_portfolio 35 * @category portfolio 36 * @copyright 2008 Penny Leach <penny@catalyst.net.nz> 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 */ 39 abstract class portfolio_caller_base { 40 41 /** @var stdClass course active during the call */ 42 protected $course; 43 44 /** @var array configuration used for export. Use set_export_config and get_export_config to access */ 45 protected $exportconfig = array(); 46 47 /** @var stdclass user currently exporting content */ 48 protected $user; 49 50 /** @var stdClass a reference to the exporter object */ 51 protected $exporter; 52 53 /** @var array can be optionally overridden by subclass constructors */ 54 protected $supportedformats; 55 56 /** @var stored_file single file exports configuration*/ 57 protected $singlefile; 58 59 /** @var stored_file|object set this for multi file exports */ 60 protected $multifiles; 61 62 /** @var string set this for generated-file exports */ 63 protected $intendedmimetype; 64 65 /** 66 * Create portfolio_caller object 67 * 68 * @param array $callbackargs argument properties 69 */ 70 public function __construct($callbackargs) { 71 $expected = call_user_func(array(get_class($this), 'expected_callbackargs')); 72 foreach ($expected as $key => $required) { 73 if (!array_key_exists($key, $callbackargs)) { 74 if ($required) { 75 $a = (object)array('arg' => $key, 'class' => get_class($this)); 76 throw new portfolio_caller_exception('missingcallbackarg', 'portfolio', null, $a); 77 } 78 continue; 79 } 80 $this->{$key} = $callbackargs[$key]; 81 } 82 } 83 84 /** 85 * If this caller wants any additional config items, 86 * they should be defined here. 87 * 88 * @param moodleform $mform passed by reference, add elements to it. 89 * @param portfolio_plugin_base $instance subclass of portfolio_plugin_base 90 */ 91 public function export_config_form(&$mform, $instance) {} 92 93 94 /** 95 * Whether this caller wants any additional 96 * config during export (eg options or metadata) 97 * 98 * @return bool 99 */ 100 public function has_export_config() { 101 return false; 102 } 103 104 /** 105 * Just like the moodle form validation function, 106 * this is passed in the data array from the form 107 * and if a non empty array is returned, form processing will stop. 108 * 109 * @param array $data data from form. 110 */ 111 public function export_config_validation($data) {} 112 113 /** 114 * How long does this reasonably expect to take.. 115 * Should we offer the user the option to wait..? 116 * This is deliberately nonstatic so it can take filesize into account 117 * the portfolio plugin can override this. 118 * (so for example even if a huge file is being sent, 119 * the download portfolio plugin doesn't care ) 120 */ 121 public abstract function expected_time(); 122 123 /** 124 * Helper method to calculate expected time for multi or single file exports 125 * 126 * @return string file time expectation 127 */ 128 public function expected_time_file() { 129 if ($this->multifiles) { 130 return portfolio_expected_time_file($this->multifiles); 131 } 132 else if ($this->singlefile) { 133 return portfolio_expected_time_file($this->singlefile); 134 } 135 return PORTFOLIO_TIME_LOW; 136 } 137 138 /** 139 * Function to build navigation 140 */ 141 public abstract function get_navigation(); 142 143 /** 144 * Helper function to get sha1 145 */ 146 public abstract function get_sha1(); 147 148 /** 149 * Helper function to calculate the sha1 for multi or single file exports 150 * 151 * @return string sha1 file exports 152 */ 153 public function get_sha1_file() { 154 if (empty($this->singlefile) && empty($this->multifiles)) { 155 throw new portfolio_caller_exception('invalidsha1file', 'portfolio', $this->get_return_url()); 156 } 157 if ($this->singlefile) { 158 return $this->singlefile->get_contenthash(); 159 } 160 $sha1s = array(); 161 foreach ($this->multifiles as $file) { 162 $sha1s[] = $file->get_contenthash(); 163 } 164 asort($sha1s); 165 return sha1(implode('', $sha1s)); 166 } 167 168 /** 169 * Generic getter for properties belonging to this instance 170 * <b>outside</b> the subclasses 171 * like name, visible etc. 172 * 173 * @param string $field property's name 174 * @return mixed 175 * @throws portfolio_export_exception 176 */ 177 public function get($field) { 178 if (property_exists($this, $field)) { 179 return $this->{$field}; 180 } 181 $a = (object)array('property' => $field, 'class' => get_class($this)); 182 throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', $this->get_return_url(), $a); 183 } 184 185 /** 186 * Generic setter for properties belonging to this instance 187 * <b>outside</b> the subclass 188 * like name, visible, etc. 189 * 190 * @param string $field property's name 191 * @param mixed $value property's value 192 * @return bool 193 * @throws moodle_exception 194 */ 195 public final function set($field, &$value) { 196 if (property_exists($this, $field)) { 197 $this->{$field} =& $value; 198 $this->dirty = true; 199 return true; 200 } 201 $a = (object)array('property' => $field, 'class' => get_class($this)); 202 throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', $this->get_return_url(), $a); 203 } 204 205 /** 206 * Stores the config generated at export time. 207 * Subclasses can retrieve values using 208 * @see get_export_config 209 * 210 * @param array $config formdata 211 */ 212 public final function set_export_config($config) { 213 $allowed = array_merge( 214 array('wait', 'hidewait', 'format', 'hideformat'), 215 $this->get_allowed_export_config() 216 ); 217 foreach ($config as $key => $value) { 218 if (!in_array($key, $allowed)) { 219 $a = (object)array('property' => $key, 'class' => get_class($this)); 220 throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a); 221 } 222 $this->exportconfig[$key] = $value; 223 } 224 } 225 226 /** 227 * Returns a particular export config value. 228 * Subclasses shouldn't need to override this 229 * 230 * @param string $key the config item to fetch 231 * @return null|mixed of export configuration 232 */ 233 public final function get_export_config($key) { 234 $allowed = array_merge( 235 array('wait', 'hidewait', 'format', 'hideformat'), 236 $this->get_allowed_export_config() 237 ); 238 if (!in_array($key, $allowed)) { 239 $a = (object)array('property' => $key, 'class' => get_class($this)); 240 throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a); 241 } 242 if (!array_key_exists($key, $this->exportconfig)) { 243 return null; 244 } 245 return $this->exportconfig[$key]; 246 } 247 248 /** 249 * Similar to the other allowed_config functions 250 * if you need export config, you must provide 251 * a list of what the fields are. 252 * Even if you want to store stuff during export 253 * without displaying a form to the user, 254 * you can use this. 255 * 256 * @return array array of allowed keys 257 */ 258 public function get_allowed_export_config() { 259 return array(); 260 } 261 262 /** 263 * After the user submits their config, 264 * they're given a confirm screen 265 * summarising what they've chosen. 266 * This function should return a table of nice strings => values 267 * of what they've chosen 268 * to be displayed in a table. 269 * 270 * @return bool 271 */ 272 public function get_export_summary() { 273 return false; 274 } 275 276 /** 277 * Called before the portfolio plugin gets control. 278 * This function should copy all the files it wants to 279 * the temporary directory, using copy_existing_file 280 * or write_new_file 281 * 282 * @see copy_existing_file() 283 * @see write_new_file() 284 */ 285 public abstract function prepare_package(); 286 287 /** 288 * Helper function to copy files into the temp area 289 * for single or multi file exports. 290 * 291 * @return stored_file|bool 292 */ 293 public function prepare_package_file() { 294 if (empty($this->singlefile) && empty($this->multifiles)) { 295 throw new portfolio_caller_exception('invalidpreparepackagefile', 'portfolio', $this->get_return_url()); 296 } 297 if ($this->singlefile) { 298 return $this->exporter->copy_existing_file($this->singlefile); 299 } 300 foreach ($this->multifiles as $file) { 301 $this->exporter->copy_existing_file($file); 302 } 303 } 304 305 /** 306 * Array of formats this caller supports. 307 * 308 * @return array list of formats 309 */ 310 public final function supported_formats() { 311 $basic = $this->base_supported_formats(); 312 if (empty($this->supportedformats)) { 313 $specific = array(); 314 } else if (!is_array($this->supportedformats)) { 315 debugging(get_class($this) . ' has set a non array value of member variable supported formats - working around but should be fixed in code'); 316 $specific = array($this->supportedformats); 317 } else { 318 $specific = $this->supportedformats; 319 } 320 return portfolio_most_specific_formats($specific, $basic); 321 } 322 323 /** 324 * Base supported formats 325 * 326 * @throws coding_exception 327 */ 328 public static function base_supported_formats() { 329 throw new coding_exception('base_supported_formats() method needs to be overridden in each subclass of portfolio_caller_base'); 330 } 331 332 /** 333 * This is the "return to where you were" url 334 */ 335 public abstract function get_return_url(); 336 337 /** 338 * Callback to do whatever capability checks required 339 * in the caller (called during the export process 340 */ 341 public abstract function check_permissions(); 342 343 /** 344 * Clean name to display to the user about this caller location 345 */ 346 public static function display_name() { 347 throw new coding_exception('display_name() method needs to be overridden in each subclass of portfolio_caller_base'); 348 } 349 350 /** 351 * Return a string to put at the header summarising this export. 352 * By default, it just display the name (usually just 'assignment' or something unhelpful 353 * 354 * @return string 355 */ 356 public function heading_summary() { 357 return get_string('exportingcontentfrom', 'portfolio', $this->display_name()); 358 } 359 360 /** 361 * Load data 362 */ 363 public abstract function load_data(); 364 365 /** 366 * Set up the required files for this export. 367 * This supports either passing files directly 368 * or passing area arguments directly through 369 * to the files api using file_storage::get_area_files 370 * 371 * @param mixed $ids one of: 372 * - single file id 373 * - single stored_file object 374 * - array of file ids or stored_file objects 375 * - null 376 * @return void 377 */ 378 public function set_file_and_format_data($ids=null /* ..pass arguments to area files here. */) { 379 $args = func_get_args(); 380 array_shift($args); // shift off $ids 381 if (empty($ids) && count($args) == 0) { 382 return; 383 } 384 $files = array(); 385 $fs = get_file_storage(); 386 if (!empty($ids)) { 387 if (is_numeric($ids) || $ids instanceof stored_file) { 388 $ids = array($ids); 389 } 390 foreach ($ids as $id) { 391 if ($id instanceof stored_file) { 392 $files[] = $id; 393 } else { 394 $files[] = $fs->get_file_by_id($id); 395 } 396 } 397 } else if (count($args) != 0) { 398 if (count($args) < 4) { 399 throw new portfolio_caller_exception('invalidfileareaargs', 'portfolio'); 400 } 401 $files = array_values(call_user_func_array(array($fs, 'get_area_files'), $args)); 402 } 403 switch (count($files)) { 404 case 0: return; 405 case 1: { 406 $this->singlefile = $files[0]; 407 return; 408 } 409 default: { 410 $this->multifiles = $files; 411 } 412 } 413 } 414 415 /** 416 * The button-location always knows best 417 * what the formats are... so it should be trusted. 418 * 419 * @todo MDL-31298 - re-analyze set_formats_from_button comment 420 * @param array $formats array of PORTFOLIO_FORMAT_XX 421 * @return void 422 */ 423 public function set_formats_from_button($formats) { 424 $base = $this->base_supported_formats(); 425 if (count($base) != count($formats) 426 || count($base) != count(array_intersect($base, $formats))) { 427 $this->supportedformats = portfolio_most_specific_formats($formats, $base); 428 return; 429 } 430 // in the case where the button hasn't actually set anything, 431 // we need to run through again and resolve conflicts 432 // TODO revisit this comment - it looks to me like it's lying 433 $this->supportedformats = portfolio_most_specific_formats($formats, $formats); 434 } 435 436 /** 437 * Adds a new format to the list of supported formats. 438 * This functions also handles removing conflicting and less specific 439 * formats at the same time. 440 * 441 * @param string $format one of PORTFOLIO_FORMAT_XX 442 * @return void 443 */ 444 protected function add_format($format) { 445 if (in_array($format, $this->supportedformats)) { 446 return; 447 } 448 $this->supportedformats = portfolio_most_specific_formats(array($format), $this->supportedformats); 449 } 450 451 /** 452 * Gets mimetype 453 * 454 * @return string 455 */ 456 public function get_mimetype() { 457 if ($this->singlefile instanceof stored_file) { 458 return $this->singlefile->get_mimetype(); 459 } else if (!empty($this->intendedmimetype)) { 460 return $this->intendedmimetype; 461 } 462 } 463 464 /** 465 * Array of arguments the caller expects to be passed through to it. 466 * This must be keyed on the argument name, and the array value is a boolean, 467 * whether it is required, or just optional 468 * eg array( 469 * id => true, 470 * somethingelse => false 471 * ) 472 */ 473 public static function expected_callbackargs() { 474 throw new coding_exception('expected_callbackargs() method needs to be overridden in each subclass of portfolio_caller_base'); 475 } 476 477 478 /** 479 * Return the context for this export. used for $PAGE->set_context 480 * 481 * @param moodle_page $PAGE global page object 482 */ 483 public abstract function set_context($PAGE); 484 } 485 486 /** 487 * Base class for module callers. 488 * 489 * This just implements a few of the abstract functions 490 * from portfolio_caller_base so that caller authors 491 * don't need to. 492 * @see also portfolio_caller_base 493 * 494 * @package core_portfolio 495 * @category portfolio 496 * @copyright 2008 Penny Leach <penny@catalyst.net.nz> 497 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 498 */ 499 abstract class portfolio_module_caller_base extends portfolio_caller_base { 500 501 /** @var object coursemodule object. set this in the constructor like $this->cm = get_coursemodule_from_instance('forum', $this->forum->id); */ 502 protected $cm; 503 504 /** @var int cmid */ 505 protected $id; 506 507 /** @var stdclass course object */ 508 protected $course; 509 510 /** 511 * Navigation passed to print_header. 512 * Override this to do something more specific than the module view page 513 * like adding more links to the breadcrumb. 514 * 515 * @return array 516 */ 517 public function get_navigation() { 518 // No extra navigation by default, link to the course module already included. 519 $extranav = array(); 520 return array($extranav, $this->cm); 521 } 522 523 /** 524 * The url to return to after export or on cancel. 525 * Defaults value is set to the module 'view' page. 526 * Override this if it's deeper inside the module. 527 * 528 * @return string 529 */ 530 public function get_return_url() { 531 global $CFG; 532 return $CFG->wwwroot . '/mod/' . $this->cm->modname . '/view.php?id=' . $this->cm->id; 533 } 534 535 /** 536 * Override the parent get function 537 * to make sure when we're asked for a course, 538 * We retrieve the object from the database as needed. 539 * 540 * @param string $key the name of get function 541 * @return stdClass 542 */ 543 public function get($key) { 544 if ($key != 'course') { 545 return parent::get($key); 546 } 547 global $DB; 548 if (empty($this->course)) { 549 $this->course = $DB->get_record('course', array('id' => $this->cm->course)); 550 } 551 return $this->course; 552 } 553 554 /** 555 * Return a string to put at the header summarising this export. 556 * by default, this function just display the name and module instance name. 557 * Override this to do something more specific 558 * 559 * @return string 560 */ 561 public function heading_summary() { 562 return get_string('exportingcontentfrom', 'portfolio', $this->display_name() . ': ' . $this->cm->name); 563 } 564 565 /** 566 * Overridden to return the course module context 567 * 568 * @param moodle_page $PAGE global PAGE 569 */ 570 public function set_context($PAGE) { 571 $PAGE->set_cm($this->cm); 572 } 573 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body