1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace mod_data\local\importer; 18 19 use context_module; 20 use core_php_time_limit; 21 use core_tag_tag; 22 use core_user; 23 use csv_import_reader; 24 use moodle_exception; 25 use stdClass; 26 27 /** 28 * CSV entries_importer class for importing data and - if needed - files as well from a zip archive. 29 * 30 * @package mod_data 31 * @copyright 2023 ISB Bayern 32 * @author Philipp Memmel 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class csv_entries_importer extends entries_importer { 36 37 /** @var array Log entries for successfully added records. */ 38 private array $addedrecordsmessages = []; 39 40 /** 41 * Declares the entries_importer to use a csv file as data file. 42 * 43 * @see entries_importer::get_import_data_file_extension() 44 */ 45 public function get_import_data_file_extension(): string { 46 return 'csv'; 47 } 48 49 /** 50 * Import records for a data instance from csv data. 51 * 52 * @param stdClass $cm Course module of the data instance. 53 * @param stdClass $data The data instance. 54 * @param string $encoding The encoding of csv data. 55 * @param string $fielddelimiter The delimiter of the csv data. 56 * 57 * @throws moodle_exception 58 */ 59 public function import_csv(stdClass $cm, stdClass $data, string $encoding, string $fielddelimiter): void { 60 global $CFG, $DB; 61 // Large files are likely to take their time and memory. Let PHP know 62 // that we'll take longer, and that the process should be recycled soon 63 // to free up memory. 64 core_php_time_limit::raise(); 65 raise_memory_limit(MEMORY_HUGE); 66 67 $iid = csv_import_reader::get_new_iid('moddata'); 68 $cir = new csv_import_reader($iid, 'moddata'); 69 70 $context = context_module::instance($cm->id); 71 72 $readcount = $cir->load_csv_content($this->get_data_file_content(), $encoding, $fielddelimiter); 73 if (empty($readcount)) { 74 throw new \moodle_exception('csvfailed', 'data', "{$CFG->wwwroot}/mod/data/edit.php?d={$data->id}"); 75 } else { 76 if (!$fieldnames = $cir->get_columns()) { 77 throw new \moodle_exception('cannotreadtmpfile', 'error'); 78 } 79 80 // Check the fieldnames are valid. 81 $rawfields = $DB->get_records('data_fields', ['dataid' => $data->id], '', 'name, id, type'); 82 $fields = []; 83 $errorfield = ''; 84 $usernamestring = get_string('username'); 85 $safetoskipfields = [get_string('user'), get_string('email'), 86 get_string('timeadded', 'data'), get_string('timemodified', 'data'), 87 get_string('approved', 'data'), get_string('tags', 'data')]; 88 $userfieldid = null; 89 foreach ($fieldnames as $id => $name) { 90 if (!isset($rawfields[$name])) { 91 if ($name == $usernamestring) { 92 $userfieldid = $id; 93 } else if (!in_array($name, $safetoskipfields)) { 94 $errorfield .= "'$name' "; 95 } 96 } else { 97 // If this is the second time, a field with this name comes up, it must be a field not provided by the user... 98 // like the username. 99 if (isset($fields[$name])) { 100 if ($name == $usernamestring) { 101 $userfieldid = $id; 102 } 103 unset($fieldnames[$id]); // To ensure the user provided content fields remain in the array once flipped. 104 } else { 105 $field = $rawfields[$name]; 106 $filepath = "$CFG->dirroot/mod/data/field/$field->type/field.class.php"; 107 if (!file_exists($filepath)) { 108 $errorfield .= "'$name' "; 109 continue; 110 } 111 require_once($filepath); 112 $classname = 'data_field_' . $field->type; 113 $fields[$name] = new $classname($field, $data, $cm); 114 } 115 } 116 } 117 118 if (!empty($errorfield)) { 119 throw new \moodle_exception('fieldnotmatched', 'data', 120 "{$CFG->wwwroot}/mod/data/edit.php?d={$data->id}", $errorfield); 121 } 122 123 $fieldnames = array_flip($fieldnames); 124 125 $cir->init(); 126 while ($record = $cir->next()) { 127 $authorid = null; 128 if ($userfieldid) { 129 if (!($author = core_user::get_user_by_username($record[$userfieldid], 'id'))) { 130 $authorid = null; 131 } else { 132 $authorid = $author->id; 133 } 134 } 135 if ($recordid = data_add_record($data, 0, $authorid)) { // Add instance to data_record. 136 foreach ($fields as $field) { 137 $fieldid = $fieldnames[$field->field->name]; 138 if (isset($record[$fieldid])) { 139 $value = $record[$fieldid]; 140 } else { 141 $value = ''; 142 } 143 144 if (method_exists($field, 'update_content_import')) { 145 $field->update_content_import($recordid, $value, 'field_' . $field->field->id); 146 } else { 147 $content = new stdClass(); 148 $content->fieldid = $field->field->id; 149 $content->content = $value; 150 $content->recordid = $recordid; 151 if ($field->file_import_supported() && $this->importfiletype === 'zip') { 152 $filecontent = $this->get_file_content_from_zip($content->content); 153 if (!$filecontent) { 154 // No corresponding file in zip archive, so no record for this field being added at all. 155 continue; 156 } 157 $contentid = $DB->insert_record('data_content', $content); 158 $field->import_file_value($contentid, $filecontent, $content->content); 159 } else { 160 $DB->insert_record('data_content', $content); 161 } 162 } 163 } 164 165 if (core_tag_tag::is_enabled('mod_data', 'data_records') && 166 isset($fieldnames[get_string('tags', 'data')])) { 167 $columnindex = $fieldnames[get_string('tags', 'data')]; 168 $rawtags = $record[$columnindex]; 169 $tags = explode(',', $rawtags); 170 foreach ($tags as $tag) { 171 $tag = trim($tag); 172 if (empty($tag)) { 173 continue; 174 } 175 core_tag_tag::add_item_tag('mod_data', 'data_records', $recordid, $context, $tag); 176 } 177 } 178 179 $this->addedrecordsmessages[] = get_string('added', 'moodle', 180 count($this->addedrecordsmessages) + 1) 181 . ". " . get_string('entry', 'data') 182 . " (ID $recordid)\n"; 183 } 184 } 185 $cir->close(); 186 $cir->cleanup(true); 187 } 188 } 189 190 /** 191 * Getter for the array of messages for added records. 192 * 193 * For each successfully added record the array contains a log message. 194 * 195 * @return array Array of message strings: For each added record one message string 196 */ 197 public function get_added_records_messages(): array { 198 return $this->addedrecordsmessages; 199 } 200 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body