MDL-71981 dataformat: indicate HTML support in writer callback.
[moodle.git] / lib / classes / dataformat.php
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/>.
17 /**
18  * Class containing utility methods for dataformats
19  *
20  * @package     core
21  * @copyright   2020 Paul Holden <paulh@moodle.com>
22  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace core;
27 use coding_exception;
28 use core_php_time_limit;
29 use stored_file;
31 /**
32  * Dataformat utility class
33  *
34  * @package     core
35  * @copyright   2020 Paul Holden <paulh@moodle.com>
36  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class dataformat {
40     /**
41      * Return an instance of a dataformat writer from given dataformat type
42      *
43      * @param string $dataformat
44      * @return dataformat\base
45      * @throws coding_exception
46      */
47     protected static function get_format_instance(string $dataformat): \core\dataformat\base {
48         $classname = 'dataformat_' . $dataformat . '\writer';
49         if (!class_exists($classname)) {
50             throw new coding_exception('Invalid dataformat', $dataformat);
51         }
53         return new $classname();
54     }
56     /**
57      * Sends a formatted data file to the browser
58      *
59      * @param string $filename
60      * @param string $dataformat
61      * @param array $columns
62      * @param Iterable $iterator
63      * @param callable|null $callback Optional callback method to apply to each record prior to writing, which accepts two
64      *      parameters as such: function($record, bool $supportshtml) returning formatted record
65      * @throws coding_exception
66      */
67     public static function download_data(string $filename, string $dataformat, array $columns, Iterable $iterator,
68             callable $callback = null): void {
70         if (ob_get_length()) {
71             throw new coding_exception('Output can not be buffered before calling download_data()');
72         }
74         $format = self::get_format_instance($dataformat);
76         // The data format export could take a while to generate.
77         core_php_time_limit::raise();
79         // Close the session so that the users other tabs in the same session are not blocked.
80         \core\session\manager::write_close();
82         // If this file was requested from a form, then mark download as complete (before sending headers).
83         \core_form\util::form_download_complete();
85         $format->set_filename($filename);
86         $format->send_http_headers();
88         $format->start_output();
89         $format->start_sheet($columns);
91         $rownum = 0;
92         foreach ($iterator as $row) {
93             if (is_callable($callback)) {
94                 $row = $callback($row, $format->supports_html());
95             }
96             if ($row === null) {
97                 continue;
98             }
99             $format->write_record($row, $rownum++);
100         }
102         $format->close_sheet($columns);
103         $format->close_output();
104     }
106     /**
107      * Writes a formatted data file with specified filename
108      *
109      * @param string $filename
110      * @param string $dataformat
111      * @param array $columns
112      * @param Iterable $iterator
113      * @param callable|null $callback
114      * @return string Complete path to the file on disk
115      */
116     public static function write_data(string $filename, string $dataformat, array $columns, Iterable $iterator,
117             callable $callback = null): string {
119         $format = self::get_format_instance($dataformat);
121         // The data format export could take a while to generate.
122         core_php_time_limit::raise();
124         // Close the session so that the users other tabs in the same session are not blocked.
125         \core\session\manager::write_close();
127         $filepath = make_request_directory() . '/' . $filename . $format->get_extension();
128         $format->set_filepath($filepath);
130         $format->start_output_to_file();
131         $format->start_sheet($columns);
133         $rownum = 0;
134         foreach ($iterator as $row) {
135             if (is_callable($callback)) {
136                 $row = $callback($row, $format->supports_html());
137             }
138             if ($row === null) {
139                 continue;
140             }
141             $format->write_record($row, $rownum++);
142         }
144         $format->close_sheet($columns);
145         $format->close_output_to_file();
147         return $filepath;
148     }
150     /**
151      * Writes a formatted data file to file storage
152      *
153      * @param array $filerecord File record for storage, 'filename' extension should be omitted as it's added by the dataformat
154      * @param string $dataformat
155      * @param array $columns
156      * @param Iterable $iterator Iterable set of records to write
157      * @param callable|null $callback Optional callback method to apply to each record prior to writing
158      * @return stored_file
159      */
160     public static function write_data_to_filearea(array $filerecord, string $dataformat, array $columns, Iterable $iterator,
161             callable $callback = null): stored_file {
163         $filepath = self::write_data($filerecord['filename'], $dataformat, $columns, $iterator, $callback);
165         // Update filename of returned file record.
166         $filerecord['filename'] = basename($filepath);
168         return get_file_storage()->create_file_from_pathname($filerecord, $filepath);
169     }