Passed
Push — 1.11.x ( 120e79...41af88 )
by Yannick
15:40 queued 06:52
created

SectionExport::getSectionData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 11
rs 9.9666
cc 1
nc 1
nop 1
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
namespace moodleexport;
6
7
use Exception;
8
9
/**
10
 * Class SectionExport.
11
 * Handles the export of course sections and their activities.
12
 *
13
 * @package moodleexport
14
 */
15
class SectionExport
16
{
17
    private $course;
18
19
    /**
20
     * Constructor to initialize the course object.
21
     *
22
     * @param object $course The course object to be exported.
23
     */
24
    public function __construct($course)
25
    {
26
        $this->course = $course;
27
    }
28
29
    /**
30
     * Export a section and its activities to the specified directory.
31
     */
32
    public function exportSection(int $sectionId, string $exportDir): void
33
    {
34
        $sectionDir = $exportDir . "/sections/section_{$sectionId}";
35
36
        if (!is_dir($sectionDir)) {
37
            mkdir($sectionDir, api_get_permissions_for_new_directories(), true);
38
        }
39
40
        if ($sectionId > 0) {
41
            $learnpath = $this->getLearnpathById($sectionId);
42
            if ($learnpath === null) {
43
                throw new Exception("Learnpath with ID $sectionId not found.");
44
            }
45
            $sectionData = $this->getSectionData($learnpath);
46
        } else {
47
            $sectionData = [
48
                'id' => 0,
49
                'number' => 0,
50
                'name' => get_lang('General'),
51
                'summary' => get_lang('GeneralResourcesCourse'),
52
                'sequence' => 0,
53
                'visible' => 1,
54
                'timemodified' => time(),
55
                'activities' => $this->getActivitiesForGeneral(),
56
            ];
57
        }
58
59
        $this->createSectionXml($sectionData, $sectionDir);
60
        $this->createInforefXml($sectionData, $sectionDir);
61
        $this->exportActivities($sectionData['activities'], $exportDir, $sectionId);
62
    }
63
64
    /**
65
     * Export the activities of a section.
66
     */
67
    private function exportActivities(array $activities, string $exportDir, int $sectionId): void
68
    {
69
        $exportClasses = [
70
            'quiz' => QuizExport::class,
71
            'glossary' => GlossaryExport::class,
72
            'url' => UrlExport::class,
73
            'assign' => AssignExport::class,
74
            'forum' => ForumExport::class,
75
            'page' => PageExport::class,
76
            'resource' => ResourceExport::class,
77
            'folder' => FolderExport::class,
78
            'feedback' => FeedbackExport::class,
79
        ];
80
81
        foreach ($activities as $activity) {
82
            $moduleName = $activity['modulename'];
83
            if (isset($exportClasses[$moduleName])) {
84
                $exportClass = new $exportClasses[$moduleName]($this->course);
85
                $exportClass->export($activity['id'], $exportDir, $activity['moduleid'], $sectionId);
86
            } else {
87
                throw new \Exception("Export for module '$moduleName' is not supported.");
88
            }
89
        }
90
    }
91
92
    /**
93
     * Get all general items not linked to any lesson (learnpath).
94
     */
95
    public function getGeneralItems(): array
96
    {
97
        $generalItems = [];
98
99
        // List of resource types and their corresponding ID keys
100
        $resourceTypes = [
101
            RESOURCE_DOCUMENT => 'source_id',
102
            RESOURCE_QUIZ => 'source_id',
103
            RESOURCE_GLOSSARY => 'glossary_id',
104
            RESOURCE_LINK => 'source_id',
105
            RESOURCE_WORK => 'source_id',
106
            RESOURCE_FORUM => 'source_id',
107
            RESOURCE_SURVEY => 'source_id',
108
        ];
109
110
        foreach ($resourceTypes as $resourceType => $idKey) {
111
            if (!empty($this->course->resources[$resourceType])) {
112
                foreach ($this->course->resources[$resourceType] as $id => $resource) {
113
                    if (!$this->isItemInLearnpath($resource, $resourceType)) {
114
                        $title = $resourceType === RESOURCE_WORK
115
                            ? ($resource->params['title'] ?? '')
116
                            : ($resource->title ?? $resource->name);
117
                        $generalItems[] = [
118
                            'id' => $resource->$idKey,
119
                            'item_type' => $resourceType,
120
                            'path' => $id,
121
                            'title' => $title,
122
                        ];
123
                    }
124
                }
125
            }
126
        }
127
128
        return $generalItems;
129
    }
130
131
    /**
132
     * Get the activities for the general section.
133
     */
134
    public function getActivitiesForGeneral(): array
135
    {
136
        $generalLearnpath = (object) [
137
            'items' => $this->getGeneralItems(),
138
            'source_id' => 0
139
        ];
140
141
        return $this->getActivitiesForSection($generalLearnpath, true);
142
    }
143
144
    /**
145
     * Check if an item is associated with any learnpath.
146
     */
147
    private function isItemInLearnpath(object $item, string $type): bool
148
    {
149
        if (!empty($this->course->resources[RESOURCE_LEARNPATH])) {
150
            foreach ($this->course->resources[RESOURCE_LEARNPATH] as $learnpath) {
151
                if (!empty($learnpath->items)) {
152
                    foreach ($learnpath->items as $learnpathItem) {
153
                        if ($learnpathItem['item_type'] === $type && $learnpathItem['path'] == $item->source_id) {
154
                            return true;
155
                        }
156
                    }
157
                }
158
            }
159
        }
160
161
        return false;
162
    }
163
164
    /**
165
     * Get the learnpath object by its ID.
166
     */
167
    public function getLearnpathById(int $sectionId): ?object
168
    {
169
        foreach ($this->course->resources[RESOURCE_LEARNPATH] as $learnpath) {
170
            if ($learnpath->source_id == $sectionId) {
171
                return $learnpath;
172
            }
173
        }
174
175
        return null;
176
    }
177
178
    /**
179
     * Get section data for a learnpath.
180
     */
181
    public function getSectionData(object $learnpath): array
182
    {
183
        return [
184
            'id' => $learnpath->source_id,
185
            'number' => $learnpath->display_order,
186
            'name' => $learnpath->name,
187
            'summary' => $learnpath->description,
188
            'sequence' => $learnpath->source_id,
189
            'visible' => $learnpath->visibility,
190
            'timemodified' => strtotime($learnpath->modified_on),
191
            'activities' => $this->getActivitiesForSection($learnpath)
192
        ];
193
    }
194
195
    /**
196
     * Get the activities for a specific section.
197
     */
198
    public function getActivitiesForSection(object $learnpath, bool $isGeneral = false): array
199
    {
200
        $activities = [];
201
        $sectionId = $isGeneral ? 0 : $learnpath->source_id;
202
203
        foreach ($learnpath->items as $item) {
204
            $this->addActivityToList($item, $sectionId, $activities);
205
        }
206
207
        return $activities;
208
    }
209
210
    /**
211
     * Add an activity to the activities list.
212
     */
213
    private function addActivityToList(array $item, int $sectionId, array &$activities): void
214
    {
215
        $activityData = null;
216
        $activityClassMap = [
217
            'quiz' => QuizExport::class,
218
            'glossary' => GlossaryExport::class,
219
            'url' => UrlExport::class,
220
            'assign' => AssignExport::class,
221
            'forum' => ForumExport::class,
222
            'page' => PageExport::class,
223
            'resource' => ResourceExport::class,
224
            'folder' => FolderExport::class,
225
            'feedback' => FeedbackExport::class,
226
        ];
227
228
        $itemType = $item['item_type'] === 'link' ? 'url' : ($item['item_type'] === 'work' ? 'assign' : ($item['item_type'] === 'survey' ? 'feedback' : $item['item_type']));
229
230
        switch ($itemType) {
231
            case 'quiz':
232
            case 'glossary':
233
            case 'assign':
234
            case 'url':
235
            case 'forum':
236
            case 'feedback':
237
                $activityId = $itemType === 'glossary' ? 1 : (int) $item['path'];
238
                $exportClass = $activityClassMap[$itemType];
239
                $exportInstance = new $exportClass($this->course);
240
                $activityData = $exportInstance->getData($activityId, $sectionId);
241
                break;
242
243
            case 'document':
244
                $documentId = (int) $item['path'];
245
                $document = \DocumentManager::get_document_data_by_id($documentId, $this->course->code);
246
247
                // Determine the type of document and get the corresponding export class
248
                $documentType = $this->getDocumentType($document['filetype'], $document['path']);
249
                if ($documentType) {
250
                    $activityClass = $activityClassMap[$documentType];
251
                    $exportInstance = new $activityClass($this->course);
252
                    $activityData = $exportInstance->getData($item['path'], $sectionId);
253
                }
254
                break;
255
        }
256
257
        // Add the activity to the list if the data exists
258
        if ($activityData) {
259
            $activities[] = [
260
                'id' => $activityData['id'],
261
                'moduleid' => $activityData['moduleid'],
262
                'type' => $item['item_type'],
263
                'modulename' => $activityData['modulename'],
264
                'name' => $activityData['name'],
265
            ];
266
        }
267
    }
268
269
    /**
270
     * Determine the document type based on filetype and path.
271
     */
272
    private function getDocumentType(string $filetype, string $path): ?string
273
    {
274
        if ('html' === pathinfo($path, PATHINFO_EXTENSION)) {
275
            return 'page';
276
        } elseif ('file' === $filetype) {
277
            return 'resource';
278
        } elseif ('folder' === $filetype) {
279
            return 'folder';
280
        }
281
282
        return null;
283
    }
284
285
    /**
286
     * Create the section.xml file.
287
     */
288
    private function createSectionXml(array $sectionData, string $destinationDir): void
289
    {
290
        $xmlContent = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
291
        $xmlContent .= '<section id="' . $sectionData['id'] . '">' . PHP_EOL;
292
        $xmlContent .= '  <number>' . $sectionData['number'] . '</number>' . PHP_EOL;
293
        $xmlContent .= '  <name>' . htmlspecialchars($sectionData['name']) . '</name>' . PHP_EOL;
294
        $xmlContent .= '  <summary>' . htmlspecialchars($sectionData['summary']) . '</summary>' . PHP_EOL;
295
        $xmlContent .= '  <summaryformat>1</summaryformat>' . PHP_EOL;
296
        $xmlContent .= '  <sequence>' . implode(',', array_column($sectionData['activities'], 'moduleid')) . '</sequence>' . PHP_EOL;
297
        $xmlContent .= '  <visible>' . $sectionData['visible'] . '</visible>' . PHP_EOL;
298
        $xmlContent .= '  <timemodified>' . $sectionData['timemodified'] . '</timemodified>' . PHP_EOL;
299
        $xmlContent .= '</section>' . PHP_EOL;
300
301
        $xmlFile = $destinationDir . '/section.xml';
302
        file_put_contents($xmlFile, $xmlContent);
303
    }
304
305
    /**
306
     * Create the inforef.xml file for the section.
307
     */
308
    private function createInforefXml(array $sectionData, string $destinationDir): void
309
    {
310
        $xmlContent = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
311
        $xmlContent .= '<inforef>' . PHP_EOL;
312
313
        foreach ($sectionData['activities'] as $activity) {
314
            $xmlContent .= '  <activity id="' . $activity['id'] . '">' . htmlspecialchars($activity['name']) . '</activity>' . PHP_EOL;
315
        }
316
317
        $xmlContent .= '</inforef>' . PHP_EOL;
318
319
        $xmlFile = $destinationDir . '/inforef.xml';
320
        file_put_contents($xmlFile, $xmlContent);
321
    }
322
}
323