Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
    abstract public 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
    abstract public function get_navigation();
142
 
143
    /**
144
     * Helper function to get sha1
145
     */
146
    abstract public 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
    final public 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
    final public 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
    final public 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
    abstract public 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
    final public 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
    abstract public 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
    abstract public 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
    abstract public 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
    abstract public 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
}