SectionExport::addActivityToList()   F
last analyzed

Complexity

Conditions 19
Paths 1152

Size

Total Lines 73
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 55
c 2
b 0
f 0
dl 0
loc 73
rs 0.3499
cc 19
nc 1152
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
     * Get all general items not linked to any lesson (learnpath).
66
     */
67
    public function getGeneralItems(): array
68
    {
69
        $generalItems = [];
70
71
        // List of resource types and their corresponding ID keys
72
        $resourceTypes = [
73
            RESOURCE_DOCUMENT => 'source_id',
74
            RESOURCE_QUIZ => 'source_id',
75
            RESOURCE_GLOSSARY => 'glossary_id',
76
            RESOURCE_LINK => 'source_id',
77
            RESOURCE_WORK => 'source_id',
78
            RESOURCE_FORUM => 'source_id',
79
            RESOURCE_SURVEY => 'source_id',
80
            RESOURCE_TOOL_INTRO => 'source_id',
81
        ];
82
83
        foreach ($resourceTypes as $resourceType => $idKey) {
84
            if (!empty($this->course->resources[$resourceType])) {
85
                foreach ($this->course->resources[$resourceType] as $id => $resource) {
86
                    if (!$this->isItemInLearnpath($resource, $resourceType)) {
87
                        $title = $resourceType === RESOURCE_WORK
88
                            ? ($resource->params['title'] ?? '')
89
                            : ($resource->title ?? $resource->name);
90
                        $generalItems[] = [
91
                            'id' => $resource->$idKey,
92
                            'item_type' => $resourceType,
93
                            'path' => $id,
94
                            'title' => $title,
95
                        ];
96
                    }
97
                }
98
            }
99
        }
100
101
        return $generalItems;
102
    }
103
104
    /**
105
     * Get the activities for the general section.
106
     */
107
    public function getActivitiesForGeneral(): array
108
    {
109
        $generalLearnpath = (object) [
110
            'items' => $this->getGeneralItems(),
111
            'source_id' => 0,
112
        ];
113
114
        $activities = $this->getActivitiesForSection($generalLearnpath, true);
115
116
        if (!in_array('folder', array_column($activities, 'modulename'))) {
117
            $activities[] = [
118
                'id' => 0,
119
                'moduleid' => 0,
120
                'modulename' => 'folder',
121
                'name' => 'Documents',
122
                'sectionid' => 0,
123
            ];
124
        }
125
126
        return $activities;
127
    }
128
129
    /**
130
     * Get the learnpath object by its ID.
131
     */
132
    public function getLearnpathById(int $sectionId): ?object
133
    {
134
        foreach ($this->course->resources[RESOURCE_LEARNPATH] as $learnpath) {
135
            if ($learnpath->source_id == $sectionId) {
136
                return $learnpath;
137
            }
138
        }
139
140
        return null;
141
    }
142
143
    /**
144
     * Get section data for a learnpath.
145
     */
146
    public function getSectionData(object $learnpath): array
147
    {
148
        return [
149
            'id' => $learnpath->source_id,
150
            'number' => $learnpath->display_order,
151
            'name' => $learnpath->name,
152
            'summary' => $learnpath->description,
153
            'sequence' => $learnpath->source_id,
154
            'visible' => $learnpath->visibility,
155
            'timemodified' => strtotime($learnpath->modified_on),
156
            'activities' => $this->getActivitiesForSection($learnpath),
157
        ];
158
    }
159
160
    /**
161
     * Get the activities for a specific section.
162
     */
163
    public function getActivitiesForSection(object $learnpath, bool $isGeneral = false): array
164
    {
165
        $activities = [];
166
        $sectionId = $isGeneral ? 0 : $learnpath->source_id;
167
168
        foreach ($learnpath->items as $item) {
169
            $this->addActivityToList($item, $sectionId, $activities);
170
        }
171
172
        return $activities;
173
    }
174
175
    /**
176
     * Export the activities of a section.
177
     */
178
    private function exportActivities(array $activities, string $exportDir, int $sectionId): void
179
    {
180
        $exportClasses = [
181
            'quiz' => QuizExport::class,
182
            'glossary' => GlossaryExport::class,
183
            'url' => UrlExport::class,
184
            'assign' => AssignExport::class,
185
            'forum' => ForumExport::class,
186
            'page' => PageExport::class,
187
            'resource' => ResourceExport::class,
188
            'folder' => FolderExport::class,
189
            'feedback' => FeedbackExport::class,
190
        ];
191
192
        foreach ($activities as $activity) {
193
            $moduleName = $activity['modulename'];
194
            if (isset($exportClasses[$moduleName])) {
195
                $exportClass = new $exportClasses[$moduleName]($this->course);
196
                $exportClass->export($activity['id'], $exportDir, $activity['moduleid'], $sectionId);
197
            } else {
198
                throw new \Exception("Export for module '$moduleName' is not supported.");
199
            }
200
        }
201
    }
202
203
    /**
204
     * Check if an item is associated with any learnpath.
205
     */
206
    private function isItemInLearnpath(object $item, string $type): bool
207
    {
208
        if (!empty($this->course->resources[RESOURCE_LEARNPATH])) {
209
            foreach ($this->course->resources[RESOURCE_LEARNPATH] as $learnpath) {
210
                if (!empty($learnpath->items)) {
211
                    foreach ($learnpath->items as $learnpathItem) {
212
                        if ($learnpathItem['item_type'] === $type && $learnpathItem['path'] == $item->source_id) {
213
                            return true;
214
                        }
215
                    }
216
                }
217
            }
218
        }
219
220
        return false;
221
    }
