MDL-69549 core: Add context export API
[moodle.git] / lib / classes / content / export / exporters / course_exporter.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 course exporter.
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  */
24 namespace core\content\export\exporters;
26 use context_course;
27 use context_module;
28 use core\content\export\exported_item;
29 use core\content\export\zipwriter;
30 use section_info;
31 use stdClass;
33 /**
34  * The course exporter.
35  *
36  * @copyright   2020 Andrew Nicols <andrew@nicols.co.uk>
37  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38  */
39 class course_exporter extends component_exporter {
41     /** @var stdClass The course being exported */
42     protected $course;
44     /** @var \course_modinfo The course_modinfo instnace for this course */
45     protected $modinfo;
47     /**
48      * Constructor for the course exporter.
49      *
50      * @param   context_course $context The context of the course to export
51      * @param   stdClass $user
52      * @param   zipwriter $archive
53      */
54     public function __construct(context_course $context, stdClass $user, zipwriter $archive) {
55         $this->course = get_course($context->instanceid);
56         $this->modinfo = get_fast_modinfo($this->course, $user->id);
58         parent::__construct($context, 'core_course', $user, $archive);
59     }
61     /**
62      * Export the course.
63      *
64      * @param   context[] $exportedcontexts A list of contexts which were successfully exported
65      */
66     public function export_course(array $exportedcontexts): void {
67         // A course export is composed of:
68         // - Course summary (including inline files)
69         // - Overview files
70         // - Section:
71         // -- Section name
72         // -- Section summary (including inline files)
73         // -- List of available activities.
75         $aboutpagelink = $this->add_course_about_page();
76         $templatedata = (object) [
77             'aboutpagelink' => $aboutpagelink,
78             'sections' => [],
79         ];
81         // Add all sections.
82         foreach ($this->modinfo->get_section_info_all() as $number => $section) {
83             $templatedata->sections[] = $this->get_course_section($exportedcontexts, $section);
84         }
86         $this->get_archive()->add_file_from_template(
87             $this->get_context(),
88             'index.html',
89             'core/content/export/course_index',
90             $templatedata
91         );
92     }
94     /**
95      * Add course about page.
96      *
97      * @return  null|string The URL to the about page if one was generated
98      */
99     protected function add_course_about_page(): ?string {
100         $hascontent = false;
102         $templatedata = (object) [
103             'summary' => '',
104             'overviewfiles' => [],
105         ];
107         // Fetch the course summary content.
108         if ($this->course->summary) {
109             $summarydata = $this->get_archive()->add_pluginfiles_for_content(
110                 $this->get_context(),
111                 '_course',
112                 $this->course->summary,
113                 'course',
114                 'summary',
115                 0,
116                 null
117             );
119             if ($summarydata->has_any_data()) {
120                 $hascontent = true;
121                 $templatedata->summary = $summarydata->get_content();
122             }
123         }
125         $files = $this->get_archive()->add_pluginfiles_for_content(
126             $this->get_context(),
127             '',
128             '',
129             'course',
130             'overviewfiles',
131             0,
132             null
133         )->get_noncontent_files();
135         if (count($files)) {
136             $templatedata->overviewfiles = $files;
137             $hascontent = true;
138         }
140         if ($hascontent) {
141             $this->get_archive()->add_file_from_template(
142                 $this->get_context(),
143                 'about.html',
144                 'core/content/export/course_summary',
145                 $templatedata
146             );
148             return $this->get_archive()->get_relative_context_path($this->get_context(), $this->get_context(), 'about.html');
149         }
151         return null;
152     }
154     /**
155      * Fetch data for the specified course section.
156      *
157      * @param   context[] $exportedcontexts A list of contexts which were successfully exported
158      * @param   section_info $section The section being exported
159      * @return  stdClass
160      */
161     protected function get_course_section(array $exportedcontexts, section_info $section): stdClass {
162         $sectiondata = (object) [
163             'number' => $section->section,
164             'title' => $section->name,
165             'summary' => '',
166             'activities' => [],
167         ];
169         $sectiondata->summary = $this->get_archive()->add_pluginfiles_for_content(
170             $this->get_context(),
171             "_course",
172             $section->summary,
173             'course',
174             'section',
175             $section->id,
176             $section->id
177         )->get_template_data()->content;
179         if (empty($this->modinfo->sections[$section->section])) {
180             return $sectiondata;
181         }
183         foreach ($this->modinfo->sections[$section->section] as $cmid) {
184             $cm = $this->modinfo->cms[$cmid];
185             if (!$cm->uservisible) {
186                 continue;
187             }
189             if (array_key_exists($cm->context->id, $exportedcontexts)) {
190                 // This activity was exported.
191                 // The link to it from the course index should be a relative link.
192                 $url = $this->get_archive()->get_relative_context_path($this->get_context(), $cm->context, 'index.html');
193             } else {
194                 // This activity was not included in the export for some reason.
195                 // Link to the live activity.
196                 $url = $cm->url;
197             }
198             $sectiondata->activities[] = (object) [
199                 'title' => $cm->name,
200                 'modname' => $cm->modfullname,
201                 'link' => $url,
202             ];
203         }
205         return $sectiondata;
206     }
208     /**
209      * Export all exportable content for an activity module.
210      *
211      * @param   context_module $modcontect
212      * @param   exportable_item[] $export_exportables
213      */
214     public function export_mod_content(context_module $modcontext, array $exportables): void {
215         $cm = $this->modinfo->get_cm($modcontext->instanceid);
216         $modname = $cm->modname;
218         $templatedata = (object) [
219             'modulelink' => $cm->url,
220             'modulename' => $cm->get_formatted_name(),
221             'intro' => null,
222             'sections' => [],
223         ];
225         if (plugin_supports('mod', $modname, FEATURE_MOD_INTRO, true)) {
226             $templatedata->intro = $this->get_mod_intro_data($modcontext);
227         }
229         $exporteditems = [];
230         foreach ($exportables as $exportable) {
231             $exporteditem = $exportable->add_to_archive($this->get_archive());
232             $templatedata->sections[] = $exporteditem->get_template_data();
233         }
235         // Add the index to the archive.
236         $this->get_archive()->add_file_from_template(
237             $modcontext,
238             'index.html',
239             'core/content/export/module_index',
240             $templatedata
241         );
242     }
244     /**
245      * Get the course_module introduction data.
246      *
247      * @param   context_module $modcontect
248      * @return  null|string The content of the intro area
249      */
250     protected function get_mod_intro_data(context_module $modcontext): ?string {
251         global $DB;
253         $cm = $this->modinfo->get_cm($modcontext->instanceid);
254         $modname = $cm->modname;
256         $record = $DB->get_record($modname, ['id' => $cm->instance], 'intro');
258         $exporteditem = $this->get_archive()->add_pluginfiles_for_content(
259             $modcontext,
260             '',
261             $record->intro,
262             "mod_{$modname}",
263             'intro',
264             0,
265             null
266         );
268         if ($exporteditem->has_any_data()) {
269             return $exporteditem->get_content();
270         }
272         return null;
273     }