diff --git a/mod/data/classes/local/csv_exporter.php b/mod/data/classes/local/csv_exporter.php new file mode 100644 index 00000000000..436eb4bbd42 --- /dev/null +++ b/mod/data/classes/local/csv_exporter.php @@ -0,0 +1,78 @@ +. + +namespace mod_data\local; + +use coding_exception; +use csv_export_writer; + +/** + * CSV exporter for mod_data. + * + * @package mod_data + * @copyright 2023 ISB Bayern + * @author Philipp Memmel + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class csv_exporter extends exporter { + + /** @var string[] Possible delimiter names. Only used internally to check if a valid delimiter name + * has been specified. + */ + private const POSSIBLE_DELIMITER_NAMES = ['comma', 'tab', 'semicolon', 'colon', 'cfg']; + + /** + * @var string name of the delimiter to use for the csv export. Possible values: + * 'comma', 'tab', 'semicolon', 'colon' or 'cfg'. + */ + private string $delimitername = 'comma'; + + /** + * Returns the csv data exported by the csv_export_writer for further handling. + * + * @see \mod_data\local\exporter::get_data_file_content() + */ + public function get_data_file_content(): string { + return csv_export_writer::print_array($this->exportdata, $this->delimitername, '"', true); + } + + /** + * Returns the file extension of this exporter. + * + * @see \mod_data\local\exporter::get_export_data_file_extension() + */ + public function get_export_data_file_extension(): string { + return 'csv'; + } + + /** + * Setter for the delimiter name which should be used in this csv_exporter object. + * + * Calling this setter is optional, the delimiter name defaults to 'comma'. + * + * @param string $delimitername one of 'comma', 'tab', 'semicolon', 'colon' or 'cfg' + * @return void + * @throws coding_exception if a wrong delimiter name has been specified + */ + public function set_delimiter_name(string $delimitername): void { + if (!in_array($delimitername, self::POSSIBLE_DELIMITER_NAMES)) { + throw new coding_exception('Wrong delimiter type', + 'Please choose on of the following delimiters: ' + . '\"comma\", \"tab\", \"semicolon\", \"colon\", \"cfg\"'); + } + $this->delimitername = $delimitername; + } +} diff --git a/mod/data/classes/local/exporter.php b/mod/data/classes/local/exporter.php new file mode 100644 index 00000000000..050ee4c963a --- /dev/null +++ b/mod/data/classes/local/exporter.php @@ -0,0 +1,149 @@ +. + +namespace mod_data\local; + +use file_serving_exception; +use zip_archive; + +/** + * Exporter class for exporting data. + * + * @package mod_data + * @copyright 2023 ISB Bayern + * @author Philipp Memmel + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class exporter { + + /** @var int Tracks the currently edited row of the export data file. */ + private int $currentrow; + + /** + * @var array The data structure containing the data for exporting. It's a 2-dimensional array of + * rows and columns. + */ + protected array $exportdata; + + /** @var string Name of the export file name without extension. */ + protected string $exportfilename; + + /** + * Creates an exporter object. + * + * This object can be used to export data to different formats. + */ + public function __construct() { + $this->currentrow = 0; + $this->exportdata = []; + $this->exportfilename = 'Exportfile'; + } + + /** + * Adds a row (array of strings) to the export data. + * + * @param array $row the row to add, $row has to be a plain array of strings + * @return void + */ + public function add_row(array $row): void { + $this->exportdata[] = $row; + $this->currentrow++; + } + + /** + * Adds a data string (so the content for a "cell") to the current row. + * + * @param string $cellcontent the content to add to the current row + * @return void + */ + public function add_to_current_row(string $cellcontent): void { + $this->exportdata[$this->currentrow][] = $cellcontent; + } + + /** + * Signal the exporter to finish the current row and jump to the next row. + * + * @return void + */ + public function next_row(): void { + $this->currentrow++; + } + + /** + * Sets the name of the export file. + * + * Only use the basename without path and without extension here. + * + * @param string $exportfilename name of the file without path and extension + * @return void + */ + public function set_export_file_name(string $exportfilename): void { + $this->exportfilename = $exportfilename; + } + + /** + * The exporter will prepare a data file from the rows and columns being added. + * Overwrite this method to generate the data file as string. + * + * @return string the data file as a string + */ + abstract protected function get_data_file_content(): string; + + /** + * Overwrite the method to return the file extension your data file will have, for example + * return 'csv'; for a csv file exporter. + * + * @return string the file extension of the data file your exporter is using + */ + abstract protected function get_export_data_file_extension(): string; + + /** + * Returns the count of currently stored records (rows excluding header row). + * + * @return int the count of records/rows + */ + public function get_records_count(): int { + // The attribute $this->exportdata also contains a header. If only one row is present, this + // usually is the header, so record count should be 0. + if (count($this->exportdata) <= 1) { + return 0; + } + return count($this->exportdata) - 1; + } + + /** + * Sends the generated export file. + * + * Care: By default this function finishes the current PHP request and directly serves the file to the user as download. + * + * @param bool $sendtouser true if the file should be sent directly to the user, if false the file content will be returned + * as string + * @return string|null file content as string if $sendtouser is true + */ + public function send_file(bool $sendtouser = true): null|string { + if (empty($this->filenamesinzip)) { + if ($sendtouser) { + send_file($this->get_data_file_content(), + $this->exportfilename . '.' . $this->get_export_data_file_extension(), + null, 0, true, true); + return null; + } else { + return $this->get_data_file_content(); + } + } + return null; + } +} diff --git a/mod/data/classes/local/exporter_utils.php b/mod/data/classes/local/exporter_utils.php new file mode 100644 index 00000000000..63d74ae85e6 --- /dev/null +++ b/mod/data/classes/local/exporter_utils.php @@ -0,0 +1,137 @@ +. + +namespace mod_data\local; + +use coding_exception; +use context; +use context_system; +use dml_exception; +use moodle_exception; + +/** + * Utility class for exporting data from a mod_data instance. + * + * @package mod_data + * @copyright 2023 ISB Bayern + * @author Philipp Memmel + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class exporter_utils { + + /** + * Exports the data of the mod_data instance to an exporter object which then can export it to a file format. + * + * @param int $dataid + * @param array $fields + * @param array $selectedfields + * @param exporter $exporter the exporter object used + * @param int $currentgroup group ID of the current group. This is used for + * exporting data while maintaining group divisions. + * @param context|null $context the context in which the operation is performed (for capability checks) + * @param bool $userdetails whether to include the details of the record author + * @param bool $time whether to include time created/modified + * @param bool $approval whether to include approval status + * @param bool $tags whether to include tags + * @return void + * @throws coding_exception + * @throws dml_exception + * @throws moodle_exception + */ + public static function data_exportdata(int $dataid, array $fields, array $selectedfields, exporter $exporter, + int $currentgroup = 0, context $context = null, bool $userdetails = false, bool $time = false, bool $approval = false, + bool $tags = false): void { + global $DB; + + if (is_null($context)) { + $context = context_system::instance(); + } + // Exporting user data needs special permission. + $userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context); + + // Populate the header in first row of export. + $header = []; + foreach ($fields as $key => $field) { + if (!in_array($field->field->id, $selectedfields)) { + // Ignore values we aren't exporting. + unset($fields[$key]); + } else { + $header[] = $field->field->name; + } + } + if ($tags) { + $header[] = get_string('tags', 'data'); + } + if ($userdetails) { + $header[] = get_string('user'); + $header[] = get_string('username'); + $header[] = get_string('email'); + } + if ($time) { + $header[] = get_string('timeadded', 'data'); + $header[] = get_string('timemodified', 'data'); + } + if ($approval) { + $header[] = get_string('approved', 'data'); + } + $exporter->add_row($header); + + $datarecords = $DB->get_records('data_records', array('dataid' => $dataid)); + ksort($datarecords); + $line = 1; + foreach ($datarecords as $record) { + // Get content indexed by fieldid. + if ($currentgroup) { + $select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, ' + . '{data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?'; + $where = array($record->id, $currentgroup); + } else { + $select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?'; + $where = array($record->id); + } + + if ($content = $DB->get_records_sql($select, $where)) { + foreach ($fields as $field) { + $contents = ''; + if (isset($content[$field->field->id])) { + $contents = $field->export_text_value($content[$field->field->id]); + } + // Just be double sure. + $contents = !empty($contents) ? $contents : ''; + $exporter->add_to_current_row($contents); + } + if ($tags) { + $itemtags = \core_tag_tag::get_item_tags_array('mod_data', 'data_records', $record->id); + $exporter->add_to_current_row(implode(', ', $itemtags)); + } + if ($userdetails) { // Add user details to the export data. + $userdata = get_complete_user_data('id', $record->userid); + $exporter->add_to_current_row(fullname($userdata)); + $exporter->add_to_current_row($userdata->username); + $exporter->add_to_current_row($userdata->email); + } + if ($time) { // Add time added / modified. + $exporter->add_to_current_row(userdate($record->timecreated)); + $exporter->add_to_current_row(userdate($record->timemodified)); + } + if ($approval) { // Add approval status. + $exporter->add_to_current_row((int) $record->approved); + } + } + $exporter->next_row(); + } + } +} diff --git a/mod/data/classes/local/ods_exporter.php b/mod/data/classes/local/ods_exporter.php new file mode 100644 index 00000000000..c1b59f893b7 --- /dev/null +++ b/mod/data/classes/local/ods_exporter.php @@ -0,0 +1,65 @@ +. + +namespace mod_data\local; + +use MoodleODSWorkbook; +use MoodleODSWriter; + +/** + * ODS exporter for mod_data. + * + * @package mod_data + * @copyright 2023 ISB Bayern + * @author Philipp Memmel + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class ods_exporter extends exporter { + + /** + * Returns the file extension of this exporter. + * + * @see \mod_data\local\exporter::get_export_data_file_extension() + */ + public function get_export_data_file_extension(): string { + return 'ods'; + } + + /** + * Returns the ods data exported by the ODS library for further handling. + * + * @see \mod_data\local\exporter::get_data_file_content() + */ + public function get_data_file_content(): string { + global $CFG; + require_once("$CFG->libdir/odslib.class.php"); + $filearg = '-'; + $workbook = new MoodleODSWorkbook($filearg); + $worksheet = []; + $worksheet[0] = $workbook->add_worksheet(''); + $rowno = 0; + foreach ($this->exportdata as $row) { + $colno = 0; + foreach ($row as $col) { + $worksheet[0]->write($rowno, $colno, $col); + $colno++; + } + $rowno++; + } + $writer = new MoodleODSWriter($worksheet); + return $writer->get_file_content(); + } +} diff --git a/mod/data/deprecatedlib.php b/mod/data/deprecatedlib.php index 10a4a03b284..aa57f404fac 100644 --- a/mod/data/deprecatedlib.php +++ b/mod/data/deprecatedlib.php @@ -100,3 +100,170 @@ function data_export_xls($export, $dataname, $count) { $workbook->close(); return $filename; } + +/** + * @deprecated since Moodle 4.3, exporting is now being done by \mod_data\local\csv_exporter + * @global object + * @param array $export + * @param string $delimiter_name + * @param object $database + * @param int $count + * @param bool $return + * @return string|void + */ +function data_export_csv($export, $delimiter_name, $database, $count, $return=false) { + global $CFG; + + debugging('Function data_export_csv has been deprecated. Exporting is now being done by ' + . '\mod_data\local\csv_exporter.', DEBUG_DEVELOPER); + require_once($CFG->libdir . '/csvlib.class.php'); + + $filename = $database . '-' . $count . '-record'; + if ($count > 1) { + $filename .= 's'; + } + if ($return) { + return csv_export_writer::print_array($export, $delimiter_name, '"', true); + } else { + csv_export_writer::download_array($filename, $export, $delimiter_name); + } +} + +/** + * @deprecated since Moodle 4.3, exporting is now being done by \mod_data\local\ods_exporter + * @global object + * @param array $export + * @param string $dataname + * @param int $count + * @param string + */ +function data_export_ods($export, $dataname, $count) { + global $CFG; + + debugging('Function data_export_ods has been deprecated. Exporting is now being done by ' + . '\mod_data\local\ods_exporter.', DEBUG_DEVELOPER); + require_once("$CFG->libdir/odslib.class.php"); + $filename = clean_filename("{$dataname}-{$count}_record"); + if ($count > 1) { + $filename .= 's'; + } + $filename .= clean_filename('-' . gmdate("Ymd_Hi")); + $filename .= '.ods'; + $filearg = '-'; + $workbook = new MoodleODSWorkbook($filearg); + $workbook->send($filename); + $worksheet = array(); + $worksheet[0] = $workbook->add_worksheet(''); + $rowno = 0; + foreach ($export as $row) { + $colno = 0; + foreach($row as $col) { + $worksheet[0]->write($rowno, $colno, $col); + $colno++; + } + $rowno++; + } + $workbook->close(); + return $filename; +} + +/** + * @deprecated since Moodle 4.3, use \mod_data\local\exporter_utils::data_exportdata with a \mod_data\local\exporter object + * @global object + * @param int $dataid + * @param array $fields + * @param array $selectedfields + * @param int $currentgroup group ID of the current group. This is used for + * exporting data while maintaining group divisions. + * @param object $context the context in which the operation is performed (for capability checks) + * @param bool $userdetails whether to include the details of the record author + * @param bool $time whether to include time created/modified + * @param bool $approval whether to include approval status + * @param bool $tags whether to include tags + * @return array + */ +function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0, $context=null, + $userdetails=false, $time=false, $approval=false, $tags = false) { + global $DB; + + debugging('Function data_get_exportdata has been deprecated. Use ' + . '\mod_data\local\exporter_utils::data_exportdata with a \mod_data\local\exporter object instead', + DEBUG_DEVELOPER); + + if (is_null($context)) { + $context = context_system::instance(); + } + // exporting user data needs special permission + $userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context); + + $exportdata = array(); + + // populate the header in first row of export + foreach($fields as $key => $field) { + if (!in_array($field->field->id, $selectedfields)) { + // ignore values we aren't exporting + unset($fields[$key]); + } else { + $exportdata[0][] = $field->field->name; + } + } + if ($tags) { + $exportdata[0][] = get_string('tags', 'data'); + } + if ($userdetails) { + $exportdata[0][] = get_string('user'); + $exportdata[0][] = get_string('username'); + $exportdata[0][] = get_string('email'); + } + if ($time) { + $exportdata[0][] = get_string('timeadded', 'data'); + $exportdata[0][] = get_string('timemodified', 'data'); + } + if ($approval) { + $exportdata[0][] = get_string('approved', 'data'); + } + + $datarecords = $DB->get_records('data_records', array('dataid'=>$dataid)); + ksort($datarecords); + $line = 1; + foreach($datarecords as $record) { + // get content indexed by fieldid + if ($currentgroup) { + $select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, {data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?'; + $where = array($record->id, $currentgroup); + } else { + $select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?'; + $where = array($record->id); + } + + if( $content = $DB->get_records_sql($select, $where) ) { + foreach($fields as $field) { + $contents = ''; + if(isset($content[$field->field->id])) { + $contents = $field->export_text_value($content[$field->field->id]); + } + $exportdata[$line][] = $contents; + } + if ($tags) { + $itemtags = \core_tag_tag::get_item_tags_array('mod_data', 'data_records', $record->id); + $exportdata[$line][] = implode(', ', $itemtags); + } + if ($userdetails) { // Add user details to the export data + $userdata = get_complete_user_data('id', $record->userid); + $exportdata[$line][] = fullname($userdata); + $exportdata[$line][] = $userdata->username; + $exportdata[$line][] = $userdata->email; + } + if ($time) { // Add time added / modified + $exportdata[$line][] = userdate($record->timecreated); + $exportdata[$line][] = userdate($record->timemodified); + } + if ($approval) { // Add approval status + $exportdata[$line][] = (int) $record->approved; + } + } + $line++; + } + $line--; + return $exportdata; +} diff --git a/mod/data/export.php b/mod/data/export.php index f4d5c79cd08..1fbc9d59f76 100644 --- a/mod/data/export.php +++ b/mod/data/export.php @@ -95,20 +95,30 @@ if ($mform->is_cancelled()) { $currentgroup = groups_get_activity_group($cm); - $exportdata = data_get_exportdata($data->id, $fields, $selectedfields, $currentgroup, $context, - $exportuser, $exporttime, $exportapproval, $tags); - $count = count($exportdata); + $exporter = null; switch ($formdata['exporttype']) { case 'csv': - data_export_csv($exportdata, $formdata['delimiter_name'], $data->name, $count); + $exporter = new \mod_data\local\csv_exporter(); + $exporter->set_delimiter_name($formdata['delimiter_name']); break; case 'ods': - data_export_ods($exportdata, $data->name, $count); + $exporter = new \mod_data\local\ods_exporter(); break; default: throw new coding_exception('Invalid export format has been specified. ' . 'Only "csv" and "ods" are currently supported.'); } + + \mod_data\local\exporter_utils::data_exportdata($data->id, $fields, $selectedfields, $exporter, $currentgroup, $context, + $exportuser, $exporttime, $exportapproval, $tags); + $count = $exporter->get_records_count(); + $filename = clean_filename("{$data->name}-{$count}_record"); + if ($count > 1) { + $filename .= 's'; + } + $filename .= clean_filename('-' . gmdate("Ymd_Hi")); + $exporter->set_export_file_name($filename); + $exporter->send_file(); } // Build header to match the rest of the UI. diff --git a/mod/data/lib.php b/mod/data/lib.php index fc16be4418c..bac7eacc0d2 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -3111,160 +3111,6 @@ function data_import_csv($cm, $data, &$csvdata, $encoding, $fielddelimiter) { return 0; } -/** - * @global object - * @param array $export - * @param string $delimiter_name - * @param object $database - * @param int $count - * @param bool $return - * @return string|void - */ -function data_export_csv($export, $delimiter_name, $database, $count, $return=false) { - global $CFG; - require_once($CFG->libdir . '/csvlib.class.php'); - - $filename = $database . '-' . $count . '-record'; - if ($count > 1) { - $filename .= 's'; - } - if ($return) { - return csv_export_writer::print_array($export, $delimiter_name, '"', true); - } else { - csv_export_writer::download_array($filename, $export, $delimiter_name); - } -} - -/** - * @global object - * @param array $export - * @param string $dataname - * @param int $count - * @param string - */ -function data_export_ods($export, $dataname, $count) { - global $CFG; - require_once("$CFG->libdir/odslib.class.php"); - $filename = clean_filename("{$dataname}-{$count}_record"); - if ($count > 1) { - $filename .= 's'; - } - $filename .= clean_filename('-' . gmdate("Ymd_Hi")); - $filename .= '.ods'; - $filearg = '-'; - $workbook = new MoodleODSWorkbook($filearg); - $workbook->send($filename); - $worksheet = array(); - $worksheet[0] = $workbook->add_worksheet(''); - $rowno = 0; - foreach ($export as $row) { - $colno = 0; - foreach($row as $col) { - $worksheet[0]->write($rowno, $colno, $col); - $colno++; - } - $rowno++; - } - $workbook->close(); - return $filename; -} - -/** - * @global object - * @param int $dataid - * @param array $fields - * @param array $selectedfields - * @param int $currentgroup group ID of the current group. This is used for - * exporting data while maintaining group divisions. - * @param object $context the context in which the operation is performed (for capability checks) - * @param bool $userdetails whether to include the details of the record author - * @param bool $time whether to include time created/modified - * @param bool $approval whether to include approval status - * @param bool $tags whether to include tags - * @return array - */ -function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0, $context=null, - $userdetails=false, $time=false, $approval=false, $tags = false) { - global $DB; - - if (is_null($context)) { - $context = context_system::instance(); - } - // exporting user data needs special permission - $userdetails = $userdetails && has_capability('mod/data:exportuserinfo', $context); - - $exportdata = array(); - - // populate the header in first row of export - foreach($fields as $key => $field) { - if (!in_array($field->field->id, $selectedfields)) { - // ignore values we aren't exporting - unset($fields[$key]); - } else { - $exportdata[0][] = $field->field->name; - } - } - if ($tags) { - $exportdata[0][] = get_string('tags', 'data'); - } - if ($userdetails) { - $exportdata[0][] = get_string('user'); - $exportdata[0][] = get_string('username'); - $exportdata[0][] = get_string('email'); - } - if ($time) { - $exportdata[0][] = get_string('timeadded', 'data'); - $exportdata[0][] = get_string('timemodified', 'data'); - } - if ($approval) { - $exportdata[0][] = get_string('approved', 'data'); - } - - $datarecords = $DB->get_records('data_records', array('dataid'=>$dataid)); - ksort($datarecords); - $line = 1; - foreach($datarecords as $record) { - // get content indexed by fieldid - if ($currentgroup) { - $select = 'SELECT c.fieldid, c.content, c.content1, c.content2, c.content3, c.content4 FROM {data_content} c, {data_records} r WHERE c.recordid = ? AND r.id = c.recordid AND r.groupid = ?'; - $where = array($record->id, $currentgroup); - } else { - $select = 'SELECT fieldid, content, content1, content2, content3, content4 FROM {data_content} WHERE recordid = ?'; - $where = array($record->id); - } - - if( $content = $DB->get_records_sql($select, $where) ) { - foreach($fields as $field) { - $contents = ''; - if(isset($content[$field->field->id])) { - $contents = $field->export_text_value($content[$field->field->id]); - } - $exportdata[$line][] = $contents; - } - if ($tags) { - $itemtags = \core_tag_tag::get_item_tags_array('mod_data', 'data_records', $record->id); - $exportdata[$line][] = implode(', ', $itemtags); - } - if ($userdetails) { // Add user details to the export data - $userdata = get_complete_user_data('id', $record->userid); - $exportdata[$line][] = fullname($userdata); - $exportdata[$line][] = $userdata->username; - $exportdata[$line][] = $userdata->email; - } - if ($time) { // Add time added / modified - $exportdata[$line][] = userdate($record->timecreated); - $exportdata[$line][] = userdate($record->timemodified); - } - if ($approval) { // Add approval status - $exportdata[$line][] = (int) $record->approved; - } - } - $line++; - } - $line--; - return $exportdata; -} - //////////////////////////////////////////////////////////////////////////////// // File API // //////////////////////////////////////////////////////////////////////////////// diff --git a/mod/data/tests/export_test.php b/mod/data/tests/export_test.php new file mode 100644 index 00000000000..20efcc29110 --- /dev/null +++ b/mod/data/tests/export_test.php @@ -0,0 +1,114 @@ +. + +namespace mod_data; + +use coding_exception; +use context_module; +use dml_exception; +use mod_data\local\csv_exporter; +use mod_data\local\exporter_utils; +use mod_data\local\mod_data_csv_importer; + +/** + * Unit tests for import.php. + * + * @package mod_data + * @category test + * @copyright 2019 Tobias Reischmann + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class export_test extends \advanced_testcase { + + /** + * Get the test data. + * + * In this instance we are setting up database records to be used in the unit tests. + * + * @return array of test instances + * @throws coding_exception + */ + protected function get_test_data(): array { + $this->resetAfterTest(true); + + $generator = $this->getDataGenerator()->get_plugin_generator('mod_data'); + $course = $this->getDataGenerator()->create_course(); + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); + $this->setUser($teacher); + $student = $this->getDataGenerator()->create_and_enrol($course, 'student', ['username' => 'student']); + + $data = $generator->create_instance(['course' => $course->id]); + $cm = get_coursemodule_from_instance('data', $data->id); + + // Add fields. + $fieldrecord = new \stdClass(); + $fieldrecord->name = 'numberfield'; // Identifier of the records for testing. + $fieldrecord->type = 'number'; + $numberfield = $generator->create_field($fieldrecord, $data); + + $fieldrecord->name = 'textfield'; + $fieldrecord->type = 'text'; + $textfield = $generator->create_field($fieldrecord, $data); + + $contents[$numberfield->field->id] = '3'; + $contents[$textfield->field->id] = 'a simple text'; + $generator->create_entry($data, $contents); + + return [ + 'teacher' => $teacher, + 'student' => $student, + 'data' => $data, + 'cm' => $cm, + ]; + } + + /** + * Tests the exporting of the content of a mod_data instance. + * + * @covers \mod_data\local\exporter + * @covers \mod_data\local\exporter_utils::data_exportdata + */ + public function test_export(): void { + global $DB; + [ + 'data' => $data, + 'cm' => $cm, + ] = $this->get_test_data(); + + $exporter = new csv_exporter(); + $exporter->set_export_file_name('testexportfile'); + $fieldrecords = $DB->get_records('data_fields', ['dataid' => $data->id], 'id'); + + $fields = []; + foreach ($fieldrecords as $fieldrecord) { + $fields[] = data_get_field($fieldrecord, $data); + } + + // We select all fields. + $selectedfields = array_map(fn($field) => $field->field->id, $fields); + $currentgroup = groups_get_activity_group($cm); + $context = context_module::instance($cm->id); + $exportuser = false; + $exporttime = false; + $exportapproval = false; + $tags = false; + + exporter_utils::data_exportdata($data->id, $fields, $selectedfields, $exporter, $currentgroup, $context, + $exportuser, $exporttime, $exportapproval, $tags); + $this->assertEquals(file_get_contents(__DIR__ . '/fixtures/test_data_export.csv'), + $exporter->send_file(false)); + } +} diff --git a/mod/data/tests/fixtures/test_data_export.csv b/mod/data/tests/fixtures/test_data_export.csv new file mode 100644 index 00000000000..7ffd4341f9f --- /dev/null +++ b/mod/data/tests/fixtures/test_data_export.csv @@ -0,0 +1,2 @@ +numberfield,textfield +3,"a simple text" diff --git a/mod/data/upgrade.txt b/mod/data/upgrade.txt index 11de206cffb..de4707eae10 100644 --- a/mod/data/upgrade.txt +++ b/mod/data/upgrade.txt @@ -3,6 +3,10 @@ information provided here is intended especially for developers. == 4.3 == * Function data_export_xls() has been deprecated and moved to deprecatedlib, because xls support has already been dropped. +* Functions data_export_csv(), data_export_ods() and data_get_exportdata() have been deprecated due to a bigger + refactoring of the way data is being exported. This is now being done by new exporter classes + \mod_data\local\csv_exporter and \mod_data\local\ods_exporter (inheriting from exporter base class + \mod_data\local\exporter) as well as \mod_data\local\exporter_utils::data_exportdata(). == 4.2 == * The field base class now has a method validate(). Overwrite it in the field type to provide validation of field type's