Skip to content

Commit

Permalink
portfolio: MDL-21030 - leap2a portfolio format support.
Browse files Browse the repository at this point in the history
This commit includes:

- leap2a portfolio format, and xml writer
- proof of concept implementation in forum and assignment modules
- a lot of refactoring of the portfolio formats in general:
    - addition of "abstract" formats - this is necessary for plugins to be able to support groups of formats
    - addition of the idea of portfolio formats conflicting with eachother - eg richhtml & plainhtml

it touches modules other than assignment and forum, because the format api
changed and now each place in moodle that exports portfolio content has to deal
with the formats it supports slightly differently.

At the moment the Mahara portfolio still doesn't support this format, because I
haven't done the Mahara side yet. The "file download" plugin supports it
though.

Still todo:

- Add support for the other places in Moodle (glossary, data, etc)
- Write tests, once the rest of the portfolio tests have been updated to use the new DB mocking stuff
- Fix a bunch of TODOs
  • Loading branch information
Penny Leach committed Dec 3, 2009
1 parent 9cbced1 commit 59dd457
Show file tree
Hide file tree
Showing 26 changed files with 993 additions and 110 deletions.
2 changes: 1 addition & 1 deletion lang/en_utf8/data.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
$string['participants'] = 'Participants';
$string['picture'] = 'Picture';
$string['pleaseaddsome'] = 'Please create some below or <a href=\"$a\">choose a predefined set</a> to get started.';
$string['portfolionotfile'] = 'Export to a portfolio rather than a file (csv only)';
$string['portfolionotfile'] = 'Export to a portfolio rather than a file (csv and leap2a only)';
$string['presetinfo'] = 'Saving as a preset will publish this template. Other users may be able to use it in their databases.';
$string['presets'] = 'Presets';
$string['radiobutton'] = 'Radio buttons';
Expand Down
1 change: 1 addition & 0 deletions lang/en_utf8/portfolio.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
$string['format_mbkp'] = 'Moodle Backup Format';
$string['format_video'] = 'Video';
$string['format_text'] = 'Plain Text';
$string['format_leap2a'] = 'LEAP2A portfolio format';
$string['hidden'] = 'Hidden';
$string['highfilesizethreshold'] = 'High transfer filesize';
$string['highfilesizethresholddesc'] = 'Filesizes over this threshold will be considered to take a high amount of time to transfer';
Expand Down
10 changes: 10 additions & 0 deletions lang/en_utf8/portfolio_format_leap2a.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

$string['entryalreadyexists'] = 'You tried to add a LEAP2A entry with an id ($a) that already exists in this feed';
$string['feedtitle'] = 'LEAP2A export from Moodle for $a';
$string['invalidentryfield'] = 'You tried to set an entry field that didn\'t exist ($a) or you can\'t set directly';
$string['invalidentryid'] = 'You tried to access an entry by an id that didn\'t exist ($a)';
$string['missingfield'] = 'Required LEAP2A entry field $a missing';
$string['nonexistantlink'] = 'A LEAP2A entry ($a->from) tried to link to a non existing entry ($a->to) with rel $a->rel';
$string['overwritingselection'] = 'Overwriting the original type of an entry ($a) to selection in make_selection';
$string['selflink'] = 'A LEAP2A entry ($a->id) tried to link to itself with rel $a->rel';
81 changes: 72 additions & 9 deletions lib/portfolio/caller.php
Original file line number Diff line number Diff line change
Expand Up @@ -314,18 +314,22 @@ public function prepare_package_file() {
* 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;
}
public final function supported_formats() {
$basic = $this->base_supported_formats();
if (empty($this->supportedformats)) {
$specific = array();
} else if (!is_array($this->supportedformats)) {
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);
$specific = array($formats);
} else {
$specific = $this->supportedformats;
}
return array(PORTFOLIO_FORMAT_FILE);
return portfolio_most_specific_formats($specific, $basic);
}

public abstract static function base_supported_formats();

