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 coding_exception; 20 use core_php_time_limit; 21 use file_packer; 22 23 /** 24 * Importer class for importing data and - if needed - files as well from a zip archive. 25 * 26 * @package mod_data 27 * @copyright 2023 ISB Bayern 28 * @author Philipp Memmel 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 */ 31 abstract class entries_importer { 32 33 /** @var string The import file path of the file which data should be imported from. */ 34 protected string $importfilepath; 35 36 /** @var string The original name of the import file name including extension of the file which data should be imported from. */ 37 protected string $importfilename; 38 39 /** @var string $importfiletype The file type of the import file. */ 40 protected string $importfiletype; 41 42 /** @var file_packer Zip file packer to extract files from a zip archive. */ 43 private file_packer $packer; 44 45 /** @var bool Tracks state if zip archive has been extracted already. */ 46 private bool $zipfileextracted; 47 48 /** @var string Temporary directory where zip archive is being extracted to. */ 49 private string $extracteddir; 50 51 /** 52 * Creates an entries_importer object. 53 * 54 * This object can be used to import data from data files (like csv) and zip archives both including a data file and files to be 55 * stored in the course module context. 56 * 57 * @param string $importfilepath the complete path of the import file including filename 58 * @param string $importfilename the import file name as uploaded by the user 59 * @throws coding_exception if a wrong file type is being used 60 */ 61 public function __construct(string $importfilepath, string $importfilename) { 62 $this->importfilepath = $importfilepath; 63 $this->importfilename = $importfilename; 64 $this->importfiletype = pathinfo($importfilename, PATHINFO_EXTENSION); 65 $this->zipfileextracted = false; 66 if ($this->importfiletype !== $this->get_import_data_file_extension() && $this->importfiletype !== 'zip') { 67 throw new coding_exception('Only "zip" or "' . $this->get_import_data_file_extension() . '" files are ' 68 . 'allowed.'); 69 } 70 } 71 72 /** 73 * Return the file extension of the import data file which is being used, for example 'csv' for a csv entries_importer. 74 * 75 * @return string the file extension of the export data file 76 */ 77 abstract public function get_import_data_file_extension(): string; 78 79 /** 80 * Returns the file content of the data file. 81 * 82 * Returns the content of the file directly if the entries_importer's file is a data file itself. 83 * If the entries_importer's file is a zip archive, the content of the first found data file in the 84 * zip archive's root will be returned. 85 * 86 * @return false|string the data file content as string; false, if file cannot be found/read 87 */ 88 public function get_data_file_content(): false|string { 89 if ($this->importfiletype !== 'zip') { 90 // We have no zip archive, so the file itself must be the data file. 91 return file_get_contents($this->importfilepath); 92 } 93 94 // So we have a zip archive and need to find the right data file in the root of the zip archive. 95 $this->extract_zip(); 96 $datafilenames = array_filter($this->packer->list_files($this->importfilepath), 97 fn($file) => pathinfo($file->pathname, PATHINFO_EXTENSION) === $this->get_import_data_file_extension() 98 && !str_contains($file->pathname, '/')); 99 if (empty($datafilenames) || count($datafilenames) > 1) { 100 return false; 101 } 102 return file_get_contents($this->extracteddir . reset($datafilenames)->pathname); 103 } 104 105 /** 106 * Returns the file content from a file which has been stored in the zip archive. 107 * 108 * @param string $filename 109 * @param string $zipsubdir 110 * @return false|string the file content as string, false if the file could not be found/read 111 */ 112 public function get_file_content_from_zip(string $filename, string $zipsubdir = 'files/'): false|string { 113 if (empty($filename)) { 114 // Nothing to return. 115 return false; 116 } 117 // Just to be sure extract if not extracted yet. 118 $this->extract_zip(); 119 if (!str_ends_with($zipsubdir, '/')) { 120 $zipsubdir .= '/'; 121 } 122 $filepathinextractedzip = $this->extracteddir . $zipsubdir . $filename; 123 return file_exists($filepathinextractedzip) ? file_get_contents($filepathinextractedzip) : false; 124 } 125 126 /** 127 * Extracts (if not already done and if we have a zip file to deal with) the zip file to a temporary directory. 128 * 129 * @return void 130 */ 131 private function extract_zip(): void { 132 if ($this->zipfileextracted || $this->importfiletype !== 'zip') { 133 return; 134 } 135 $this->packer = get_file_packer(); 136 core_php_time_limit::raise(180); 137 $this->extracteddir = make_request_directory(); 138 if (!str_ends_with($this->extracteddir, '/')) { 139 $this->extracteddir .= '/'; 140 } 141 $this->packer->extract_to_pathname($this->importfilepath, $this->extracteddir); 142 $this->zipfileextracted = true; 143 } 144 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body