Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
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 return true; 199 } 200 $a = (object)array('property' => $field, 'class' => get_class($this)); 201 throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', $this->get_return_url(), $a); 202 } 203 204 /** 205 * Stores the config generated at export time. 206 * Subclasses can retrieve values using 207 * @see get_export_config 208 * 209 * @param array $config formdata 210 */ 211 public final function set_export_config($config) { 212 $allowed = array_merge( 213 array('wait', 'hidewait', 'format', 'hideformat'), 214 $this->get_allowed_export_config() 215 ); 216 foreach ($config as $key => $value) { 217 if (!in_array($key, $allowed)) { 218 $a = (object)array('property' => $key, 'class' => get_class($this)); 219 throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a); 220 } 221 $this->exportconfig[$key] = $value; 222 } 223 } 224 225 /** 226 * Returns a particular export config value. 227 * Subclasses shouldn't need to override this 228 * 229 * @param string $key the config item to fetch 230 * @return null|mixed of export configuration 231 */ 232 public final function get_export_config($key) { 233 $allowed = array_merge( 234 array('wait', 'hidewait', 'format', 'hideformat'), 235 $this->get_allowed_export_config() 236 ); 237 if (!in_array($key, $allowed)) { 238 $a = (object)array('property' => $key, 'class' => get_class($this)); 239 throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a); 240 } 241 if (!array_key_exists($key, $this->exportconfig)) { 242 return null; 243 } 244 return $this->exportconfig[$key]; 245 } 246 247 /** 248 * Similar to the other allowed_config functions 249 * if you need export config, you must provide 250 * a list of what the fields are. 251 * Even if you want to store stuff during export 252 * without displaying a form to the user, 253 * you can use this. 254 * 255 * @return array array of allowed keys 256 */ 257 public function get_allowed_export_config() { 258 return array(); 259 } 260 261 /** 262 * After the user submits their config, 263 * they're given a confirm screen 264 * summarising what they've chosen. 265 * This function should return a table of nice strings => values 266 * of what they've chosen 267 * to be displayed in a table. 268 * 269 * @return bool 270 */ 271 public function get_export_summary() { 272 return false; 273 } 274 275 /** 276 * Called before the portfolio plugin gets control. 277 * This function should copy all the files it wants to 278 * the temporary directory, using copy_existing_file 279 * or write_new_file 280 * 281 * @see copy_existing_file() 282 * @see write_new_file() 283 */ 284 public abstract function prepare_package(); 285 286 /** 287 * Helper function to copy files into the temp area 288 * for single or multi file exports. 289 * 290 * @return stored_file|bool 291 */ 292 public function prepare_package_file() { 293 if (empty($this->singlefile) && empty($this->multifiles)) { 294 throw new portfolio_caller_exception('invalidpreparepackagefile', 'portfolio', $this->get_return_url()); 295 } 296 if ($this->singlefile) { 297 return $this->exporter->copy_existing_file($this->singlefile); 298 } 299 foreach ($this->multifiles as $file) { 300 $this->exporter->copy_existing_file($file); 301 } 302 } 303 304 /** 305 * Array of formats this caller supports. 306 * 307 * @return array list of formats 308 */ 309 public final function supported_formats() { 310 $basic = $this->base_supported_formats(); 311 if (empty($this->supportedformats)) { 312 $specific = array(); 313 } else if (!is_array($this->supportedformats)) { 314 debugging(get_class($this) . ' has set a non array value of member variable supported formats - working around but should be fixed in code'); 315 $specific = array($this->supportedformats); 316 } else { 317 $specific = $this->supportedformats; 318 } 319 return portfolio_most_specific_formats($specific, $basic); 320 } 321 322 /** 323 * Base supported formats 324 * 325 * @throws coding_exception 326 */ 327 public static function base_supported_formats() { 328 throw new coding_exception('base_supported_formats() method needs to be overridden in each subclass of portfolio_caller_base'); 329 } 330 331 /** 332 * This is the "return to where you were" url 333 */ 334 public abstract function get_return_url(); 335 336 /** 337 * Callback to do whatever capability checks required 338 * in the caller (called during the export process 339 */ 340 public abstract function check_permissions(); 341 342 /** 343 * Clean name to display to the user about this caller location 344 */ 345 public static function display_name() { 346 throw new coding_exception('display_name() method needs to be overridden in each subclass of portfolio_caller_base'); 347 } 348 349 /** 350 * Return a string to put at the header summarising this export. 351 * By default, it just display the name (usually just 'assignment' or something unhelpful 352 * 353 * @return string 354 */ 355 public function heading_summary() { 356 return get_string('exportingcontentfrom', 'portfolio', $this->display_name()); 357 } 358 359 /** 360 * Load data 361 */ 362 public abstract function load_data(); 363 364 /** 365 * Set up the required files for this export. 366 * This supports either passing files directly 367 * or passing area arguments directly through 368 * to the files api using file_storage::get_area_files 369 * 370 * @param mixed $ids one of: 371 * - single file id 372 * - single stored_file object 373 * - array of file ids or stored_file objects 374 * - null 375 * @return void 376 */ 377 public function set_file_and_format_data($ids=null /* ..pass arguments to area files here. */) { 378 $args = func_get_args(); 379 array_shift($args); // shift off $ids 380 if (empty($ids) && count($args) == 0) { 381 return; 382 } 383 $files = array(); 384 $fs = get_file_storage(); 385 if (!empty($ids)) { 386 if (is_numeric($ids) || $ids instanceof stored_file) { 387 $ids = array($ids); 388 } 389 foreach ($ids as $id) { 390 if ($id instanceof stored_file) { 391 $files[] = $id; 392 } else { 393 $files[] = $fs->get_file_by_id($id); 394 } 395 } 396 } else if (count($args) != 0) { 397 if (count($args) < 4) { 398 throw new portfolio_caller_exception('invalidfileareaargs', 'portfolio'); 399 } 400 $files = array_values(call_user_func_array(array($fs, 'get_area_files'), $args)); 401 } 402 switch (count($files)) { 403 case 0: return; 404 case 1: { 405 $this->singlefile = $files[0]; 406 return; 407 } 408 default: { 409 $this->multifiles = $files; 410 } 411 } 412 } 413 414 /** 415 * The button-location always knows best 416 * what the formats are... so it should be trusted. 417 * 418 * @todo MDL-31298 - re-analyze set_formats_from_button comment 419 * @param array $formats array of PORTFOLIO_FORMAT_XX 420 * @return void 421 */ 422 public function set_formats_from_button($formats) { 423 $base = $this->base_supported_formats(); 424 if (count($base) != count($formats) 425 || count($base) != count(array_intersect($base, $formats))) { 426 $this->supportedformats = portfolio_most_specific_formats($formats, $base); 427 return; 428 } 429 // in the case where the button hasn't actually set anything, 430 // we need to run through again and resolve conflicts 431 // TODO revisit this comment - it looks to me like it's lying 432 $this->supportedformats = portfolio_most_specific_formats($formats, $formats); 433 } 434 435 /** 436 * Adds a new format to the list of supported formats. 437 * This functions also handles removing conflicting and less specific 438 * formats at the same time. 439 * 440 * @param string $format one of PORTFOLIO_FORMAT_XX 441 * @return void 442 */ 443 protected function add_format($format) { 444 if (in_array($format, $this->supportedformats)) { 445 return; 446 } 447 $this->supportedformats = portfolio_most_specific_formats(array($format), $this->supportedformats); 448 } 449 450 /** 451 * Gets mimetype 452 * 453 * @return string 454 */ 455 public function get_mimetype() { 456 if ($this->singlefile instanceof stored_file) { 457 return $this->singlefile->get_mimetype(); 458 } else if (!empty($this->intendedmimetype)) { 459 return $this->intendedmimetype; 460 } 461 } 462 463 /** 464 * Array of arguments the caller expects to be passed through to it. 465 * This must be keyed on the argument name, and the array value is a boolean, 466 * whether it is required, or just optional 467 * eg array( 468 * id => true, 469 * somethingelse => false 470 * ) 471 */ 472 public static function expected_callbackargs() { 473 throw new coding_exception('expected_callbackargs() method needs to be overridden in each subclass of portfolio_caller_base'); 474 } 475 476 477 /** 478 * Return the context for this export. used for $PAGE->set_context 479 * 480 * @param moodle_page $PAGE global page object 481 */ 482 public abstract function set_context($PAGE); 483 } 484 485 /** 486 * Base class for module callers. 487 * 488 * This just implements a few of the abstract functions 489 * from portfolio_caller_base so that caller authors 490 * don't need to. 491 * @see also portfolio_caller_base 492 * 493 * @package core_portfolio 494 * @category portfolio 495 * @copyright 2008 Penny Leach <penny@catalyst.net.nz> 496 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 497 */ 498 abstract class portfolio_module_caller_base extends portfolio_caller_base { 499 500 /** @var object coursemodule object. set this in the constructor like $this->cm = get_coursemodule_from_instance('forum', $this->forum->id); */ 501 protected $cm; 502 503 /** @var int cmid */ 504 protected $id; 505 506 /** @var stdclass course object */ 507 protected $course; 508 509 /** 510 * Navigation passed to print_header. 511 * Override this to do something more specific than the module view page 512 * like adding more links to the breadcrumb. 513 * 514 * @return array 515 */ 516 public function get_navigation() { 517 // No extra navigation by default, link to the course module already included. 518 $extranav = array(); 519 return array($extranav, $this->cm); 520 } 521 522 /** 523 * The url to return to after export or on cancel. 524 * Defaults value is set to the module 'view' page. 525 * Override this if it's deeper inside the module. 526 * 527 * @return string 528 */ 529 public function get_return_url() { 530 global $CFG; 531 return $CFG->wwwroot . '/mod/' . $this->cm->modname . '/view.php?id=' . $this->cm->id; 532 } 533 534 /** 535 * Override the parent get function 536 * to make sure when we're asked for a course, 537 * We retrieve the object from the database as needed. 538 * 539 * @param string $key the name of get function 540 * @return stdClass 541 */ 542 public function get($key) { 543 if ($key != 'course') { 544 return parent::get($key); 545 } 546 global $DB; 547 if (empty($this->course)) { 548 $this->course = $DB->get_record('course', array('id' => $this->cm->course)); 549 } 550 return $this->course; 551 } 552 553 /** 554 * Return a string to put at the header summarising this export. 555 * by default, this function just display the name and module instance name. 556 * Override this to do something more specific 557 * 558 * @return string 559 */ 560 public function heading_summary() { 561 return get_string('exportingcontentfrom', 'portfolio', $this->display_name() . ': ' . $this->cm->name); 562 } 563 564 /** 565 * Overridden to return the course module context 566 * 567 * @param moodle_page $PAGE global PAGE 568 */ 569 public function set_context($PAGE) { 570 $PAGE->set_cm($this->cm); 571 } 572 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body