From: Penny Leach Date: Sun, 10 Jan 2010 21:46:19 +0000 (+0100) Subject: more portfolio work in progress, needs rebasing X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=refs%2Fheads%2Fportfolio;p=moodle.git more portfolio work in progress, needs rebasing --- diff --git a/lib/portfolio/formats.php b/lib/portfolio/formats.php index f4899561ff..752bcbc32e 100644 --- a/lib/portfolio/formats.php +++ b/lib/portfolio/formats.php @@ -107,7 +107,7 @@ abstract class portfolio_format { * * @return boolean */ - public static function conflicts($format) { + public function conflicts($format) { return false; } } @@ -128,6 +128,15 @@ class portfolio_format_file extends portfolio_format { public static function file_output($file, $options=null) { throw new portfolio_exception('fileoutputnotsupported', 'portfolio'); } + + public function conflicts($format) { + if (get_class($this) == 'portfolio_format_file') { return false; } + $f = portfolio_format_object($format); + $formats = portfolio_supported_formats(); + $const = array_search(get_class($this), $formats); + debugging("const is $const"); + return portfolio_format_is_subclass($format, PORTFOLIO_FORMAT_FILE) || ($const && $f->conflicts($const)); + } } /** @@ -153,8 +162,8 @@ class portfolio_format_plainhtml extends portfolio_format_file { return array('text/html'); } - public static function conflicts($format) { - return ($format == PORTFOLIO_FORMAT_RICHHTML); + public function conflicts($format) { + return $format == PORTFOLIO_FORMAT_RICHHTML || parent::conflicts($format); } } @@ -181,9 +190,8 @@ class portfolio_format_text extends portfolio_format_file { return array('text/plain'); } - public static function conflicts($format ) { - return ($format == PORTFOLIO_FORMAT_PLAINHTML - || $format == PORTFOLIO_FORMAT_RICHHTML); + public function conflicts($format ) { + return $format == PORTFOLIO_FORMAT_RICHHTML || parent::conflicts($format); } } @@ -215,8 +223,8 @@ class portfolio_format_richhtml extends portfolio_format_rich { } return self::make_tag($file, $path, $attributes); } - public static function conflicts($format) { // TODO revisit the conflict with file, since we zip here - return ($format == PORTFOLIO_FORMAT_PLAINHTML || $format == PORTFOLIO_FORMAT_FILE); + public function conflicts($format) { + return ($format == PORTFOLIO_FORMAT_PLAINHTML || portfolio_format_is_subclass($format, PORTFOLIO_FORMAT_FILE)); } } diff --git a/lib/portfoliolib.php b/lib/portfoliolib.php index fa4065a4b7..b6b37642ad 100644 --- a/lib/portfoliolib.php +++ b/lib/portfoliolib.php @@ -136,13 +136,13 @@ class portfolio_add_button { $file = substr($backtrace[0]['file'], strlen($CFG->dirroot)); } else if (!is_readable($CFG->dirroot . $file)) { - throw new portfolio_button_exception('nocallbackfile', 'portfolio', $file); + throw new portfolio_button_exception('nocallbackfile', 'portfolio', '', $file); } $this->callbackfile = $file; require_once($CFG->libdir . '/portfolio/caller.php'); // require the base class first require_once($CFG->dirroot . $file); if (!class_exists($class)) { - throw new portfolio_button_exception('nocallbackclass', 'portfolio', $class); + throw new portfolio_button_exception('nocallbackclass', 'portfolio', '', $class); } // this will throw exceptions @@ -219,7 +219,7 @@ class portfolio_add_button { * eg leap2a etc */ public function set_format_by_intended_file($extn, $extraformats=null) { - $mimetype = mimeinfo('type', 'something. ' . $extn); + $mimetype = mimeinfo('type', 'something.' . $extn); $fileformat = portfolio_format_from_mimetype($mimetype); $this->intendedmimetype = $fileformat; if (is_string($extraformats)) { @@ -680,14 +680,17 @@ function portfolio_most_specific_formats($specificformats, $generalformats) { require_once($CFG->libdir . '/portfolio/formats.php'); $fobj = new $allformats[$f]; foreach ($generalformats as $key => $cf) { + if ($f == $cf) { + continue; + } $cfclass = $allformats[$cf]; - if ($fobj instanceof $cfclass && $cfclass != get_class($fobj)) { - debugging("unsetting $key $cf because it's not specific enough ($f is better)"); + if ($fobj instanceof $cfclass) { + //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"); + //debugging("unsetting $key $cf because it conflicts with $f"); unset($generalformats[$key]); } } @@ -715,6 +718,13 @@ function portfolio_format_object($name) { return new $formats[$name]; } +function portfolio_format_is_subclass($format1, $format2) { + $formats = portfolio_supported_formats(); + $f1 = portfolio_format_object($format1); + return ($f1 instanceof $formats[$format2]); + +} + /** * helper function to return an instance of a plugin (with config loaded) * diff --git a/mod/data/export.php b/mod/data/export.php index 9489d13c38..fe8bb1e757 100644 --- a/mod/data/export.php +++ b/mod/data/export.php @@ -101,7 +101,8 @@ if (array_key_exists('portfolio', $formdata) && !empty($formdata['portfolio'])) $formdata['id'] = $cm->id; require_once($CFG->libdir . '/portfoliolib.php'); $button = new portfolio_add_button(); - $button->set_callback_options('data_portfolio_caller', $formdata, '/mod/data/locallib.php'); + $button->set_callback_options('data_full_portfolio_caller', $formdata, '/mod/data/locallib.php'); + //todo ? allow more formats? if ($formdata['exporttype'] == 'csv') { $button->set_format_by_intended_file('csv'); // so we can do mime checking } diff --git a/mod/data/export_form.php b/mod/data/export_form.php index 9c27e41891..bbc14f120b 100644 --- a/mod/data/export_form.php +++ b/mod/data/export_form.php @@ -61,10 +61,11 @@ class mod_data_export_form extends moodleform { if ($CFG->enableportfolios && has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->_cm->id))) { require_once($CFG->libdir . '/portfoliolib.php'); require_once($CFG->dirroot . '/mod/data/locallib.php'); + //@todo revisit this - maybe it should be a checkbox instead if ($portfoliooptions = portfolio_instance_select( portfolio_instances(), - call_user_func(array('data_portfolio_caller', 'base_supported_formats')), - 'data_portfolio_caller', + call_user_func(array('data_full_portfolio_caller', 'base_supported_formats')), + 'data_full_portfolio_caller', mimeinfo('type', 'export.csv'), 'instance', true, diff --git a/mod/data/lib.php b/mod/data/lib.php index fc3c85fad4..e3d7394a80 100755 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -1252,9 +1252,14 @@ function data_print_template($template, $records, $data, $search='', $page=0, $r || (data_isowner($record->id) && has_capability('mod/data:exportownentry', $context))))) { require_once($CFG->libdir . '/portfoliolib.php'); $button = new portfolio_add_button(); - $button->set_callback_options('data_portfolio_caller', array('id' => $cm->id, 'recordid' => $record->id), '/mod/data/locallib.php'); - list($formats, $files) = data_portfolio_caller::formats($fields, $record); - $button->set_formats($formats); + $button->set_callback_options('data_entry_portfolio_caller', array('id' => $cm->id, 'recordid' => $record->id), '/mod/data/locallib.php'); + if ($file = data_entry_portfolio_caller::is_single_file_export($fields, $record)) { + $button->set_format_by_file($file); + } else if ($files = data_entry_portfolio_caller::get_all_files($fields, $record)) { + $button->set_formats(PORTFOLIO_FORMAT_RICHHTML); + } else { + $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML); + } $replacement[] = $button->to_html(PORTFOLIO_ADD_ICON_LINK); } else { $replacement[] = ''; diff --git a/mod/data/locallib.php b/mod/data/locallib.php index b8c34c1efe..b7c9be369e 100644 --- a/mod/data/locallib.php +++ b/mod/data/locallib.php @@ -15,6 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . + /** * @package mod-data * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} @@ -22,27 +23,215 @@ */ /** - * @package mod-data - * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * class to handle exporting the full database */ +class data_full_portfolio_caller extends portfolio_module_caller_base { -class data_portfolio_caller extends portfolio_module_caller_base { - - /** @var int */ - protected $recordid; - /** @var string */ - protected $exporttype; - /** @var string */ + /** field delimiter - used for csv */ protected $delimiter_name; - /** @var object */ + /** data object - represents row in database */ private $data; - /**#@+ @var array */ + + /** selected fields to export */ private $selectedfields; + + /** array of field objects for each field */ private $fields; + + /** array of field types for each field - same keys as {@link $fields} */ private $fieldtypes; + + /** all the loaded data to export */ private $exportdata; + + /** + * callback arguments that are expected from the portfolio api + * + * @return array keyed on field name, value boolean required or not + */ + public static function expected_callbackargs() { + return array( + 'id' => true, + 'delimiter_name' => false, // @todo true? + //'exporttype' => false, // @todo revisit this + ); + } + + /** + * @param array $callbackargs + */ + public function __construct($callbackargs) { + parent::__construct($callbackargs); + if (empty($this->exporttype)) { + $this->exporttype = 'csv'; + } + $this->selectedfields = array(); + foreach ($callbackargs as $key => $value) { + if (strpos($key, 'field_') === 0) { + $this->selectedfields[] = substr($key, 6); + } + } + } + + /** + * load up all the data required for this export + */ + public function load_data() { + global $DB; + if (!$this->cm = get_coursemodule_from_id('data', $this->id)) { + throw new portfolio_caller_exception('invalidid', 'data'); + } + $this->data = $DB->get_record('data', array('id' => $this->cm->instance)); + $fieldrecords = $DB->get_records('data_fields', array('dataid'=>$this->cm->instance), 'id'); + // populate objets for this databases fields + $this->fields = array(); + foreach ($fieldrecords as $fieldrecord) { + $tmp = data_get_field($fieldrecord, $this->data); + $this->fields[] = $tmp; + $this->fieldtypes[] = $tmp->type; + } + $this->exportdata = data_get_exportdata($this->cm->instance, $this->fields, $this->selectedfields); + } + + /** + * @todo penny later when we suport exporting to more than just csv, we may + * need to ask the user here if we have not already passed it + * + * @return bool + */ + public function has_export_config() { + return false; + } + + /** + * how long we expect this export to take + * @return one of PORTFOLIO_TIME_XX constant + */ + public function expected_time() { + return portfolio_expected_time_db(count($this->exportdata)); + } + + /** + * calculate the sha1 of this export + * + * @return string + */ + public function get_sha1() { + $str = ''; + foreach ($this->exportdata as $data) { + if (is_array($data) || is_object($data)) { + $testkey = array_pop(array_keys($data)); + if (is_array($data[$testkey]) || is_object($data[$testkey])) { + foreach ($data as $d) { + $str .= implode(',', (array)$d); + } + } else { + $str .= implode(',', (array)$data); + } + } else { + $str .= $data; + } + } + return sha1($str . ',' . $this->exporttype); + } + + /** + * prepare the package for exporting off to the portfolio plugin + */ + public function prepare_package() { + global $DB; + $count = count($this->exportdata); + $content = ''; + $filename = ''; + switch ($this->exporttype) { + case 'csv': + $content = data_export_csv($this->exportdata, $this->delimiter_name, $this->cm->name, $count, true); + $filename = clean_filename($this->cm->name . '.csv'); + break; + case 'xls': + // todo why? + throw new portfolio_caller_exception('notimplemented', 'portfolio', '', 'xls'); + $content = data_export_xls($this->exportdata, $this->cm->name, $count, true); + break; + case 'ods': + // todo why + throw new portfolio_caller_exception('notimplemented', 'portfolio', '', 'ods'); + $content = data_export_ods($this->exportdata, $this->cm->name, $count, true); + break; + default: + throw new portfolio_caller_exception('notimplemented', 'portfolio', '', $this->exporttype); + break; + } + return $this->exporter->write_new_file( + $content, + $filename, + ($this->exporter->get('format') instanceof PORTFOLIO_FORMAT_RICH) // if we have associate files, this is a 'manifest' + ); + // todo attachments?! + } + + /** + * make sure the user can still export this database + * @return bool + */ + public function check_permissions() { + return has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id)); + } + + /** + * display name for showing in navigation + * @return string + */ + public static function display_name() { + return get_string('modulename', 'data'); + } + + /** + * perform any necessary __wakeup duties - load up the data field class definitions + * @return void + */ + public function __wakeup() { + global $CFG; + if (empty($CFG)) { + return; // too early yet + } + foreach ($this->fieldtypes as $key => $field) { + require_once($CFG->dirroot . '/mod/data/field/' . $field .'/field.class.php'); + $this->fields[$key] = unserialize(serialize($this->fields[$key])); + } + } + + public static function base_supported_formats() { + return array(PORTFOLIO_FORMAT_SPREADSHEET); + } +} + + + +/** + * class to handle the exporting of a single entry + */ +class data_entry_portfolio_caller extends portfolio_module_caller_base { + + /** id of entry to export */ + protected $recordid; + + /** the mdl_data record for this entry */ + private $data; + + /** selected fields to export */ + private $selectedfields; + + /** array of field objects for each field */ + private $fields; + + /** array of field types for each field - same keys as {@link $fields} */ + private $fieldtypes; + + /** type of export - single or singlefile */ + private $exportype; + /**#@-*/ /**#@+ @var object */ private $singlerecord; @@ -55,8 +244,6 @@ class data_portfolio_caller extends portfolio_module_caller_base { return array( 'id' => true, 'recordid' => false, - 'delimiter_name' => false, - 'exporttype' => false, ); } /** @@ -64,9 +251,6 @@ class data_portfolio_caller extends portfolio_module_caller_base { */ public function __construct($callbackargs) { parent::__construct($callbackargs); - if (empty($this->exporttype)) { - $this->exporttype = 'csv'; - } $this->selectedfields = array(); foreach ($callbackargs as $key => $value) { if (strpos($key, 'field_') === 0) { @@ -93,30 +277,19 @@ class data_portfolio_caller extends portfolio_module_caller_base { $this->fieldtypes[] = $tmp->type; } - if ($this->recordid) { - //user has selected to export one single entry rather than the whole thing - // which is completely different - $this->singlerecord = $DB->get_record('data_records', array('id' => $this->recordid)); - $this->singlerecord->content = $DB->get_records('data_content', array('recordid' => $this->singlerecord->id)); - $this->exporttype = 'single'; - - list($formats, $files) = self::formats($this->fields, $this->singlerecord); - if (count($files) == 1 && count($this->fields) == 1) { - $this->singlefile = $files[0]; - $this->exporttype = 'singlefile'; - } else if (count($files) > 0) { - $this->multifiles = $files; - } + $this->singlerecord = $DB->get_record('data_records', array('id' => $this->recordid)); + $this->singlerecord->content = $DB->get_records('data_content', array('recordid' => $this->singlerecord->id)); + $this->exporttype = 'single'; + + if ($singlefile = self::is_single_file_export($this->fields, $this->singlerecord)) { + $this->singlefile = $singlefile; + $this->exporttype = 'singlefile'; } else { - // all records as csv or whatever - $this->exportdata = data_get_exportdata($this->cm->instance, $this->fields, $this->selectedfields); + $this->multifiles = self::get_all_files($this->fields, $this->singlerecord); } } /** - * @todo penny later when we suport exporting to more than just csv, we may - * need to ask the user here if we have not already passed it - * * @return bool */ public function has_export_config() { @@ -128,25 +301,22 @@ class data_portfolio_caller extends portfolio_module_caller_base { * @return mixed */ public function expected_time() { - if ($this->exporttype == 'single') { - return PORTFOLIO_TIME_LOW; + if ($this->multifiles || $this->singlefile) { + return $this->expected_time_file(); } - return portfolio_expected_time_db(count($this->exportdata)); + return PORTFOLIO_TIME_LOW; } /** + * sha1 of this export * @return string */ public function get_sha1() { if ($this->exporttype == 'singlefile') { return $this->singlefile->get_contenthash(); } - $loopdata = $this->exportdata; - if ($this->exporttype == 'single') { - $loopdata = $this->singlerecord; - } $str = ''; - foreach ($loopdata as $data) { + foreach ($this->singlerecord as $data) { if (is_array($data) || is_object($data)) { $testkey = array_pop(array_keys($data)); if (is_array($data[$testkey]) || is_object($data[$testkey])) { @@ -162,12 +332,12 @@ class data_portfolio_caller extends portfolio_module_caller_base { } return sha1($str . ',' . $this->exporttype); } + /** - * @global object + * do whatever necessary to get the package ready to be picked up by the portfolio plugin */ public function prepare_package() { global $DB; - $count = count($this->exportdata); $content = ''; $filename = ''; switch ($this->exporttype) { @@ -177,18 +347,6 @@ class data_portfolio_caller extends portfolio_module_caller_base { $content = $this->exportsingle(); $filename = clean_filename($this->cm->name . '-entry.html'); break; - case 'csv': - $content = data_export_csv($this->exportdata, $this->delimiter_name, $this->cm->name, $count, true); - $filename = clean_filename($this->cm->name . '.csv'); - break; - case 'xls': - throw new portfolio_caller_exception('notimplemented', 'portfolio', '', 'xls'); - $content = data_export_xls($this->exportdata, $this->cm->name, $count, true); - break; - case 'ods': - throw new portfolio_caller_exception('notimplemented', 'portfolio', '', 'ods'); - $content = data_export_ods($this->exportdata, $this->cm->name, $count, true); - break; default: throw new portfolio_caller_exception('notimplemented', 'portfolio', '', $this->exporttype); break; @@ -201,13 +359,18 @@ class data_portfolio_caller extends portfolio_module_caller_base { } /** - * @return bool + * make sure that the user can still export this entry + * + * @return boolean */ public function check_permissions() { - return has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id)); + $c = get_context_instance(CONTEXT_MODULE, $this->cm->id); + return (has_capability('mod/data:exportentry', $c) + || ($this->singlerecord->userid == $this->exporter->get('user')->id && has_capability('mod/data:exportownentry', $c))); } /** + * nice display name for navigation * @return string */ public static function display_name() { @@ -216,12 +379,12 @@ class data_portfolio_caller extends portfolio_module_caller_base { /** * @global object - * @return bool|void + * @return void */ public function __wakeup() { global $CFG; if (empty($CFG)) { - return true; // too early yet + return; // too early yet } foreach ($this->fieldtypes as $key => $field) { require_once($CFG->dirroot . '/mod/data/field/' . $field .'/field.class.php'); @@ -286,29 +449,46 @@ class data_portfolio_caller extends portfolio_module_caller_base { } /** - * @param array $fields - * @param object $record - * @uses PORTFOLIO_FORMAT_PLAINHTML - * @uses PORTFOLIO_FORMAT_RICHHTML - * @return array + * helper function to retrieve the files used in this export + * + * @param array $fields the fields belonging to this data entry + * @param object $record the entry record + * + * @return array of $file records */ - public static function formats($fields, $record) { - $formats = array(PORTFOLIO_FORMAT_PLAINHTML); - $includedfiles = array(); - foreach ($fields as $singlefield) { - if (is_callable(array($singlefield, 'get_file'))) { - $includedfiles[] = $singlefield->get_file($record->id); + public static function get_all_files($fields, $record) { + static $files = array(); + if (!array_key_exists($record->id, $files)) { + $files[$record->id] = array(); + foreach ($fields as $singlefield) { + if (is_callable(array($singlefield, 'get_file'))) { + $files[$record->id][] = $singlefield->get_file($record->id); + } } } - if (count($includedfiles) == 1 && count($fields) == 1) { - $formats= array(portfolio_format_from_mimetype($includedfiles[0]->get_mimetype())); - } else if (count($includedfiles) > 0) { - $formats = array(PORTFOLIO_FORMAT_RICHHTML); + return $files[$record->id]; + } + + /** + * tiny helper function to figure out if this is a single file export + * + * @param array $fields the fields belonging to this data entry + * @param object $record the entry record + * + * @return mixed stored_file or false + */ + public static function is_single_file_export($fields, $record) { + $files = self::get_all_files($fields, $record); + //print_object($files); + // print_object($fields); + debugging("files count was " . count($files) . " and fields count was " . count($fields)); + if (count($files) == 1 && count($fields) == 1) { + return array_shift($files); } - return array($formats, $includedfiles); + return false; } public static function base_supported_formats() { - return array(PORTFOLIO_FORMAT_SPREADSHEET, PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML); + return array(PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML); } } diff --git a/portfolio/add.php b/portfolio/add.php index ec0c9293f0..379fb78504 100644 --- a/portfolio/add.php +++ b/portfolio/add.php @@ -94,7 +94,7 @@ if (!empty($dataid)) { } else { $exporter->print_header('confirmcancel'); echo $OUTPUT->box_start(); - $yesbutton = new single_button(new moodle_url($CFG->wwwroot . '/portfolio/add.php', array('id' => $dataid, 'cancel' => 1, 'cancelsure' => 1, 'logreturn' => $logreturn)). get_string('yes')); + $yesbutton = new single_button(new moodle_url($CFG->wwwroot . '/portfolio/add.php', array('id' => $dataid, 'cancel' => 1, 'cancelsure' => 1, 'logreturn' => $logreturn)), get_string('yes')); $nobutton = new single_button(new moodle_url($CFG->wwwroot . '/portfolio/add.php', array('id' => $dataid)), get_string('no')); if ($logreturn) { $nobutton->url = $CFG->wwwroot . '/user/portfoliologs.php';