Passed
Pull Request — master (#6894)
by
unknown
09:02
created

FeedbackExport   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 180
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 108
c 1
b 0
f 0
dl 0
loc 180
rs 10
wmc 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getPresentation() 0 17 1
A createQuestionXml() 0 25 2
A createFeedbackXml() 0 31 2
A mapQuestionType() 0 11 1
A getData() 0 39 3
A export() 0 20 1
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CourseBundle\Component\CourseCopy\Moodle\Activities;
8
9
use Chamilo\CourseBundle\Component\CourseCopy\Moodle\Builder\MoodleExport;
10
11
use const ENT_QUOTES;
12
use const ENT_XML1;
13
use const PHP_EOL;
14
15
/**
16
 * Exports Chamilo surveys into a single Moodle "feedback" activity.
17
 *
18
 * NOTE:
19
 * - Relies on $this->course structure injected via ActivityExport constructor.
20
 * - Expects a helper providing admin user data. Replace MoodleExport::getAdminUserData()
21
 *   with your actual provider or adapter if it lives elsewhere.
22
 */
23
class FeedbackExport extends ActivityExport
24
{
25
    /**
26
     * Export a survey to Moodle Feedback format.
27
     *
28
     * @param int    $activityId survey ID in Chamilo
29
     * @param string $exportDir  destination root for the export (course temp dir)
30
     * @param int    $moduleId   module ID for Moodle backup structure
31
     * @param int    $sectionId  target section ID within the course
32
     */
33
    public function export($activityId, $exportDir, $moduleId, $sectionId): void
34
    {
35
        // Prepare the activity folder: .../activities/feedback_<moduleId>
36
        $feedbackDir = $this->prepareActivityDirectory($exportDir, 'feedback', (int) $moduleId);
37
38
        // Gather normalized survey data from Chamilo resources
39
        $surveyData = $this->getData((int) $activityId, (int) $sectionId);
40
41
        // Produce all required XML artifacts for Moodle backup
42
        $this->createFeedbackXml($surveyData, $feedbackDir);
43
        $this->createModuleXml($surveyData, $feedbackDir);
44
        $this->createInforefXml($surveyData, $feedbackDir);
45
        $this->createCalendarXml($surveyData, $feedbackDir);
46
        $this->createCommentsXml($surveyData, $feedbackDir);
47
        $this->createCompetenciesXml($surveyData, $feedbackDir);
48
        $this->createCompletionXml($surveyData, $feedbackDir);
49
        $this->createFiltersXml($surveyData, $feedbackDir);
50
        $this->createGradeHistoryXml($surveyData, $feedbackDir);
51
        $this->createGradesXml($surveyData, $feedbackDir);
52
        $this->createRolesXml($surveyData, $feedbackDir);
53
    }
54
55
    /**
56
     * Collect survey data, including questions and options, from Chamilo.
57
     */
58
    public function getData(int $surveyId, int $sectionId): array
59
    {
60
        // TODO: Replace this with your own provider if different:
61
        // e.g. $adminId = $this->adminProvider->getAdminUserId();
62
        $adminData = MoodleExport::getAdminUserData();
63
        $adminId = (int) $adminData['id'];
64
65
        $survey = $this->course->resources['survey'][$surveyId] ?? null;
66
67
        $questions = [];
68
        foreach ($this->course->resources['survey_question'] ?? [] as $question) {
69
            if ((int) ($question->survey_id ?? 0) === $surveyId) {
70
                $questions[] = [
71
                    'id' => (int) $question->id,
72
                    'text' => (string) $question->survey_question,
73
                    'type' => (string) $question->survey_question_type,
74
                    'options' => array_map(
75
                        static fn ($answer) => $answer['option_text'],
76
                        (array) ($question->answers ?? [])
77
                    ),
78
                    'position' => (int) ($question->sort ?? 0),
79
                    'label' => '', // Keep empty unless you map labels explicitly
80
                ];
81
            }
82
        }
83
84
        return [
85
            'id' => $surveyId,
86
            'moduleid' => $surveyId,
87
            'modulename' => 'feedback',
88
            'contextid' => (int) $this->course->info['real_id'],
89
            'sectionid' => $sectionId,
90
            'sectionnumber' => 0,
91
            'name' => (string) ($survey->title ?? ('Survey '.$surveyId)),
92
            'intro' => (string) ($survey->intro ?? ''),
93
            'timemodified' => time(),
94
            'questions' => $questions,
95
            'users' => [$adminId],
96
            'files' => [],
97
        ];
98
    }
99
100
    /**
101
     * Build feedback.xml (the core activity file for Moodle backup).
102
     */
103
    private function createFeedbackXml(array $surveyData, string $feedbackDir): void
104
    {
105
        $xml = '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
106
        $xml .= '<activity id="'.$surveyData['id'].'" moduleid="'.$surveyData['moduleid'].'" modulename="feedback" contextid="'.$surveyData['contextid'].'">'.PHP_EOL;
107
        $xml .= '  <feedback id="'.$surveyData['id'].'">'.PHP_EOL;
108
        $xml .= '    <name>'.htmlspecialchars((string) $surveyData['name']).'</name>'.PHP_EOL;
109
        $xml .= '    <intro>'.htmlspecialchars((string) $surveyData['intro']).'</intro>'.PHP_EOL;
110
        $xml .= '    <introformat>1</introformat>'.PHP_EOL;
111
        $xml .= '    <anonymous>1</anonymous>'.PHP_EOL;
112
        $xml .= '    <email_notification>0</email_notification>'.PHP_EOL;
113
        $xml .= '    <multiple_submit>0</multiple_submit>'.PHP_EOL;
114
        $xml .= '    <autonumbering>1</autonumbering>'.PHP_EOL;
115
        $xml .= '    <site_after_submit></site_after_submit>'.PHP_EOL;
116
        $xml .= '    <page_after_submit></page_after_submit>'.PHP_EOL;
117
        $xml .= '    <page_after_submitformat>1</page_after_submitformat>'.PHP_EOL;
118
        $xml .= '    <publish_stats>0</publish_stats>'.PHP_EOL;
119
        $xml .= '    <timeopen>0</timeopen>'.PHP_EOL;
120
        $xml .= '    <timeclose>0</timeclose>'.PHP_EOL;
121
        $xml .= '    <timemodified>'.$surveyData['timemodified'].'</timemodified>'.PHP_EOL;
122
        $xml .= '    <completionsubmit>0</completionsubmit>'.PHP_EOL;
123
        $xml .= '    <items>'.PHP_EOL;
124
125
        foreach ($surveyData['questions'] as $q) {
126
            $xml .= $this->createQuestionXml($q);
127
        }
128
129
        $xml .= '    </items>'.PHP_EOL;
130
        $xml .= '  </feedback>'.PHP_EOL;
131
        $xml .= '</activity>';
132
133
        $this->createXmlFile('feedback', $xml, $feedbackDir);
134
    }
135
136
    /**
137
     * Render a single question in Moodle Feedback XML format.
138
     */
139
    private function createQuestionXml(array $question): string
140
    {
141
        $name = htmlspecialchars(strip_tags((string) ($question['text'] ?? '')), ENT_XML1 | ENT_QUOTES, 'UTF-8');
142
        $label = htmlspecialchars(strip_tags((string) ($question['label'] ?? '')), ENT_XML1 | ENT_QUOTES, 'UTF-8');
143
        $presentation = $this->getPresentation($question);
144
        $hasValue = (($question['type'] ?? '') === 'pagebreak') ? '0' : '1';
145
        $pos = (int) ($question['position'] ?? 0);
146
        $id = (int) ($question['id'] ?? 0);
147
        $typ = $this->mapQuestionType((string) ($question['type'] ?? ''));
148
149
        $xml = '      <item id="'.$id.'">'.PHP_EOL;
150
        $xml .= '        <template>0</template>'.PHP_EOL;
151
        $xml .= '        <name>'.$name.'</name>'.PHP_EOL;
152
        $xml .= '        <label>'.$label.'</label>'.PHP_EOL;
153
        $xml .= '        <presentation>'.$presentation.'</presentation>'.PHP_EOL;
154
        $xml .= '        <typ>'.$typ.'</typ>'.PHP_EOL;
155
        $xml .= '        <hasvalue>'.$hasValue.'</hasvalue>'.PHP_EOL;
156
        $xml .= '        <position>'.$pos.'</position>'.PHP_EOL;
157
        $xml .= '        <required>0</required>'.PHP_EOL;
158
        $xml .= '        <dependitem>0</dependitem>'.PHP_EOL;
159
        $xml .= '        <dependvalue></dependvalue>'.PHP_EOL;
160
        $xml .= '        <options>h</options>'.PHP_EOL;
161
        $xml .= '      </item>'.PHP_EOL;
162
163
        return $xml;
164
    }
165
166
    /**
167
     * Encode presentation string depending on question type.
168
     */
169
    private function getPresentation(array $question): string
170
    {
171
        $type = (string) ($question['type'] ?? '');
172
        $opts = array_map('strip_tags', (array) ($question['options'] ?? []));
173
        $opts = array_map(
174
            static fn ($o) => htmlspecialchars((string) $o, ENT_XML1 | ENT_QUOTES, 'UTF-8'),
175
            $opts
176
        );
177
178
        // Moodle feedback encodes the widget type as a single char:
179
        // r = radio, c = checkbox, d = dropdown, textareas use "<cols>|<rows>"
180
        return match ($type) {
181
            'yesno', 'multiplechoice', 'multiplechoiceother' => 'r&gt;&gt;&gt;&gt;&gt;'.implode(PHP_EOL.'|', $opts),
182
            'multipleresponse' => 'c&gt;&gt;&gt;&gt;&gt;'.implode(PHP_EOL.'|', $opts),
183
            'dropdown' => 'd&gt;&gt;&gt;&gt;&gt;'.implode(PHP_EOL.'|', $opts),
184
            'open' => '30|5', // textarea: cols|rows
185
            default => '',
186
        };
187
    }
188
189
    /**
190
     * Map Chamilo survey question types to Moodle feedback types.
191
     */
192
    private function mapQuestionType(string $chamiloType): string
193
    {
194
        return [
195
            'yesno' => 'multichoice',
196
            'multiplechoice' => 'multichoice',
197
            'multipleresponse' => 'multichoice',
198
            'dropdown' => 'multichoice',
199
            'multiplechoiceother' => 'multichoice',
200
            'open' => 'textarea',
201
            'pagebreak' => 'pagebreak',
202
        ][$chamiloType] ?? 'unknown';
203
    }
204
}
205