diff --git a/lang/en_utf8/data.php b/lang/en_utf8/data.php
index e59622fc8e6..55aa7880f16 100644
--- a/lang/en_utf8/data.php
+++ b/lang/en_utf8/data.php
@@ -216,7 +216,7 @@ $string['pagesize'] = 'Entries per page';
$string['participants'] = 'Participants';
$string['picture'] = 'Picture';
$string['pleaseaddsome'] = 'Please create some below or choose a predefined set 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';
diff --git a/lang/en_utf8/portfolio.php b/lang/en_utf8/portfolio.php
index 0e93ccf4ed2..ed8e471dcf6 100644
--- a/lang/en_utf8/portfolio.php
+++ b/lang/en_utf8/portfolio.php
@@ -51,6 +51,7 @@ $string['format_image'] = 'Image';
$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';
diff --git a/lang/en_utf8/portfolio_format_leap2a.php b/lang/en_utf8/portfolio_format_leap2a.php
new file mode 100644
index 00000000000..be4b6101e6e
--- /dev/null
+++ b/lang/en_utf8/portfolio_format_leap2a.php
@@ -0,0 +1,10 @@
+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';
diff --git a/lib/portfolio/caller.php b/lib/portfolio/caller.php
index 0c852eceb17..ae600aad4b2 100644
--- a/lib/portfolio/caller.php
+++ b/lib/portfolio/caller.php
@@ -314,18 +314,22 @@ abstract class portfolio_caller_base {
* as this function must 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
@@ -392,7 +396,7 @@ abstract class portfolio_caller_base {
}
}
} 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));
@@ -401,7 +405,6 @@ abstract class portfolio_caller_base {
case 0: return;
case 1: {
$this->singlefile = $files[0];
- $this->supportedformats = array(portfolio_format_from_file($this->singlefile));
return;
}
default: {
@@ -410,6 +413,66 @@ abstract class portfolio_caller_base {
}
}
+ /**
+ * 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,
diff --git a/lib/portfolio/constants.php b/lib/portfolio/constants.php
index f21dfebb576..1492cea6516 100644
--- a/lib/portfolio/constants.php
+++ b/lib/portfolio/constants.php
@@ -142,6 +142,17 @@ define('PORTFOLIO_FORMAT_SPREADSHEET', 'spreadsheet');
*/
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
diff --git a/lib/portfolio/exceptions.php b/lib/portfolio/exceptions.php
index dee46e5020c..28ab057a049 100644
--- a/lib/portfolio/exceptions.php
+++ b/lib/portfolio/exceptions.php
@@ -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 {}
diff --git a/lib/portfolio/exporter.php b/lib/portfolio/exporter.php
index 8935dfedfe2..4934324e113 100644
--- a/lib/portfolio/exporter.php
+++ b/lib/portfolio/exporter.php
@@ -152,6 +152,8 @@ class portfolio_exporter {
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};
@@ -287,7 +289,7 @@ class portfolio_exporter {
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.
diff --git a/lib/portfolio/formats.php b/lib/portfolio/formats.php
index e97b823ed3c..fdf86b80ebc 100644
--- a/lib/portfolio/formats.php
+++ b/lib/portfolio/formats.php
@@ -53,9 +53,34 @@ class portfolio_format_file {
* 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;
}
}
@@ -77,9 +102,14 @@ class portfolio_format_image extends portfolio_format_file {
* 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);
+ }
}
/**
@@ -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);
+
}
/**
@@ -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 '';
}
return '' . $file->get_filename() . '';
}
+ 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 '' . $file->get_filename() . '';
+ }
+
+ 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.
diff --git a/lib/portfolio/formats/leap2a/lib.php b/lib/portfolio/formats/leap2a/lib.php
new file mode 100644
index 00000000000..2912ccd9954
--- /dev/null
+++ b/lib/portfolio/formats/leap2a/lib.php
@@ -0,0 +1,477 @@
+.
+ *
+ * @package moodle
+ * @subpackage portfolio
+ * @author Penny Leach
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL
+ * @copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com
+ *
+ * This file contains the LEAP2a writer used by portfolio_format_leap2a
+ */
+
+/**
+ * object to encapsulate the writing of leap2a.
+ * should be used like:
+ *
+ * $writer = portfolio_format_leap2a::leap2a_writer($USER);
+ * $entry = new portfolio_format_leap2a_entry('forumpost6', $title, 'leaptype', 'somecontent')
+ * $entry->add_link('something', 'has_part')->add_link('somethingelse', 'has_part');
+ * .. etc
+ * $writer->add_entry($entry);
+ * $xmlstr = $writer->to_xml();
+ *
+ * @TODO find a way to ensure that all referenced files are included
+ */
+class portfolio_format_leap2a_writer {
+
+ /** the domdocument object used to create elements */
+ private $dom;
+ /** the top level feed element */
+ private $feed;
+ /** the user exporting data */
+ private $user;
+ /** the id of the feed - this is unique to the user and date and used for portfolio ns as well as feed id */
+ private $id;
+ /** the entries for the feed - keyed on id */
+ private $entries = array();
+
+ /**
+ * constructor - usually generated from portfolio_format_leap2a::leap2a_writer($USER);
+ *
+ * @param stdclass $user the user exporting (almost always $USER)
+ *
+ */
+ public function __construct(stdclass $user) { // todo something else - exporter, format, etc
+ global $CFG;
+ $this->user = $user;
+ $this->exporttime = time();
+ $this->id = $CFG->wwwroot . '/portfolio/export/leap2a/' . $this->user->id . '/' . $this->exporttime;
+
+ $this->dom = new DomDocument('1.0', 'utf-8');
+
+ $this->feed = $this->dom->createElement('feed');
+ $this->feed->setAttribute('xmlns', 'http://www.w3.org/2005/Atom');
+ $this->feed->setAttribute('xmlns:rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
+ $this->feed->setAttribute('xmlns:leap', 'http://wiki.cetis.ac.uk/2009-03/LEAP2A_predicates#');
+ $this->feed->setAttribute('xmlns:leaptype', 'http://wiki.cetis.ac.uk/2009-03/LEAP2A_types#');
+ $this->feed->setAttribute('xmlns:categories', 'http://wiki.cetis.ac.uk/2009-03/LEAP2A_categories/');
+ $this->feed->setAttribute('xmlns:portfolio', $this->id); // this is just a ns for ids of elements for convenience
+
+ $this->dom->appendChild($this->feed);
+
+ $this->feed->appendChild($this->dom->createElement('id', $this->id));
+ $this->feed->appendChild($this->dom->createElement('title', get_string('feedtitle', 'portfolio_format_leap2a', fullname($this->user))));
+
+ $generator = $this->dom->createElement('generator', 'Moodle');
+ $generator->setAttribute('uri', $CFG->wwwroot);
+ $generator->setAttribute('version', $CFG->version);
+
+ $this->feed->appendChild($generator);
+
+ $author = $this->dom->createElement('author');
+ $author->appendChild($this->dom->createElement('name', fullname($this->user)));
+ $author->appendChild($this->dom->createElement('email', $this->user->email));
+ $author->appendChild($this->dom->CreateElement('uri', $CFG->wwwroot . '/user/view.php?id=' . $this->user->id));
+
+ $this->feed->appendChild($author);
+ // header done, we can start appending entry elements now
+ }
+
+ /**
+ * adds a entry to the feed ready to be exported
+ *
+ * @param portfolio_format_leap2a_entry $entry the entry to add
+ */
+ public function add_entry(portfolio_format_leap2a_entry $entry) {
+ if (array_key_exists($entry->id, $this->entries)) {
+ throw new portfolio_format_leap2a_exception('entryalreadyexists', 'portfolio_format_leap2a', '', $entry->id);
+ }
+ $this->entries[$entry->id] = $entry;
+ return $entry;
+ }
+
+ /**
+ * make an entry that has previously been added into the feed into a selection.
+ *
+ * @param mixed $selectionentry the entry to make a selection (id or entry object)
+ * @param array $ids array of ids this selection includes
+ * @param string $selectiontype http://wiki.cetis.ac.uk/2009-03/LEAP2A_categories/selection_type
+ */
+ public function make_selection($selectionentry, $ids, $selectiontype) {
+ $selectionid = null;
+ if ($selectionentry instanceof portfolio_format_leap2a_entry) {
+ $selectionid = $selectionentry->id;
+ } else if (is_string($selectionentry)) {
+ $selectionid = $selectionentry;
+ }
+ if (!array_key_exists($selectionid, $this->entries)) {
+ throw new portfolio_format_leap2a_exception('invalidentryid', 'portfolio_format_leap2a', '', $selectionid);
+ }
+ foreach ($ids as $entryid) {
+ if (!array_key_exists($entryid, $this->entries)) {
+ throw new portfolio_format_leap2a_exception('invalidentryid', 'portfolio_format_leap2a', '', $entryid);
+ }
+ $this->entries[$selectionid]->add_link($entryid, 'has_part');
+ $this->entries[$entryid]->add_link($selectionid, 'is_part_of');
+ }
+ $this->entries[$selectionid]->add_category($selectiontype, 'selection_type');
+ if ($this->entries[$selectionid]->type != 'selection') {
+ debugging(get_string('overwritingselection', 'portfolio_format_leap2a', $this->entries[$selectionid]->type));
+ $this->entries[$selectionid]->type = 'selection';
+ }
+ }
+
+ /**
+ * validate the feed and all entries
+ */
+ private function validate() {
+ foreach ($this->entries as $entry) {
+ // first call the entry's own validation method
+ // which will throw an exception if there's anything wrong
+ $entry->validate();
+ // now make sure that all links are in place
+ foreach ($entry->links as $linkedid => $rel) {
+ // the linked to entry exists
+ if (!array_key_exists($linkedid, $this->entries)) {
+ $a = (object)array('rel' => $rel->type, 'to' => $linkedid, 'from' => $entry->id);
+ throw new portfolio_format_leap2a_exception('nonexistantlink', 'portfolio_format_leap2a', '', $a);
+ }
+ // and contains a link back to us
+ if (!array_key_exists($entry->id, $this->entries[$linkedid]->links)) {
+
+ }
+ // we could later check that the reltypes were properly inverse, but nevermind for now.
+ }
+ }
+ }
+
+ /**
+ * return the entire feed as a string
+ * calls validate() first on everything
+ *
+ * @return string
+ */
+ public function to_xml() {
+ $this->validate();
+ foreach ($this->entries as $entry) {
+ $this->feed->appendChild($entry->to_dom($this->dom, $this->user));
+ }
+ return $this->dom->saveXML();
+ }
+}
+
+/**
+ * this class represents a single leap2a entry.
+ * you can create these directly and then add them to the main leap feed object
+ */
+class portfolio_format_leap2a_entry {
+
+ /** entry id - something like forumpost6, must be unique to the feed **/
+ public $id;
+ /** title of the entry **/
+ public $title;
+ /** leap2a entry type **/
+ public $type;
+ /** optional author (only if different to feed author) **/
+ public $author;
+ /** summary - for split long content **/
+ public $summary;
+ /** main content of the entry. can be html,text,xhtml or a stored_file **/
+ public $content;
+ /** updated date - unix timestamp */
+ public $updated;
+ /** published date (ctime) - unix timestamp */
+ public $published;
+
+ /** used internally for file content **/
+ private $contentsrc;
+ /** used internally for file content **/
+ private $referencedfile;
+
+ /** the required fields for a leap2a entry */
+ private $requiredfields = array( 'id', 'title', 'type');
+
+ /** extra fields which usually should be set (except author) but are not required */
+ private $optionalfields = array('author', 'updated', 'published', 'content', 'summary');
+
+ /** links from this entry to other entries */
+ public $links = array();
+
+ /** attachments to this entry */
+ public $attachments = array();
+
+ /** categories for this entry */
+ private $categories = array();
+
+ /**
+ * constructor. All arguments are required (and will be validated)
+ * http://wiki.cetis.ac.uk/2009-03/LEAP2A_types
+ *
+ * @param string $id unique id of this entry.
+ * could be something like forumpost6 for example.
+ * This must be unique to the entire feed.
+ * @param string $title title of the entry. This is pure atom.
+ * @param string $type the leap type of this entry.
+ * @param mixed $content the content of the entry. string (xhtml/html/text) or stored_file
+ */
+ public function __construct($id, $title, $type, $content=null) {
+ $this->id = $id;
+ $this->title = $title;
+ $this->type = $type;
+ $this->content = $this->__set('content', $content);
+
+ }
+
+ /**
+ * override __set to do proper dispatching for different things
+ * only allows the optional and required leap2a entry fields to be set
+ */
+ public function __set($field, $value) {
+ // detect the case where content is being set to be a file directly
+ if ($field == 'content' && $value instanceof stored_file) {
+ return $this->set_content_file($value);
+ }
+ if (in_array($field, $this->requiredfields) || in_array($field, $this->optionalfields)) {
+ return $this->{$field} = $value;
+ }
+ throw new portfolio_format_leap2a_exception('invalidentryfield', 'portfolio_format_leap2a', '', $field);
+ }
+
+ /**
+ * sets the content of this entry to have a source
+ * this will take care of namespacing the filepath
+ * to the final path in the resulting zip file.
+ *
+ * @param stored_file $file the file to link to
+ * @param boolean $overridetype (default true) will set the entry rdf type to resource,
+ * overriding what was previously set.
+ * will be ignored if type is empty already
+ */
+ public function set_content_file(stored_file $file, $overridetype=true) {
+ $this->contentsrc = portfolio_format_leap2a::get_file_directory() . $file->get_filename();
+ if (empty($overridetype) || empty($this->type)) {
+ $this->type = 'resource';
+ }
+ $this->referencedfile = $file;
+ }
+
+ /**
+ * validate this entry.
+ * at the moment this just makes sure required fields exist
+ * but it could also check things against a list, for example
+ */
+ public function validate() {
+ foreach ($this->requiredfields as $key) {
+ if (empty($this->{$key})) {
+ throw new portfolio_format_leap2a_exception('missingfield', 'portfolio_format_leap2a', '', $key);
+ }
+ }
+ if ($this->type == 'selection') {
+ if (count($this->links) == 0) {
+ throw new portfolio_format_leap2a_exception('emptyselection', 'portfolio_format_leap2a');
+ }
+ //TODO make sure we have a category with a scheme 'selection_type'
+ }
+ }
+
+ /**
+ * add a link from this entry to another one
+ * these will be collated at the end of the export (during to_xml)
+ * and validated at that point. This function does no validation
+ * http://wiki.cetis.ac.uk/2009-03/LEAP2A_relationships
+ *
+ * @param mixed $otherentry portfolio_format_leap2a_entry or its id
+ * @param string $reltype (no leap: ns required)
+ *
+ * @return the current entry object. This is so that these calls can be chained
+ * eg $entry->add_link('something6', 'has_part')->add_link('something7', 'has_part');
+ *
+ */
+ public function add_link($otherentry, $reltype, $displayorder=null) {
+ if ($otherentry instanceof portfolio_format_leap2a_entry) {
+ $otherentry = $otherentry->id;
+ }
+ if ($otherentry == $this->id) {
+ throw new portfolio_format_leap2a_exception('selflink', 'portfolio_format_leap2a', '', (object)array('rel' => $reltype, 'id' => $this->id));
+ }
+ // add on the leap: ns if required
+ if (!in_array($reltype, array('related', 'alternate', 'enclosure'))) {
+ $rel = 'leap:' . $reltype;
+ }
+
+ $this->links[$otherentry] = (object)array('rel' => $reltype, 'order' => $displayorder);
+
+ return $this;
+ }
+
+ /**
+ * add an attachment to the feed.
+ * adding the file to the files area has to be handled outside this class separately.
+ *
+ * @param stored_file $file the file to add
+ */
+ public function add_attachment(stored_file $file) {
+ $this->attachments[$file->get_id()] = $file;
+ }
+
+ /**
+ * add a category to this entry
+ * http://wiki.cetis.ac.uk/2009-03/LEAP2A_categories
+ *
+ * @param string $term eg 'Offline'
+ * @param string $scheme (optional) eg resource_type
+ * @param string $label (optional) eg File
+ *
+ * "tags" should just pass a term here and no scheme or label.
+ * they will be automatically normalised if they have spaces.
+ */
+ public function add_category($term, $scheme=null, $label=null) {
+ // "normalise" terms and set their label if they have spaces
+ // see http://wiki.cetis.ac.uk/2009-03/LEAP2A_categories#Plain_tags for more information
+ if (empty($scheme) && strpos($term, ' ') !== false) {
+ $label = $term;
+ $term = str_replace(' ', '-', $term);
+ }
+ $this->categories[] = (object)array(
+ 'term' => $term,
+ 'scheme' => $scheme,
+ 'label' => $label,
+ );
+ }
+
+ /**
+ * Create an entry element and append all the children
+ * And return it rather than adding it to the dom.
+ * This is handled by the main writer object.
+ *
+ * @param DomDocument $dom use this to create elements
+ *
+ * @return DomElement
+ */
+ public function to_dom(DomDocument $dom, $feedauthor) {
+ $entry = $dom->createElement('entry');
+ $entry->appendChild($dom->createElement('id', $this->id));
+ $entry->appendChild($dom->createElement('title', $this->title));
+ if ($this->author && $this->author->id != $feedauthor->id) {
+ $author = $dom->createElement('author');
+ $author->appendChild($dom->createElement('name', fullname($this->author)));
+ $entry->appendChild($author);
+ }
+ // selectively add uncomplicated optional elements
+ foreach (array('updated', 'published') as $field) {
+ if ($this->{$field}) {
+ // TODO get the proper date format
+ $entry->appendChild($dom->createElement($field, $this->{$field}));
+ }
+ }
+ // deal with referenced files first since it's simple
+ if ($this->contentsrc) {
+ $content = $dom->createElement('content');
+ $content->setAttribute('src', $this->contentsrc);
+ $content->setAttribute('type', $this->referencedfile->get_mimetype());
+ $entry->appendChild($content);
+ } else if (empty($this->content)) {
+ $entry->appendChild($dom->createElement('content'));
+ } else {
+ $content = $this->create_xhtmlish_element($dom, 'content', $this->content);
+ $entry->appendChild($content);
+ }
+
+ if (!empty($this->summary)) {
+ $summary = $this->create_xhtmlish_element($dom, 'summary', $this->summary);
+ $entry->appendChild($summary);
+ }
+
+ $type = $dom->createElement('rdf:type');
+ $type->setAttribute('rdf:resource', 'leaptype:' . $this->type);
+ $entry->appendChild($type);
+
+ foreach ($this->links as $otherentry => $l) {
+ $link = $dom->createElement('link');
+ $link->setAttribute('rel', $l->rel);
+ $link->setAttribute('href', $otherentry);
+ if ($l->order) {
+ $link->setAttribute('leap:display_order', $l->order);
+ }
+ $entry->appendChild($link);
+ }
+ foreach ($this->attachments as $id => $file) {
+ $link = $dom->createElement('link');
+ $link->setAttribute('rel', 'enclosure');
+ $link->setAttribute('href', portfolio_format_leap2a::get_file_directory() . $file->get_filename());
+ $link->setAttribute('length', $file->get_filesize());
+ $entry->appendChild($link);
+ }
+ foreach ($this->categories as $category) {
+ $cat = $dom->createElement('category');
+ $cat->setAttribute('term', $category->term);
+ if ($category->scheme) {
+ $cat->setAttribute('scheme', $category->scheme);
+ }
+ if ($category->label && $category->label != $category->term) {
+ $cat->setAttribute('label', $category->label);
+ }
+ $entry->appendChild($cat);
+ }
+ return $entry;
+ }
+
+ /**
+ * try to load whatever is in $content into xhtml and add it to the dom.
+ * failing that, load the html, escape it, and set it as the body of the tag
+ * either way it sets the type attribute of the top level element
+ * moodle should always provide xhtml content, but user-defined content can't be trusted
+ *
+ * @param DomDocument $dom the dom doc to use
+ * @param string $tagname usually 'content' or 'summary'
+ * @param string $content the content to use, either xhtml or html.
+ *
+ * @return DomElement
+ */
+ private function create_xhtmlish_element(DomDocument $dom, $tagname, $content) {
+ $topel = $dom->createElement($tagname);
+ $maybexml = true;
+ if (strpos($content, '<') === false && strpos($content, '>') === false) {
+ $maybexml = false;
+ }
+ // try to load content as xml
+ $tmp = new DomDocument();
+ if ($maybexml && @$tmp->loadXML('
' . $content . '
')) {
+ $topel->setAttribute('type', 'xhtml');
+ $content = $dom->importNode($tmp->documentElement, true);
+ $content->setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
+ $topel->appendChild($content);
+ // if that fails, it could still be html
+ } else if ($maybexml && @$tmp->loadHTML($content)) {
+ $topel->setAttribute('type', 'html');
+ $topel->nodeValue = $content;
+ // TODO figure out how to convert this to xml
+ // TODO because we end up with wrapped around it
+ // which is annoying
+ // either we already know it's text from the first check
+ // or nothing else has worked anyway
+ } else {
+ $topel->nodeValue = $content;
+ $topel->setAttribute('type', 'text');
+ return $topel;
+ }
+ return $topel;
+ }
+}
diff --git a/lib/portfolio/forms.php b/lib/portfolio/forms.php
index 50928129e53..b5566745cd1 100644
--- a/lib/portfolio/forms.php
+++ b/lib/portfolio/forms.php
@@ -254,22 +254,7 @@ class portfolio_instance_select extends moodleform {
function definition() {
$this->caller = $this->_customdata['caller'];
- $options = portfolio_instance_select(
- portfolio_instances(),
- $this->caller->supported_formats($this->caller),
- get_class($this->caller),
- $this->caller->get('singlefile'),
- 'instance',
- true,
- true
- );
- // TODO maybe add on some information to the user if they're already exporting
- // and some of the options were skipped because they are for plugins that don't support
- // multiple exports per session
- if (empty($options)) {
- debugging('noavailableplugins', 'portfolio');
- return false;
- }
+ $options = $this->_customdata['options'];
$mform =& $this->_form;
$mform->addElement('select', 'instance', get_string('selectplugin', 'portfolio'), $options);
$mform->addElement('hidden', 'id', $this->_customdata['id']);
diff --git a/lib/portfolio/plugin.php b/lib/portfolio/plugin.php
index f9bb5f67186..ca2b6893bf9 100644
--- a/lib/portfolio/plugin.php
+++ b/lib/portfolio/plugin.php
@@ -109,7 +109,7 @@ abstract class portfolio_plugin_base {
* @return array list of formats
*/
public static function supported_formats() {
- return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML);
+ return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICH);
}
/**
diff --git a/lib/portfoliolib.php b/lib/portfoliolib.php
index 6e5aa6821d2..352bc72e144 100644
--- a/lib/portfoliolib.php
+++ b/lib/portfoliolib.php
@@ -145,10 +145,10 @@ class portfolio_add_button {
* sets the available export formats for this content
* this function will also poll the static function in the caller class
* and make sure we're not overriding a format that has nothing to do with mimetypes
- * eg if you pass IMAGE here but the caller can export LEAP it will keep LEAP as well.
+ * eg if you pass IMAGE here but the caller can export LEAP2A it will keep LEAP2A as well.
* see portfolio_most_specific_formats for more information
*
- * @param array $formats if the calling code knows better than the static method on the calling class (supported_formats)
+ * @param array $formats if the calling code knows better than the static method on the calling class (base_supported_formats)
* eg, if it's going to be a single file, or if you know it's HTML, you can pass it here instead
* this is almost always the case so you should always use this.
* {@see portfolio_format_from_file} for how to get the appropriate formats to pass here for uploaded files.
@@ -163,20 +163,33 @@ class portfolio_add_button {
if (empty($this->callbackclass)) {
throw new portfolio_button_exception('noclassbeforeformats', 'portfolio');
}
- $callerformats = call_user_func(array($this->callbackclass, 'supported_formats'));
+ $callerformats = call_user_func(array($this->callbackclass, 'base_supported_formats'));
$this->formats = portfolio_most_specific_formats($formats, $callerformats);
}
+ public function reset_formats() {
+ $this->set_formats();
+ }
+
+
/**
* if we already know we have exactly one file,
* bypass set_formats and just pass the file
* so we can detect the formats by mimetype.
*
- * @param stored_file $file
+ * @param stored_file $file file to set the format from
+ * @param mixed $extraformats any additional formats other than by mimetype
+ * eg leap2a etc
*/
- public function set_format_by_file(stored_file $file) {
+ public function set_format_by_file(stored_file $file, $extraformats=null) {
$this->file = $file;
- $this->formats = array(portfolio_format_from_file($file));
+ if (is_string($extraformats)) {
+ $this->set_formats(array(portfolio_format_from_file($file), $extraformats));
+ } else if (is_array($extraformats)) {
+ $this->set_formats(array_merge(array(portfolio_format_from_file($file)), $extraformats));
+ } else {
+ $this->set_formats(portfolio_format_from_file($file));
+ }
}
/*
@@ -230,8 +243,10 @@ class portfolio_add_button {
$formoutput .= "\n" . '';
$formoutput .= "\n" . '';
$formoutput .= "\n" . '';
+ $formoutput .= "\n" . '';
$linkoutput .= 'callbackfile=' . $this->callbackfile . '&callbackclass='
- . $this->callbackclass . '&course=' . (!empty($COURSE) ? $COURSE->id : 0);
+ . $this->callbackclass . '&course=' . (!empty($COURSE) ? $COURSE->id : 0)
+ . '&callerformats=' . implode(',', $this->formats);
$selectoutput = '';
if (count($this->instances) == 1) {
$tmp = array_values($this->instances);
@@ -478,7 +493,8 @@ function portfolio_supported_formats() {
PORTFOLIO_FORMAT_SPREADSHEET => 'portfolio_format_spreadsheet',
PORTFOLIO_FORMAT_PRESENTATION => 'portfolio_format_presentation',
/*PORTFOLIO_FORMAT_MBKP, */ // later
- /*PORTFOLIO_FORMAT_LEAP, */ // also later
+ PORTFOLIO_FORMAT_LEAP2A => 'portfolio_format_leap2a',
+ PORTFOLIO_FORMAT_RICH => 'portfolio_format_rich',
);
}
@@ -538,13 +554,17 @@ function portfolio_supported_formats_intersect($callerformats, $pluginformats) {
$intersection = array();
foreach ($callerformats as $cf) {
if (!array_key_exists($cf, $allformats)) {
- debugging(get_string('invalidformat', 'portfolio', $cf));
+ if (!portfolio_format_is_abstract($cf)) {
+ debugging(get_string('invalidformat', 'portfolio', $cf));
+ }
continue;
}
$cfobj = new $allformats[$cf]();
foreach ($pluginformats as $p => $pf) {
if (!array_key_exists($pf, $allformats)) {
- debugging(get_string('invalidformat', 'portfolio', $pf));
+ if (!portfolio_format_is_abstract($pf)) {
+ debugging(get_string('invalidformat', 'portfolio', $pf));
+ }
unset($pluginformats[$p]); // to avoid the same warning over and over
continue;
}
@@ -556,8 +576,34 @@ function portfolio_supported_formats_intersect($callerformats, $pluginformats) {
return $intersection;
}
+/**
+ * tiny helper to figure out whether a portfolio format is abstract
+ *
+ * @param string $format the format to test
+ *
+ * @retun bool
+ */
+function portfolio_format_is_abstract($format) {
+ if (class_exists($format)) {
+ $class = $format;
+ } else if (class_exists('portfolio_format_' . $format)) {
+ $class = 'portfolio_format_' . $format;
+ } else {
+ $allformats = portfolio_supported_formats();
+ if (array_key_exists($format, $allformats)) {
+ $class = $allformats[$format];
+ }
+ }
+ if (empty($class)) {
+ return true; // it may as well be, we can't instantiate it :)
+ }
+ $rc = new ReflectionClass($class);
+ return $rc->isAbstract();
+}
+
/**
* return the combination of the two arrays of formats with duplicates in terms of specificity removed
+* and also removes conflicting formats
* use case: a module is exporting a single file, so the general formats would be FILE and MBKP
* while the specific formats would be the specific subclass of FILE based on mime (say IMAGE)
* and this function would return IMAGE and MBKP
@@ -569,20 +615,39 @@ function portfolio_supported_formats_intersect($callerformats, $pluginformats) {
*/
function portfolio_most_specific_formats($specificformats, $generalformats) {
$allformats = portfolio_supported_formats();
+ if (empty($specificformats)) {
+ return $generalformats;
+ } else if (empty($generalformats)) {
+ return $specificformats;
+ }
foreach ($specificformats as $f) {
// look for something less specific and remove it, ie outside of the inheritance tree of the current formats.
if (!array_key_exists($f, $allformats)) {
- throw new portfolio_button_exception('invalidformat', 'portfolio', $f);
+ if (!portfolio_format_is_abstract($pf)) {
+ throw new portfolio_button_exception('invalidformat', 'portfolio', $f);
+ }
}
$fobj = new $allformats[$f];
foreach ($generalformats as $key => $cf) {
$cfclass = $allformats[$cf];
- if ($fobj instanceof $cfclass) {
- unset($generalformats[$cf]);
+ if ($fobj instanceof $cfclass && $cfclass != get_class($fobj)) {
+ debugging("unsetting $key $cf because it's not specific enough ($f is better)");
+ unset($generalformats[$key]);
+ }
+ // check for conflicts
+ if ($fobj->conflicts($cf)) {
+ debugging("unsetting $key $cf because it conflicts with $f");
+ unset($generalformats[$key]);
}
}
+ //debugging('inside loop');
+ //print_object($generalformats);
}
- return array_merge(array_values($specificformats), array_values($generalformats));
+
+ //debugging('final formats');
+ $finalformats = array_unique(array_merge(array_values($specificformats), array_values($generalformats)));
+ //print_object($finalformats);
+ return $finalformats;
}
/**
@@ -784,9 +849,9 @@ function portfolio_report_insane($insane, $instances=false, $return=false) {
* @param string $classfile file containing the callback class definition
* @param array $callbackargs arguments to pass to the callback class
*/
-function portfolio_fake_add_url($instanceid, $classname, $classfile, $callbackargs) {
+function portfolio_fake_add_url($instanceid, $classname, $classfile, $callbackargs, $formats) {
global $CFG;
- $url = $CFG->wwwroot . '/portfolio/add.php?instance=' . $instanceid . '&callbackclass=' . $classname . '&callbackfile=' . $classfile . '&sesskey=' . sesskey();
+ $url = $CFG->wwwroot . '/portfolio/add.php?instance=' . $instanceid . '&callbackclass=' . $classname . '&callbackfile=' . $classfile . '&sesskey=' . sesskey();
if (is_object($callbackargs)) {
$callbackargs = (array)$callbackargs;
@@ -795,8 +860,9 @@ function portfolio_fake_add_url($instanceid, $classname, $classfile, $callbackar
return $url;
}
foreach ($callbackargs as $key => $value) {
- $url .= '&ca_' . $key . '=' . urlencode($value);
+ $url .= '&ca_' . $key . '=' . urlencode($value);
}
+ $url .= '&callerformats=' . implode(',', $formats);
return $url;
}
diff --git a/lib/simpletest/portfolio_testclass.php b/lib/simpletest/portfolio_testclass.php
index 7cb77adcb52..d9185b01d88 100644
--- a/lib/simpletest/portfolio_testclass.php
+++ b/lib/simpletest/portfolio_testclass.php
@@ -103,6 +103,9 @@ class portfolio_caller_test extends portfolio_caller_base {
public static function expected_callbackargs() {
return array();
}
+ public static function base_supported_formats() {
+ return array(PORTFOLIO_FORMAT_RICH, PORTFOLIO_FORMAT_FILE);
+ }
}
class portfolio_exporter_test extends portfolio_exporter {
diff --git a/mod/assignment/lib.php b/mod/assignment/lib.php
index a7e3c28d231..d7547b4df48 100644
--- a/mod/assignment/lib.php
+++ b/mod/assignment/lib.php
@@ -1821,7 +1821,6 @@ class assignment_base {
}
if (count($files) > 1 && $this->portfolio_exportable() && has_capability('mod/assignment:exportownsubmission', $this->context)) {
$button->set_callback_options('assignment_portfolio_caller', array('id' => $this->cm->id));
- $button->set_formats(PORTFOLIO_PORMAT_FILE);
$output .= ' ' . $button->to_html();
}
}
@@ -3395,10 +3394,7 @@ class assignment_portfolio_caller extends portfolio_module_caller_base {
throw new portfolio_caller_exception('notexportable', 'portfolio', $this->get_return_url());
}
- $this->set_file_and_format_data($this->fileid, $this->assignment->context->id, 'assignment_submission', $this->user->id);
- if (empty($this->supportedformats) && is_callable(array($this->assignment, 'portfolio_supported_formats'))) {
- $this->supportedformats = $this->assignment->portfolio_supported_formats();
- }
+ $this->set_file_and_format_data($this->fileid, $this->assignment->context->id, 'assignment_submission', $this->user->id, 'timemodified', false);
}
public function prepare_package() {
@@ -3406,6 +3402,26 @@ class assignment_portfolio_caller extends portfolio_module_caller_base {
if (is_callable(array($this->assignment, 'portfolio_prepare_package'))) {
return $this->assignment->portfolio_prepare_package($this->exporter, $this->user->id);
}
+ if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
+ $leapwriter = $this->exporter->get('format')->leap2a_writer();
+ $files = array();
+ if ($this->singlefile) {
+ $files[] = $this->singlefile;
+ } elseif ($this->multifiles) {
+ $files = $this->multifiles;
+ } else {
+ throw new portfolio_caller_exception('invalidpreparepackagefile', 'portfolio', $this->get_return_url());
+ }
+ $baseid = 'assignment' . $this->assignment->assignment->assignmenttype . $this->assignment->assignment->id . 'submission';
+ foreach ($files as $file) {
+ $id = $baseid . $file->get_id();
+ $entry = new portfolio_format_leap2a_entry($id, $file->get_filename(), 'resource', $file);
+ $entry->add_category('offline', 'resource_type');
+ $leapwriter->add_entry($entry);
+ $this->exporter->copy_existing_file($file);
+ }
+ return $this->exporter->write_new_file($leapwriter->to_xml(), $this->exporter->get('format')->manifest_name(), true);
+ }
return $this->prepare_package_file();
}
@@ -3441,6 +3457,10 @@ class assignment_portfolio_caller extends portfolio_module_caller_base {
public static function display_name() {
return get_string('modulename', 'assignment');
}
+
+ public static function base_supported_formats() {
+ return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_LEAP2A);
+ }
}
/**
diff --git a/mod/assignment/type/online/assignment.class.php b/mod/assignment/type/online/assignment.class.php
index f358694d00a..bd2b68c6d13 100644
--- a/mod/assignment/type/online/assignment.class.php
+++ b/mod/assignment/type/online/assignment.class.php
@@ -110,6 +110,7 @@ class assignment_online extends assignment_base {
echo format_text($text, $submission->data2);
$button = new portfolio_add_button();
$button->set_callback_options('assignment_portfolio_caller', array('id' => $this->cm->id), '/mod/assignment/lib.php');
+ $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML); //TODO this might have files?
$button->render();
} else if (!has_capability('mod/assignment:submit', $context)) { //fix for #4604
echo '
'. get_string('guestnosubmit', 'assignment').'
';
@@ -285,11 +286,19 @@ class assignment_online extends assignment_base {
function portfolio_prepare_package($exporter, $userid=0) {
$submission = $this->get_submission($userid);
- $exporter->write_new_file(format_text($submission->data1, $submission->data2), 'assignment.html', false);
- }
-
- function portfolio_supported_formats() {
- return array(PORTFOLIO_FORMAT_PLAINHTML);
+ $html = format_text($submission->data1, $submission->data2);
+ if ($exporter->get('formatclass') == PORTFOLIO_FORMAT_PLAINHTML) {
+ return $exporter->write_new_file($html, 'assignment.html', false);
+ } else if ($exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
+ $leapwriter = $exporter->get('format')->leap2a_writer();
+ $entry = new portfolio_format_leap2a_entry('assignmentonline' . $this->assignment->id, $this->assignment->name, 'resource', $html); // TODO entry?
+ $entry->add_category('web', 'resource_type');
+ $leapwriter->add_entry($entry);
+ return $exporter->write_new_file($leapwriter->to_xml(), $exporter->get('format')->manifest_name(), true);
+ //TODO attached files?!
+ } else {
+ die('wtf ;' . $exporter->get('formatclass'));
+ }
}
function extend_settings_navigation($node) {
diff --git a/mod/assignment/type/upload/assignment.class.php b/mod/assignment/type/upload/assignment.class.php
index c6d59850147..3d00d6ffd3b 100644
--- a/mod/assignment/type/upload/assignment.class.php
+++ b/mod/assignment/type/upload/assignment.class.php
@@ -368,7 +368,7 @@ class assignment_upload extends assignment_base {
}
if (count($files) > 1 && has_capability('mod/assignment:exportownsubmission', $this->context)) {
$button->set_callback_options('assignment_portfolio_caller', array('id' => $this->cm->id), '/mod/assignment/lib.php');
- $button->set_formats(PORTFOLIO_FORMAT_FILE);
+ $button->reset_formats(); // reset what we set before, since it's multi-file
$output .= $button->to_html();
}
}
diff --git a/mod/chat/lib.php b/mod/chat/lib.php
index cd93a43a965..5f0d2082172 100644
--- a/mod/chat/lib.php
+++ b/mod/chat/lib.php
@@ -1272,7 +1272,7 @@ class chat_portfolio_caller extends portfolio_module_caller_base {
/**
* @return array
*/
- public static function supported_formats() {
+ public static function base_supported_formats() {
return array(PORTFOLIO_FORMAT_PLAINHTML);
}
/**
diff --git a/mod/data/export.php b/mod/data/export.php
index 09cd98328d6..244b09f42c0 100644
--- a/mod/data/export.php
+++ b/mod/data/export.php
@@ -101,7 +101,7 @@ if (array_key_exists('portfolio', $formdata) && !empty($formdata['portfolio']))
// fake portfolio callback stuff and redirect
$formdata['id'] = $cm->id;
$formdata['exporttype'] = 'csv'; // force for now
- $url = portfolio_fake_add_url($formdata['portfolio'], 'data_portfolio_caller', '/mod/data/lib.php', $formdata);
+ $url = portfolio_fake_add_url($formdata['portfolio'], 'data_portfolio_caller', '/mod/data/lib.php', $formdata, array());
redirect($url);
}
diff --git a/mod/data/export_form.php b/mod/data/export_form.php
index db1af478fff..46f60c8dd33 100644
--- a/mod/data/export_form.php
+++ b/mod/data/export_form.php
@@ -33,6 +33,9 @@ class mod_data_export_form extends moodleform {
$typesarray[] = &MoodleQuickForm::createElement('select', 'delimiter_name', null, $choices);
$typesarray[] = &MoodleQuickForm::createElement('radio', 'exporttype', null, get_string('excel', 'data'), 'xls');
$typesarray[] = &MoodleQuickForm::createElement('radio', 'exporttype', null, get_string('ods', 'data'), 'ods');
+ if ($CFG->enableportfolios) {
+ $typesarray[] = &MoodleQuickForm::createElement('radio', 'exporttype', null, get_string('format_leap2a', 'portfolio'), 'leap2a');
+ }
$mform->addGroup($typesarray, 'exportar', '', array(''), false);
$mform->addRule('exportar', null, 'required');
$mform->setDefault('exporttype', 'csv');
@@ -56,10 +59,10 @@ class mod_data_export_form extends moodleform {
}
$this->add_checkbox_controller(1, null, null, 1);
require_once($CFG->libdir . '/portfoliolib.php');
- if (has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->_cm->id))) {
+ if ($CFG->enableportfolios && has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->_cm->id))) {
if ($portfoliooptions = portfolio_instance_select(
portfolio_instances(),
- call_user_func(array('data_portfolio_caller', 'supported_formats')),
+ call_user_func(array('data_portfolio_caller', 'base_supported_formats')),
'data_portfolio_caller', null, '', true, true)) {
$mform->addElement('header', 'notice', get_string('portfolionotfile', 'data') . ':');
$portfoliooptions[0] = get_string('none');
diff --git a/mod/data/lib.php b/mod/data/lib.php
index 0c2de57a475..3ce3ac4775e 100755
--- a/mod/data/lib.php
+++ b/mod/data/lib.php
@@ -2915,7 +2915,6 @@ class data_portfolio_caller extends portfolio_module_caller_base {
$this->exporttype = 'single';
list($formats, $files) = self::formats($this->fields, $this->singlerecord);
- $this->supportedformats = $formats;
if (count($files) == 1 && count($this->fields) == 1) {
$this->singlefile = $files[0];
$this->exporttype = 'singlefile';
@@ -3104,7 +3103,6 @@ class data_portfolio_caller extends portfolio_module_caller_base {
* @param array $fields
* @param object $record
* @uses PORTFOLIO_FORMAT_PLAINHTML
- * @uses PORTFOLIO_FORMAT_FILE
* @uses PORTFOLIO_FORMAT_RICHHTML
* @return array
*/
@@ -3119,10 +3117,14 @@ class data_portfolio_caller extends portfolio_module_caller_base {
if (count($includedfiles) == 1 && count($fields) == 1) {
$formats= array(portfolio_format_from_file($includedfiles[0]));
} else if (count($includedfiles) > 0) {
- $formats = array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML);
+ $formats = array(PORTFOLIO_FORMAT_RICHHTML);
}
return array($formats, $includedfiles);
}
+
+ public static function base_supported_formats() {
+ return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML);
+ }
}
function data_extend_navigation($navigation, $course, $module, $cm) {
diff --git a/mod/forum/lib.php b/mod/forum/lib.php
index 92468d7ef41..6c9aa8d3a0f 100644
--- a/mod/forum/lib.php
+++ b/mod/forum/lib.php
@@ -3508,7 +3508,7 @@ function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=fa
if (empty($attachments)) {
$button->set_formats(PORTFOLIO_FORMAT_PLAINHTML);
} else {
- $button->set_formats(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML);
+ $button->set_formats(PORTFOLIO_FORMAT_RICHHTML);
}
$porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK);
@@ -7978,7 +7978,8 @@ class forum_portfolio_caller extends portfolio_module_caller_base {
private $forum;
private $discussion;
private $posts;
- private $keyedfiles;
+ private $keyedfiles; // just using multifiles isn't enough if we're exporting a full thread
+
/**
* @return array
*/
@@ -8033,11 +8034,11 @@ class forum_portfolio_caller extends portfolio_module_caller_base {
$modcontext = get_context_instance(CONTEXT_MODULE, $this->cm->id);
if ($this->post) {
- $this->set_file_and_format_data($this->attachment, $modcontext->id, 'forum_attachment', $this->post->id);
+ $this->set_file_and_format_data($this->attachment, $modcontext->id, 'forum_attachment', $this->post->id, 'timemodified', false);
if (!empty($this->multifiles)) {
$this->keyedfiles[$this->post->id] = $this->multifiles;
} else if (!empty($this->singlefile)) {
- $this->keyedfiles[$this->post->id] = $this->singlefile;
+ $this->keyedfiles[$this->post->id] = array($this->singlefile);
}
} else { // whole thread
$fs = get_file_storage();
@@ -8050,14 +8051,17 @@ class forum_portfolio_caller extends portfolio_module_caller_base {
$this->multifiles = array_merge($this->multifiles, array_values($this->keyedfiles[$post->id]));
}
}
- if ($this->attachment) {
- // do nothing
- } else if (!empty($this->multifiles) || !empty($this->singlefile)) {
- $this->supportedformats = array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML);
+ if (empty($this->multifiles) && !empty($this->singlefile)) {
+ $this->multifiles = array($this->singlefile); // copy_files workaround
+ }
+ // depending on whether there are files or not, we might have to change richhtml/plainhtml
+ if (!empty($this->multifiles)) {
+ $this->add_format(PORTFOLIO_FORMAT_RICHHTML);
} else {
- $this->supportedformats = array(PORTFOLIO_FORMAT_PLAINHTML);
+ $this->add_format(PORTFOLIO_FORMAT_PLAINHTML);
}
}
+
/**
* @global object
* @return string
@@ -8082,35 +8086,103 @@ class forum_portfolio_caller extends portfolio_module_caller_base {
return array($navlinks, $this->cm);
}
/**
+ * either a whole discussion
+ * a single post, with or without attachment
+ * or just an attachment with no post
+ *
* @global object
* @global object
* @uses PORTFOLIO_FORMAT_RICH
* @return mixed
*/
function prepare_package() {
- global $CFG, $SESSION;
- // either a whole discussion
- // a single post, with or without attachment
- // or just an attachment with no post
- $manifest = ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH);
- if (!$this->post) { // whole discussion
- $content = '';
+ global $CFG;
+
+ // set up the leap2a writer if we need it
+ $writingleap = false;
+ if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
+ $leapwriter = $this->exporter->get('format')->leap2a_writer();
+ $writingleap = true;
+ }
+ if ($this->attachment) { // simplest case first - single file attachment
+ $this->copy_files(array($this->singlefile), $this->attachment);
+ if ($writingleap) { // if we're writing leap, make the manifest to go along with the file
+ $entry = new portfolio_format_leap2a_entry($id, $this->attachment->get_filename(), 'resource', $this->attachment);
+ $entry->add_category('offline', 'resource_type');
+ $leapwriter->add_entry($entry);
+ return $this->exporter->write_new_file($leapwriter->to_xml(), $this->exporter->get('format')->manifest_name(), true);
+ }
+
+ } else if (empty($this->post)) { // exporting whole discussion
+ $content = ''; // if we're just writing HTML, start a string to add each post to
+ $ids = array(); // if we're writing leap2a, keep track of all entryids so we can add a selection element
foreach ($this->posts as $post) {
- $content .= '