Passed
Push — 1.11.x ( 80df5b...400f7a )
by Yannick
15:38
created

updateCourses()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 2
nc 2
nop 1
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
$cidReset = true;
5
6
require_once __DIR__.'/../inc/global.inc.php';
7
api_protect_admin_script();
8
9
/**
10
 * Generates a CSV model string showing how the CSV file should be structured for course updates.
11
 */
12
function generateCsvModel(array $fields): string
13
{
14
    $headerCsv = "<strong>Code</strong>;Title;CourseCategory;Language;";
15
16
    $exampleCsv = "<b>COURSE001</b>;Introduction to Biology;BIO;english;";
17
18
    foreach ($fields as $field) {
19
        $fieldType = (int) $field['field_type'];
20
        switch ($fieldType) {
21
            case ExtraField::FIELD_TYPE_CHECKBOX:
22
                $exampleValue = '1'; // 1 for true, 0 for false
23
                break;
24
            case ExtraField::FIELD_TYPE_TAG:
25
                $exampleValue = 'tag1,tag2,tag3'; // Comma separated list of tags
26
                break;
27
            default:
28
                $exampleValue = 'xxx'; // Example value for text fields
29
        }
30
31
        $headerCsv .= "<span style=\"color:red;\">".$field['field_variable']."</span>;";
32
33
        $exampleCsv .= "<span style=\"color:red;\">$exampleValue</span>;";
34
    }
35
36
    $modelCsv = $headerCsv."\n".$exampleCsv;
37
38
    return $modelCsv;
39
}
40
41
/**
42
 * Generates an XML model string showing how the XML file should be structured for course updates.
43
 */
44
function generateXmlModel(array $fields): string
45
{
46
    $modelXml = "&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n";
47
    $modelXml .= "&lt;Courses&gt;\n";
48
    $modelXml .= "    &lt;Course&gt;\n";
49
    $modelXml .= "        <b>&lt;Code&gt;COURSE001&lt;/Code&gt;</b>\n";
50
    $modelXml .= "        &lt;Title&gt;Introduction to Biology&lt;/Title&gt;\n";
51
    $modelXml .= "        &lt;CourseCategory&gt;BIO&lt;/CourseCategory&gt;\n";
52
    $modelXml .= "        &lt;Language&gt;english&lt;/Language&gt;\n";
53
    foreach ($fields as $field) {
54
        switch ($field['field_type']) {
55
            case ExtraField::FIELD_TYPE_CHECKBOX:
56
                $exampleValue = '1'; // 1 for true, 0 for false
57
                break;
58
            case ExtraField::FIELD_TYPE_TAG:
59
                $exampleValue = 'tag1,tag2,tag3'; // Comma separated list of tags
60
                break;
61
            default:
62
                $exampleValue = 'xxx'; // Example value for text fields
63
        }
64
65
        $modelXml .= "        <span style=\"color:red;\">&lt;".$field['field_variable']."&gt;$exampleValue&lt;/".$field['field_variable']."&gt;</span>\n";
66
    }
67
    $modelXml .= "    &lt;/Course&gt;\n";
68
    $modelXml .= "&lt;/Courses&gt;";
69
70
    return $modelXml;
71
}
72
73
/**
74
 * Function to validate course data from the CSV/XML file.
75
 */
76
function validateCourseData(array $courses): array
77
{
78
    $errors = [];
79
    $courseCodes = [];
80
81
    foreach ($courses as $course) {
82
        if (empty($course['Code'])) {
83
            $errors[] = get_lang("CodeIsRequired");
84
        } else {
85
            $courseId = api_get_course_int_id($course['Code']);
86
            if (!$courseId) {
87
                $errors[] = get_lang("CourseCodeDoesNotExist").': '.$course['Code'];
88
            } elseif (in_array($course['Code'], $courseCodes)) {
89
                $errors[] = get_lang("DuplicateCode").': '.$course['Code'];
90
            }
91
92
            $courseCodes[] = $course['Code'];
93
        }
94
    }
95
96
    return $errors;
97
}
98
99
/**
100
 * Update course data in the database.
101
 */
102
function updateCourse(array $courseData, int $courseId): void
103
{
104
    $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
105
    $params = [
106
        'title' => $courseData['Title'],
107
        'course_language' => $courseData['Language'],
108
        'category_code' => $courseData['CourseCategory'],
109
        'visual_code' => $courseData['Code'],
110
    ];
111
    Database::update($courseTable, $params, ['id = ?' => $courseId]);
112
    $courseData['code'] = $courseData['Code'];
113
    $courseData['item_id'] = $courseId;
114
    $courseFieldValue = new ExtraFieldValue('course');
115
    $courseFieldValue->saveFieldValues($courseData);
116
}
117
118
/**
119
 * Function to update courses from the imported data.
120
 */
