MDL-33552 - portfolio api E_STRICT fixes
[moodle.git] / lib / portfolio / caller.php
CommitLineData
87fcac8d 1<?php
93dd2725
RW
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
87fcac8d 17/**
93dd2725 18 * This file contains the base classes that are extended to create portfolio export functionality.
87fcac8d 19 *
93dd2725
RW
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}
87fcac8d 22 *
93dd2725
RW
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
87fcac8d 26 */
27
0972665f
PS
28defined('MOODLE_INTERNAL') || die();
29
87fcac8d 30/**
93dd2725
RW
31 * Base class for callers
32 *
18cdcdbf
SH
33 * @link See http://docs.moodle.org/dev/Adding_a_Portfolio_Button_to_a_page
34 * @see also portfolio_module_caller_base
93dd2725
RW
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 */
87fcac8d 41abstract class portfolio_caller_base {
42
93dd2725 43 /** @var stdClass course active during the call */
87fcac8d 44 protected $course;
45
18cdcdbf 46 /** @var array configuration used for export. Use set_export_config and get_export_config to access */
11fbe3fc 47 protected $exportconfig = array();
87fcac8d 48
93dd2725 49 /** @var stdclass user currently exporting content */
87fcac8d 50 protected $user;
51
93dd2725 52 /** @var stdClass a reference to the exporter object */
87fcac8d 53 protected $exporter;
54
93dd2725 55 /** @var array can be optionally overridden by subclass constructors */
87fcac8d 56 protected $supportedformats;
57
93dd2725 58 /** @var stored_file single file exports configuration*/
6be1dcae 59 protected $singlefile;
60
93dd2725 61 /** @var stored_file|object set this for multi file exports */
6be1dcae 62 protected $multifiles;
0d06b6fd 63
93dd2725 64 /** @var string set this for generated-file exports */
521a6ab0
PL
65 protected $intendedmimetype;
66
93dd2725
RW
67 /**
68 * Create portfolio_caller object
69 *
70 * @param array $callbackargs argument properties
71 */
0d06b6fd 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) {
9d429f0b 77 $a = (object)array('arg' => $key, 'class' => get_class($this));
0d06b6fd 78 throw new portfolio_caller_exception('missingcallbackarg', 'portfolio', null, $a);
79 }
80 continue;
81 }
82 $this->{$key} = $callbackargs[$key];
83 }
84 }
85
87fcac8d 86 /**
93dd2725
RW
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 */
87fcac8d 93 public function export_config_form(&$mform, $instance) {}
94
95
96 /**
93dd2725
RW
97 * Whether this caller wants any additional
98 * config during export (eg options or metadata)
99 *
100 * @return bool
101 */
87fcac8d 102 public function has_export_config() {
103 return false;
104 }
105
106 /**
93dd2725
RW
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 */
87fcac8d 113 public function export_config_validation($data) {}
114
115 /**
93dd2725
RW
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 */
87fcac8d 123 public abstract function expected_time();
124
6be1dcae 125 /**
93dd2725
RW
126 * Helper method to calculate expected time for multi or single file exports
127 *
128 * @return string file time expectation
129 */
6be1dcae 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
87fcac8d 140 /**
93dd2725
RW
141 * Function to build navigation
142 */
87fcac8d 143 public abstract function get_navigation();
144
145 /**
93dd2725
RW
146 * Helper function to get sha1
147 */
87fcac8d 148 public abstract function get_sha1();
149
6be1dcae 150 /**
93dd2725
RW
151 * Helper function to calculate the sha1 for multi or single file exports
152 *
153 * @return string sha1 file exports
154 */
6be1dcae 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
93dd2725
RW
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 */
87fcac8d 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 /**
93dd2725
RW
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 */
87fcac8d 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 /**
93dd2725
RW
208 * Stores the config generated at export time.
209 * Subclasses can retrieve values using
18cdcdbf 210 * @see get_export_config
93dd2725
RW
211 *
212 * @param array $config formdata
213 */
87fcac8d 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 /**
93dd2725
RW
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 */
87fcac8d 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 /**
93dd2725
RW
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 */
87fcac8d 260 public function get_allowed_export_config() {
261 return array();
262 }
263
264 /**
93dd2725
RW
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 */
87fcac8d 274 public function get_export_summary() {
275 return false;
276 }
277
278 /**
93dd2725
RW
279 * Called before the portfolio plugin gets control.
280 * This function should copy all the files it wants to
18cdcdbf
SH
281 * the temporary directory, using copy_existing_file
282 * or write_new_file
283 *
284 * @see copy_existing_file()
285 * @see write_new_file()
93dd2725 286 */
87fcac8d 287 public abstract function prepare_package();
288
6be1dcae 289 /**
93dd2725
RW
290 * Helper function to copy files into the temp area
291 * for single or multi file exports.
292 *
293 * @return stored_file|bool
294 */
6be1dcae 295 public function prepare_package_file() {
5836bac7 296 if (empty($this->singlefile) && empty($this->multifiles)) {
297 throw new portfolio_caller_exception('invalidpreparepackagefile', 'portfolio', $this->get_return_url());
298 }
6be1dcae 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
87fcac8d 307 /**
93dd2725
RW
308 * Array of formats this caller supports.
309 *
310 * @return array list of formats
311 */
59dd457e
PL
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)) {
2ae76ab6 317 debugging(get_class($this) . ' has set a non array value of member variable supported formats - working around but should be fixed in code');
549c1ae6 318 $specific = array($this->supportedformats);
59dd457e
PL
319 } else {
320 $specific = $this->supportedformats;
87fcac8d 321 }
59dd457e 322 return portfolio_most_specific_formats($specific, $basic);
87fcac8d 323 }
324
93dd2725
RW
325 /**
326 * Base supported formats
327 *
328 * @throws coding_exception
329 */
7c109ea3
PS
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 }
87fcac8d 333
334 /**
93dd2725
RW
335 * This is the "return to where you were" url
336 */
87fcac8d 337 public abstract function get_return_url();
338
339 /**
93dd2725
RW
340 * Callback to do whatever capability checks required
341 * in the caller (called during the export process
342 */
87fcac8d 343 public abstract function check_permissions();
344
345 /**
93dd2725
RW
346 * Clean name to display to the user about this caller location
347 */
7c109ea3
PS
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 }
87fcac8d 351
352 /**
93dd2725
RW
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 */
87fcac8d 358 public function heading_summary() {
359 return get_string('exportingcontentfrom', 'portfolio', $this->display_name());
360 }
0d06b6fd 361
93dd2725
RW
362 /**
363 * Load data
364 */
0d06b6fd 365 public abstract function load_data();
366
086831a6 367 /**
93dd2725
RW
368 * Set up the required files for this export.
369 * This supports either passing files directly
086831a6
PL
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
0424cfe3 375 * - single stored_file object
086831a6
PL
376 * - array of file ids or stored_file objects
377 * - null
93dd2725 378 * @return void
086831a6 379 */
6be1dcae 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) {
64f93798 400 if (count($args) < 4) {
6be1dcae 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];
6be1dcae 409 return;
410 }
411 default: {
412 $this->multifiles = $files;
413 }
414 }
415 }
416
59dd457e 417 /**
93dd2725 418 * The button-location always knows best
59dd457e
PL
419 * what the formats are... so it should be trusted.
420 *
93dd2725 421 * @todo MDL-31298 - re-analyze set_formats_from_button comment
59dd457e 422 * @param array $formats array of PORTFOLIO_FORMAT_XX
93dd2725 423 * @return void
59dd457e
PL
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))) {
549c1ae6
PL
429 $this->supportedformats = portfolio_most_specific_formats($formats, $base);
430 return;
59dd457e
PL
431 }
432 // in the case where the button hasn't actually set anything,
433 // we need to run through again and resolve conflicts
549c1ae6
PL
434 // TODO revisit this comment - it looks to me like it's lying
435 $this->supportedformats = portfolio_most_specific_formats($formats, $formats);
59dd457e
PL
436 }
437
438 /**
93dd2725
RW
439 * Adds a new format to the list of supported formats.
440 * This functions also handles removing conflicting and less specific
59dd457e
PL
441 * formats at the same time.
442 *
443 * @param string $format one of PORTFOLIO_FORMAT_XX
59dd457e
PL
444 * @return void
445 */
446 protected function add_format($format) {
447 if (in_array($format, $this->supportedformats)) {
448 return;
449 }
a1e1af19 450 $this->supportedformats = portfolio_most_specific_formats(array($format), $this->supportedformats);
59dd457e
PL
451 }
452
93dd2725
RW
453 /**
454 * Gets mimetype
455 *
456 * @return string
457 */
521a6ab0
PL
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
086831a6 466 /**
93dd2725
RW
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,
0424cfe3 469 * whether it is required, or just optional
086831a6
PL
470 * eg array(
471 * id => true,
93dd2725 472 * somethingelse => false
086831a6 473 * )
086831a6 474 */
7c109ea3
PS
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 }
0d06b6fd 478
90636e2b
PL
479
480 /**
93dd2725 481 * Return the context for this export. used for $PAGE->set_context
90636e2b 482 *
93dd2725 483 * @param moodle_page $PAGE global page object
90636e2b
PL
484 */
485 public abstract function set_context($PAGE);
87fcac8d 486}
487
488/**
93dd2725 489 * Base class for module callers.
18cdcdbf 490 *
93dd2725
RW
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}
18cdcdbf 495 * @see also portfolio_caller_base
93dd2725
RW
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 */
87fcac8d 502abstract class portfolio_module_caller_base extends portfolio_caller_base {
503
93dd2725 504 /** @var object coursemodule object. set this in the constructor like $this->cm = get_coursemodule_from_instance('forum', $this->forum->id); */
87fcac8d 505 protected $cm;
506
93dd2725 507 /** @var int cmid */
0d06b6fd 508 protected $id;
509
93dd2725 510 /** @var stdclass course object */
87fcac8d 511 protected $course;
512
513 /**
93dd2725
RW
514 * Navigation passed to print_header.
515 * Override this to do something more specific than the module view page
516 *
517 * @return array
518 */
87fcac8d 519 public function get_navigation() {
520 $extranav = array('name' => $this->cm->name, 'link' => $this->get_return_url());
521 return array($extranav, $this->cm);
522 }
523
524 /**
93dd2725
RW
525 * The url to return to after export or on cancel.
526 * Defaults value is set to the module 'view' page.
527 * Override this if it's deeper inside the module.
528 *
529 * @return string
530 */
87fcac8d 531 public function get_return_url() {
532 global $CFG;
533 return $CFG->wwwroot . '/mod/' . $this->cm->modname . '/view.php?id=' . $this->cm->id;
534 }
535
536 /**
93dd2725
RW
537 * Override the parent get function
538 * to make sure when we're asked for a course,
539 * We retrieve the object from the database as needed.
540 *
541 * @param string $key the name of get function
542 * @return stdClass
543 */
87fcac8d 544 public function get($key) {
545 if ($key != 'course') {
546 return parent::get($key);
547 }
548 global $DB;
549 if (empty($this->course)) {
550 $this->course = $DB->get_record('course', array('id' => $this->cm->course));
551 }
552 return $this->course;
553 }
554
555 /**
93dd2725
RW
556 * Return a string to put at the header summarising this export.
557 * by default, this function just display the name and module instance name.
558 * Override this to do something more specific
559 *
560 * @return string
561 */
87fcac8d 562 public function heading_summary() {
563 return get_string('exportingcontentfrom', 'portfolio', $this->display_name() . ': ' . $this->cm->name);
564 }
90636e2b
PL
565
566 /**
93dd2725
RW
567 * Overridden to return the course module context
568 *
569 * @param moodle_page $PAGE global PAGE
90636e2b
PL
570 */
571 public function set_context($PAGE) {
572 $PAGE->set_cm($this->cm);
573 }
87fcac8d 574}