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