/**
* this is the "return to where you were" url
Expand Down Expand Up @@ -392,7 +396,7 @@ public function set_file_and_format_data($ids=null /* ..pass arguments to area f
}
}
} else if (count($args) != 0) {
if (count($args) != 3) {
if (count($args) < 3) {
throw new portfolio_caller_exception('invalidfileareaargs', 'portfolio');
}
$files = array_values(call_user_func_array(array($fs, 'get_area_files'), $args));
Expand All @@ -401,7 +405,6 @@ public function set_file_and_format_data($ids=null /* ..pass arguments to area f
case 0: return;
case 1: {
$this->singlefile = $files[0];
$this->supportedformats = array(portfolio_format_from_file($this->singlefile));
return;
}
default: {
Expand All @@ -410,6 +413,66 @@ public function set_file_and_format_data($ids=null /* ..pass arguments to area f
}
}

/**
* the button-location always knows best
* what the formats are... so it should be trusted.
*
* @param array $formats array of PORTFOLIO_FORMAT_XX
*/
public function set_formats_from_button($formats) {
$base = $this->base_supported_formats();
if (count($base) != count($formats)
|| count($base) != count(array_intersect($base, $formats))) {
return $this->supportedformats = portfolio_most_specific_formats($formats, $base);
}
// in the case where the button hasn't actually set anything,
// we need to run through again and resolve conflicts
$removed = array();
foreach ($formats as $f1key => $f1) {
if (in_array($f1, $removed)) {
continue;
}
$f1obj = portfolio_format_object($f1);
foreach ($formats as $f2key => $f2) {
if (in_array($f2, $removed)) {
continue;
}
if ($f1obj->conflicts($f2)) {
unset($formats[$f2key]);
$removed[] = $f2;
}
}
}
$this->supportedformats = $formats;
}

/**
* adds a new format to the list of supported formats.
* handles removing conflicting and less specific
* formats at the same time.
*
* @param string $format one of PORTFOLIO_FORMAT_XX
*
* @return void
*/
protected function add_format($format) {
if (in_array($format, $this->supportedformats)) {
return;
}
$formatobj = portfolio_format_object($format);
foreach ($this->supportedformats as $key => $f) {
$f2obj = portfolio_format_object($f);
if ($formatobj->conflicts($f)) {
unset($this->supportedformats[$key]);
}
$class = get_class($f2obj);
if ($formatobj instanceof $class) {
unset($this->supportedformats[$key]);
}
}
$this->supportedformats[] = $format;
}

/**
* array of arguments the caller expects to be passed through to it
* this must be keyed on the argument name, and the array value is a boolean,
Expand Down
11 changes: 11 additions & 0 deletions lib/portfolio/constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@
*/
define('PORTFOLIO_FORMAT_PRESENTATION', 'presentation');

/**
* abstract - just used to say, "we support all these"
*/
define('PORTFOLIO_FORMAT_RICH', 'rich');

/**
* leap2a http://wiki.cetis.ac.uk/LEAP_2.0
* supported by mahara and and others
*/
define('PORTFOLIO_FORMAT_LEAP2A', 'leap2a');

// ************************************************** //
// EXPORT TIME LEVELS
// these should correspond to a string
Expand Down
5 changes: 5 additions & 0 deletions lib/portfolio/exceptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,8 @@ class portfolio_plugin_exception extends portfolio_exception {}
* exception for interacting with the button class
*/
class portfolio_button_exception extends portfolio_exception {}