222
223
    /**
224
     * Add an activity to the activities list.
225
     */
226
    private function addActivityToList(array $item, int $sectionId, array &$activities): void
227
    {
228
        static $documentsFolderAdded = false;
229
        if (!$documentsFolderAdded && $sectionId === 0) {
230
            $activities[] = [
231
                'id' => 0,
232
                'moduleid' => 0,
233
                'type' => 'folder',
234
                'modulename' => 'folder',
235
                'name' => 'Documents',
236
            ];
237
            $documentsFolderAdded = true;
238
        }
239
240
        $activityData = null;
241
        $activityClassMap = [
242
            'quiz' => QuizExport::class,
243
            'glossary' => GlossaryExport::class,
244
            'url' => UrlExport::class,
245
            'assign' => AssignExport::class,
246
            'forum' => ForumExport::class,
247
            'page' => PageExport::class,
248
            'resource' => ResourceExport::class,
249
            'feedback' => FeedbackExport::class,
250
        ];
251
252
        if ($item['id'] == 'course_homepage') {
253
            $item['item_type'] = 'page';
254
            $item['path'] = 0;
255
        }
256
257
        $itemType = $item['item_type'] === 'link' ? 'url' :
258
            ($item['item_type'] === 'work' ? 'assign' :
259
                ($item['item_type'] === 'survey' ? 'feedback' : $item['item_type']));
260
261
        switch ($itemType) {
262
            case 'quiz':
263
            case 'glossary':
264
            case 'assign':
265
            case 'url':
266
            case 'forum':
267
            case 'feedback':
268
            case 'page':
269
                $activityId = $itemType === 'glossary' ? 1 : (int) $item['path'];
270
                $exportClass = $activityClassMap[$itemType];
271
                $exportInstance = new $exportClass($this->course);
272
                $activityData = $exportInstance->getData($activityId, $sectionId);
273
                break;
274
275
            case 'document':
276
                if ($sectionId > 0) {
277
                    $documentId = (int) $item['path'];
278
                    $document = \DocumentManager::get_document_data_by_id($documentId, $this->course->code);
279
280
                    // Determine the type of document and get the corresponding export class
281
                    $documentType = $this->getDocumentType($document['filetype'], $document['path']);
282
                    if ($documentType) {
283
                        $activityClass = $activityClassMap[$documentType];
284
                        $exportInstance = new $activityClass($this->course);
285
                        $activityData = $exportInstance->getData($item['path'], $sectionId);
286
                    }
287
                    break;
288
                }
289
        }
290
291
        // Add the activity to the list if the data exists
292
        if ($activityData) {
293
            $activities[] = [
294
                'id' => $activityData['id'],
295
                'moduleid' => $activityData['moduleid'],
296
                'type' => $item['item_type'],
297
                'modulename' => $activityData['modulename'],
298
                'name' => $activityData['name'],
299
            ];
300
        }
301
    }
302
303
    /**
304
     * Determine the document type based on filetype and path.
305
     */
306
    private function getDocumentType(string $filetype, string $path): ?string
307
    {
308
        if ('html' === pathinfo($path, PATHINFO_EXTENSION)) {
309
            return 'page';
310
        } elseif ('file' === $filetype) {
311
            return 'resource';
312
        } /*elseif ('folder' === $filetype) {
313
            return 'folder';
314
        }*/
315
316
        return null;
317
    }
318
319
    /**
320
     * Create the section.xml file.
321
     */
322
    private function createSectionXml(array $sectionData, string $destinationDir): void
323
    {
324
        $xmlContent = '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
325
        $xmlContent .= '<section id="'.$sectionData['id'].'">'.PHP_EOL;
326
        $xmlContent .= '  <number>'.$sectionData['number'].'</number>'.PHP_EOL;
327
        $xmlContent .= '  <name>'.htmlspecialchars($sectionData['name']).'</name>'.PHP_EOL;
328
        $xmlContent .= '  <summary>'.htmlspecialchars($sectionData['summary']).'</summary>'.PHP_EOL;
329
        $xmlContent .= '  <summaryformat>1</summaryformat>'.PHP_EOL;
330
        $xmlContent .= '  <sequence>'.implode(',', array_column($sectionData['activities'], 'moduleid')).'</sequence>'.PHP_EOL;
331
        $xmlContent .= '  <visible>'.$sectionData['visible'].'</visible>'.PHP_EOL;
332
        $xmlContent .= '  <timemodified>'.$sectionData['timemodified'].'</timemodified>'.PHP_EOL;
333
        $xmlContent .= '</section>'.PHP_EOL;
334
335
        $xmlFile = $destinationDir.'/section.xml';
336
        file_put_contents($xmlFile, $xmlContent);
337
    }
338
339
    /**
340
     * Create the inforef.xml file for the section.
341
     */
342
    private function createInforefXml(array $sectionData, string $destinationDir): void
343
    {
344
        $xmlContent = '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
345
        $xmlContent .= '<inforef>'.PHP_EOL;
346
347
        foreach ($sectionData['activities'] as $activity) {
348
            $xmlContent .= '  <activity id="'.$activity['id'].'">'.htmlspecialchars($activity['name']).'</activity>'.PHP_EOL;
349
        }
350
351
        $xmlContent .= '</inforef>'.PHP_EOL;
352
353
        $xmlFile = $destinationDir.'/inforef.xml';
354
        file_put_contents($xmlFile, $xmlContent);
355
    }
356
}
357