NOMDL fixed typo in variable name
[moodle.git] / lib / portfolio / caller.php
blob7bfeb8252cdc1283149b91ff07361dba607afeb0
1 <?php
2 /**
3 * Moodle - Modular Object-Oriented Dynamic Learning Environment
4 * http://moodle.org
5 * Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * @package core
21 * @subpackage portfolio
22 * @author Penny Leach <penny@catalyst.net.nz>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
24 * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
26 * This file contains the base classes for places in moodle that want to
27 * add export functionality to subclass from.
28 * See http://docs.moodle.org/en/Development:Adding_a_Portfolio_Button_to_a_page
31 defined('MOODLE_INTERNAL') || die();
33 /**
34 * base class for callers
36 * See http://docs.moodle.org/en/Development:Adding_a_Portfolio_Button_to_a_page
37 * {@see also portfolio_module_caller_base}
39 abstract class portfolio_caller_base {
41 /**
42 * stdclass object
43 * course that was active during the caller
45 protected $course;
47 /**
48 * named array of export config
49 * use{@link set_export_config} and {@link get_export_config} to access
51 protected $exportconfig = array();
53 /**
54 * stdclass object
55 * user currently exporting content
57 protected $user;
59 /**
60 * a reference to the exporter object
62 protected $exporter;
64 /**
65 * this can be overridden in subclasses constructors if they want
67 protected $supportedformats;
69 /**
70 * set this for single file exports
72 protected $singlefile;
74 /**
75 * set this for multi file exports
77 protected $multifiles;
79 /**
80 * set this for generated-file exports
82 protected $intendedmimetype;
84 public function __construct($callbackargs) {
85 $expected = call_user_func(array(get_class($this), 'expected_callbackargs'));
86 foreach ($expected as $key => $required) {
87 if (!array_key_exists($key, $callbackargs)) {
88 if ($required) {
89 $a = (object)array('arg' => $key, 'class' => get_class($this));
90 throw new portfolio_caller_exception('missingcallbackarg', 'portfolio', null, $a);
92 continue;
94 $this->{$key} = $callbackargs[$key];
98 /**
99 * if this caller wants any additional config items
100 * they should be defined here.
102 * @param array $mform moodleform object (passed by reference) to add elements to
103 * @param object $instance subclass of portfolio_plugin_base
104 * @param integer $userid id of user exporting content
106 public function export_config_form(&$mform, $instance) {}
110 * whether this caller wants any additional
111 * config during export (eg options or metadata)
113 * @return boolean
115 public function has_export_config() {
116 return false;
120 * just like the moodle form validation function
121 * this is passed in the data array from the form
122 * and if a non empty array is returned, form processing will stop.
124 * @param array $data data from form.
125 * @return array keyvalue pairs - form element => error string
127 public function export_config_validation($data) {}
130 * how long does this reasonably expect to take..
131 * should we offer the user the option to wait..
132 * this is deliberately nonstatic so it can take filesize into account
133 * the portfolio plugin can override this.
134 * (so for example even if a huge file is being sent,
135 * the download portfolio plugin doesn't care )
137 * @return string (see PORTFOLIO_TIME_* constants)
139 public abstract function expected_time();
142 * helper method to calculate expected time for multi or single file exports
144 public function expected_time_file() {
145 if ($this->multifiles) {
146 return portfolio_expected_time_file($this->multifiles);
148 else if ($this->singlefile) {
149 return portfolio_expected_time_file($this->singlefile);
151 return PORTFOLIO_TIME_LOW;
155 * used for displaying the navigation during the export screens.
157 * this function must be implemented, but can really return anything.
158 * an Exporting.. string will be added on the end.
159 * @return array of $extranav and $cm
161 * to pass to build_navigation
164 public abstract function get_navigation();
169 public abstract function get_sha1();
172 * helper function to calculate the sha1 for multi or single file exports
174 public function get_sha1_file() {
175 if (empty($this->singlefile) && empty($this->multifiles)) {
176 throw new portfolio_caller_exception('invalidsha1file', 'portfolio', $this->get_return_url());
178 if ($this->singlefile) {
179 return $this->singlefile->get_contenthash();
181 $sha1s = array();
182 foreach ($this->multifiles as $file) {
183 $sha1s[] = $file->get_contenthash();
185 asort($sha1s);
186 return sha1(implode('', $sha1s));
190 * generic getter for properties belonging to this instance
191 * <b>outside</b> the subclasses
192 * like name, visible etc.
194 public function get($field) {
195 if (property_exists($this, $field)) {
196 return $this->{$field};
198 $a = (object)array('property' => $field, 'class' => get_class($this));
199 throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', $this->get_return_url(), $a);
203 * generic setter for properties belonging to this instance
204 * <b>outside</b> the subclass
205 * like name, visible, etc.
208 public final function set($field, &$value) {
209 if (property_exists($this, $field)) {
210 $this->{$field} =& $value;
211 $this->dirty = true;
212 return true;
214 $a = (object)array('property' => $field, 'class' => get_class($this));
215 throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', $this->get_return_url(), $a);
219 * stores the config generated at export time.
220 * subclasses can retrieve values using
221 * {@link get_export_config}
223 * @param array $config formdata
225 public final function set_export_config($config) {
226 $allowed = array_merge(
227 array('wait', 'hidewait', 'format', 'hideformat'),
228 $this->get_allowed_export_config()
230 foreach ($config as $key => $value) {
231 if (!in_array($key, $allowed)) {
232 $a = (object)array('property' => $key, 'class' => get_class($this));
233 throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a);
235 $this->exportconfig[$key] = $value;
240 * returns a particular export config value.
241 * subclasses shouldn't need to override this
243 * @param string key the config item to fetch
245 public final function get_export_config($key) {
246 $allowed = array_merge(
247 array('wait', 'hidewait', 'format', 'hideformat'),
248 $this->get_allowed_export_config()
250 if (!in_array($key, $allowed)) {
251 $a = (object)array('property' => $key, 'class' => get_class($this));
252 throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a);
254 if (!array_key_exists($key, $this->exportconfig)) {
255 return null;
257 return $this->exportconfig[$key];
261 * Similar to the other allowed_config functions
262 * if you need export config, you must provide
263 * a list of what the fields are.
265 * even if you want to store stuff during export
266 * without displaying a form to the user,
267 * you can use this.
269 * @return array array of allowed keys
271 public function get_allowed_export_config() {
272 return array();
276 * after the user submits their config
277 * they're given a confirm screen
278 * summarising what they've chosen.
280 * this function should return a table of nice strings => values
281 * of what they've chosen
282 * to be displayed in a table.
284 * @return array array of config items.
286 public function get_export_summary() {
287 return false;
291 * called before the portfolio plugin gets control
292 * this function should copy all the files it wants to
293 * the temporary directory, using {@see copy_existing_file}
294 * or {@see write_new_file}
296 public abstract function prepare_package();
299 * helper function to copy files into the temp area
300 * for single or multi file exports.
302 public function prepare_package_file() {
303 if (empty($this->singlefile) && empty($this->multifiles)) {
304 throw new portfolio_caller_exception('invalidpreparepackagefile', 'portfolio', $this->get_return_url());
306 if ($this->singlefile) {
307 return $this->exporter->copy_existing_file($this->singlefile);
309 foreach ($this->multifiles as $file) {
310 $this->exporter->copy_existing_file($file);
315 * array of formats this caller supports
316 * the intersection of what this function returns
317 * and what the selected portfolio plugin supports
318 * will be used
319 * use the constants PORTFOLIO_FORMAT_*
321 * @return array list of formats
324 public final function supported_formats() {
325 $basic = $this->base_supported_formats();
326 if (empty($this->supportedformats)) {
327 $specific = array();
328 } else if (!is_array($this->supportedformats)) {
329 debugging(get_class($this) . ' has set a non array value of member variable supported formats - working around but should be fixed in code');
330 $specific = array($this->supportedformats);
331 } else {
332 $specific = $this->supportedformats;
334 return portfolio_most_specific_formats($specific, $basic);
337 public static function base_supported_formats() {
338 throw new coding_exception('base_supported_formats() method needs to be overridden in each subclass of portfolio_caller_base');
342 * this is the "return to where you were" url
344 * @return string url
346 public abstract function get_return_url();
349 * callback to do whatever capability checks required
350 * in the caller (called during the export process
352 public abstract function check_permissions();
355 * nice name to display to the user about this caller location
357 public static function display_name() {
358 throw new coding_exception('display_name() method needs to be overridden in each subclass of portfolio_caller_base');
362 * return a string to put at the header summarising this export
363 * by default, just the display name (usually just 'assignment' or something unhelpful
365 * @return string
367 public function heading_summary() {
368 return get_string('exportingcontentfrom', 'portfolio', $this->display_name());
371 public abstract function load_data();
374 * set up the required files for this export.
375 * this supports either passing files directly
376 * or passing area arguments directly through
377 * to the files api using file_storage::get_area_files
379 * @param mixed $ids one of:
380 * - single file id
381 * - single stored_file object
382 * - array of file ids or stored_file objects
383 * - null
384 * @param int $contextid (optional), passed to {@link see file_storage::get_area_files}
385 * @param string $component (optional), passed to {@link see file_storage::get_area_files}
386 * @param string $filearea (optional), passed to {@link see file_storage::get_area_files}
387 * @param int $itemid (optional), passed to {@link see file_storage::get_area_files}
388 * @param string $sort (optional), passed to {@link see file_storage::get_area_files}
389 * @param bool $includedirs (optional), passed to {@link see file_storage::get_area_files}
391 public function set_file_and_format_data($ids=null /* ..pass arguments to area files here. */) {
392 $args = func_get_args();
393 array_shift($args); // shift off $ids
394 if (empty($ids) && count($args) == 0) {
395 return;
397 $files = array();
398 $fs = get_file_storage();
399 if (!empty($ids)) {
400 if (is_numeric($ids) || $ids instanceof stored_file) {
401 $ids = array($ids);
403 foreach ($ids as $id) {
404 if ($id instanceof stored_file) {
405 $files[] = $id;
406 } else {
407 $files[] = $fs->get_file_by_id($id);
410 } else if (count($args) != 0) {
411 if (count($args) < 4) {
412 throw new portfolio_caller_exception('invalidfileareaargs', 'portfolio');
414 $files = array_values(call_user_func_array(array($fs, 'get_area_files'), $args));
416 switch (count($files)) {
417 case 0: return;
418 case 1: {
419 $this->singlefile = $files[0];
420 return;
422 default: {
423 $this->multifiles = $files;
429 * the button-location always knows best
430 * what the formats are... so it should be trusted.
432 * @param array $formats array of PORTFOLIO_FORMAT_XX
434 public function set_formats_from_button($formats) {
435 $base = $this->base_supported_formats();
436 if (count($base) != count($formats)
437 || count($base) != count(array_intersect($base, $formats))) {
438 $this->supportedformats = portfolio_most_specific_formats($formats, $base);
439 return;
441 // in the case where the button hasn't actually set anything,
442 // we need to run through again and resolve conflicts
443 // TODO revisit this comment - it looks to me like it's lying
444 $this->supportedformats = portfolio_most_specific_formats($formats, $formats);
448 * adds a new format to the list of supported formats.
449 * handles removing conflicting and less specific
450 * formats at the same time.
452 * @param string $format one of PORTFOLIO_FORMAT_XX
454 * @return void
456 protected function add_format($format) {
457 if (in_array($format, $this->supportedformats)) {
458 return;
460 $this->supportedformats = portfolio_most_specific_formats(array($format), $this->supportedformats);
463 public function get_mimetype() {
464 if ($this->singlefile instanceof stored_file) {
465 return $this->singlefile->get_mimetype();
466 } else if (!empty($this->intendedmimetype)) {
467 return $this->intendedmimetype;
472 * array of arguments the caller expects to be passed through to it
473 * this must be keyed on the argument name, and the array value is a boolean,
474 * whether it is required, or just optional
475 * eg array(
476 * id => true,
477 * somethingelse => false,
480 * @return array
482 public static function expected_callbackargs() {
483 throw new coding_exception('expected_callbackargs() method needs to be overridden in each subclass of portfolio_caller_base');
488 * return the context for this export. used for $PAGE->set_context
490 * @return stdclass
492 public abstract function set_context($PAGE);
496 * base class for module callers
497 * this just implements a few of the abstract functions
498 * from portfolio_caller_base so that caller authors
499 * don't need to.
501 * See http://docs.moodle.org/en/Development:Adding_a_Portfolio_Button_to_a_page
502 * {@see also portfolio_caller_base}
504 abstract class portfolio_module_caller_base extends portfolio_caller_base {
507 * coursemodule object
508 * set this in the constructor like
509 * $this->cm = get_coursemodule_from_instance('forum', $this->forum->id);
511 protected $cm;
515 * int cmid
517 protected $id;
520 * stdclass course object
522 protected $course;
525 * navigation passed to print_header
526 * override this to do something more specific than the module view page
528 public function get_navigation() {
529 $extranav = array('name' => $this->cm->name, 'link' => $this->get_return_url());
530 return array($extranav, $this->cm);
534 * the url to return to after export or on cancel
535 * defaults to the module 'view' page
536 * override this if it's deeper inside the module
538 public function get_return_url() {
539 global $CFG;
540 return $CFG->wwwroot . '/mod/' . $this->cm->modname . '/view.php?id=' . $this->cm->id;
544 * override the parent get function
545 * to make sure when we're asked for a course
546 * we retrieve the object from the database as needed
548 public function get($key) {
549 if ($key != 'course') {
550 return parent::get($key);
552 global $DB;
553 if (empty($this->course)) {
554 $this->course = $DB->get_record('course', array('id' => $this->cm->course));
556 return $this->course;
560 * return a string to put at the header summarising this export
561 * by default, just the display name and the module instance name
562 * override this to do something more specific
564 public function heading_summary() {
565 return get_string('exportingcontentfrom', 'portfolio', $this->display_name() . ': ' . $this->cm->name);
569 * overridden to return the course module context
571 public function set_context($PAGE) {
572 $PAGE->set_cm($this->cm);