MDL-69549 core: Add context export API
[moodle.git] / lib / classes / content / export / exportable_items / exportable_stored_file.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  * The definition of an item which can be exported.
19  *
20  * @package     core
21  * @copyright   2020 Andrew Nicols <andrew@nicols.co.uk>
22  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 declare(strict_types=1);
27 namespace core\content\export\exportable_items;
29 use context;
30 use core\content\export\exportable_item;
31 use core\content\export\exported_item;
32 use core\content\export\zipwriter;
33 use moodle_url;
34 use stored_file;
36 /**
37  * An object used to represent content which can be served.
38  *
39  * @copyright   2020 Andrew Nicols <andrew@nicols.co.uk>
40  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  */
42 class exportable_stored_file extends exportable_item {
44     /** @var string The destination path of the text content */
45     protected $folderpath;
47     /** @var stored_file The file to be exported */
48     protected $file;
50     /** @var int The itemid to use in the pluginfile URL */
51     protected $pluginfileitemid;
53     /**
54      * Create a new exportable_item instance.
55      *
56      * If no filearea or itemid  is specified the no attempt will be made to export files.
57      *
58      * @param   context $context The context that this content belongs to
59      * @param   string $component
60      * @param   string $uservisiblename The name displayed to the user when filtering
61      * @param   stored_file $file
62      * @param   null|int $pluginfileitemid The itemid as used in the pluginfile URL.
63      *          If no itemid is used, then a null value can be provided
64      * @param   string $folderpath Any sub-directory to place files in
65      */
66     public function __construct(
67         context $context,
68         string $component,
69         string $uservisiblename,
70         stored_file $file,
71         ?int $pluginfileitemid = null,
72         string $folderpath = ''
73     ) {
74         parent::__construct($context, $component, $uservisiblename);
76         $this->file = $file;
77         $this->folderpath = $folderpath;
78         $this->pluginfileitemid = $pluginfileitemid;
79     }
81     /**
82      * Create a set of exportable_items from a set of area paramaters as passed to get_areas_files().
83      *
84      * If no filearea or itemid  is specified the no attempt will be made to export files.
85      *
86      * @param   context $context The context that this content belongs to
87      * @param   string $component
88      * @param   string $filearea
89      * @param   null|int $itemid
90      * @param   null|int $pluginfileitemid The itemid as used in the pluginfile URL.
91      *          If no itemid is used, then a null value can be provided
92      * @param   string $folderpath Any sub-directory to place files in
93      * @return  array
94      */
95     public static function create_from_area_params(
96         context $context,
97         string $component,
98         string $filearea,
99         ?int $itemid,
100         ?int $pluginfileitemid = null,
101         string $folderpath = ''
102     ): array {
103         $fs = get_file_storage();
104         if ($itemid === null) {
105             $itemid = false;
106         }
108         $exportables = [];
109         foreach ($fs->get_area_files($context->id, $component, $filearea, $itemid) as $file) {
110             if ($file->is_directory()) {
111                 // Do not export directories.
112                 // If they contain file contents the directory structure will be created in the zip file.
113                 continue;
114             }
115             $filepath = $file->get_filepath() . $file->get_filename();
116             $exportables[] = new self($context, $component, $filepath, $file, $pluginfileitemid, $folderpath);
117         }
119         return $exportables;
120     }
122     /**
123      * Add the content to the archive.
124      *
125      * @param   zipwriter $archive
126      */
127     public function add_to_archive(zipwriter $archive): ?exported_item {
128         // Export the content to [contextpath]/[filepath].
129         $relativefilepath = $this->get_filepath_for_file();
131         $archive->add_file_from_stored_file(
132             $this->get_context(),
133             $relativefilepath,
134             $this->file
135         );
137         $exporteditem = new exported_item();
138         $exporteditem->set_title($this->get_user_visible_name());
140         if ($archive->is_file_in_archive($this->context, $relativefilepath)) {
141             // The file was successfully added to the archive.
142             $exporteditem->add_file($relativefilepath, false);
143         } else {
144             // The file was not added. Link to the live version instead.
145             $exporteditem->add_file(
146                 $relativefilepath,
147                 false,
148                 self::get_pluginfile_url_for_stored_file($this->file, $this->pluginfileitemid)
149             );
150         }
152         return $exporteditem;
153     }
155     /**
156      * Get the filepath for the specified stored_file.
157      *
158      * @return  string
159      */
160     protected function get_filepath_for_file(): string {
161         $folderpath = rtrim($this->folderpath);
163         if (!empty($folderpath)) {
164             $folderpath .= '/';
165         }
166         return sprintf(
167             '%s%s%s%s',
168             $folderpath,
169             $this->file->get_filearea(),
170             $this->file->get_filepath(),
171             $this->file->get_filename()
172         );
173     }
175     /**
176      * Get the pluginfile URL for a stored file.
177      *
178      * Note: The itemid in the pluginfile may be omitted in some URLs, despite an itemid being present in the database.
179      * Equally, the itemid in the URL may not match the itemid in the files table.
180      *
181      * The pluginfileitemid argument provided to this function is the variant in the URL, and not the one in the files
182      * table.
183      *
184      * @param   stored_file $file The file whose link will be generated
185      * @param   null|int $pluginfileitemid The itemid of the file in pluginfile URL.
186      *
187      */
188     protected static function get_pluginfile_url_for_stored_file(stored_file $file, ?int $pluginfileitemid): string {
189         $link = moodle_url::make_pluginfile_url(
190             $file->get_contextid(),
191             $file->get_component(),
192             $file->get_filearea(),
193             $pluginfileitemid,
194             $file->get_filepath(),
195             $file->get_filename(),
196             true,
197             true
198         );
200         return $link->out(false);
201     }