121
function updateCourses(array $courses): void
122
{
123
    foreach ($courses as $course) {
124
        $courseId = api_get_course_int_id($course['Code']);
125
        updateCourse($course, $courseId);
126
    }
127
}
128
129
/**
130
 * Function to parse CSV data.
131
 */
132
function parseCsvCourseData(string $file, array $extraFields): array
133
{
134
    $data = Import::csv_reader($file);
135
    $courses = [];
136
137
    foreach ($data as $row) {
138
        $courseData = [];
139
        foreach ($row as $key => $value) {
140
            if (empty($key)) {
141
                continue;
142
            }
143
            if (in_array($key, array_column($extraFields, 'variable'))) {
144
                $processedValue = processExtraFieldValue($key, $value, $extraFields);
145
                $courseData['extra_'.$key] = $processedValue;
146
            } else {
147
                $courseData[$key] = $value;
148
            }
149
        }
150
151
        $courses[] = $courseData;
152
    }
153
154
    return $courses;
155
}
156
157
/**
158
 * Function to parse XML data.
159
 */
160
function parseXmlCourseData(string $file, array $extraFields): array
161
{
162
    $xmlContent = Import::xml($file);
163
    $courses = [];
164
165
    foreach ($xmlContent->filter('Courses > Course') as $xmlCourse) {
166
        $courseData = [];
167
        foreach ($xmlCourse->childNodes as $node) {
168
            if ($node->nodeName !== '#text') {
169
                $key = $node->nodeName;
170
                if (empty($key)) {
171
                    continue;
172
                }
173
                $value = $node->nodeValue;
174
                if (in_array($key, array_column($extraFields, 'variable'))) {
175
                    $processedValue = processExtraFieldValue($key, $value, $extraFields);
176
                    $courseData['extra_'.$key] = $processedValue;
177
                } else {
178
                    $courseData[$key] = $value;
179
                }
180
            }
181
        }
182
183
        if (!empty($courseData)) {
184
            $courses[] = $courseData;
185
        }
186
    }
187
188
    return $courses;
189
}
190
191
/**
192
 * Processes the value of an extra field based on its type.
193
 *
194
 * This function takes the name and value of an extra field, along with an array of all extra fields, and processes
195
 * the value according to the field type. For checkbox fields, it returns an array with the field name as the key
196
 * and '1' (checked) or '0' (unchecked) as the value. For tag fields, it splits the string by commas into an array.
197
 * For other types, it returns the value as is.
198
 */
