portfolio & data MDL-21032 add leap2a to database module
authorPenny Leach <penny@liip.ch>
Sun, 2 May 2010 18:01:19 +0000 (18:01 +0000)
committerPenny Leach <penny@liip.ch>
Sun, 2 May 2010 18:01:19 +0000 (18:01 +0000)
This commit changes drastically the previous portfolio api support in
mod/data to get rid of csv etc and add leap2a, refactor and tidy up a
lot.

mod/data/lang/en/data.php
mod/data/lib.php
mod/data/locallib.php
mod/data/view.php

index 27002cc..f2eee44 100755 (executable)
@@ -127,6 +127,7 @@ $string['exportaszip'] = 'Export as zip';
 $string['exportaszip_help'] = 'The export as zip feature allows you to save the templates and fields as a preset zip for download. The zip may then be imported to another course.';
 $string['exportdatabaserecords'] = 'Export Database records';
 $string['exportedtozip'] = 'Exported to temporary zip...';
+$string['exportownentries'] = 'Export your own entries only? ({$a->mine}/{$a->all})';
 $string['failedpresetdelete'] = 'Error deleting a preset!';
 $string['fieldadded'] = 'Field added';
 $string['fieldallowautolink'] = 'Allow autolink';
index 10dec08..41e4278 100755 (executable)
@@ -1254,7 +1254,7 @@ function data_print_template($template, $records, $data, $search='', $page=0, $r
 
         $patterns[]='##export##';
 
-        if (($template == 'singletemplate' || $template == 'listtemplate')
+        if ($CFG->enableportfolios && ($template == 'singletemplate' || $template == 'listtemplate')
             && ((has_capability('mod/data:exportentry', $context)
                 || (data_isowner($record->id) && has_capability('mod/data:exportownentry', $context))))) {
             require_once($CFG->libdir . '/portfoliolib.php');
index b8c34c1..5198af6 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+require_once($CFG->dirroot . '/mod/data/lib.php');
+
 /**
- * @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
+ * The class to handle entry exports of a database module
  */
-
 class data_portfolio_caller extends portfolio_module_caller_base {
 
-    /** @var int */
+    /** @var int the single record to export */
     protected $recordid;
-    /** @var string */
-    protected $exporttype;
-    /** @var string */
-    protected $delimiter_name;
 
-    /** @var object */
+    /** @var object the record from the data table */
     private $data;
-    /**#@+ @var array */
-    private $selectedfields;
+
+    /**#@+ @var array the fields used and their fieldtypes */
     private $fields;
     private $fieldtypes;
-    private $exportdata;
-    /**#@-*/
-    /**#@+ @var object */
-    private $singlerecord;
-    private $singlefield;
-    /**#@-*/
+
+    /** @var object the records to export */
+    private $records;
+
+    /** @var int how many records are 'mine' */
+    private $minecount;
+
     /**
+     * the required callback arguments for a single-record export
+     *
      * @return array
      */
     public static function expected_callbackargs() {
         return array(
-            'id'             => true,
-            'recordid'       => false,
-            'delimiter_name' => false,
-            'exporttype'     => false,
+            'id'       => true,
+            'recordid' => false,
         );
     }
+
     /**
-     * @param array $callbackargs
+     * @param array $callbackargs the arguments passed through
      */
     public function __construct($callbackargs) {
         parent::__construct($callbackargs);
-        if (empty($this->exporttype)) {
-            $this->exporttype = 'csv';
-        }
+        // set up the list of fields to export
         $this->selectedfields = array();
         foreach ($callbackargs as $key => $value) {
             if (strpos($key, 'field_') === 0) {
@@ -76,15 +71,19 @@ class data_portfolio_caller extends portfolio_module_caller_base {
     }
 
     /**
-     * @global object
+     * load up the data needed for the export
+     *
+     * @global object $DB
      */
     public function load_data() {
-        global $DB;
+        global $DB, $USER;
         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');
+        if (!$this->data = $DB->get_record('data', array('id' => $this->cm->instance))) {
+            throw new portfolio_caller_exception('invalidid', 'data');
+        }
+        $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) {
@@ -93,105 +92,138 @@ class data_portfolio_caller extends portfolio_module_caller_base {
             $this->fieldtypes[]  = $tmp->type;
         }
 
+        $this->records = array();
         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;
-            }
+            $tmp = $DB->get_record('data_records', array('id' => $this->recordid));
+            $tmp->content = $DB->get_records('data_content', array('recordid' => $this->recordid));
+            $this->records[] = $tmp;
         } else {
-            // all records as csv or whatever
-            $this->exportdata = data_get_exportdata($this->cm->instance, $this->fields, $this->selectedfields);
+            $where = array('dataid' => $this->data->id);
+            if (!has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id))) {
+                $where['userid'] = $USER->id; // get them all in case, we'll unset ones that aren't ours later if necessary
+            }
+            $tmp = $DB->get_records('data_records', $where);
+            foreach ($tmp as $t) {
+                $t->content = $DB->get_records('data_content', array('recordid' => $t->id));
+                $this->records[] = $t;
+            }
+            $this->minecount = $DB->count_records('data_records', array('dataid' => $this->data->id, 'userid' => $USER->id));
         }
-    }
 
-    /**
-     * @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;
+        if ($this->recordid) {
+            list($formats, $files) = self::formats($this->fields, $this->records[0]);
+            $this->set_file_and_format_data($files);
+        }
     }
 
     /**
-     * @uses PORTFOLIO_TIME_LOW
-     * @return mixed
+     * How long we think the export will take
+     * Single entry is probably not too long.
+     * But we check for filesizes
+     * Else base it on the number of records
+     *
+     * @return one of PORTFOLIO_TIME_XX constants
      */
     public function expected_time() {
-        if ($this->exporttype == 'single') {
-            return PORTFOLIO_TIME_LOW;
+        if ($this->recordid) {
+            return $this->expected_time_file();
+        } else {
+            return portfolio_expected_time_db(count($this->records));
         }
-        return portfolio_expected_time_db(count($this->exportdata));
     }
 
     /**
+     * Calculate the shal1 of this export
+     * Dependent on the export format.
      * @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;
+        // in the case that we're exporting a subclass of 'file' and we have a singlefile,
+        // then we're not exporting any metadata, just the file by itself by mimetype.
+        if ($this->exporter->get('format') instanceof portfolio_format_file && $this->singlefile) {
+            return $this->get_sha1_file();
         }
+        // otherwise we're exporting some sort of multipart content so use the data
         $str = '';
-        foreach ($loopdata 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);
+        foreach ($this->records as $record) {
+            foreach ($record 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 .= implode(',', (array)$data);
+                    $str .= $data;
                 }
-            } else {
-                $str .= $data;
             }
         }
-        return sha1($str . ',' . $this->exporttype);
+        return sha1($str . ',' . $this->exporter->get('formatclass'));
     }
+
     /**
-     * @global object
+     * Prepare the package for export
+     *
+     * @return stored_file object
      */
     public function prepare_package() {
         global $DB;
-        $count = count($this->exportdata);
+        $leapwriter = null;
         $content = '';
         $filename = '';
-        switch ($this->exporttype) {
-            case 'singlefile':
-                return $this->get('exporter')->copy_existing_file($this->singlefile);
-            case 'single':
-                $content = $this->exportsingle();
+        $uid = $this->exporter->get('user')->id;
+        $users = array(); //cache
+        $onlymine = $this->get_export_config('mineonly');
+        if ($this->exporter->get('formatclass') == PORTFOLIO_FORMAT_LEAP2A) {
+            $leapwriter = $this->exporter->get('format')->leap2a_writer();
+            $ids = array();
+        }
+
+        if ($this->exporter->get('format') instanceof portfolio_format_file && $this->singlefile) {
+            return $this->get('exporter')->copy_existing_file($this->singlefile);
+        }
+        foreach ($this->records  as $key => $record) {
+            if ($onlymine && $record->userid != $uid) {
+                unset($this->records[$key]); // sha1
+                continue;
+            }
+            list($tmpcontent, $files)  = $this->exportentry($record);
+            $content .= $tmpcontent;
+            if ($leapwriter) {
+                $entry = new portfolio_format_leap2a_entry('dataentry' . $record->id, $this->data->name, 'resource', $tmpcontent);
+                $entry->published = $record->timecreated;
+                $entry->updated = $record->timemodified;
+                if ($record->userid != $uid) {
+                    if (!array_key_exists($record->userid, $users)) {
+                        $users[$record->userid] = $DB->get_record('user', array('id' => $record->userid), 'id,firstname,lastname');
+                    }
+                    $entry->author = $users[$record->userid];
+                }
+                $ids[] = $entry->id;
+                foreach ($files as $file) {
+                    $entry->add_attachment($file);
+                }
+                $leapwriter->add_entry($entry);
+            }
+        }
+        if ($leapwriter) {
+            if (count($this->records) > 1) { // make a selection element to tie them all together
+                $selection = new portfolio_format_leap2a_entry('datadb' . $this->data->id,
+                    get_string('entries', 'data') . ': ' . $this->data->name, 'selection');
+                $leapwriter->add_entry($selection);
+                $leapwriter->make_selection($selection, $ids, 'Grouping');
+            }
+            $filename = $this->exporter->get('format')->manifest_name();
+            $content = $leapwriter->to_xml();
+        } else {
+            if (count($this->records) == 1) {
                 $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;
+            } else {
+                $filename = clean_filename($this->cm->name . '-full.html');
+            }
         }
         return $this->exporter->write_new_file(
             $content,
@@ -201,10 +233,21 @@ class data_portfolio_caller extends portfolio_module_caller_base {
     }
 
     /**
+     * Verify the user can still export this entry
+     *
      * @return bool
      */
     public function check_permissions() {
-        return has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id));
+        if ($this->recordid) {
+            if (data_isowner($this->recordid)) {
+                return has_capability('mod/data:exportownentry', get_context_instance(CONTEXT_MODULE, $this->cm->id));
+            }
+            return has_capability('mod/data:exportentry', get_context_instance(CONTEXT_MODULE, $this->cm->id));
+        }
+        if ($this->has_export_config() && !$this->get_export_config('mineonly')) {
+            return has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id));
+        }
+        return has_capability('mod/data:exportownentry', get_context_instance(CONTEXT_MODULE, $this->cm->id));
     }
 
     /**
@@ -230,32 +273,33 @@ class data_portfolio_caller extends portfolio_module_caller_base {
     }
 
     /**
-     * @global object
-     * @return string
+     * Prepare a single entry for export, replacing all the content etc
+     *
+     * @param stdclass $record the entry to export
+     *
+     * @return array with key 0 = the html content, key 1 = array of attachments
      */
-    private function exportsingle() {
-        global $DB;
+    private function exportentry($record) {
     // Replacing tags
         $patterns = array();
         $replacement = array();
         $context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
 
+        $files = array();
     // Then we generate strings to replace for normal tags
         $format = $this->get('exporter')->get('format');
         foreach ($this->fields as $field) {
             $patterns[]='[['.$field->field->name.']]';
             if (is_callable(array($field, 'get_file'))) {
-                // TODO this used to be:
-                // if ($field instanceof data_field_file) {
-                // - see  MDL-16493
-                if (!$file = $field->get_file($this->singlerecord->id)) {
+                if (!$file = $field->get_file($record->id)) {
                     $replacement[] = '';
                     continue; // probably left empty
                 }
                 $replacement[] = $format->file_output($file);
                 $this->get('exporter')->copy_existing_file($file);
+                $files[] = $file;
             } else {
-                $replacement[] = $field->display_browse_field($this->singlerecord->id, 'singletemplate');
+                $replacement[] = $field->display_browse_field($record->id, 'singletemplate');
             }
         }
 
@@ -278,19 +322,24 @@ class data_portfolio_caller extends portfolio_module_caller_base {
         $replacement[] = '';
         $replacement[] = '';
         $replacement[] = '';
-        $replacement[] = userdate($this->singlerecord->timecreated);
-        $replacement[] = userdate($this->singlerecord->timemodified);
+        $replacement[] = userdate($record->timecreated);
+        $replacement[] = userdate($record->timemodified);
 
         // actual replacement of the tags
-        return str_ireplace($patterns, $replacement, $this->data->singletemplate);
+        return array(str_ireplace($patterns, $replacement, $this->data->singletemplate), $files);
     }
 
     /**
-     * @param array $fields
-     * @param object $record
+     * Given the fields being exported, and the single record,
+     * work out which export format(s) we can use
+     *
+     * @param array $fields array of field objects
+     * @param object $record The data record object
+     *
      * @uses PORTFOLIO_FORMAT_PLAINHTML
      * @uses PORTFOLIO_FORMAT_RICHHTML
-     * @return array
+     *
+     * @return array of PORTFOLIO_XX constants
      */
     public static function formats($fields, $record) {
         $formats = array(PORTFOLIO_FORMAT_PLAINHTML);
@@ -301,14 +350,52 @@ class data_portfolio_caller extends portfolio_module_caller_base {
             }
         }
         if (count($includedfiles) == 1 && count($fields) == 1) {
-            $formats= array(portfolio_format_from_mimetype($includedfiles[0]->get_mimetype()));
+            $formats = array(portfolio_format_from_mimetype($includedfiles[0]->get_mimetype()));
         } else if (count($includedfiles) > 0) {
             $formats = array(PORTFOLIO_FORMAT_RICHHTML);
         }
         return array($formats, $includedfiles);
     }
 
+    public static function has_files($data) {
+        global $DB;
+        $fieldrecords = $DB->get_records('data_fields', array('dataid' => $data->id), 'id');
+        // populate objets for this databases fields
+        foreach ($fieldrecords as $fieldrecord) {
+            $field = data_get_field($fieldrecord, $data);
+            if (is_callable(array($field, 'get_file'))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * base supported formats before we know anything about the export
+     */
     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, PORTFOLIO_FORMAT_LEAP2A);
+    }
+
+    public function has_export_config() {
+        // if we're exporting more than just a single entry,
+        // and we have the capability to export all entries,
+        // then ask whether we want just our own, or all of them
+        return (empty($this->recordid) // multi-entry export
+            && $this->minecount > 0    // some of them are mine
+            && $this->minecount != count($this->records) // not all of them are mine
+            && has_capability('mod/data:exportallentries', get_context_instance(CONTEXT_MODULE, $this->cm->id))); // they actually have a choice in the matter
+    }
+
+    public function export_config_form(&$mform, $instance) {
+        if (!$this->has_export_config()) {
+            return;
+        }
+        $mform->addElement('selectyesno', 'mineonly', get_string('exportownentries', 'data', (object)array('mine' => $this->minecount, 'all' => count($this->records))));
+        $mform->setDefault('mineonly', 1);
+    }
+
+    public function get_allowed_export_config() {
+        return array('mineonly');
     }
 }
index 06d107b..83017b9 100755 (executable)
         $records = array();
     }
 
+    if ($mode == '' && $CFG->enableportfolios) {
+        require_once($CFG->libdir . '/portfoliolib.php');
+        $button = new portfolio_add_button();
+        $button->set_callback_options('data_portfolio_caller', array('id' => $cm->id), '/mod/data/locallib.php');
+        if (data_portfolio_caller::has_files($data)) {
+            $button->set_formats(array(PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_LEAP2A)); // no plain html for us
+        }
+        echo $button->to_html(PORTFOLIO_ADD_FULL_FORM);
+    }
+
     //Advanced search form doesn't make sense for single (redirects list view)
     if (($maxcount || $mode == 'asearch') && $mode != 'single') {
         data_print_preference_form($data, $perpage, $search, $sort, $order, $search_array, $advanced, $mode);