/**
* leap2a exception - for invalid api calls
*/
class portfolio_format_leap2a_exception extends portfolio_exception {}
4 changes: 3 additions & 1 deletion lib/portfolio/exporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ public function __construct(&$instance, &$caller, $callerfile) {
public function get($field) {
if ($field == 'format') {
return portfolio_format_object($this->format);
} else if ($field == 'formatclass') {
return $this->format;
}
if (property_exists($this, $field)) {
return $this->{$field};
Expand Down Expand Up @@ -287,7 +289,7 @@ public function process_stage_config() {
if ($this->caller->has_export_config()) {
$callerobj = $this->caller;
}
$formats = portfolio_supported_formats_intersect($this->caller->supported_formats($this->caller), $this->instance->supported_formats());
$formats = portfolio_supported_formats_intersect($this->caller->supported_formats(), $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.
Expand Down
101 changes: 96 additions & 5 deletions lib/portfolio/formats.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,34 @@ public static function get_file_directory() {
* given a file, return a snippet of markup in whatever format
* to link to that file.
* usually involves the path given by {@link get_file_directory}
* this is not supported in subclasses of portfolio_format_file
* since they're all just single files.
*/
public static function file_output($file) {
return '';
throw new portfolio_exception('fileoutputnotsupported', 'portfolio');
}

/**
* whether this format conflicts with the given format
* this is used for the case where an export location
* "generally" supports something like FORMAT_PLAINHTML
* but then in a specific export case, must add attachments
* which means that FORMAT_RICHHTML is supported in that case
* which implies removing support for FORMAT_PLAINHTML.
* Note that conflicts don't have to be bi-directional
* (eg FORMAT_PLAINHTML conflicts with FORMAT_RICHHTML
* but not the other way around) and things within the class hierarchy
* are resolved automatically anyway.
*
* This is really just between subclasses of format_rich
* and subclasses of format_file.
*
* @param string $format one of the FORMAT_XX constants
*
* @return boolean
*/
public static function conflicts($format) {
return false;
}
}

Expand All @@ -77,9 +102,14 @@ public static function mimetypes() {
* in case we want to be really specific.
*/
class portfolio_format_plainhtml extends portfolio_format_file {

public static function mimetypes() {
return array('text/html');
}

public static function conflicts($format) {
return ($format == PORTFOLIO_FORMAT_RICHHTML);
}
}

/**
Expand All @@ -104,16 +134,38 @@ class portfolio_format_text extends portfolio_format_file {
public static function mimetypes() {
return array('text/plain');
}

public static function conflicts($format ) {
return ($format == PORTFOLIO_FORMAT_PLAINHTML
|| $format == PORTFOLIO_FORMAT_RICHHTML);
}
}

/**
* base class for rich formats.
* these are multipart - eg things with attachments
*/
class portfolio_format_rich {
abstract class portfolio_format_rich {
public static function mimetypes() {
return array(null);
}

public static function conflicts($format) {
return false;
}

/**
* given a file, return a snippet of markup in whatever format
* to link to that file.
* usually involves the path given by {@link get_file_directory}
* this is not supported in subclasses of portfolio_format_file
* since they're all just single files.
*
* @param stored_file $file the file to link to
* @param mixed $extras any extra arguments
*/
public static abstract function file_output($file, $extras=null);

}

/**
Expand All @@ -124,23 +176,62 @@ class portfolio_format_richhtml extends portfolio_format_rich {
public static function get_file_directory() {
return 'site_files';
}
public static function file_output($file) {
public static function file_output($file, $extras=null) {
$path = self::get_file_directory() . '/' . $file->get_filename();
if (in_array($file->get_mimetype(), portfolio_format_image::mimetypes())) {
return '<img src="' . $path . '" alt="' . $file->get_filename() . '" />';
}
return '<a href="' . $path . '">' . $file->get_filename() . '</a>';
}
public static function conflicts($format) { // TODO revisit the conflict with file, since we zip here
return ($format == PORTFOLIO_FORMAT_PLAINHTML || $format == PORTFOLIO_FORMAT_FILE);
}

}

class portfolio_format_leap extends portfolio_format_rich { }
class portfolio_format_leap2a extends portfolio_format_rich {

public static function get_file_directory() {
return 'files/';
}

/**
* return the link to a file
*
* @param stored_file $file
* @param boolean $entry whether the file is a LEAP2A entry or just a bundled file (default false)
*/
public static function file_output($file, $entry=false) {
$id = '';
if ($entry) {
$id = 'portfolio:file' . $file->get_id;
} else {
$id = self::get_file_directory() . '/' . $file->get_filename();
}
return '<a rel="enclosure" href="' . $id . '">' . $file->get_filename() . '</a>';
}

public static function leap2a_writer(stdclass $user=null) {
global $CFG;
if (empty($user)) {
global $USER;
$user = $USER;
}
require_once($CFG->libdir . '/portfolio/formats/leap2a/lib.php');
return new portfolio_format_leap2a_writer($user);
}

public static function manifest_name() {
return 'leap2a.xml';
}
}


/**
* 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_rich {}
//class portfolio_format_mbkp extends portfolio_format_rich {}

/**
* 'PDF format', subtype of file.
Expand Down
Loading

0 comments on commit 59dd457

Please sign in to comment.