199
function processExtraFieldValue(string $fieldName, $value, array $extraFields)
200
{
201
    $fieldIndex = array_search($fieldName, array_column($extraFields, 'variable'));
202
    if ($fieldIndex === false) {
203
        return $value;
204
    }
205
206
    $fieldType = $extraFields[$fieldIndex]['field_type'];
207
208
    switch ($fieldType) {
209
        case ExtraField::FIELD_TYPE_CHECKBOX:
210
            $newValue = 0;
211
            if ($value == '1') {
212
                $newValue = ['extra_'.$fieldName => '1'];
213
            }
214
            return $newValue;
215
        case ExtraField::FIELD_TYPE_TAG:
216
            return explode(',', $value);
217
        default:
218
            return $value;
219
    }
220
}
221
222
$toolName = get_lang('UpdateCourseListXMLCSV');
223
$interbreadcrumb[] = ["url" => 'index.php', "name" => get_lang('PlatformAdmin')];
224
225
$form = new FormValidator('course_update_import');
226
$form->addHeader(get_lang('UpdateCourseListXMLCSV'));
227
$form->addFile('importFile', get_lang('ImportCSVFileLocation'));
228
229
$form->addElement('radio', 'file_type', get_lang('FileType'), get_lang('CSV'), 'csv');
230
$form->addElement('radio', 'file_type', '', get_lang('XML'), 'xml');
231
232
$defaults['file_type'] = 'csv';
233
$form->setDefaults($defaults);
234
235
$form->addButtonImport(get_lang('Import'));
236
237
if ($form->validate()) {
238
    if (!isset($_FILES['importFile']['error']) || is_array($_FILES['importFile']['error'])) {
239
        Display::addFlash(Display::return_message(get_lang('InvalidFileUpload'), 'error'));
240
    } else {
241
        switch ($_FILES['importFile']['error']) {
242
            case UPLOAD_ERR_OK:
243
                break;
244
            case UPLOAD_ERR_NO_FILE:
245
                Display::addFlash(Display::return_message(get_lang('NoFileSent'), 'error'));
246
                break;
247
            case UPLOAD_ERR_INI_SIZE:
248
            case UPLOAD_ERR_FORM_SIZE:
249
                Display::addFlash(Display::return_message(get_lang('ExceededFileSizeLimit'), 'error'));
250
                break;
251
            default:
252
                Display::addFlash(Display::return_message(get_lang('UnknownErrors'), 'error'));
253
        }
254
    }
255
256
    $fileType = $_POST['file_type'];
257
    $fileExt = strtolower(pathinfo($_FILES['importFile']['name'], PATHINFO_EXTENSION));
258
259
    if (($fileType === 'csv' && $fileExt !== 'csv') || ($fileType === 'xml' && $fileExt !== 'xml')) {
260
        Display::addFlash(Display::return_message(get_lang('InvalidFileType'), 'error'));
261
    } else {
262
        $file = $_FILES['importFile']['tmp_name'];
263
        $extraField = new ExtraField('course');
264
        $allExtraFields = $extraField->get_all();
265
        $successfulUpdates = [];
266
        $failedUpdates = [];
267
        try {
268
            if ($fileType === 'csv') {
269
                $courses = parseCsvCourseData($file, $allExtraFields);
270
            } else {
271
                $courses = parseXmlCourseData($file, $allExtraFields);
272
            }
273
274
            foreach ($courses as $course) {
275
                $courseErrors = validateCourseData([$course]);
276
                if (!empty($courseErrors)) {
277
                    $failedUpdates[] = $course['Code'].': '.implode(', ', $courseErrors);
278
                    continue;
279
                }
280
                try {
281
                    updateCourses([$course]);
282
                    $successfulUpdates[] = $course['Code'];
283
                } catch (Exception $e) {
284
                    $failedUpdates[] = $course['Code'].': '.$e->getMessage();
285
                }
286
            }
287
288
            if (!empty($successfulUpdates)) {
289
                Display::addFlash(Display::return_message(get_lang('CoursesUpdatedSuccessfully').': '.implode(', ', $successfulUpdates), 'success'));
290
            }
291
292
            if (!empty($failedUpdates)) {
293
                foreach ($failedUpdates as $error) {
294
                    Display::addFlash(Display::return_message(get_lang('UpdateFailedForCourses').': '.$error, 'error'));
295
                }
296
            }
297
        } catch (Exception $e) {
298
            Display::addFlash(Display::return_message($e->getMessage(), 'error'));
299
        }
300
    }
301
}
302
303
$htmlHeadXtra[] = "<script>
304
    $(document).ready(function() {
305
        function showFileType(type) {
306
            if (type === 'csv') {
307
                $('#csv-model').show();
308
                $('#xml-model').hide();
309
            } else {
310
                $('#csv-model').hide();
311
                $('#xml-model').show();
312
            }
313
        }
314
315
        showFileType($('input[name=file_type]:checked').val());
316
317
        $('input[name=file_type]').on('change', function() {
318
            showFileType($(this).val());
319
        });
320
    });
321
</script>";
322
323
Display::display_header($toolName);
324
325
$form->display();
326
327
$extraField = new ExtraField('course');
328
$allExtraFields = $extraField->get_all();
329
330
$extraFields = [];
331
foreach ($allExtraFields as $field) {
332
    $extraFields[] = [
333
        'field_variable' => $field['variable'],
334
        'field_type' => $field['field_type'],
335
    ];
336
}
337
338
$csvContent = generateCsvModel($extraFields);
339
$xmlContent = generateXmlModel($extraFields);
340
echo '<div id="csv-model"><p>' . get_lang('CSVMustLookLike') . ' (' . get_lang('MandatoryFields') . '):</p>';
341
echo '<blockquote><pre>' . $csvContent . '</pre></blockquote></div>';
342
echo '<div id="xml-model" style="display: none;"><p>' . get_lang('XMLMustLookLike') . ' (' . get_lang('MandatoryFields') . '):</p>';
343
echo '<blockquote><pre>' . $xmlContent . '</pre></blockquote></div>';
344
345
Display::display_footer();
346