MDL-14591 - split portfoliolib into logical parts for better readability
Especially as some of the functions shared by base classes (portfolio plugin vs caller) were the same it was getting a bit unweildy to have all this in one file.
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
/**
|
||||
* Moodle - Modular Object-Oriented Dynamic Learning Environment
|
||||
* http://moodle.org
|
||||
* Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package moodle
|
||||
* @subpackage portfolio
|
||||
* @author Penny Leach <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This file contains the base classes for places in moodle that want to
|
||||
* add export functionality to subclass from.
|
||||
* See http://docs.moodle.org/en/Development:Adding_a_Portfolio_Button_to_a_page
|
||||
*/
|
||||
|
||||
/**
|
||||
* base class for callers
|
||||
*
|
||||
* See http://docs.moodle.org/en/Development:Adding_a_Portfolio_Button_to_a_page
|
||||
* {@see also portfolio_module_caller_base}
|
||||
*/
|
||||
abstract class portfolio_caller_base {
|
||||
|
||||
/**
|
||||
* stdclass object
|
||||
* course that was active during the caller
|
||||
*/
|
||||
protected $course;
|
||||
|
||||
/**
|
||||
* named array of export config
|
||||
* use{@link set_export_config} and {@link get_export_config} to access
|
||||
*/
|
||||
protected $exportconfig;
|
||||
|
||||
/**
|
||||
* stdclass object
|
||||
* user currently exporting content
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* a reference to the exporter object
|
||||
*/
|
||||
protected $exporter;
|
||||
|
||||
/**
|
||||
* this can be overridden in subclasses constructors if they want
|
||||
*/
|
||||
protected $supportedformats;
|
||||
|
||||
/**
|
||||
* if this caller wants any additional config items
|
||||
* they should be defined here.
|
||||
*
|
||||
* @param array $mform moodleform object (passed by reference) to add elements to
|
||||
* @param object $instance subclass of portfolio_plugin_base
|
||||
* @param integer $userid id of user exporting content
|
||||
*/
|
||||
public function export_config_form(&$mform, $instance) {}
|
||||
|
||||
|
||||
/**
|
||||
* whether this caller wants any additional
|
||||
* config during export (eg options or metadata)
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has_export_config() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* just like the moodle form validation function
|
||||
* this is passed in the data array from the form
|
||||
* and if a non empty array is returned, form processing will stop.
|
||||
*
|
||||
* @param array $data data from form.
|
||||
* @return array keyvalue pairs - form element => error string
|
||||
*/
|
||||
public function export_config_validation($data) {}
|
||||
|
||||
/**
|
||||
* how long does this reasonably expect to take..
|
||||
* should we offer the user the option to wait..
|
||||
* this is deliberately nonstatic so it can take filesize into account
|
||||
* the portfolio plugin can override this.
|
||||
* (so for exmaple even if a huge file is being sent,
|
||||
* the download portfolio plugin doesn't care )
|
||||
*
|
||||
* @return string (see PORTFOLIO_TIME_* constants)
|
||||
*/
|
||||
public abstract function expected_time();
|
||||
|
||||
/**
|
||||
* used for displaying the navigation during the export screens.
|
||||
*
|
||||
* this function must be implemented, but can really return anything.
|
||||
* an Exporting.. string will be added on the end.
|
||||
* @return array of $extranav and $cm
|
||||
*
|
||||
* to pass to build_navigation
|
||||
*
|
||||
*/
|
||||
public abstract function get_navigation();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public abstract function get_sha1();
|
||||
|
||||
/*
|
||||
* generic getter for properties belonging to this instance
|
||||
* <b>outside</b> the subclasses
|
||||
* like name, visible etc.
|
||||
*/
|
||||
public function get($field) {
|
||||
if (property_exists($this, $field)) {
|
||||
return $this->{$field};
|
||||
}
|
||||
$a = (object)array('property' => $field, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', $this->get_return_url(), $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* generic setter for properties belonging to this instance
|
||||
* <b>outside</b> the subclass
|
||||
* like name, visible, etc.
|
||||
*
|
||||
*/
|
||||
public final function set($field, &$value) {
|
||||
if (property_exists($this, $field)) {
|
||||
$this->{$field} =& $value;
|
||||
$this->dirty = true;
|
||||
return true;
|
||||
}
|
||||
$a = (object)array('property' => $field, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', $this->get_return_url(), $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* stores the config generated at export time.
|
||||
* subclasses can retrieve values using
|
||||
* {@link get_export_config}
|
||||
*
|
||||
* @param array $config formdata
|
||||
*/
|
||||
public final function set_export_config($config) {
|
||||
$allowed = array_merge(
|
||||
array('wait', 'hidewait', 'format', 'hideformat'),
|
||||
$this->get_allowed_export_config()
|
||||
);
|
||||
foreach ($config as $key => $value) {
|
||||
if (!in_array($key, $allowed)) {
|
||||
$a = (object)array('property' => $key, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a);
|
||||
}
|
||||
$this->exportconfig[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a particular export config value.
|
||||
* subclasses shouldn't need to override this
|
||||
*
|
||||
* @param string key the config item to fetch
|
||||
*/
|
||||
public final function get_export_config($key) {
|
||||
$allowed = array_merge(
|
||||
array('wait', 'hidewait', 'format', 'hideformat'),
|
||||
$this->get_allowed_export_config()
|
||||
);
|
||||
if (!in_array($key, $allowed)) {
|
||||
$a = (object)array('property' => $key, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a);
|
||||
}
|
||||
if (!array_key_exists($key, $this->exportconfig)) {
|
||||
return null;
|
||||
}
|
||||
return $this->exportconfig[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the other allowed_config functions
|
||||
* if you need export config, you must provide
|
||||
* a list of what the fields are.
|
||||
*
|
||||
* even if you want to store stuff during export
|
||||
* without displaying a form to the user,
|
||||
* you can use this.
|
||||
*
|
||||
* @return array array of allowed keys
|
||||
*/
|
||||
public function get_allowed_export_config() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* after the user submits their config
|
||||
* they're given a confirm screen
|
||||
* summarising what they've chosen.
|
||||
*
|
||||
* this function should return a table of nice strings => values
|
||||
* of what they've chosen
|
||||
* to be displayed in a table.
|
||||
*
|
||||
* @return array array of config items.
|
||||
*/
|
||||
public function get_export_summary() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* called before the portfolio plugin gets control
|
||||
* this function should copy all the files it wants to
|
||||
* the temporary directory, using {@see copy_existing_file}
|
||||
* or {@see write_new_file}
|
||||
*/
|
||||
public abstract function prepare_package();
|
||||
|
||||
/**
|
||||
* array of formats this caller supports
|
||||
* the intersection of what this function returns
|
||||
* and what the selected portfolio plugin supports
|
||||
* will be used
|
||||
* use the constants PORTFOLIO_FORMAT_*
|
||||
* if $caller is passed, that can be used for more specific guesses
|
||||
* as this function <b>must</b> be called statically.
|
||||
*
|
||||
* @return array list of formats
|
||||
*/
|
||||
public static function supported_formats($caller=null) {
|
||||
if ($caller && $formats = $caller->get('supportedformats')) {
|
||||
if (is_array($formats)) {
|
||||
return $formats;
|
||||
}
|
||||
debugging(get_class($caller) . ' has set a non array value of member variable supported formats - working around but should be fixed in code');
|
||||
return array($formats);
|
||||
}
|
||||
return array(PORTFOLIO_FORMAT_FILE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* this is the "return to where you were" url
|
||||
*
|
||||
* @return string url
|
||||
*/
|
||||
public abstract function get_return_url();
|
||||
|
||||
/**
|
||||
* callback to do whatever capability checks required
|
||||
* in the caller (called during the export process
|
||||
*/
|
||||
public abstract function check_permissions();
|
||||
|
||||
/**
|
||||
* nice name to display to the user about this caller location
|
||||
*/
|
||||
public abstract static function display_name();
|
||||
|
||||
/**
|
||||
* return a string to put at the header summarising this export
|
||||
* by default, just the display name (usually just 'assignment' or something unhelpful
|
||||
*/
|
||||
public function heading_summary() {
|
||||
return get_string('exportingcontentfrom', 'portfolio', $this->display_name());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* base class for module callers
|
||||
* this just implements a few of the abstract functions
|
||||
* from portfolio_caller_base so that caller authors
|
||||
* don't need to.
|
||||
*
|
||||
* See http://docs.moodle.org/en/Development:Adding_a_Portfolio_Button_to_a_page
|
||||
* {@see also portfolio_caller_base}
|
||||
*/
|
||||
abstract class portfolio_module_caller_base extends portfolio_caller_base {
|
||||
|
||||
/**
|
||||
* coursemodule object
|
||||
* set this in the constructor like
|
||||
* $this->cm = get_coursemodule_from_instance('forum', $this->forum->id);
|
||||
*/
|
||||
protected $cm;
|
||||
|
||||
/**
|
||||
* stdclass course object
|
||||
*/
|
||||
protected $course;
|
||||
|
||||
/**
|
||||
* navigation passed to print_header
|
||||
* override this to do something more specific than the module view page
|
||||
*/
|
||||
public function get_navigation() {
|
||||
$extranav = array('name' => $this->cm->name, 'link' => $this->get_return_url());
|
||||
return array($extranav, $this->cm);
|
||||
}
|
||||
|
||||
/**
|
||||
* the url to return to after export or on cancel
|
||||
* defaults to the module 'view' page
|
||||
* override this if it's deeper inside the module
|
||||
*/
|
||||
public function get_return_url() {
|
||||
global $CFG;
|
||||
return $CFG->wwwroot . '/mod/' . $this->cm->modname . '/view.php?id=' . $this->cm->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* override the parent get function
|
||||
* to make sure when we're asked for a course
|
||||
* we retrieve the object from the database as needed
|
||||
*/
|
||||
public function get($key) {
|
||||
if ($key != 'course') {
|
||||
return parent::get($key);
|
||||
}
|
||||
global $DB;
|
||||
if (empty($this->course)) {
|
||||
$this->course = $DB->get_record('course', array('id' => $this->cm->course));
|
||||
}
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
/**
|
||||
* return a string to put at the header summarising this export
|
||||
* by default, just hte display name and the module instance name
|
||||
* override this to do something more specific
|
||||
*/
|
||||
public function heading_summary() {
|
||||
return get_string('exportingcontentfrom', 'portfolio', $this->display_name() . ': ' . $this->cm->name);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
/**
|
||||
* Moodle - Modular Object-Oriented Dynamic Learning Environment
|
||||
* http://moodle.org
|
||||
* Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package moodle
|
||||
* @subpackage portfolio
|
||||
* @author Penny Leach <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This file contains all the defined constants to do with portfolios.
|
||||
*/
|
||||
|
||||
// ************************************************** //
|
||||
// EXPORT STAGE CONSTANTS
|
||||
// ************************************************** //
|
||||
|
||||
/**
|
||||
* display a form to the user
|
||||
* this one might not be used if neither
|
||||
* the plugin, or the caller has any config.
|
||||
*/
|
||||
define('PORTFOLIO_STAGE_CONFIG', 1);
|
||||
|
||||
/**
|
||||
* summarise the form and ask for confirmation
|
||||
* if we skipped PORTFOLIO_STAGE_CONFIG,
|
||||
* just confirm the send.
|
||||
*/
|
||||
define('PORTFOLIO_STAGE_CONFIRM', 2);
|
||||
|
||||
/**
|
||||
* either queue the event and skip to PORTFOLIO_STAGE_FINISHED
|
||||
* or continue to PORTFOLIO_STAGE_PACKAGE
|
||||
*/
|
||||
|
||||
define('PORTFOLIO_STAGE_QUEUEORWAIT', 3);
|
||||
|
||||
/**
|
||||
* package up the various bits
|
||||
* during this stage both the caller
|
||||
* and the plugin get their package methods called
|
||||
*/
|
||||
define('PORTFOLIO_STAGE_PACKAGE', 4);
|
||||
|
||||
/*
|
||||
* the portfolio plugin must send the file
|
||||
*/
|
||||
define('PORTFOLIO_STAGE_SEND', 5);
|
||||
|
||||
/**
|
||||
* cleanup the temporary area
|
||||
*/
|
||||
define('PORTFOLIO_STAGE_CLEANUP', 6);
|
||||
|
||||
/**
|
||||
* display the "finished notification"
|
||||
*/
|
||||
define('PORTFOLIO_STAGE_FINISHED', 7);
|
||||
|
||||
|
||||
|
||||
// ************************************************** //
|
||||
// EXPORT FORMAT CONSTANTS
|
||||
// these should always correspond to a string
|
||||
// in the portfolio module, called format_{$value}
|
||||
// ************************************************** //
|
||||
|
||||
|
||||
/**
|
||||
* file - the most basic fallback format.
|
||||
* this should always be supported
|
||||
* in remote system.s
|
||||
*/
|
||||
define('PORTFOLIO_FORMAT_FILE', 'file');
|
||||
|
||||
/**
|
||||
* moodle backup - the plugin needs to be able to write a complete backup
|
||||
* the caller need to be able to export the particular XML bits to insert
|
||||
* into moodle.xml (?and the file bits if necessary)
|
||||
*/
|
||||
define('PORTFOLIO_FORMAT_MBKP', 'mbkp');
|
||||
|
||||
/**
|
||||
* html - subtype of file
|
||||
*/
|
||||
define('PORTFOLIO_FORMAT_HTML', 'html');
|
||||
|
||||
/**
|
||||
* image - subtype of file
|
||||
*/
|
||||
define('PORTFOLIO_FORMAT_IMAGE', 'image');
|
||||
|
||||
/**
|
||||
* video - subtype of file
|
||||
*/
|
||||
define('PORTFOLIO_FORMAT_VIDEO', 'video');
|
||||
|
||||
/**
|
||||
* text - subtype of file
|
||||
*/
|
||||
define('PORTFOLIO_FORMAT_TEXT', 'text');
|
||||
|
||||
|
||||
// ************************************************** //
|
||||
// EXPORT TIME LEVELS
|
||||
// these should correspond to a string
|
||||
// in the portfolio module, called time_{$value}
|
||||
// ************************************************** //
|
||||
|
||||
|
||||
/**
|
||||
* no delay. don't even offer the user the option
|
||||
* of not waiting for the transfer
|
||||
*/
|
||||
define('PORTFOLIO_TIME_LOW', 'low');
|
||||
|
||||
/**
|
||||
* a small delay. user can still easily opt to
|
||||
* watch this transfer and wait.
|
||||
*/
|
||||
define('PORTFOLIO_TIME_MODERATE', 'moderate');
|
||||
|
||||
/**
|
||||
* slow. the user really should not be given the option
|
||||
* to choose this.
|
||||
*/
|
||||
define('PORTFOLIO_TIME_HIGH', 'high');
|
||||
|
||||
/**
|
||||
* very slow, or immediate transfers not supported
|
||||
*/
|
||||
define('PORTFOLIO_TIME_FORCEQUEUE', 'queue');
|
||||
|
||||
// ************************************************** //
|
||||
// BUTTON FORMATS
|
||||
// available ways to add the portfolio export to a page
|
||||
// ************************************************** //
|
||||
|
||||
/**
|
||||
* a whole form, containing a drop down menu (where necessary)
|
||||
* and a submit button
|
||||
*/
|
||||
define('PORTFOLIO_ADD_FULL_FORM', 1);
|
||||
|
||||
|
||||
/**
|
||||
* a whole form, containing a drop down menu (where necessary)
|
||||
* but has an icon instead of a button to submit
|
||||
*/
|
||||
define('PORTFOLIO_ADD_ICON_FORM', 2);
|
||||
|
||||
/**
|
||||
* just an icon with a link around it (yuk, as will result in a long url
|
||||
* only use where necessary)
|
||||
*/
|
||||
define('PORTFOLIO_ADD_ICON_LINK', 3);
|
||||
|
||||
/**
|
||||
* just some text with a link around it (yuk, as will result in a long url
|
||||
* only use where necessary)
|
||||
*/
|
||||
define('PORTFOLIO_ADD_TEXT_LINK', 4);
|
||||
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Moodle - Modular Object-Oriented Dynamic Learning Environment
|
||||
* http://moodle.org
|
||||
* Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package moodle
|
||||
* @subpackage portfolio
|
||||
* @author Penny Leach <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This file contains all the portfolio exception classes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* top level portfolio exception.
|
||||
* sometimes caught and rethrown as {@see portfolio_export_exception}
|
||||
*/
|
||||
class portfolio_exception extends moodle_exception {}
|
||||
|
||||
/**
|
||||
* exception to throw during an export - will clean up session and tempdata
|
||||
*/
|
||||
class portfolio_export_exception extends portfolio_exception {
|
||||
|
||||
/**
|
||||
* constructor.
|
||||
* @param object $exporter instance of portfolio_exporter (will handle null case)
|
||||
* @param string $errorcode language string key
|
||||
* @param string $module language string module (optional, defaults to moodle)
|
||||
* @param string $continue url to continue to (optional, defaults to wwwroot)
|
||||
* @param mixed $a language string data (optional, defaults to null)
|
||||
*/
|
||||
public function __construct($exporter, $errorcode, $module=null, $continue=null, $a=null) {
|
||||
if (!empty($exporter) && $exporter instanceof portfolio_exporter) {
|
||||
if (empty($continue)) {
|
||||
$caller = $exporter->get('caller');
|
||||
if (!empty($caller) && $caller instanceof portfolio_caller_base) {
|
||||
$continue = $exporter->get('caller')->get_return_url();
|
||||
}
|
||||
}
|
||||
if (!defined('FULLME') || FULLME != 'cron') {
|
||||
$exporter->process_stage_cleanup();
|
||||
}
|
||||
} else {
|
||||
global $SESSION;
|
||||
if (!empty($SESSION->portfolioexport)) {
|
||||
debugging(get_string('exportexceptionnoexporter', 'portfolio'));
|
||||
}
|
||||
}
|
||||
parent::__construct($errorcode, $module, $continue, $a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* exception for callers to throw when they have a problem.
|
||||
* usually caught and rethrown as {@see portfolio_export_exception}
|
||||
*/
|
||||
class portfolio_caller_exception extends portfolio_exception {}
|
||||
|
||||
/**
|
||||
* exception for portfolio plugins to throw when they have a problem.
|
||||
* usually caught and rethrown as {@see portfolio_export_exception}
|
||||
*/
|
||||
class portfolio_plugin_exception extends portfolio_exception {}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,687 @@
|
||||
<?php
|
||||
/**
|
||||
* Moodle - Modular Object-Oriented Dynamic Learning Environment
|
||||
* http://moodle.org
|
||||
* Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package moodle
|
||||
* @subpackage portfolio
|
||||
* @author Penny Leach <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This file contains the class definition for the exporter object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The class that handles the various stages of the actual export
|
||||
* and the communication between the caller and the portfolio plugin.
|
||||
* this is stored in the database between page requests in serialized base64 encoded form
|
||||
* also contains helper methods for the plugin and caller to use (at the end of the file)
|
||||
* {@see get_base_filearea} - where to write files to
|
||||
* {@see write_new_file} - write some content to a file in the export filearea
|
||||
* {@see copy_existing_file} - copy an existing file into the export filearea
|
||||
* {@see get_tempfiles} - return list of all files in the export filearea
|
||||
*/
|
||||
class portfolio_exporter {
|
||||
|
||||
/**
|
||||
* the caller object used during the export
|
||||
*/
|
||||
private $caller;
|
||||
|
||||
/** the portfolio plugin instanced used during the export
|
||||
*/
|
||||
private $instance;
|
||||
|
||||
/**
|
||||
* if there has been no config form displayed to the user
|
||||
*/
|
||||
private $noconfig;
|
||||
|
||||
/**
|
||||
* the navigation to display on the wizard screens
|
||||
* built from build_navigation
|
||||
*/
|
||||
private $navigation;
|
||||
|
||||
/**
|
||||
* the user currently exporting content
|
||||
* always $USER, but more conveniently placed here
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/** the file to include that contains the class defintion
|
||||
* of the portfolio instance plugin
|
||||
* used to re-waken the object after sleep
|
||||
*/
|
||||
public $instancefile;
|
||||
|
||||
/**
|
||||
* the file to include that contains the class definition
|
||||
* of the caller object
|
||||
* used to re-waken the object after sleep
|
||||
*/
|
||||
public $callerfile;
|
||||
|
||||
/**
|
||||
* the current stage of the export
|
||||
*/
|
||||
private $stage;
|
||||
|
||||
/**
|
||||
* whether something (usually the portfolio plugin)
|
||||
* has forced queuing
|
||||
*/
|
||||
private $forcequeue;
|
||||
|
||||
/**
|
||||
* id of this export
|
||||
* matches record in portfolio_tempdata table
|
||||
* and used for itemid for file storage.
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* the session key during the export
|
||||
* used to avoid hijacking transfers
|
||||
*/
|
||||
private $sesskey;
|
||||
|
||||
/**
|
||||
* array of stages that have had the portfolio plugin already steal control from them
|
||||
*/
|
||||
private $alreadystolen;
|
||||
|
||||
/**
|
||||
* construct a new exporter for use
|
||||
*
|
||||
* @param portfolio_plugin_base subclass $instance portfolio instance (passed by reference)
|
||||
* @param portfolio_caller_base subclass $caller portfolio caller (passed by reference)
|
||||
* @param string $callerfile path to callerfile (relative to dataroot)
|
||||
* @param string $navigation result of build_navigation (passed to print_header)
|
||||
*/
|
||||
public function __construct(&$instance, &$caller, $callerfile, $navigation) {
|
||||
$this->instance =& $instance;
|
||||
$this->caller =& $caller;
|
||||
if ($instance) {
|
||||
$this->instancefile = 'portfolio/type/' . $instance->get('plugin') . '/lib.php';
|
||||
$this->instance->set('exporter', $this);
|
||||
}
|
||||
$this->callerfile = $callerfile;
|
||||
$this->stage = PORTFOLIO_STAGE_CONFIG;
|
||||
$this->navigation = $navigation;
|
||||
$this->caller->set('exporter', $this);
|
||||
$this->alreadystolen = array();
|
||||
}
|
||||
|
||||
/*
|
||||
* generic getter for properties belonging to this instance
|
||||
* <b>outside</b> the subclasses
|
||||
* like name, visible etc.
|
||||
*/
|
||||
public function get($field) {
|
||||
if (property_exists($this, $field)) {
|
||||
return $this->{$field};
|
||||
}
|
||||
$a = (object)array('property' => $field, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this, 'invalidproperty', 'portfolio', null, $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* generic setter for properties belonging to this instance
|
||||
* <b>outside</b> the subclass
|
||||
* like name, visible, etc.
|
||||
*/
|
||||
public function set($field, &$value) {
|
||||
if (property_exists($this, $field)) {
|
||||
$this->{$field} =& $value;
|
||||
if ($field == 'instance') {
|
||||
$this->instancefile = 'portfolio/type/' . $this->instance->get('plugin') . '/lib.php';
|
||||
$this->instance->set('exporter', $this);
|
||||
}
|
||||
$this->dirty = true;
|
||||
return true;
|
||||
}
|
||||
$a = (object)array('property' => $field, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this, 'invalidproperty', 'portfolio', null, $a);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* process the given stage calling whatever functions are necessary
|
||||
*
|
||||
* @param int $stage (see PORTFOLIO_STAGE_* constants)
|
||||
* @param boolean $alreadystolen used to avoid letting plugins steal control twice.
|
||||
*
|
||||
* @return boolean whether or not to process the next stage. this is important as the function is called recursively.
|
||||
*/
|
||||
public function process_stage($stage, $alreadystolen=false) {
|
||||
$this->set('stage', $stage);
|
||||
if ($alreadystolen) {
|
||||
$this->alreadystolen[$stage] = true;
|
||||
} else {
|
||||
if (!array_key_exists($stage, $this->alreadystolen)) {
|
||||
$this->alreadystolen[$stage] = false;
|
||||
}
|
||||
}
|
||||
$this->save();
|
||||
if (!$this->alreadystolen[$stage] && $url = $this->instance->steal_control($stage)) {
|
||||
redirect($url);
|
||||
break;
|
||||
}
|
||||
|
||||
$waiting = $this->instance->get_export_config('wait');
|
||||
if ($stage > PORTFOLIO_STAGE_QUEUEORWAIT && empty($waiting)) {
|
||||
$stage = PORTFOLIO_STAGE_FINISHED;
|
||||
}
|
||||
$functionmap = array(
|
||||
PORTFOLIO_STAGE_CONFIG => 'config',
|
||||
PORTFOLIO_STAGE_CONFIRM => 'confirm',
|
||||
PORTFOLIO_STAGE_QUEUEORWAIT => 'queueorwait',
|
||||
PORTFOLIO_STAGE_PACKAGE => 'package',
|
||||
PORTFOLIO_STAGE_CLEANUP => 'cleanup',
|
||||
PORTFOLIO_STAGE_SEND => 'send',
|
||||
PORTFOLIO_STAGE_FINISHED => 'finished'
|
||||
);
|
||||
|
||||
$function = 'process_stage_' . $functionmap[$stage];
|
||||
try {
|
||||
if ($this->$function()) {
|
||||
// if we get through here it means control was returned
|
||||
// as opposed to wanting to stop processing
|
||||
// eg to wait for user input.
|
||||
$this->save();
|
||||
$stage++;
|
||||
return $this->process_stage($stage);
|
||||
} else {
|
||||
$this->save();
|
||||
return false;
|
||||
}
|
||||
} catch (portfolio_caller_exception $e) {
|
||||
portfolio_export_rethrow_exception($this, $e);
|
||||
} catch (portfolio_plugin_exception $e) {
|
||||
portfolio_export_rethrow_exception($this, $e);
|
||||
} catch (portfolio_export_exception $e) {
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
debugging(get_string('thirdpartyexception', 'portfolio', get_class($e)));
|
||||
portfolio_export_rethrow_exception($this, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function to return the portfolio instance
|
||||
*
|
||||
* @return portfolio_plugin_base subclass
|
||||
*/
|
||||
public function instance() {
|
||||
return $this->instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function to return the caller object
|
||||
*
|
||||
* @return portfolio_caller_base subclass
|
||||
*/
|
||||
public function caller() {
|
||||
return $this->caller;
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the 'config' stage of the export
|
||||
*
|
||||
* @return boolean whether or not to process the next stage. this is important as the control function is called recursively.
|
||||
*/
|
||||
public function process_stage_config() {
|
||||
|
||||
$pluginobj = $callerobj = null;
|
||||
if ($this->instance->has_export_config()) {
|
||||
$pluginobj = $this->instance;
|
||||
}
|
||||
if ($this->caller->has_export_config()) {
|
||||
$callerobj = $this->caller;
|
||||
}
|
||||
$formats = portfolio_supported_formats_intersect($this->caller->supported_formats($this->caller), $this->instance->supported_formats());
|
||||
$expectedtime = $this->instance->expected_time($this->caller->expected_time());
|
||||
if (count($formats) == 0) {
|
||||
// something went wrong, we should not have gotten this far.
|
||||
throw new portfolio_export_exception($this, 'nocommonformats', 'portfolio', null, get_class($this->caller));
|
||||
}
|
||||
// even if neither plugin or caller wants any config, we have to let the user choose their format, and decide to wait.
|
||||
if ($pluginobj || $callerobj || count($formats) > 1 || ($expectedtime != PORTFOLIO_TIME_LOW && $expectedtime != PORTFOLIO_TIME_FORCEQUEUE)) {
|
||||
$customdata = array(
|
||||
'instance' => $this->instance,
|
||||
'plugin' => $pluginobj,
|
||||
'caller' => $callerobj,
|
||||
'userid' => $this->user->id,
|
||||
'formats' => $formats,
|
||||
'expectedtime' => $expectedtime,
|
||||
);
|
||||
$mform = new portfolio_export_form('', $customdata);
|
||||
if ($mform->is_cancelled()){
|
||||
$this->cancel_request();
|
||||
} else if ($fromform = $mform->get_data()){
|
||||
if (!confirm_sesskey()) {
|
||||
throw new portfolio_export_exception($this, 'confirmsesskeybad');
|
||||
}
|
||||
$pluginbits = array();
|
||||
$callerbits = array();
|
||||
foreach ($fromform as $key => $value) {
|
||||
if (strpos($key, 'plugin_') === 0) {
|
||||
$pluginbits[substr($key, 7)] = $value;
|
||||
} else if (strpos($key, 'caller_') === 0) {
|
||||
$callerbits[substr($key, 7)] = $value;
|
||||
}
|
||||
}
|
||||
$callerbits['format'] = $pluginbits['format'] = $fromform->format;
|
||||
$pluginbits['wait'] = $fromform->wait;
|
||||
if ($expectedtime == PORTFOLIO_TIME_LOW) {
|
||||
$pluginbits['wait'] = 1;
|
||||
$pluginbits['hidewait'] = 1;
|
||||
} else if ($expectedtime == PORTFOLIO_TIME_FORCEQUEUE) {
|
||||
$pluginbits['wait'] = 0;
|
||||
$pluginbits['hidewait'] = 1;
|
||||
$this->forcequeue = true;
|
||||
}
|
||||
$callerbits['hideformat'] = $pluginbits['hideformat'] = (count($formats) == 1);
|
||||
$this->caller->set_export_config($callerbits);
|
||||
$this->instance->set_export_config($pluginbits);
|
||||
return true;
|
||||
} else {
|
||||
$this->print_header('configexport');
|
||||
print_simple_box_start();
|
||||
$mform->display();
|
||||
print_simple_box_end();
|
||||
print_footer();
|
||||
return false;;
|
||||
}
|
||||
} else {
|
||||
$this->noexportconfig = true;
|
||||
$format = array_shift($formats);
|
||||
$config = array(
|
||||
'hidewait' => 1,
|
||||
'wait' => (($expectedtime == PORTFOLIO_TIME_LOW) ? 1 : 0),
|
||||
'format' => $format,
|
||||
'hideformat' => 1
|
||||
);
|
||||
$this->instance->set_export_config($config);
|
||||
$this->caller->set_export_config(array('format' => $format, 'hideformat' => 1));
|
||||
if ($expectedtime == PORTFOLIO_TIME_FORCEQUEUE) {
|
||||
$this->forcequeue = true;
|
||||
}
|
||||
return true;
|
||||
// do not break - fall through to confirm
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the 'confirm' stage of the export
|
||||
*
|
||||
* @return boolean whether or not to process the next stage. this is important as the control function is called recursively.
|
||||
*/
|
||||
public function process_stage_confirm() {
|
||||
global $CFG, $DB;
|
||||
|
||||
$previous = $DB->get_records(
|
||||
'portfolio_log',
|
||||
array(
|
||||
'userid' => $this->user->id,
|
||||
'portfolio' => $this->instance->get('id'),
|
||||
'caller_sha1' => $this->caller->get_sha1(),
|
||||
)
|
||||
);
|
||||
if (isset($this->noexportconfig) && empty($previous)) {
|
||||
return true;
|
||||
}
|
||||
$strconfirm = get_string('confirmexport', 'portfolio');
|
||||
$yesurl = $CFG->wwwroot . '/portfolio/add.php?stage=' . PORTFOLIO_STAGE_QUEUEORWAIT;
|
||||
$nourl = $CFG->wwwroot . '/portfolio/add.php?cancel=1';
|
||||
$this->print_header('confirmexport');
|
||||
print_simple_box_start();
|
||||
print_heading(get_string('confirmsummary', 'portfolio'), '', 4);
|
||||
$mainsummary = array();
|
||||
if (!$this->instance->get_export_config('hideformat')) {
|
||||
$mainsummary[get_string('selectedformat', 'portfolio')] = get_string('format_' . $this->instance->get_export_config('format'), 'portfolio');
|
||||
}
|
||||
if (!$this->instance->get_export_config('hidewait')) {
|
||||
$mainsummary[get_string('selectedwait', 'portfolio')] = get_string(($this->instance->get_export_config('wait') ? 'yes' : 'no'));
|
||||
}
|
||||
if ($previous) {
|
||||
$previousstr = '';
|
||||
foreach ($previous as $row) {
|
||||
$previousstr .= userdate($row->time);
|
||||
if ($row->caller_class != get_class($this->caller)) {
|
||||
require_once($CFG->dirroot . '/' . $row->caller_file);
|
||||
$previousstr .= ' (' . call_user_func(array($row->caller_class, 'display_name')) . ')';
|
||||
}
|
||||
$previousstr .= '<br />';
|
||||
}
|
||||
$mainsummary[get_string('exportedpreviously', 'portfolio')] = $previousstr;
|
||||
}
|
||||
if (!$csummary = $this->caller->get_export_summary()) {
|
||||
$csummary = array();
|
||||
}
|
||||
if (!$isummary = $this->instance->get_export_summary()) {
|
||||
$isummary = array();
|
||||
}
|
||||
$mainsummary = array_merge($mainsummary, $csummary, $isummary);
|
||||
$table = new StdClass;
|
||||
$table->data = array();
|
||||
foreach ($mainsummary as $string => $value) {
|
||||
$table->data[] = array($string, $value);
|
||||
}
|
||||
print_table($table);
|
||||
notice_yesno($strconfirm, $yesurl, $nourl);
|
||||
print_simple_box_end();
|
||||
print_footer();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the 'queueornext' stage of the export
|
||||
*
|
||||
* @return boolean whether or not to process the next stage. this is important as the control function is called recursively.
|
||||
*/
|
||||
public function process_stage_queueorwait() {
|
||||
global $SESSION;
|
||||
$wait = $this->instance->get_export_config('wait');
|
||||
if (empty($wait)) {
|
||||
events_trigger('portfolio_send', $this->id);
|
||||
unset($SESSION->portfolioexport);
|
||||
return $this->process_stage_finished(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the 'package' stage of the export
|
||||
*
|
||||
* @return boolean whether or not to process the next stage. this is important as the control function is called recursively.
|
||||
*/
|
||||
public function process_stage_package() {
|
||||
// now we've agreed on a format,
|
||||
// the caller is given control to package it up however it wants
|
||||
// and then the portfolio plugin is given control to do whatever it wants.
|
||||
if (!$this->caller->prepare_package()) {
|
||||
throw new portfolio_export_exception($this, 'callercouldnotpackage', 'portfolio');
|
||||
}
|
||||
if (!$package = $this->instance->prepare_package()) {
|
||||
throw new portfolio_export_exception($this, 'plugincouldnotpackage', 'portfolio');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the 'cleanup' stage of the export
|
||||
*
|
||||
* @return boolean whether or not to process the next stage. this is important as the control function is called recursively.
|
||||
*/
|
||||
public function process_stage_cleanup($pullok=false) {
|
||||
global $CFG, $DB, $SESSION;
|
||||
|
||||
if (!$pullok && $this->get('instance') && !$this->get('instance')->is_push()) {
|
||||
unset($SESSION->portfolioexport);
|
||||
return true;
|
||||
}
|
||||
if ($this->get('instance')) {
|
||||
// might not be set - before export really starts
|
||||
$this->get('instance')->cleanup();
|
||||
}
|
||||
$DB->delete_records('portfolio_tempdata', array('id' => $this->id));
|
||||
$fs = get_file_storage();
|
||||
$fs->delete_area_files(SYSCONTEXTID, 'portfolio_exporter', $this->id);
|
||||
unset($SESSION->portfolioexport);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the 'send' stage of the export
|
||||
*
|
||||
* @return boolean whether or not to process the next stage. this is important as the control function is called recursively.
|
||||
*/
|
||||
public function process_stage_send() {
|
||||
// send the file
|
||||
if (!$this->instance->send_package()) {
|
||||
throw new portfolio_export_exception($this, 'failedtosendpackage', 'portfolio');
|
||||
}
|
||||
// log the transfer
|
||||
global $DB;
|
||||
$l = array(
|
||||
'userid' => $this->user->id,
|
||||
'portfolio' => $this->instance->get('id'),
|
||||
'caller_file' => $this->callerfile,
|
||||
'caller_sha1' => $this->caller->get_sha1(),
|
||||
'caller_class' => get_class($this->caller),
|
||||
'time' => time(),
|
||||
);
|
||||
$DB->insert_record('portfolio_log', $l);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* processes the 'finish' stage of the export
|
||||
*
|
||||
* @return boolean whether or not to process the next stage. this is important as the control function is called recursively.
|
||||
*/
|
||||
public function process_stage_finished($queued=false) {
|
||||
$returnurl = $this->caller->get_return_url();
|
||||
$continueurl = $this->instance->get_continue_url();
|
||||
$extras = $this->instance->get_extra_finish_options();
|
||||
|
||||
$key = 'exportcomplete';
|
||||
if ($queued) {
|
||||
$key = 'exportqueued';
|
||||
if ($this->forcequeue) {
|
||||
$key = 'exportqueuedforced';
|
||||
}
|
||||
}
|
||||
$this->print_header($key, false);
|
||||
if ($returnurl) {
|
||||
echo '<a href="' . $returnurl . '">' . get_string('returntowhereyouwere', 'portfolio') . '</a><br />';
|
||||
}
|
||||
if ($continueurl) {
|
||||
echo '<a href="' . $continueurl . '">' . get_string('continuetoportfolio', 'portfolio') . '</a><br />';
|
||||
}
|
||||
if (is_array($extras)) {
|
||||
foreach ($extras as $link => $string) {
|
||||
echo '<a href="' . $link . '">' . $string . '</a><br />';
|
||||
}
|
||||
}
|
||||
print_footer();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* local print header function to be reused across the export
|
||||
*
|
||||
* @param string $titlestring key for a portfolio language string
|
||||
* @param string $headerstring key for a portfolio language string
|
||||
*/
|
||||
public function print_header($headingstr, $summary=true) {
|
||||
$titlestr = get_string('exporting', 'portfolio');
|
||||
$headerstr = get_string('exporting', 'portfolio');
|
||||
|
||||
print_header($titlestr, $headerstr, $this->navigation);
|
||||
print_heading(get_string($headingstr, 'portfolio'));
|
||||
|
||||
if (!$summary) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_simple_box_start();
|
||||
echo $this->caller->heading_summary();
|
||||
print_simple_box_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* cancels a potfolio request and cleans up the tempdata
|
||||
* and redirects the user back to where they started
|
||||
*/
|
||||
public function cancel_request() {
|
||||
if (!isset($this)) {
|
||||
return;
|
||||
}
|
||||
$this->process_stage_cleanup(true);
|
||||
redirect($this->caller->get_return_url());
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes out the contents of this object and all its data to the portfolio_tempdata table and sets the 'id' field.
|
||||
*/
|
||||
public function save() {
|
||||
global $DB;
|
||||
if (empty($this->id)) {
|
||||
$r = (object)array(
|
||||
'data' => base64_encode(serialize($this)),
|
||||
'expirytime' => time() + (60*60*24),
|
||||
'userid' => $this->user->id,
|
||||
);
|
||||
$this->id = $DB->insert_record('portfolio_tempdata', $r);
|
||||
$this->save(); // call again so that id gets added to the save data.
|
||||
} else {
|
||||
$DB->set_field('portfolio_tempdata', 'data', base64_encode(serialize($this)), array('id' => $this->id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rewakens the data from the database given the id
|
||||
* makes sure to load the required files with the class definitions
|
||||
*
|
||||
* @param int $id id of data
|
||||
*
|
||||
* @return portfolio_exporter
|
||||
*/
|
||||
public static function rewaken_object($id) {
|
||||
global $DB, $CFG;
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
if (!$data = $DB->get_record('portfolio_tempdata', array('id' => $id))) {
|
||||
throw new portfolio_exception('invalidtempid', 'portfolio');
|
||||
}
|
||||
$exporter = unserialize(base64_decode($data->data));
|
||||
if ($exporter->instancefile) {
|
||||
require_once($CFG->dirroot . '/' . $exporter->instancefile);
|
||||
}
|
||||
require_once($CFG->dirroot . '/' . $exporter->callerfile);
|
||||
$exporter = unserialize(serialize($exporter));
|
||||
return $exporter;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function to create the beginnings of a file_record object
|
||||
* to create a new file in the portfolio_temporary working directory
|
||||
* use {@see write_new_file} or {@see copy_existing_file} externally
|
||||
*
|
||||
* @param string $name filename of new record
|
||||
*/
|
||||
private function new_file_record_base($name) {
|
||||
return (object)array_merge($this->get_base_filearea(), array(
|
||||
'filepath' => '/',
|
||||
'filename' => $name,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* verifies a rewoken object
|
||||
*
|
||||
* checks to make sure it belongs to the same user and session as is currently in use.
|
||||
*
|
||||
* @throws portfolio_exception
|
||||
*/
|
||||
public function verify_rewaken() {
|
||||
global $USER;
|
||||
if ($this->get('user')->id != $USER->id) {
|
||||
throw new portfolio_exception('notyours', 'portfolio');
|
||||
}
|
||||
if (!confirm_sesskey($this->get('sesskey'))) {
|
||||
throw new portfolio_exception('confirmsesskeybad');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* copies a file from somewhere else in moodle
|
||||
* to the portfolio temporary working directory
|
||||
* associated with this export
|
||||
*
|
||||
* @param $oldfile stored_file object
|
||||
* @return new stored_file object
|
||||
*/
|
||||
public function copy_existing_file($oldfile) {
|
||||
$fs = get_file_storage();
|
||||
$file_record = $this->new_file_record_base($oldfile->get_filename());
|
||||
try {
|
||||
return $fs->create_file_from_storedfile($file_record, $oldfile->get_id());
|
||||
} catch (file_exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* writes out some content to a file in the
|
||||
* portfolio temporary working directory
|
||||
* associated with this export
|
||||
*
|
||||
* @param string $content content to write
|
||||
* @param string $name filename to use
|
||||
* @return new stored_file object
|
||||
*/
|
||||
public function write_new_file($content, $name) {
|
||||
$fs = get_file_storage();
|
||||
$file_record = $this->new_file_record_base($name);
|
||||
return $fs->create_file_from_string($file_record, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns an arary of files in the temporary working directory
|
||||
* for this export
|
||||
* always use this instead of the files api directly
|
||||
*
|
||||
* @return array of stored_file objects keyed by name
|
||||
*/
|
||||
public function get_tempfiles() {
|
||||
$fs = get_file_storage();
|
||||
$files = $fs->get_area_files(SYSCONTEXTID, 'portfolio_exporter', $this->id, '', false);
|
||||
if (empty($files)) {
|
||||
return array();
|
||||
}
|
||||
$returnfiles = array();
|
||||
foreach ($files as $f) {
|
||||
$returnfiles[$f->get_filename()] = $f;
|
||||
}
|
||||
return $returnfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the context, filearea, and itemid
|
||||
* parts of a filearea (not filepath) to be used by
|
||||
* plugins if they want to do things like zip up the contents of
|
||||
* the temp area to here, or something that can't be done just using
|
||||
* write_new_file, copy_existing_file or get_tempfiles
|
||||
*
|
||||
* @return array contextid, filearea, itemid are the keys.
|
||||
*/
|
||||
public function get_base_filearea() {
|
||||
return array(
|
||||
'contextid' => SYSCONTEXTID,
|
||||
'filearea' => 'portfolio_exporter',
|
||||
'itemid' => $this->id,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* Moodle - Modular Object-Oriented Dynamic Learning Environment
|
||||
* http://moodle.org
|
||||
* Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package moodle
|
||||
* @subpackage portfolio
|
||||
* @author Penny Leach <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This file contains all the class definitions of the export formats.
|
||||
* They are implemented in php classes rather than just a simpler hash
|
||||
* Because it provides an easy way to do subtyping using php inheritance.
|
||||
*/
|
||||
|
||||
/**
|
||||
* the most basic type - pretty much everything is a subtype
|
||||
*/
|
||||
class portfolio_format_file {
|
||||
public static function mimetypes() {
|
||||
return array(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* image format, subtype of file.
|
||||
*/
|
||||
class portfolio_format_image extends portfolio_format_file {
|
||||
public static function mimetypes() {
|
||||
return mimeinfo_from_icon('type', 'image.gif', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* html format - could be used for an external cms or something
|
||||
*
|
||||
* in case we want to be really specific.
|
||||
*/
|
||||
class portfolio_format_html extends portfolio_format_file {
|
||||
public static function mimetypes() {
|
||||
return array('text/html');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* video format, subtype of file.
|
||||
*
|
||||
* I guess there could be a youtube/google video plugin
|
||||
* and anyway, the flickr plugin can support it already
|
||||
*/
|
||||
class portfolio_format_video extends portfolio_format_file {
|
||||
public static function mimetypes() {
|
||||
return mimeinfo_from_icon('type', 'video.gif', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* class for plain text format.. not sure why we would need this yet
|
||||
* but since resource module wants to export it... we can
|
||||
*/
|
||||
class portfolio_format_text extends portfolio_format_file {
|
||||
public static function mimetypes() {
|
||||
return array('text/plain');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* later.... a moodle plugin might support this.
|
||||
* it's commented out in portfolio_supported_formats so cannot currently be used.
|
||||
*/
|
||||
class portfolio_format_mbkp extends portfolio_format_file {}
|
||||
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
/**
|
||||
* Moodle - Modular Object-Oriented Dynamic Learning Environment
|
||||
* http://moodle.org
|
||||
* Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package moodle
|
||||
* @subpackage portfolio
|
||||
* @author Penny Leach <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This file contains all the form definitions used by the portfolio code.
|
||||
*/
|
||||
|
||||
// make sure we include moodleform first!
|
||||
require_once ($CFG->libdir.'/formslib.php');
|
||||
|
||||
/**
|
||||
* During-export config form.
|
||||
*
|
||||
* This is the form that is actually used while exporting.
|
||||
* Plugins and callers don't get to define their own class
|
||||
* as we have to handle form elements from both places
|
||||
* See the docs here for more information:
|
||||
* http://docs.moodle.org/en/Development:Writing_a_Portfolio_Plugin#has_export_config
|
||||
* http://docs.moodle.org/en/Development:Adding_a_Portfolio_Button_to_a_page#has_export_config
|
||||
*/
|
||||
final class portfolio_export_form extends moodleform {
|
||||
|
||||
public function definition() {
|
||||
|
||||
$mform =& $this->_form;
|
||||
$mform->addElement('hidden', 'stage', PORTFOLIO_STAGE_CONFIG);
|
||||
$mform->addElement('hidden', 'instance', $this->_customdata['instance']->get('id'));
|
||||
|
||||
if (array_key_exists('formats', $this->_customdata) && is_array($this->_customdata['formats'])) {
|
||||
if (count($this->_customdata['formats']) > 1) {
|
||||
$options = array();
|
||||
foreach ($this->_customdata['formats'] as $key) {
|
||||
$options[$key] = get_string('format_' . $key, 'portfolio');
|
||||
}
|
||||
$mform->addElement('select', 'format', get_string('availableformats', 'portfolio'), $options);
|
||||
} else {
|
||||
$f = array_shift($this->_customdata['formats']);
|
||||
$mform->addElement('hidden', 'format', $f);
|
||||
}
|
||||
}
|
||||
|
||||
// only display the option to wait or not if it's applicable
|
||||
if (array_key_exists('expectedtime', $this->_customdata)
|
||||
&& $this->_customdata['expectedtime'] != PORTFOLIO_TIME_LOW
|
||||
&& $this->_customdata['expectedtime'] != PORTFOLIO_TIME_FORCEQUEUE) {
|
||||
$radioarray = array();
|
||||
$radioarray[] = &MoodleQuickForm::createElement('radio', 'wait', '', get_string('wait', 'portfolio'), 1);
|
||||
$radioarray[] = &MoodleQuickForm::createElement('radio', 'wait', '', get_string('dontwait', 'portfolio'), 0);
|
||||
$mform->addGroup($radioarray, 'radioar', get_string('wanttowait_' . $this->_customdata['expectedtime'], 'portfolio') , array(' '), false);
|
||||
$mform->setDefault('wait', 0);
|
||||
} else {
|
||||
if ($this->_customdata['expectedtime'] == PORTFOLIO_TIME_LOW) {
|
||||
$mform->addElement('hidden', 'wait', 1);
|
||||
} else {
|
||||
$mform->addElement('hidden', 'wait', 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('plugin', $this->_customdata) && is_object($this->_customdata['plugin'])) {
|
||||
$this->_customdata['plugin']->export_config_form($mform, $this->_customdata['userid']);
|
||||
}
|
||||
|
||||
if (array_key_exists('caller', $this->_customdata) && is_object($this->_customdata['caller'])) {
|
||||
$this->_customdata['caller']->export_config_form($mform, $this->_customdata['instance'], $this->_customdata['userid']);
|
||||
}
|
||||
|
||||
$this->add_action_buttons(true, get_string('next'));
|
||||
}
|
||||
|
||||
public function validation($data) {
|
||||
|
||||
$errors = array();
|
||||
|
||||
if (array_key_exists('plugin', $this->_customdata) && is_object($this->_customdata['plugin'])) {
|
||||
$pluginerrors = $this->_customdata['plugin']->export_config_validation($data);
|
||||
if (is_array($pluginerrors)) {
|
||||
$errors = $pluginerrors;
|
||||
}
|
||||
}
|
||||
if (array_key_exists('caller', $this->_customdata) && is_object($this->_customdata['caller'])) {
|
||||
$callererrors = $this->_customdata['caller']->export_config_validation($data);
|
||||
if (is_array($callererrors)) {
|
||||
$errors = array_merge($errors, $callererrors);
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin config form
|
||||
*
|
||||
* This form is extendable by plugins who want the admin to be able to configure more than just the name of the instance.
|
||||
* This is NOT done by subclassing this class, see the docs for portfolio_plugin_base for more information:
|
||||
* http://docs.moodle.org/en/Development:Writing_a_Portfolio_Plugin#has_admin_config
|
||||
*/
|
||||
final class portfolio_admin_form extends moodleform {
|
||||
|
||||
protected $instance;
|
||||
protected $plugin;
|
||||
|
||||
public function definition() {
|
||||
global $CFG;
|
||||
$this->plugin = $this->_customdata['plugin'];
|
||||
$this->instance = (isset($this->_customdata['instance'])
|
||||
&& is_subclass_of($this->_customdata['instance'], 'portfolio_plugin_base'))
|
||||
? $this->_customdata['instance'] : null;
|
||||
|
||||
$mform =& $this->_form;
|
||||
$strrequired = get_string('required');
|
||||
|
||||
$mform->addElement('hidden', 'edit', ($this->instance) ? $this->instance->get('id') : 0);
|
||||
$mform->addElement('hidden', 'new', $this->plugin);
|
||||
$mform->addElement('hidden', 'plugin', $this->plugin);
|
||||
|
||||
// let the plugin add the fields they want (either statically or not)
|
||||
if (portfolio_static_function($this->plugin, 'has_admin_config')) {
|
||||
if (!$this->instance) {
|
||||
$insane = portfolio_instance_sanity_check($this->instance);
|
||||
portfolio_static_function($this->plugin, 'admin_config_form', $mform);
|
||||
} else {
|
||||
$insane = portfolio_plugin_sanity_check($this->plugin);
|
||||
$this->instance->admin_config_form($mform);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($insane) && is_array($insane)) {
|
||||
$insane = array_shift($insane);
|
||||
}
|
||||
if (isset($insane) && is_string($insane)) { // something went wrong, warn...
|
||||
$mform->addElement('warning', 'insane', null, get_string($insane, 'portfolio_' . $this->plugin));
|
||||
}
|
||||
|
||||
$mform->addElement('text', 'name', get_string('name'), 'maxlength="100" size="30"');
|
||||
$mform->addRule('name', $strrequired, 'required', null, 'client');
|
||||
|
||||
|
||||
// and set the data if we have some.
|
||||
if ($this->instance) {
|
||||
$data = array('name' => $this->instance->get('name'));
|
||||
foreach ($this->instance->get_allowed_config() as $config) {
|
||||
$data[$config] = $this->instance->get_config($config);
|
||||
}
|
||||
$this->set_data($data);
|
||||
} else {
|
||||
$this->set_data(array('name' => portfolio_static_function($this->plugin, 'get_name')));
|
||||
}
|
||||
|
||||
$this->add_action_buttons(true, get_string('save', 'portfolio'));
|
||||
}
|
||||
|
||||
public function validation($data) {
|
||||
global $DB;
|
||||
|
||||
$errors = array();
|
||||
if ($DB->count_records('portfolio_instance', array('name' => $data['name'], 'plugin' => $data['plugin'])) > 1) {
|
||||
$errors = array('name' => get_string('err_uniquename', 'portfolio'));
|
||||
}
|
||||
|
||||
$pluginerrors = array();
|
||||
if ($this->instance) {
|
||||
$pluginerrors = $this->instance->admin_config_validation($data);
|
||||
}
|
||||
else {
|
||||
$pluginerrors = portfolio_static_function($this->plugin, 'admin_config_validation', $data);
|
||||
}
|
||||
if (is_array($pluginerrors)) {
|
||||
$errors = array_merge($errors, $pluginerrors);
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User config form.
|
||||
*
|
||||
* This is the form for letting the user configure an instance of a plugin.
|
||||
* In order to extend this, you don't subclass this in the plugin..
|
||||
* see the docs in portfolio_plugin_base for more information:
|
||||
* http://docs.moodle.org/en/Development:Writing_a_Portfolio_Plugin#has_user_config
|
||||
*/
|
||||
final class portfolio_user_form extends moodleform {
|
||||
|
||||
protected $instance;
|
||||
protected $userid;
|
||||
|
||||
public function definition() {
|
||||
$this->instance = $this->_customdata['instance'];
|
||||
$this->userid = $this->_customdata['userid'];
|
||||
|
||||
$this->_form->addElement('hidden', 'config', $this->instance->get('id'));
|
||||
|
||||
$this->instance->user_config_form($this->_form, $this->userid);
|
||||
|
||||
$data = array();
|
||||
foreach ($this->instance->get_allowed_user_config() as $config) {
|
||||
$data[$config] = $this->instance->get_user_config($config, $this->userid);
|
||||
}
|
||||
$this->set_data($data);
|
||||
$this->add_action_buttons(true, get_string('save', 'portfolio'));
|
||||
}
|
||||
|
||||
public function validation($data) {
|
||||
|
||||
$errors = $this->instance->user_config_validation($data);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Form that just contains the dropdown menu of available instances
|
||||
*
|
||||
* This is not used by portfolio_add_button, but on the first step of the export
|
||||
* if the plugin instance has not yet been selected.
|
||||
*/
|
||||
class portfolio_instance_select extends moodleform {
|
||||
|
||||
private $caller;
|
||||
|
||||
function definition() {
|
||||
$this->caller = $this->_customdata['caller'];
|
||||
$options = portfolio_instance_select(
|
||||
portfolio_instances(),
|
||||
$this->caller->supported_formats($this->caller),
|
||||
get_class($this->caller),
|
||||
'instance',
|
||||
true,
|
||||
true
|
||||
);
|
||||
if (empty($options)) {
|
||||
debugging('noavailableplugins', 'portfolio');
|
||||
return false;
|
||||
}
|
||||
$mform =& $this->_form;
|
||||
$mform->addElement('select', 'instance', get_string('selectplugin', 'portfolio'), $options);
|
||||
$this->add_action_buttons(true, get_string('next'));
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,750 @@
|
||||
<?php
|
||||
/**
|
||||
* Moodle - Modular Object-Oriented Dynamic Learning Environment
|
||||
* http://moodle.org
|
||||
* Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package moodle
|
||||
* @subpackage portfolio
|
||||
* @author Penny Leach <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
|
||||
* @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
|
||||
*
|
||||
* This file contains the base classes for portfolio plugins to inherit from:
|
||||
* portfolio_plugin_pull_base and portfolio_plugin_push_base
|
||||
* which both in turn inherit from portfolio_plugin_base.
|
||||
* See http://docs.moodle.org/en/Development:Writing_a_Portfolio_Plugin
|
||||
*/
|
||||
|
||||
/**
|
||||
* the base class for portfolio plugins
|
||||
* all plugins must subclass this
|
||||
* either via {@see portfolio_plugin_pull_base} or {@see portfolio_plugin_push_base}
|
||||
*/
|
||||
abstract class portfolio_plugin_base {
|
||||
|
||||
/**
|
||||
* boolean
|
||||
* whether this object needs writing out to the database
|
||||
*/
|
||||
protected $dirty;
|
||||
|
||||
/**
|
||||
* integer
|
||||
* id of instance
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* string
|
||||
* name of instance
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* string
|
||||
* plugin this instance belongs to
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* boolean
|
||||
* whether this instance is visible or not
|
||||
*/
|
||||
protected $visible;
|
||||
|
||||
/**
|
||||
* named array
|
||||
* admin configured config
|
||||
* use {@link set_config} and {@get_config} to access
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
*
|
||||
* user config cache
|
||||
* named array of named arrays
|
||||
* keyed on userid and then on config field => value
|
||||
* use {@link get_user_config} and {@link set_user_config} to access.
|
||||
*/
|
||||
protected $userconfig;
|
||||
|
||||
/**
|
||||
* named array
|
||||
* export config during export
|
||||
* use {@link get_export_config} and {@link set export_config} to access.
|
||||
*/
|
||||
protected $exportconfig;
|
||||
|
||||
/**
|
||||
* stdclass object
|
||||
* user currently exporting data
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* a reference to the exporter object
|
||||
*/
|
||||
protected $exporter;
|
||||
|
||||
/**
|
||||
* array of formats this portfolio supports
|
||||
* the intersection of what this function returns
|
||||
* and what the caller supports will be used
|
||||
* use the constants PORTFOLIO_FORMAT_*
|
||||
*
|
||||
* @return array list of formats
|
||||
*/
|
||||
public static function supported_formats() {
|
||||
return array(PORTFOLIO_FORMAT_FILE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* how long does this reasonably expect to take..
|
||||
* should we offer the user the option to wait..
|
||||
* this is deliberately nonstatic so it can take filesize into account
|
||||
*
|
||||
* @param string $callertime - what the caller thinks
|
||||
* the portfolio plugin instance
|
||||
* is given the final say
|
||||
* because it might be (for example) download.
|
||||
* @return string (see PORTFOLIO_TIME_* constants)
|
||||
*/
|
||||
public abstract function expected_time($callertime);
|
||||
|
||||
/**
|
||||
* is this plugin push or pill.
|
||||
* if push, cleanup will be called directly after send_package
|
||||
* if not, cleanup will be called after portfolio/file.php is requested
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public abstract function is_push();
|
||||
|
||||
public static abstract function get_name();
|
||||
|
||||
/**
|
||||
* check sanity of plugin
|
||||
* if this function returns something non empty, ALL instances of your plugin
|
||||
* will be set to invisble and not be able to be set back until it's fixed
|
||||
*
|
||||
* @return mixed - string = error string KEY (must be inside plugin_$yourplugin) or 0/false if you're ok
|
||||
*/
|
||||
public static function plugin_sanity_check() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check sanity of instances
|
||||
* if this function returns something non empty, the instance will be
|
||||
* set to invislbe and not be able to be set back until it's fixed.
|
||||
*
|
||||
* @return mixed - string = error string KEY (must be inside plugin_$yourplugin) or 0/false if you're ok
|
||||
*/
|
||||
public function instance_sanity_check() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* does this plugin need any configuration by the administrator?
|
||||
*
|
||||
* if you override this to return true,
|
||||
* you <b>must</b> implement {@see admin_config_form}
|
||||
*/
|
||||
public static function has_admin_config() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* can this plugin be configured by the user in their profile?
|
||||
*
|
||||
* if you override this to return true,
|
||||
* you <b>must</b> implement {@see user_config_form}
|
||||
*/
|
||||
public function has_user_config() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* does this plugin need configuration during export time?
|
||||
*
|
||||
* if you override this to return true,
|
||||
* you <b>must</b> implement {@see export_config_form}
|
||||
*/
|
||||
public function has_export_config() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* just like the moodle form validation function
|
||||
* this is passed in the data array from the form
|
||||
* and if a non empty array is returned, form processing will stop.
|
||||
*
|
||||
* @param array $data data from form.
|
||||
* @return array keyvalue pairs - form element => error string
|
||||
*/
|
||||
public function export_config_validation() {}
|
||||
|
||||
/**
|
||||
* just like the moodle form validation function
|
||||
* this is passed in the data array from the form
|
||||
* and if a non empty array is returned, form processing will stop.
|
||||
*
|
||||
* @param array $data data from form.
|
||||
* @return array keyvalue pairs - form element => error string
|
||||
*/
|
||||
public function user_config_validation() {}
|
||||
|
||||
/**
|
||||
* sets the export time config from the moodle form.
|
||||
* you can also use this to set export config that
|
||||
* isn't actually controlled by the user
|
||||
* eg things that your subclasses want to keep in state
|
||||
* across the export.
|
||||
* keys must be in {@see get_allowed_export_config}
|
||||
*
|
||||
* this is deliberately not final (see boxnet plugin)
|
||||
*
|
||||
* @param array $config named array of config items to set.
|
||||
*/
|
||||
public function set_export_config($config) {
|
||||
$allowed = array_merge(
|
||||
array('wait', 'hidewait', 'format', 'hideformat'),
|
||||
$this->get_allowed_export_config()
|
||||
);
|
||||
foreach ($config as $key => $value) {
|
||||
if (!in_array($key, $allowed)) {
|
||||
$a = (object)array('property' => $key, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a);
|
||||
}
|
||||
$this->exportconfig[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gets an export time config value.
|
||||
* subclasses should not override this.
|
||||
*
|
||||
* @param string key field to fetch
|
||||
*
|
||||
* @return string config value
|
||||
*
|
||||
*/
|
||||
public final function get_export_config($key) {
|
||||
$allowed = array_merge(
|
||||
array('hidewait', 'wait', 'format', 'hideformat'),
|
||||
$this->get_allowed_export_config()
|
||||
);
|
||||
if (!in_array($key, $allowed)) {
|
||||
$a = (object)array('property' => $key, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidexportproperty', 'portfolio', $this->get_return_url(), $a);
|
||||
}
|
||||
if (!array_key_exists($key, $this->exportconfig)) {
|
||||
return null;
|
||||
}
|
||||
return $this->exportconfig[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* after the user submits their config
|
||||
* they're given a confirm screen
|
||||
* summarising what they've chosen.
|
||||
*
|
||||
* this function should return a table of nice strings => values
|
||||
* of what they've chosen
|
||||
* to be displayed in a table.
|
||||
*
|
||||
* @return array array of config items.
|
||||
*/
|
||||
public function get_export_summary() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* called after the caller has finished having control
|
||||
* of its prepare_package function.
|
||||
* this function should read all the files from the portfolio
|
||||
* working file area and zip them and send them or whatever it wants.
|
||||
* {@see get_tempfiles} to get the list of files.
|
||||
*
|
||||
*/
|
||||
public abstract function prepare_package();
|
||||
|
||||
/**
|
||||
* this is the function that is responsible for sending
|
||||
* the package to the remote system,
|
||||
* or whatever request is necessary to initiate the transfer.
|
||||
*
|
||||
* @return boolean success
|
||||
*/
|
||||
public abstract function send_package();
|
||||
|
||||
|
||||
/**
|
||||
* once everything is done and the user
|
||||
* has the finish page displayed to them
|
||||
* the base class takes care of printing them
|
||||
* "return to where you are" or "continue to portfolio" links
|
||||
* this function allows for exta finish options from the plugin
|
||||
*
|
||||
* @return array named array of links => titles
|
||||
*/
|
||||
public function get_extra_finish_options() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* the url for the user to continue to their portfolio
|
||||
*
|
||||
* @return string url or false.
|
||||
*/
|
||||
public abstract function get_continue_url();
|
||||
|
||||
/**
|
||||
* mform to display to the user in their profile
|
||||
* if your plugin can't be configured by the user,
|
||||
* (see {@link has_user_config})
|
||||
* don't bother overriding this function
|
||||
*
|
||||
* @param moodleform $mform passed by reference, add elements to it
|
||||
*/
|
||||
public function user_config_form(&$mform) {}
|
||||
|
||||
/**
|
||||
* mform to display to the admin configuring the plugin.
|
||||
* if your plugin can't be configured by the admin,
|
||||
* (see {@link} has_admin_config)
|
||||
* don't bother overriding this function
|
||||
*
|
||||
* this function can be called statically or non statically,
|
||||
* depending on whether it's creating a new instance (statically),
|
||||
* or editing an existing one (non statically)
|
||||
*
|
||||
* @param moodleform $mform passed by reference, add elements to it.
|
||||
*/
|
||||
public function admin_config_form(&$mform) {}
|
||||
|
||||
/**
|
||||
* just like the moodle form validation function
|
||||
* this is passed in the data array from the form
|
||||
* and if a non empty array is returned, form processing will stop.
|
||||
*
|
||||
* @param array $data data from form.
|
||||
* @return array keyvalue pairs - form element => error string
|
||||
*/
|
||||
public function admin_config_validation($data) {}
|
||||
/**
|
||||
* mform to display to the user exporting data using this plugin.
|
||||
* if your plugin doesn't need user input at this time,
|
||||
* (see {@link has_export_config}
|
||||
* don't bother overrideing this function
|
||||
*
|
||||
* @param moodleform $mform passed by reference, add elements to it.
|
||||
*/
|
||||
public function export_config_form(&$mform) {}
|
||||
|
||||
/**
|
||||
* override this if your plugin doesn't allow multiple instances
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function allows_multiple() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* If at any point the caller wants to steal control
|
||||
* it can, by returning something that isn't false
|
||||
* in this function
|
||||
* The controller will redirect to whatever url
|
||||
* this function returns.
|
||||
* Afterwards, you can redirect back to portfolio/add.php?postcontrol=1
|
||||
* and {@link post_control} is called before the rest of the processing
|
||||
* for the stage is done
|
||||
*
|
||||
* @param int stage to steal control *before* (see constants PARAM_STAGE_*}
|
||||
*
|
||||
* @return boolean or string url
|
||||
*/
|
||||
public function steal_control($stage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* after a plugin has elected to steal control,
|
||||
* and control returns to portfolio/add.php|postcontrol=1,
|
||||
* this function is called, and passed the stage that was stolen control from
|
||||
* and the request (get and post but not cookie) parameters
|
||||
* this is useful for external systems that need to redirect the user back
|
||||
* with some extra data in the url (like auth tokens etc)
|
||||
* for an example implementation, see boxnet portfolio plugin.
|
||||
*
|
||||
* @param int $stage the stage before control was stolen
|
||||
* @param array $params a merge of $_GET and $_POST
|
||||
*
|
||||
*/
|
||||
|
||||
public function post_control($stage, $params) { }
|
||||
|
||||
/**
|
||||
* this function creates a new instance of a plugin
|
||||
* saves it in the database, saves the config
|
||||
* and returns it.
|
||||
* you shouldn't need to override it
|
||||
* unless you're doing something really funky
|
||||
*
|
||||
* @param string $plugin portfolio plugin to create
|
||||
* @param string $name name of new instance
|
||||
* @param array $config what the admin config form returned
|
||||
*
|
||||
* @return object subclass of portfolio_plugin_base
|
||||
*/
|
||||
public static function create_instance($plugin, $name, $config) {
|
||||
global $DB, $CFG;
|
||||
$new = (object)array(
|
||||
'plugin' => $plugin,
|
||||
'name' => $name,
|
||||
);
|
||||
if (!portfolio_static_function($plugin, 'allows_multiple')) {
|
||||
// check we don't have one already
|
||||
if ($DB->record_exists('portfolio_instance', array('plugin' => $plugin))) {
|
||||
throw new portfolio_exception('multipledisallowed', 'portfolio', '', $plugin);
|
||||
}
|
||||
}
|
||||
$newid = $DB->insert_record('portfolio_instance', $new);
|
||||
require_once($CFG->dirroot . '/portfolio/type/' . $plugin . '/lib.php');
|
||||
$classname = 'portfolio_plugin_' . $plugin;
|
||||
$obj = new $classname($newid);
|
||||
$obj->set_config($config);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* construct a plugin instance
|
||||
* subclasses should not need to override this unless they're doing something special
|
||||
* and should call parent::__construct afterwards
|
||||
*
|
||||
* @param int $instanceid id of plugin instance to construct
|
||||
* @param mixed $record stdclass object or named array - use this is you already have the record to avoid another query
|
||||
*
|
||||
* @return object subclass of portfolio_plugin_base
|
||||
*/
|
||||
public function __construct($instanceid, $record=null) {
|
||||
global $DB;
|
||||
if (!$record) {
|
||||
if (!$record = $DB->get_record('portfolio_instance', array('id' => $instanceid))) {
|
||||
throw new portfolio_exception('invalidinstance', 'portfolio');
|
||||
}
|
||||
}
|
||||
foreach ((array)$record as $key =>$value) {
|
||||
if (property_exists($this, $key)) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
$this->config = new StdClass;
|
||||
$this->userconfig = array();
|
||||
$this->exportconfig = array();
|
||||
foreach ($DB->get_records('portfolio_instance_config', array('instance' => $instanceid)) as $config) {
|
||||
$this->config->{$config->name} = $config->value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* a list of fields that can be configured per instance.
|
||||
* this is used for the save handlers of the config form
|
||||
* and as checks in set_config and get_config
|
||||
*
|
||||
* @return array array of strings (config item names)
|
||||
*/
|
||||
public static function get_allowed_config() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* a list of fields that can be configured by the user.
|
||||
* this is used for the save handlers in the config form
|
||||
* and as checks in set_user_config and get_user_config.
|
||||
*
|
||||
* @return array array of strings (config field names)
|
||||
*/
|
||||
public function get_allowed_user_config() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* a list of fields that can be configured by the user.
|
||||
* this is used for the save handlers in the config form
|
||||
* and as checks in set_export_config and get_export_config.
|
||||
*
|
||||
* @return array array of strings (config field names)
|
||||
*/
|
||||
public function get_allowed_export_config() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* saves (or updates) the config stored in portfolio_instance_config.
|
||||
* you shouldn't need to override this unless you're doing something funky.
|
||||
*
|
||||
* @param array $config array of config items.
|
||||
*/
|
||||
public final function set_config($config) {
|
||||
global $DB;
|
||||
foreach ($config as $key => $value) {
|
||||
// try set it in $this first
|
||||
try {
|
||||
$this->set($key, $value);
|
||||
continue;
|
||||
} catch (portfolio_exception $e) { }
|
||||
if (!in_array($key, $this->get_allowed_config())) {
|
||||
$a = (object)array('property' => $key, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidconfigproperty', 'portfolio', null, $a);
|
||||
}
|
||||
if (!isset($this->config->{$key})) {
|
||||
$DB->insert_record('portfolio_instance_config', (object)array(
|
||||
'instance' => $this->id,
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
));
|
||||
} else if ($this->config->{$key} != $value) {
|
||||
$DB->set_field('portfolio_instance_config', 'value', $value, array('name' => $key, 'instance' => $this->id));
|
||||
}
|
||||
$this->config->{$key} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the value of a particular config item
|
||||
*
|
||||
* @param string $key key to fetch
|
||||
*
|
||||
* @return string the corresponding value
|
||||
*/
|
||||
public final function get_config($key) {
|
||||
if (!in_array($key, $this->get_allowed_config())) {
|
||||
$a = (object)array('property' => $key, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidconfigproperty', 'portfolio', null, $a);
|
||||
}
|
||||
if (isset($this->config->{$key})) {
|
||||
return $this->config->{$key};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the value of a config item for a particular user
|
||||
*
|
||||
* @param string $key key to fetch
|
||||
* @param integer $userid id of user (defaults to current)
|
||||
*
|
||||
* @return string the corresponding value
|
||||
*
|
||||
*/
|
||||
public final function get_user_config($key, $userid=0) {
|
||||
global $DB;
|
||||
|
||||
if (empty($userid)) {
|
||||
$userid = $this->user->id;
|
||||
}
|
||||
|
||||
if ($key != 'visible') { // handled by the parent class
|
||||
if (!in_array($key, $this->get_allowed_user_config())) {
|
||||
$a = (object)array('property' => $key, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invaliduserproperty', 'portfolio', null, $a);
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($userid, $this->userconfig)) {
|
||||
$this->userconfig[$userid] = (object)array_fill_keys(array_merge(array('visible'), $this->get_allowed_user_config()), null);
|
||||
foreach ($DB->get_records('portfolio_instance_user', array('instance' => $this->id, 'userid' => $userid)) as $config) {
|
||||
$this->userconfig[$userid]->{$config->name} = $config->value;
|
||||
}
|
||||
}
|
||||
if ($this->userconfig[$userid]->visible === null) {
|
||||
$this->set_user_config(array('visible' => 1), $userid);
|
||||
}
|
||||
return $this->userconfig[$userid]->{$key};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* sets config options for a given user
|
||||
*
|
||||
* @param mixed $config array or stdclass containing key/value pairs to set
|
||||
* @param integer $userid userid to set config for (defaults to current)
|
||||
*
|
||||
*/
|
||||
public final function set_user_config($config, $userid=0) {
|
||||
global $DB;
|
||||
|
||||
if (empty($userid)) {
|
||||
$userid = $this->user->id;
|
||||
}
|
||||
|
||||
foreach ($config as $key => $value) {
|
||||
if ($key != 'visible' && !in_array($key, $this->get_allowed_user_config())) {
|
||||
$a = (object)array('property' => $key, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invaliduserproperty', 'portfolio', null, $a);
|
||||
}
|
||||
if (!$existing = $DB->get_record('portfolio_instance_user', array('instance'=> $this->id, 'userid' => $userid, 'name' => $key))) {
|
||||
$DB->insert_record('portfolio_instance_user', (object)array(
|
||||
'instance' => $this->id,
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
'userid' => $userid,
|
||||
));
|
||||
} else if ($existing->value != $value) {
|
||||
$DB->set_field('portfolio_instance_user', 'value', $value, array('name' => $key, 'instance' => $this->id, 'userid' => $userid));
|
||||
}
|
||||
$this->userconfig[$userid]->{$key} = $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* generic getter for properties belonging to this instance
|
||||
* <b>outside</b> the subclasses
|
||||
* like name, visible etc.
|
||||
*
|
||||
*/
|
||||
public final function get($field) {
|
||||
if (property_exists($this, $field)) {
|
||||
return $this->{$field};
|
||||
}
|
||||
$a = (object)array('property' => $field, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', null, $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* generic setter for properties belonging to this instance
|
||||
* <b>outside</b> the subclass
|
||||
* like name, visible, etc.
|
||||
*
|
||||
*/
|
||||
public final function set($field, $value) {
|
||||
if (property_exists($this, $field)) {
|
||||
$this->{$field} =& $value;
|
||||
$this->dirty = true;
|
||||
return true;
|
||||
}
|
||||
$a = (object)array('property' => $field, 'class' => get_class($this));
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'invalidproperty', 'portfolio', null, $a);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* saves stuff that's been stored in the object to the database
|
||||
* you shouldn't need to override this
|
||||
* unless you're doing something really funky.
|
||||
* and if so, call parent::save when you're done.
|
||||
*/
|
||||
public function save() {
|
||||
global $DB;
|
||||
if (!$this->dirty) {
|
||||
return true;
|
||||
}
|
||||
$fordb = new StdClass();
|
||||
foreach (array('id', 'name', 'plugin', 'visible') as $field) {
|
||||
$fordb->{$field} = $this->{$field};
|
||||
}
|
||||
$DB->update_record('portfolio_instance', $fordb);
|
||||
$this->dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes everything from the database about this plugin instance.
|
||||
* you shouldn't need to override this unless you're storing stuff
|
||||
* in your own tables. and if so, call parent::delete when you're done.
|
||||
*/
|
||||
public function delete() {
|
||||
global $DB;
|
||||
$DB->delete_records('portfolio_instance_config', array('instance' => $this->get('id')));
|
||||
$DB->delete_records('portfolio_instance_user', array('instance' => $this->get('id')));
|
||||
$DB->delete_records('portfolio_instance', array('id' => $this->get('id')));
|
||||
$this->dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* perform any required cleanup functions
|
||||
*/
|
||||
public function cleanup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function mnet_publishes() {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* class to inherit from for 'push' type plugins
|
||||
* eg those that send the file via a HTTP post or whatever
|
||||
*/
|
||||
abstract class portfolio_plugin_push_base extends portfolio_plugin_base {
|
||||
|
||||
public function is_push() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* class to inherit from for 'pull' type plugins
|
||||
* eg those that write a file and wait for the remote system to request it
|
||||
* from portfolio/file.php
|
||||
* if you're using this you must do $this->set('file', $file) so that it can be served.
|
||||
*/
|
||||
abstract class portfolio_plugin_pull_base extends portfolio_plugin_base {
|
||||
|
||||
protected $file;
|
||||
|
||||
public function is_push() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* before sending the file when the pull is requested, verify the request parameters
|
||||
* these might include a token of some sort of whatever
|
||||
*
|
||||
* @param array request parameters (POST wins over GET)
|
||||
*/
|
||||
public abstract function verify_file_request_params($params);
|
||||
|
||||
/**
|
||||
* called from portfolio/file.php
|
||||
* this function sends the stored file out to the browser
|
||||
* the default is to just use send_stored_file,
|
||||
* but other implementations might do something different
|
||||
* for example, send back the file base64 encoded and encrypted
|
||||
* mahara does this but in the response to an xmlrpc request
|
||||
* rather than through file.php
|
||||
*/
|
||||
public function send_file() {
|
||||
$file = $this->get('file');
|
||||
if (!($file instanceof stored_file)) {
|
||||
throw new portfolio_export_exception($this->get('exporter'), 'filenotfound', 'portfolio');
|
||||
}
|
||||
send_stored_file($file, 0, 0, true, null, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
+94
-2161
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,8 @@ class portfolio_plugin_download extends portfolio_plugin_pull_base {
|
||||
$zipper = new zip_packer();
|
||||
|
||||
$filename = 'portfolio-export.zip';
|
||||
if ($newfile = $zipper->archive_to_storage($files, SYSCONTEXTID, 'portfolio_exporter', $this->exporter->get('id'), '/final/', $filename, $this->user->id)) {
|
||||
list ($contextid, $filearea, $itemid) = $this->get('exporter')->get_base_filearea();
|
||||
if ($newfile = $zipper->archive_to_storage($files, $contextid, $filearea, $itemid, '/final/', $filename, $this->user->id)) {
|
||||
$this->set('file', $newfile);
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user