Passed
Pull Request — master (#5698)
by
unknown
07:08
created

SurveyUtil::updateInvitedCount()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 36
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 21
nc 1
nop 3
dl 0
loc 36
rs 9.584
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\AbstractResource;
6
use Chamilo\CoreBundle\Entity\Course;
7
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
8
use Chamilo\CoreBundle\Entity\User;
9
use Chamilo\CoreBundle\Framework\Container;
10
use Chamilo\CourseBundle\Entity\CGroup;
11
use Chamilo\CourseBundle\Entity\CSurvey;
12
use Chamilo\CourseBundle\Entity\CSurveyAnswer;
13
use Chamilo\CourseBundle\Entity\CSurveyInvitation;
14
use Chamilo\CourseBundle\Entity\CSurveyQuestion;
15
use Chamilo\CourseBundle\Entity\CSurveyQuestionOption;
16
use ChamiloSession as Session;
17
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
18
use Chamilo\CoreBundle\Component\Utils\ToolIcon;
19
use Chamilo\CoreBundle\Component\Utils\ObjectIcon;
20
use Chamilo\CoreBundle\Component\Utils\StateIcon;
21
22
/**
23
 * This class offers a series of general utility functions for survey querying and display.
24
 */
25
class SurveyUtil
26
{
27
    /**
28
     * Checks whether the given survey has a pagebreak question as the first
29
     * or the last question.
30
     * If so, break the current process, displaying an error message.
31
     *
32
     * @param int  $survey_id Survey ID (database ID)
33
     * @param bool $continue  Optional. Whether to continue the current
34
     *                        process or exit when breaking condition found. Defaults to true (do not break).
35
     */
36
    public static function check_first_last_question($survey_id, $continue = true)
37
    {
38
        // Table definitions
39
        $tbl_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
40
        $course_id = api_get_course_int_id();
41
        $survey_id = (int) $survey_id;
42
43
        // Getting the information of the question
44
        $sql = "SELECT * FROM $tbl_survey_question
45
                WHERE survey_id='".$survey_id."'
46
                ORDER BY sort ASC";
47
        $result = Database::query($sql);
48
        $total = Database::num_rows($result);
49
        $counter = 1;
50
        $error = false;
51
        while ($row = Database::fetch_assoc($result)) {
52
            if (1 == $counter && 'pagebreak' === $row['type']) {
53
                echo Display::return_message(get_lang('The page break cannot be the first'), 'error', false);
54
                $error = true;
55
            }
56
            if ($counter == $total && 'pagebreak' === $row['type']) {
57
                echo Display::return_message(get_lang('The page break cannot be the last one'), 'error', false);
58
                $error = true;
59
            }
60
            $counter++;
61
        }
62
63
        if (!$continue && $error) {
64
            Display::display_footer();
65
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
66
        }
67
    }
68
69
    /**
70
     * This function removes an (or multiple) answer(s) of a user on a question of a survey.
71
     *
72
     * @param mixed   The user id or email of the person who fills the survey
73
     * @param int The survey id
74
     * @param int The question id
75
     * @param int The option id
76
     *
77
     * @author Patrick Cool <[email protected]>, Ghent University
78
     *
79
     * @version January 2007
80
     */
81
    public static function remove_answer($user, $survey_id, $question_id)
82
    {
83
        $table = Database::get_course_table(TABLE_SURVEY_ANSWER);
84
        $sql = "DELETE FROM $table
85
				WHERE
86
                    user = '".Database::escape_string($user)."' AND
87
                    survey_id = '".intval($survey_id)."' AND
88
                    question_id = '".intval($question_id)."'";
89
        Database::query($sql);
90
    }
91
92
    public static function saveAnswer(
93
        $user,
94
        CSurvey $survey,
95
        CSurveyQuestion $question,
96
        $optionId,
97
        $optionValue,
98
        $otherOption = ''
99
    ) {
100
        // Make the survey anonymous
101
        if (1 == $survey->getAnonymous()) {
102
            $surveyUser = Session::read('surveyuser');
103
            if (empty($surveyUser)) {
104
                $user = md5($user.time());
105
                Session::write('surveyuser', $user);
106
            } else {
107
                $user = Session::read('surveyuser');
108
            }
109
        }
110
111
        if (!empty($otherOption)) {
112
            $optionId = $optionId.'@:@'.$otherOption;
113
        }
114
115
        $sessionId = api_get_session_id();
116
        $answer = new CSurveyAnswer();
117
        $answer
118
            ->setUser($user)
119
            ->setSurvey($survey)
120
            ->setQuestion($question)
121
            ->setOptionId($optionId)
122
            ->setValue((int) $optionValue)
123
            ->setSessionId($sessionId ?: null)
124
        ;
125
126
        $em = Database::getManager();
127
        $em->persist($answer);
128
        $em->flush();
129
130
        $insertId = $answer->getIid();
131
        if ($insertId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $insertId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
132
            return true;
133
        }
134
135
        return false;
136
    }
137
138
    /**
139
     * This function checks the parameters that are used in this page.
140
     *
141
     * @return string $people_filled The header, an error and the footer if any parameter fails, else it returns true
142
     *
143
     * @author Patrick Cool <[email protected]>, Ghent University
144
     *
145
     * @version February 2007
146
     */
147
    public static function check_parameters($people_filled)
148
    {
149
        $error = false;
150
151
        // Getting the survey data
152
        $survey_data = SurveyManager::get_survey($_GET['survey_id']);
153
154
        // $_GET['survey_id'] has to be numeric
155
        if (!is_numeric($_GET['survey_id'])) {
156
            $error = get_lang('Unknown survey id');
157
        }
158
159
        // $_GET['action']
160
        $allowed_actions = [
161
            'overview',
162
            'questionreport',
163
            'userreport',
164
            'comparativereport',
165
            'completereport',
166
            'deleteuserreport',
167
        ];
168
        if (isset($_GET['action']) && !in_array($_GET['action'], $allowed_actions)) {
169
            $error = get_lang('Action not allowed');
170
        }
171
172
        // User report
173
        if (isset($_GET['action']) && 'userreport' == $_GET['action']) {
174
            if (0 == $survey_data['anonymous']) {
175
                foreach ($people_filled as $key => &$value) {
176
                    $people_filled_userids[] = $value['invited_user'];
177
                }
178
            } else {
179
                $people_filled_userids = $people_filled;
180
            }
181
182
            if (isset($_GET['user']) && !in_array($_GET['user'], $people_filled_userids)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $people_filled_userids does not seem to be defined for all execution paths leading up to this point.
Loading history...
183
                $error = get_lang('Unknow user');
184
            }
185
        }
186
187
        // Question report
188
        if (isset($_GET['action']) && 'questionreport' == $_GET['action']) {
189
            if (isset($_GET['question']) && !is_numeric($_GET['question'])) {
190
                $error = get_lang('Unknown question');
191
            }
192
        }
193
194
        if ($error) {
195
            $tool_name = get_lang('Reporting');
196
            Display::addFlash(
197
                Display::return_message(
198
                    get_lang('Error').': '.$error,
199
                    'error',
200
                    false
201
                )
202
            );
203
            Display::display_header($tool_name);
204
            Display::display_footer();
205
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
206
        }
207
208
        return true;
209
    }
210
211
    public static function handleReportingActions(CSurvey $survey, array $people_filled = [])
212
    {
213
        $action = $_GET['action'] ?? '';
214
215
        switch ($action) {
216
            case 'questionreport':
217
                self::display_question_report($survey);
218
                break;
219
            case 'userreport':
220
                self::displayUserReport($survey, $people_filled);
221
                break;
222
            case 'comparativereport':
223
                self::display_comparative_report();
224
                break;
225
            case 'completereport':
226
                echo self::displayCompleteReport($survey);
227
                break;
228
            case 'deleteuserreport':
229
                self::delete_user_report($survey, $_GET['user']);
230
                break;
231
        }
232
    }
233
234
    /**
235
     * This function deletes the report of an user who wants to retake the survey.
236
     *
237
     * @param int $survey_id
238
     * @param int $user_id
239
     *
240
     * @author Christian Fasanando Flores <[email protected]>
241
     *
242
     * @version November 2008
243
     */
244
    public static function delete_user_report($survey_id, $user_id)
245
    {
246
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
247
        $table_survey_invitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
248
        $table_survey = Database::get_course_table(TABLE_SURVEY);
249
250
        $course_id = api_get_course_int_id();
251
        $survey_id = (int) $survey_id;
252
        $user_id = Database::escape_string($user_id);
253
254
        if (!empty($survey_id) && !empty($user_id)) {
255
            // delete data from survey_answer by user_id and survey_id
256
            $sql = "DELETE FROM $table_survey_answer
257
			        WHERE c_id = $course_id AND survey_id = '".$survey_id."' AND user = '".$user_id."'";
258
            Database::query($sql);
259
            // update field answered from survey_invitation by user_id and survey_id
260
            $sql = "UPDATE $table_survey_invitation SET answered = '0'
261
			        WHERE
262
			            c_id = $course_id AND
263
			            survey_id = (
264
                            SELECT iid FROM $table_survey
265
                            WHERE
266
                                iid = '".$survey_id."'
267
                        ) AND
268
			            user_id = '".$user_id."'";
269
            $result = Database::query($sql);
270
        }
271
272
        if (false !== $result) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
273
            $message = get_lang('The user\'s answers to the survey have been succesfully removed.').'<br />
274
					<a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action=userreport&survey_id='
275
                .$survey_id.'">'.
276
                get_lang('Go back').'</a>';
277
            echo Display::return_message($message, 'confirmation', false);
278
        }
279
    }
280
281
    /**
282
     * @return string
283
     */
284
    public static function displayUserReportForm(CSurvey $survey, array $people_filled = [])
285
    {
286
        $surveyId = $survey->getIid();
287
        // Step 1: selection of the user
288
        echo "<script>
289
        function jumpMenu(targ,selObj,restore) {
290
            eval(targ+\".location='\"+selObj.options[selObj.selectedIndex].value+\"'\");
291
            if (restore) selObj.selectedIndex=0;
292
        }
293
		</script>";
294
        echo get_lang('Select user who filled the survey').'<br />';
295
        echo '<select name="user" onchange="jumpMenu(\'parent\',this,0)">';
296
        echo '<option value="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='
297
            .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'">'
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($_GET['action']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

297
            ./** @scrutinizer ignore-type */ Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'">'
Loading history...
298
            .get_lang('User').'</option>';
299
300
        foreach ($people_filled as $key => &$person) {
301
            if (0 == $survey->getAnonymous()) {
302
                $name = $person['user_info']['complete_name_with_username'];
303
                $id = $person['user_id'];
304
                if ('' == $id) {
305
                    $id = $person['invited_user'];
306
                    $name = $person['invited_user'];
307
                }
308
            } else {
309
                $name = get_lang('Anonymous').' '.($key + 1);
310
                $id = $person;
311
            }
312
            echo '<option value="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='
313
                .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&user='
314
                .Security::remove_XSS($id).'&'.api_get_cidreq().'" ';
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($id) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

314
                ./** @scrutinizer ignore-type */ Security::remove_XSS($id).'&'.api_get_cidreq().'" ';
Loading history...
315
            if (isset($_GET['user']) && $_GET['user'] == $id) {
316
                echo 'selected="selected"';
317
            }
318
            echo '>'.$name.'</option>';
319
        }
320
        echo '</select>';
321
    }
322
323
    /**
324
     * @param int   $userId
325
     * @param array $survey_data
326
     * @param bool  $addMessage
327
     */
328
    public static function displayUserReportAnswers($userId, CSurvey $survey, $addMessage = true)
329
    {
330
        // Database table definitions
331
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
332
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
333
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
334
        $course_id = api_get_course_int_id();
335
        $surveyId = $survey->getIid();
336
        $userId = Database::escape_string($userId);
337
338
        $content = '';
339
        // Step 2: displaying the survey and the answer of the selected users
340
        if (!empty($userId)) {
341
            if ($addMessage) {
342
                $content .= Display::return_message(
343
                    get_lang('This screen displays an exact copy of the form as it was filled by the user'),
344
                    'normal',
345
                    false
346
                );
347
            }
348
349
            // Getting all the questions and options
350
            $sql = "SELECT
351
			            survey_question.iid question_id,
352
			            survey_question.survey_id,
353
			            survey_question.survey_question,
354
			            survey_question.display,
355
			            survey_question.max_value,
356
			            survey_question.sort,
357
			            survey_question.type,
358
                        survey_question_option.iid question_option_id,
359
                        survey_question_option.option_text,
360
                        survey_question_option.sort as option_sort
361
					FROM $table_survey_question survey_question
362
					LEFT JOIN $table_survey_question_option survey_question_option
363
					ON
364
					    survey_question.iid = survey_question_option.question_id
365
					WHERE
366
					    survey_question NOT LIKE '%{{%' AND
367
					    survey_question.survey_id = '".$surveyId."'
368
					ORDER BY survey_question.sort, survey_question_option.sort ASC";
369
            $result = Database::query($sql);
370
            while ($row = Database::fetch_assoc($result)) {
371
                if ('pagebreak' !== $row['type']) {
372
                    $questions[$row['sort']]['question_id'] = $row['question_id'];
373
                    $questions[$row['sort']]['survey_id'] = $row['survey_id'];
374
                    $questions[$row['sort']]['survey_question'] = $row['survey_question'];
375
                    $questions[$row['sort']]['display'] = $row['display'];
376
                    $questions[$row['sort']]['type'] = $row['type'];
377
                    $questions[$row['sort']]['maximum_score'] = $row['max_value'];
378
                    $questions[$row['sort']]['options'][$row['question_option_id']] = $row['option_text'];
379
                }
380
            }
381
382
            // Getting all the answers of the user
383
            $sql = "SELECT * FROM $table_survey_answer
384
			        WHERE
385
                        survey_id = '".$surveyId."' AND
386
                        user = '".$userId."'";
387
            $result = Database::query($sql);
388
            while ($row = Database::fetch_assoc($result)) {
389
                $answers[$row['question_id']][] = $row['option_id'];
390
                $all_answers[$row['question_id']][] = $row;
391
            }
392
393
            // Displaying all the questions
394
            foreach ($questions as &$question) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $questions does not seem to be defined for all execution paths leading up to this point.
Loading history...
395
                // If the question type is a scoring then we have to format the answers differently
396
                switch ($question['type']) {
397
                    case 'score':
398
                        $finalAnswer = [];
399
                        if (is_array($question) && is_array($all_answers)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $all_answers does not seem to be defined for all execution paths leading up to this point.
Loading history...
400
                            foreach ($all_answers[$question['question_id']] as $key => &$answer_array) {
401
                                $finalAnswer[$answer_array['option_id']] = $answer_array['value'];
402
                            }
403
                        }
404
                        break;
405
                    case 'multipleresponse':
406
                        $finalAnswer = isset($answers[$question['question_id']]) ? $answers[$question['question_id']] : '';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $answers does not seem to be defined for all execution paths leading up to this point.
Loading history...
407
                        break;
408
                    default:
409
                        $finalAnswer = '';
410
                        if (isset($all_answers[$question['question_id']])) {
411
                            $finalAnswer = $all_answers[$question['question_id']][0]['option_id'];
412
                        }
413
                        break;
414
                }
415
416
                $display = survey_question::createQuestion($question['type']);
417
                $url = api_get_self();
418
                $form = new FormValidator('question', 'post', $url);
419
                $form->addHtml('<div class="survey_question_wrapper"><div class="survey_question">');
420
                $form->addHtml($question['survey_question']);
421
                $display->render($form, $question, $finalAnswer);
422
                $form->addHtml('</div></div>');
423
                $content .= $form->returnForm();
424
            }
425
        }
426
427
        return $content;
428
    }
429
430
    /**
431
     * This function displays the user report which is basically nothing more
432
     * than a one-page display of all the questions
433
     * of the survey that is filled with the answers of the person who filled the survey.
434
     *
435
     * @return string html code of the one-page survey with the answers of the selected user
436
     *
437
     * @author Patrick Cool <[email protected]>, Ghent University
438
     *
439
     * @version February 2007 - Updated March 2008
440
     */
441
    public static function displayUserReport(CSurvey $survey, $people_filled, $addActionBar = true)
442
    {
443
        $surveyId = $survey->getIid();
444
        $reportingUrl = api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq();
445
446
        // Actions bar
447
        if ($addActionBar) {
448
            $actions = '<a href="'.$reportingUrl.'">'.
449
                Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Reporting overview'))
450
                .'</a>';
451
            if (isset($_GET['user'])) {
452
                if (api_is_allowed_to_edit()) {
453
                    // The delete link
454
                    $actions .= '<a
455
                        href="'.$reportingUrl.'&action=deleteuserreport&user='.Security::remove_XSS($_GET['user']).'" >'.
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($_GET['user']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

455
                        href="'.$reportingUrl.'&action=deleteuserreport&user='./** @scrutinizer ignore-type */ Security::remove_XSS($_GET['user']).'" >'.
Loading history...
456
                        Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Delete')).'</a>';
457
                }
458
459
                // Export the user report
460
                $actions .= '<a href="javascript: void(0);" onclick="document.form1a.submit();">'
461
                    .Display::getMdiIcon(ActionIcon::EXPORT_CSV, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('CSV export')).'</a> ';
462
                $actions .= '<a href="javascript: void(0);" onclick="document.form1b.submit();">'
463
                    .Display::getMdiIcon(ActionIcon::EXPORT_SPREADSHEET, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Excel export')).'</a> ';
464
                $actions .= '<form id="form1a" name="form1a" method="post" action="'.api_get_self().'?action='
465
                    .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'&user_id='
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($_GET['action']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

465
                    ./** @scrutinizer ignore-type */ Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'&user_id='
Loading history...
466
                    .Security::remove_XSS($_GET['user']).'">';
467
                $actions .= '<input type="hidden" name="export_report" value="export_report">';
468
                $actions .= '<input type="hidden" name="export_format" value="csv">';
469
                $actions .= '</form>';
470
                $actions .= '<form id="form1b" name="form1b" method="post" action="'.api_get_self().'?action='
471
                    .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'&user_id='
472
                    .Security::remove_XSS($_GET['user']).'">';
473
                $actions .= '<input type="hidden" name="export_report" value="export_report">';
474
                $actions .= '<input type="hidden" name="export_format" value="xls">';
475
                $actions .= '</form>';
476
                $actions .= '<form id="form2" name="form2" method="post" action="'.api_get_self().'?action='
477
                    .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'">';
478
            }
479
            echo Display::toolbarAction('survey', [$actions]);
480
        }
481
482
        echo self::displayUserReportForm($survey, $people_filled);
483
        if (isset($_GET['user'])) {
484
            echo self::displayUserReportAnswers($_GET['user'], $survey);
485
        }
486
    }
487
488
    /**
489
     * This function displays the report by question.
490
     *
491
     * It displays a table with all the options of the question and the number of users who have answered positively on
492
     * the option. The number of users who answered positive on a given option is expressed in an absolute number, in a
493
     * percentage of the total and graphically using bars By clicking on the absolute number you get a list with the
494
     * persons who have answered this. You can then click on the name of the person and you will then go to the report
495
     * by user where you see all the answers of that user.
496
     *
497
     * @return string html code that displays the report by question
498
     *
499
     * @todo allow switching between horizontal and vertical.
500
     * @todo multiple response: percentage are probably not OK
501
     * @todo the question and option text have to be shortened and should expand when the user clicks on it.
502
     * @todo the pagebreak and comment question types should not be shown => removed from $survey_data before
503
     *
504
     * @author Patrick Cool <[email protected]>, Ghent University
505
     *
506
     * @version February 2007 - Updated March 2008
507
     */
508
    public static function display_question_report(CSurvey $survey)
509
    {
510
        $singlePage = isset($_GET['single_page']) ? (int) $_GET['single_page'] : 0;
511
        // Determining the offset of the sql statement (the n-th question of the survey)
512
        $offset = !isset($_GET['question']) ? 0 : (int) $_GET['question'];
513
        $currentQuestion = isset($_GET['question']) ? (int) $_GET['question'] : 0;
514
        $surveyId = $survey->getIid();
515
        $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : '';
516
        $course_id = api_get_course_int_id();
517
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
518
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
519
520
        $actions = '<a
521
            href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq().'">'.
522
            Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Reporting overview'),
523
                '',
524
                ICON_SIZE_MEDIUM
525
            ).'</a>';
526
        $actions .= Display::url(
527
            Display::getMdiIcon(ActionIcon::EXPORT_PDF, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('ExportToPdf')),
528
            'javascript: void(0);',
529
            ['onclick' => 'exportToPdf();']
530
        );
531
532
        echo Display::toolbarAction('survey', [$actions]);
533
534
        $fromUntil = sprintf(
535
            get_lang('FromXUntilY'),
536
            api_get_local_time($survey->getAvailFrom()),
537
            api_get_local_time($survey->getAvailTill())
538
        );
539
        $max = 80;
540
        $data = [
541
            get_lang('SurveyTitle') => cut(strip_tags($survey->getTitle()), $max),
542
            get_lang('SurveySubTitle') => cut(strip_tags($survey->getSubtitle()), $max),
543
            get_lang('Dates') => $fromUntil,
544
            get_lang('SurveyIntroduction') => cut(strip_tags($survey->getIntro()), $max),
545
        ];
546
547
        $table = new HTML_Table(['id' => 'pdf_table', 'class' => 'table']);
548
        $row = 0;
549
        foreach ($data as $label => $item) {
550
            $table->setCellContents($row, 0, $label);
551
            $table->setCellContents($row, 1, $item);
552
            $row++;
553
        }
554
555
        $questions = $survey->getQuestions();
556
        $numberOfQuestions = 0;
557
        foreach ($questions as $question) {
558
            if ('pagebreak' !== $question->getType()) {
559
                $numberOfQuestions++;
560
            }
561
        }
562
563
        $newQuestionList = [];
564
        if ($numberOfQuestions > 0) {
565
            $limitStatement = null;
566
            if (!$singlePage) {
567
                echo '<div id="question_report_questionnumbers" class="pagination">';
568
                if (0 != $currentQuestion) {
569
                    echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='.$action.'&'
0 ignored issues
show
Bug introduced by
Are you sure $action of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

569
                    echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='./** @scrutinizer ignore-type */ $action.'&'
Loading history...
570
                        .api_get_cidreq().'&survey_id='.$surveyId.'&question='.($offset - 1).'">'
571
                        .get_lang('Previous question').'</a></li>';
572
                }
573
574
                for ($i = 1; $i <= $numberOfQuestions; $i++) {
575
                    if ($offset != $i - 1) {
576
                        echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='.$action.'&'
577
                            .api_get_cidreq().'&survey_id='.$surveyId.'&question='.($i - 1).'">'.$i.'</a></li>';
578
                    } else {
579
                        echo '<li class="disabled"s><a href="#">'.$i.'</a></li>';
580
                    }
581
                }
582
                if ($currentQuestion < ($numberOfQuestions - 1)) {
583
                    echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='.$action.'&'
584
                        .api_get_cidreq().'&survey_id='.$surveyId.'&question='.($offset + 1).'">'
585
                        .get_lang('Next question').'</li></a>';
586
                }
587
                echo '</ul>';
588
                echo '</div>';
589
                $limitStatement = " LIMIT $offset, 1";
590
            }
591
592
            // Getting the question information
593
            /*$sql = "SELECT * FROM $table_survey_question
594
                    WHERE
595
                        survey_id = $surveyId AND
596
                        survey_question NOT LIKE '%{{%' AND
597
                        type <>'pagebreak'
598
                    ORDER BY sort ASC
599
                    $limitStatement";
600
            $result = Database::query($sql);
601
            while ($row = Database::fetch_array($result)) {*/
602
            foreach ($questions as $question) {
603
                if (strpos($question->getSurveyQuestion(), '%{{%') && 'pagebreak' !== $question->getType()) {
604
                    continue;
605
                }
606
                $newQuestionList[$question->getIid()] = $question;
607
            }
608
        }
609
        echo '<div id="question_results">';
610
        /** @var CSurveyQuestion $question */
611
        foreach ($newQuestionList as $question) {
612
            $chartData = [];
613
            $options = [];
614
            $questionId = $question->getIid();
615
616
            echo '<div class="question-item">';
617
            echo '<div class="title-question">';
618
            echo strip_tags($question->getSurveyQuestion());
619
            echo '</div>';
620
            $type = $question->getType();
621
622
            if ('score' === $type) {
623
                /** @todo This function should return the options as this is needed further in the code */
624
                $options = self::display_question_report_score($survey, $question, $offset);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options is correct as self::display_question_r...ey, $question, $offset) targeting SurveyUtil::display_question_report_score() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
625
            } elseif ('open' === $type || 'comment' === $type) {
626
                echo '<div class="open-question">';
627
                /** @todo Also get the user who has answered this */
628
                $sql = "SELECT * FROM $table_survey_answer
629
                        WHERE
630
                            survey_id= $surveyId AND
631
                            question_id = $questionId ";
632
                $result = Database::query($sql);
633
                while ($row = Database::fetch_assoc($result)) {
634
                    echo $row['option_id'].'<hr noshade="noshade" size="1" />';
635
                }
636
                echo '</div>';
637
            } else {
638
                // Getting the options ORDER BY sort ASC
639
                $sql = "SELECT * FROM $table_survey_question_option
640
                        WHERE
641
                            survey_id = $surveyId AND
642
                            question_id = $questionId
643
                        ORDER BY sort ASC";
644
                $result = Database::query($sql);
645
                while ($row = Database::fetch_assoc($result)) {
646
                    $options[$row['iid']] = $row;
647
                }
648
                // Getting the answers
649
                $sql = "SELECT *, count(iid) as total
650
                        FROM $table_survey_answer
651
                        WHERE
652
                            survey_id = $surveyId AND
653
                            question_id = $questionId
654
                        GROUP BY option_id, value";
655
                $result = Database::query($sql);
656
                $number_of_answers = [];
657
                $data = [];
658
                while ($row = Database::fetch_assoc($result)) {
659
                    if (!isset($number_of_answers[$row['question_id']])) {
660
                        $number_of_answers[$row['question_id']] = 0;
661
                    }
662
                    $number_of_answers[$row['question_id']] += $row['total'];
663
                    if ('multiplechoiceother' === $type) {
664
                        $parts = ch_multiplechoiceother::decodeOptionValue($row['option_id']);
665
                        $row['option_id'] = $parts[0];
666
                    }
667
                    $data[$row['option_id']] = $row;
668
                }
669
670
                foreach ($options as $option) {
671
                    $optionText = strip_tags($option['option_text']);
672
                    $optionText = html_entity_decode($optionText);
673
                    $votes = 0;
674
                    if (isset($data[$option['iid']]['total'])) {
675
                        $votes = $data[$option['iid']]['total'];
676
                    }
677
                    array_push($chartData, ['option' => $optionText, 'votes' => $votes]);
678
                }
679
                $chartContainerId = 'chartContainer'.$questionId;
680
                echo '<div id="'.$chartContainerId.'" style="text-align:center;">';
681
                echo self::drawChart($chartData, false, $chartContainerId, false);
682
                echo '</div>';
683
684
                // displaying the table: headers
685
                echo '<table class="display-survey table" id="table_'.$chartContainerId.'">';
686
                echo '';
687
                echo '	<tr>';
688
                echo '		<th style="width: 50%">&nbsp;</th>';
689
                echo '		<th style="width: 10%">'.get_lang('AbsoluteTotal').'</th>';
690
                echo '		<th style="width: 10%">'.get_lang('Percentage').'</th>';
691
                echo '		<th style="width: 30%">'.get_lang('VisualRepresentation').'</th>';
692
                echo '	</tr>';
693
694
                // Displaying the table: the content
695
                if (is_array($options)) {
696
                    foreach ($options as $key => &$value) {
697
                        if ('multiplechoiceother' === $type && 'other' === $value['option_text']) {
698
                            $value['option_text'] = get_lang('SurveyOtherAnswer');
699
                        }
700
701
                        $absolute_number = null;
702
                        if (isset($data[$value['iid']])) {
703
                            $absolute_number = $data[$value['iid']]['total'];
704
                        }
705
                        if ('percentage' === $type && empty($absolute_number)) {
706
                            continue;
707
                        }
708
                        $number_of_answers[$option['question_id']] = isset($number_of_answers[$option['question_id']])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $option does not seem to be defined for all execution paths leading up to this point.
Loading history...
709
                            ? $number_of_answers[$option['question_id']]
710
                            : 0;
711
                        if (0 == $number_of_answers[$option['question_id']]) {
712
                            $answers_number = 0;
713
                        } else {
714
                            $answers_number = $absolute_number / $number_of_answers[$option['question_id']] * 100;
715
                        }
716
                        echo '	<tr>';
717
                        echo '<td>'.$value['option_text'].'</td>';
718
                        echo '<td>';
719
                        if (0 != $absolute_number) {
720
                            echo '<a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='.$action
721
                                .'&survey_id='.$surveyId.'&question='.$offset.'&viewoption='
722
                                .$value['iid'].'">'.$absolute_number.'</a>';
723
                        } else {
724
                            echo '0';
725
                        }
726
727
                        echo '      </td>';
728
                        echo '<td>'.round($answers_number, 2).' %</td>';
729
                        echo '<td>';
730
                        $size = $answers_number * 2;
731
                        if ($size > 0) {
732
                            echo '<div
733
                                    style="border:1px solid #264269; background-color:#aecaf4; height:10px;
734
                                    width:'.$size.'px">
735
                                    &nbsp;
736
                                    </div>';
737
                        } else {
738
                            echo '<div style="text-align: left;">'.get_lang("No data available").'</div>';
739
                        }
740
                        echo ' </td>';
741
                        echo ' </tr>';
742
                    }
743
                }
744
745
                $optionResult = '';
746
                if (isset($option['question_id']) && isset($number_of_answers[$option['question_id']])) {
747
                    if (0 == $number_of_answers[$option['question_id']]) {
748
                        $optionResult = '0';
749
                    } else {
750
                        $optionResult = $number_of_answers[$option['question_id']];
751
                    }
752
                }
753
754
                // displaying the table: footer (totals)
755
                echo '	<tr>
756
                            <td><b>'.get_lang('Total').'</b></td>
757
                            <td><b>'.$optionResult.'</b></td>
758
                            <td>&nbsp;</td>
759
                            <td>&nbsp;</td>
760
                        </tr>
761
                        </table>';
762
            }
763
            echo '</div>';
764
        }
765
        echo '</div>';
766
767
        // Survey information, needed for the PDF export.
768
        echo Display::page_subheader(get_lang('Survey')).'<br />';
769
        $table->display();
770
771
        if (isset($_GET['viewoption'])) {
772
            echo '<div class="answered-people">';
773
            echo '<h4>'.get_lang('People who have chosen this answer').': '
774
                .strip_tags($options[Security::remove_XSS($_GET['viewoption'])]['option_text']).'</h4>';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $options seems to be defined by a foreach iteration on line 611. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
775
776
            if (is_numeric($_GET['value'])) {
777
                $sql_restriction = "AND value='".Database::escape_string($_GET['value'])."'";
778
            }
779
780
            $sql = "SELECT user FROM $table_survey_answer
781
                    WHERE
782
                        c_id = $course_id AND
783
                        option_id = '".Database::escape_string($_GET['viewoption'])."'
784
                        $sql_restriction";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sql_restriction does not seem to be defined for all execution paths leading up to this point.
Loading history...
785
            $result = Database::query($sql);
786
            echo '<ul>';
787
            while ($row = Database::fetch_assoc($result)) {
788
                $user_info = api_get_user_info($row['user']);
789
                echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action=userreport&survey_id='
790
                    .$surveyId.'&user='.$row['user'].'">'
791
                    .$user_info['complete_name_with_username']
792
                    .'</a></li>';
793
            }
794
            echo '</ul>';
795
            echo '</div>';
796
        }
797
    }
798
799
    /**
800
     * Display score data about a survey question.
801
     *
802
     * @param    int    The offset of results shown
803
     */
804
    public static function display_question_report_score(CSurvey $survey, CSurveyQuestion $question, $offset)
805
    {
806
        // Database table definitions
807
        $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : '';
808
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
809
        $surveyId = $survey->getIid();
810
        $questionId = $question->getIid();
811
        $options = $survey->getOptions();
812
        // Getting the options
813
        /*$sql = "SELECT * FROM $table_survey_question_option
814
                WHERE
815
                    survey_id= $surveyId AND
816
                    question_id = '".intval($question['iid'])."'
817
                ORDER BY sort ASC";
818
        $result = Database::query($sql);
819
        while ($row = Database::fetch_array($result)) {*/
820
        foreach ($options as $option) {
821
            $options[$option->getIid()] = $option;
822
        }
823
824
        // Getting the answers
825
        $sql = "SELECT *, count(iid) as total
826
                FROM $table_survey_answer
827
                WHERE
828
                   survey_id= $surveyId AND
829
                   question_id = '".$questionId."'
830
                GROUP BY option_id, value";
831
        $result = Database::query($sql);
832
        $number_of_answers = 0;
833
        while ($row = Database::fetch_array($result)) {
834
            $number_of_answers += $row['total'];
835
            $data[$row['option_id']][$row['value']] = $row;
836
        }
837
838
        $chartData = [];
839
        /** @var CSurveyQuestionOption $option */
840
        foreach ($options as $option) {
841
            $optionId = $option->getIid();
842
            $optionText = strip_tags($option->getOptionText());
843
            $optionText = html_entity_decode($optionText);
844
            for ($i = 1; $i <= $question->getMaxValue(); $i++) {
845
                $votes = null;
846
                if (isset($data[$optionId][$i])) {
847
                    $votes = $data[$optionId][$i]['total'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.
Loading history...
848
                }
849
850
                if (empty($votes)) {
851
                    $votes = '0';
852
                }
853
                array_push(
854
                    $chartData,
855
                    [
856
                        'serie' => $optionText,
857
                        'option' => $i,
858
                        'votes' => $votes,
859
                    ]
860
                );
861
            }
862
        }
863
        echo '<div id="chartContainer" class="col-md-12">';
864
        echo self::drawChart($chartData, true);
865
        echo '</div>';
866
867
        // Displaying the table: headers
868
        echo '<table class="table">';
869
        echo '	<tr>';
870
        echo '		<th>&nbsp;</th>';
871
        echo '		<th>'.get_lang('Score').'</th>';
872
        echo '		<th>'.get_lang('Absolute total').'</th>';
873
        echo '		<th>'.get_lang('Percentage').'</th>';
874
        echo '		<th>'.get_lang('Graphic').'</th>';
875
        echo '	</tr>';
876
        // Displaying the table: the content
877
        foreach ($options as $key => $value) {
878
            $optionId = $value->getIid();
879
            for ($i = 1; $i <= $question->getMaxValue(); $i++) {
880
                $absolute_number = null;
881
                if (isset($data[$optionId][$i])) {
882
                    $absolute_number = $data[$optionId][$i]['total'];
883
                }
884
885
                echo '<tr>';
886
                echo '<td>'.$value->getOptionText().'</td>';
887
                echo '<td>'.$i.'</td>';
888
889
                echo '<td><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='.$action
0 ignored issues
show
Bug introduced by
Are you sure $action of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

889
                echo '<td><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='./** @scrutinizer ignore-type */ $action
Loading history...
890
                    .'&survey_id='.$surveyId.'&question='.Security::remove_XSS($offset)
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($offset) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

890
                    .'&survey_id='.$surveyId.'&question='./** @scrutinizer ignore-type */ Security::remove_XSS($offset)
Loading history...
891
                    .'&viewoption='.$optionId.'&value='.$i.'">'.$absolute_number.'</a></td>';
892
893
                $percentage = 0;
894
                $size = 0;
895
                if (!empty($number_of_answers)) {
896
                    $percentage = round($absolute_number / $number_of_answers * 100, 2);
897
                    $size = ($absolute_number / $number_of_answers * 100 * 2);
898
                }
899
                echo '<td>'.$percentage.' %</td>';
900
                echo '<td>';
901
                if ($size > 0) {
902
                    echo '<div
903
                            style="border:1px solid #264269;
904
                            background-color:#aecaf4;
905
                            height:10px; width:'.$size.'px">
906
                            &nbsp;
907
                        </div>';
908
                }
909
                echo '		</td>';
910
                echo '	</tr>';
911
            }
912
        }
913
        // Displaying the table: footer (totals)
914
        echo '	<tr>';
915
        echo '		<td style="border-top:1px solid black"><b>'.get_lang('Total').'</b></td>';
916
        echo '		<td style="border-top:1px solid black">&nbsp;</td>';
917
        echo '		<td style="border-top:1px solid black"><b>'.$number_of_answers.'</b></td>';
918
        echo '		<td style="border-top:1px solid black">&nbsp;</td>';
919
        echo '		<td style="border-top:1px solid black">&nbsp;</td>';
920
        echo '	</tr>';
921
        echo '</table>';
922
    }
923
924
    /**
925
     * This functions displays the complete reporting.
926
     *
927
     * @param int  $userId
928
     * @param bool $addActionBar
929
     * @param bool $addFilters
930
     * @param bool $addExtraFields
931
     *
932
     * @return string
933
     */
934
    public static function displayCompleteReport(
935
        CSurvey $survey,
936
        $userId = 0,
937
        $addActionBar = true,
938
        $addFilters = true,
939
        $addExtraFields = true
940
    ) {
941
        // Database table definitions
942
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
943
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
944
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
945
946
        $surveyId = $survey->getIid();
947
        $course_id = api_get_course_int_id();
948
949
        if (empty($surveyId) || empty($course_id)) {
950
            return '';
951
        }
952
953
        $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : '';
954
        $content = '';
955
        if ($addActionBar) {
956
            $actions = '<a
957
                href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq().'">'
958
                .Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Reporting overview'),
959
                    [],
960
                    ICON_SIZE_MEDIUM
961
                )
962
                .'</a>';
963
            $actions .= '<a class="survey_export_link" href="javascript: void(0);" onclick="document.form1a.submit();">'
964
                .Display::getMdiIcon(ActionIcon::EXPORT_CSV, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('CSV export')).'</a>';
965
            $actions .= '<a class="survey_export_link" href="javascript: void(0);" onclick="document.form1b.submit();">'
966
                .Display::getMdiIcon(ActionIcon::EXPORT_SPREADSHEET, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Excel export')).'</a>';
967
            $actions .= '<a class="survey_export_link" href="javascript: void(0);" onclick="document.form1c.submit();">'
968
                .Display::getMdiIcon(ActionIcon::EXPORT_ARCHIVE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('ExportAsCompactCSV')).'</a>';
969
970
            $content .= Display::toolbarAction('survey', [$actions]);
971
972
            // The form
973
            $content .= '<form
974
                id="form1a" name="form1a" method="post" action="'.api_get_self().'?action='.$action.'&survey_id='
0 ignored issues
show
Bug introduced by
Are you sure $action of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

974
                id="form1a" name="form1a" method="post" action="'.api_get_self().'?action='./** @scrutinizer ignore-type */ $action.'&survey_id='
Loading history...
975
                .$surveyId.'&'.api_get_cidreq().'">';
976
            $content .= '<input type="hidden" name="export_report" value="export_report">';
977
            $content .= '<input type="hidden" name="export_format" value="csv">';
978
            $content .= '</form>';
979
            $content .= '<form id="form1b" name="form1b" method="post" action="'.api_get_self(
980
                ).'?action='.$action.'&survey_id='
981
                .$surveyId.'&'.api_get_cidreq().'">';
982
            $content .= '<input type="hidden" name="export_report" value="export_report">';
983
            $content .= '<input type="hidden" name="export_format" value="xls">';
984
            $content .= '</form>';
985
            $content .= '<form id="form1c" name="form1c" method="post" action="'.api_get_self(
986
                ).'?action='.$action.'&survey_id='
987
                .$surveyId.'&'.api_get_cidreq().'">';
988
            $content .= '<input type="hidden" name="export_report" value="export_report">';
989
            $content .= '<input type="hidden" name="export_format" value="csv-compact">';
990
            $content .= '</form>';
991
        }
992
993
        $content .= '<form
994
            id="form2"
995
            name="form2"
996
            method="post"
997
            action="'.api_get_self().'?action='.$action.'&survey_id='.$surveyId.'&'.api_get_cidreq().'">';
998
        $content .= '<br /><table class="table table-hover table-striped data_table" border="1">';
999
        // Getting the number of options per question
1000
        $content .= '<tr>';
1001
        $content .= '<th>';
1002
1003
        if ($addFilters) {
1004
            if ((isset($_POST['submit_question_filter']) && $_POST['submit_question_filter']) ||
1005
                (isset($_POST['export_report']) && $_POST['export_report'])
1006
            ) {
1007
                $content .= '<button class="cancel"
1008
                                type="submit"
1009
                                name="reset_question_filter" value="'.get_lang('Reset filter').'">'.
1010
                                get_lang('Reset filter').'</button>';
1011
            }
1012
            $content .= '<button
1013
                            class = "save"
1014
                            type="submit" name="submit_question_filter" value="'.get_lang('Filter').'">'.
1015
                            get_lang('Filter').'</button>';
1016
            $content .= '</th>';
1017
        }
1018
1019
        $display_extra_user_fields = false;
1020
        if ($addExtraFields) {
1021
            if (!(isset($_POST['submit_question_filter']) && $_POST['submit_question_filter'] ||
1022
                    isset($_POST['export_report']) && $_POST['export_report']) ||
1023
                !empty($_POST['fields_filter'])
1024
            ) {
1025
                // Show user fields section with a big th colspan that spans over all fields
1026
                $extra_user_fields = UserManager::get_extra_fields(
1027
                    0,
1028
                    0,
1029
                    5,
1030
                    'ASC',
1031
                    false,
1032
                    true
1033
                );
1034
                $num = count($extra_user_fields);
1035
                if ($num > 0) {
1036
                    $content .= '<th '.($num > 0 ? ' colspan="'.$num.'"' : '').'>';
1037
                    $content .= '<label>';
1038
                    if ($addFilters) {
1039
                        $content .= '<input type="checkbox" name="fields_filter" value="1" checked="checked"/> ';
1040
                    }
1041
                    $content .= get_lang('Profile attributes');
1042
                    $content .= '</label>';
1043
                    $content .= '</th>';
1044
                    $display_extra_user_fields = true;
1045
                }
1046
            }
1047
        }
1048
1049
        $sql = "SELECT
1050
                  q.iid question_id,
1051
                  q.type,
1052
                  q.survey_question,
1053
                  count(o.iid) as number_of_options
1054
				FROM $table_survey_question q
1055
				LEFT JOIN $table_survey_question_option o
1056
				ON q.iid = o.question_id
1057
				WHERE
1058
				    survey_question NOT LIKE '%{{%' AND
1059
				    q.survey_id = '".$surveyId."'
1060
				GROUP BY q.iid
1061
				ORDER BY q.sort ASC";
1062
        $result = Database::query($sql);
1063
        $questions = [];
1064
        while ($row = Database::fetch_array($result)) {
1065
            // We show the questions if
1066
            // 1. there is no question filter and the export button has not been clicked
1067
            // 2. there is a quesiton filter but the question is selected for display
1068
            if (!(isset($_POST['submit_question_filter']) && $_POST['submit_question_filter']) ||
1069
                (is_array($_POST['questions_filter']) &&
1070
                in_array($row['question_id'], $_POST['questions_filter']))
1071
            ) {
1072
                // We do not show comment and pagebreak question types
1073
                if ('pagebreak' !== $row['type']) {
1074
                    $content .= ' <th';
1075
                    if ($row['number_of_options'] > 0 && 'percentage' !== $row['type']) {
1076
                        $content .= ' colspan="'.$row['number_of_options'].'"';
1077
                    }
1078
                    $content .= '>';
1079
                    $content .= '<label>';
1080
                    if ($addFilters) {
1081
                        $content .= '<input
1082
                                type="checkbox"
1083
                                name="questions_filter[]" value="'.$row['question_id'].'" checked="checked"/>';
1084
                    }
1085
                    $content .= $row['survey_question'];
1086
                    $content .= '</label>';
1087
                    $content .= '</th>';
1088
                }
1089
                // No column at all if it's not a question
1090
            }
1091
            $questions[$row['question_id']] = $row;
1092
        }
1093
        $content .= '	</tr>';
1094
1095
        // Getting all the questions and options
1096
        $content .= '	<tr>';
1097
        $content .= '		<th>&nbsp;</th>'; // the user column
1098
1099
        if (!(isset($_POST['submit_question_filter']) && $_POST['submit_question_filter'] ||
1100
            isset($_POST['export_report']) && $_POST['export_report']) || !empty($_POST['fields_filter'])
1101
        ) {
1102
            if ($addExtraFields) {
1103
                // show the fields names for user fields
1104
                foreach ($extra_user_fields as &$field) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $extra_user_fields does not seem to be defined for all execution paths leading up to this point.
Loading history...
1105
                    $content .= '<th>'.$field[3].'</th>';
1106
                }
1107
            }
1108
        }
1109
1110
        // cells with option (none for open question)
1111
        $sql = "SELECT
1112
                    sq.iid question_id,
1113
                    sq.survey_id,
1114
                    sq.survey_question,
1115
                    sq.display,
1116
                    sq.sort,
1117
                    sq.type,
1118
                    sqo.iid question_option_id,
1119
                    sqo.option_text,
1120
                    sqo.sort as option_sort
1121
				FROM $table_survey_question sq
1122
				LEFT JOIN $table_survey_question_option sqo
1123
				ON sq.iid = sqo.question_id
1124
				WHERE
1125
				    survey_question NOT LIKE '%{{%' AND
1126
				    sq.survey_id = $surveyId
1127
				ORDER BY sq.sort ASC, sqo.sort ASC";
1128
        $result = Database::query($sql);
1129
1130
        $display_percentage_header = 1;
1131
        $possible_answers = [];
1132
        // in order to display only once the cell option (and not 100 times)
1133
        while ($row = Database::fetch_array($result)) {
1134
            // We show the options if
1135
            // 1. there is no question filter and the export button has not been clicked
1136
            // 2. there is a question filter but the question is selected for display
1137
            if (!(isset($_POST['submit_question_filter']) && $_POST['submit_question_filter']) ||
1138
                (is_array($_POST['questions_filter']) && in_array($row['question_id'], $_POST['questions_filter']))
1139
            ) {
1140
                // we do not show comment and pagebreak question types
1141
                if ('open' == $row['type'] || 'comment' == $row['type']) {
1142
                    $content .= '<th>&nbsp;-&nbsp;</th>';
1143
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1144
                    $display_percentage_header = 1;
1145
                } elseif ('percentage' == $row['type'] && $display_percentage_header) {
1146
                    $content .= '<th>&nbsp;%&nbsp;</th>';
1147
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1148
                    $display_percentage_header = 0;
1149
                } elseif ('percentage' == $row['type']) {
1150
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1151
                } elseif ('pagebreak' != $row['type'] && 'percentage' != $row['type']) {
1152
                    $content .= '<th>';
1153
                    $content .= $row['option_text'];
1154
                    $content .= '</th>';
1155
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1156
                    $display_percentage_header = 1;
1157
                }
1158
            }
1159
        }
1160
1161
        $content .= '	</tr>';
1162
1163
        $userCondition = '';
1164
        if (!empty($userId)) {
1165
            $userId = (int) $userId;
1166
            $userCondition = " AND user = $userId ";
1167
        }
1168
1169
        // Getting all the answers of the users
1170
        $old_user = '';
1171
        $answers_of_user = [];
1172
        $sql = "SELECT * FROM $table_survey_answer
1173
                WHERE
1174
                    survey_id = $surveyId
1175
                    $userCondition
1176
                ORDER BY iid, user ASC";
1177
        $result = Database::query($sql);
1178
        $i = 1;
1179
        while ($row = Database::fetch_array($result)) {
1180
            if ($old_user != $row['user'] && '' != $old_user) {
1181
                $userParam = $old_user;
1182
                if (0 != $survey->getAnonymous()) {
1183
                    $userParam = $i;
1184
                    $i++;
1185
                }
1186
                $content .= self::display_complete_report_row(
1187
                    $survey,
1188
                    $possible_answers,
1189
                    $answers_of_user,
1190
                    $userParam,
1191
                    $questions,
1192
                    $display_extra_user_fields
1193
                );
1194
                $answers_of_user = [];
1195
            }
1196
            if (isset($questions[$row['question_id']]) &&
1197
                'open' != $questions[$row['question_id']]['type'] &&
1198
                'comment' != $questions[$row['question_id']]['type']
1199
            ) {
1200
                $answers_of_user[$row['question_id']][$row['option_id']] = $row;
1201
            } else {
1202
                $answers_of_user[$row['question_id']][0] = $row;
1203
            }
1204
            $old_user = $row['user'];
1205
        }
1206
1207
        $userParam = $old_user;
1208
        if (0 != $survey->getAnonymous()) {
1209
            $userParam = $i;
1210
            $i++;
1211
        }
1212
1213
        $content .= self::display_complete_report_row(
1214
            $survey,
1215
            $possible_answers,
1216
            $answers_of_user,
1217
            $userParam,
1218
            $questions,
1219
            $display_extra_user_fields
1220
        );
1221
1222
        // This is to display the last user
1223
        $content .= '</table>';
1224
        $content .= '</form>';
1225
1226
        return $content;
1227
    }
1228
1229
    /**
1230
     * Return user answers in a row.
1231
     *
1232
     * @return string
1233
     */
1234
    public static function display_complete_report_row(
1235
        CSurvey $survey,
1236
        $possible_options,
1237
        $answers_of_user,
1238
        $user,
1239
        $questions,
1240
        $display_extra_user_fields = false
1241
    ) {
1242
        $user = Security::remove_XSS($user);
1243
        $surveyId = $survey->getIid();
1244
1245
        if (empty($surveyId)) {
1246
            return '';
1247
        }
1248
1249
        $content = '<tr>';
1250
        $url = api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq();
1251
        if (0 == $survey->getAnonymous()) {
1252
            if (0 !== (int) $user) {
1253
                $userInfo = api_get_user_info($user);
1254
                $user_displayed = '-';
1255
                if (!empty($userInfo)) {
1256
                    $user_displayed = $userInfo['complete_name_with_username'];
1257
                }
1258
1259
                $content .= '<th>
1260
                    <a href="'.$url.'&action=userreport&user='.$user.'">'
0 ignored issues
show
Bug introduced by
Are you sure $user of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1260
                    <a href="'.$url.'&action=userreport&user='./** @scrutinizer ignore-type */ $user.'">'
Loading history...
1261
                        .$user_displayed.'
1262
                    </a>
1263
                    </th>'; // the user column
1264
            } else {
1265
                $content .= '<th>'.$user.'</th>'; // the user column
1266
            }
1267
        } else {
1268
            $content .= '<th>'.get_lang('Anonymous').' '.$user.'</th>';
1269
        }
1270
1271
        if ($display_extra_user_fields) {
1272
            // Show user fields data, if any, for this user
1273
            $user_fields_values = UserManager::get_extra_user_data(
1274
                $user,
1275
                false,
1276
                false,
1277
                false,
1278
                true
1279
            );
1280
            foreach ($user_fields_values as &$value) {
1281
                $content .= '<td align="center">'.$value.'</td>';
1282
            }
1283
        }
1284
1285
        if (is_array($possible_options)) {
1286
            foreach ($possible_options as $question_id => &$possible_option) {
1287
                if ('open' === $questions[$question_id]['type'] || 'comment' === $questions[$question_id]['type']) {
1288
                    $content .= '<td align="center">';
1289
                    if (isset($answers_of_user[$question_id]) && isset($answers_of_user[$question_id]['0'])) {
1290
                        $content .= $answers_of_user[$question_id]['0']['option_id'];
1291
                    }
1292
                    $content .= '</td>';
1293
                } else {
1294
                    foreach ($possible_option as $option_id => $value) {
1295
                        if ('multiplechoiceother' === $questions[$question_id]['type']) {
1296
                            foreach ($answers_of_user[$question_id] as $key => $newValue) {
1297
                                $parts = ch_multiplechoiceother::decodeOptionValue($key);
1298
                                if (isset($parts[0])) {
1299
                                    $data = $answers_of_user[$question_id][$key];
1300
                                    unset($answers_of_user[$question_id][$key]);
1301
                                    $newKey = $parts[0];
1302
                                    $answers_of_user[$question_id][$newKey] = $data;
1303
                                }
1304
                            }
1305
                        }
1306
                        if ('percentage' === $questions[$question_id]['type']) {
1307
                            if (!empty($answers_of_user[$question_id][$option_id])) {
1308
                                $content .= "<td align='center'>";
1309
                                $content .= $answers_of_user[$question_id][$option_id]['value'];
1310
                                $content .= "</td>";
1311
                            }
1312
                        } else {
1313
                            $content .= '<td align="center">';
1314
                            if (!empty($answers_of_user[$question_id][$option_id])) {
1315
                                if (0 != $answers_of_user[$question_id][$option_id]['value']) {
1316
                                    $content .= $answers_of_user[$question_id][$option_id]['value'];
1317
                                } else {
1318
                                    $content .= 'v';
1319
                                }
1320
                            }
1321
                        }
1322
                    }
1323
                }
1324
            }
1325
        }
1326
1327
        $content .= '</tr>';
1328
1329
        return $content;
1330
    }
1331
1332
    /**
1333
     * Quite similar to display_complete_report(), returns an HTML string
1334
     * that can be used in a csv file.
1335
     *
1336
     * @param array $survey_data The basic survey data as initially obtained by SurveyManager::get_survey()
1337
     * @param int   $user_id     The ID of the user asking for the report
1338
     * @param bool  $compact     Whether to present the long (v marks with multiple columns per question) or compact
1339
     *                           (one column per question) answers format
1340
     *
1341
     * @todo consider merging this function with display_complete_report
1342
     *
1343
     * @throws Exception
1344
     *
1345
     * @return string The contents of a csv file
1346
     *
1347
     * @author Patrick Cool <[email protected]>, Ghent University
1348
     *
1349
     * @version February 2007
1350
     */
1351
    public static function export_complete_report($survey_data, $user_id = 0, $compact = false)
1352
    {
1353
        $surveyId = isset($_GET['survey_id']) ? (int) $_GET['survey_id'] : 0;
1354
1355
        if (empty($surveyId)) {
1356
            return false;
1357
        }
1358
1359
        $course = api_get_course_info();
1360
        $course_id = $course['real_id'];
1361
1362
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
1363
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
1364
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
1365
1366
        $translate = false;
1367
        if ('true' === api_get_setting('editor.translate_html')) {
1368
            $translate = true;
1369
        }
1370
1371
        // The first column
1372
        $return = ';';
1373
1374
        // Show extra fields blank space (enough for extra fields on next line)
1375
        $extra_user_fields = UserManager::get_extra_fields(
1376
            0,
1377
            0,
1378
            5,
1379
            'ASC',
1380
            false,
1381
            true
1382
        );
1383
1384
        $num = count($extra_user_fields);
1385
        $return .= str_repeat(';', $num);
1386
1387
        $sql = "SELECT
1388
                    questions.iid,
1389
                    questions.type,
1390
                    questions.survey_question,
1391
                    count(options.iid) as number_of_options
1392
				FROM $table_survey_question questions
1393
                LEFT JOIN $table_survey_question_option options
1394
				ON
1395
				  questions.iid = options.question_id
1396
				WHERE
1397
				    survey_question NOT LIKE '%{{%' AND
1398
				    questions.type <> 'pagebreak' AND
1399
				    questions.survey_id = $surveyId
1400
				GROUP BY questions.iid
1401
				ORDER BY questions.sort ASC";
1402
1403
        $result = Database::query($sql);
1404
        while ($row = Database::fetch_array($result)) {
1405
            if ($translate) {
1406
                $row['survey_question'] = api_get_filtered_multilingual_HTML_string($row['survey_question'], $course['language']);
1407
            }
1408
            // We show the questions if
1409
            // 1. there is no question filter and the export button has not been clicked
1410
            // 2. there is a quesiton filter but the question is selected for display
1411
            if (!(isset($_POST['submit_question_filter'])) ||
1412
                (isset($_POST['submit_question_filter']) &&
1413
                    is_array($_POST['questions_filter']) &&
1414
                    in_array($row['iid'], $_POST['questions_filter']))
1415
            ) {
1416
                if (0 == $row['number_of_options'] || $compact) {
1417
                    $return .= str_replace(
1418
                        "\r\n",
1419
                        '  ',
1420
                        api_html_entity_decode(strip_tags($row['survey_question']), ENT_QUOTES)
1421
                    )
1422
                    .';';
1423
                } else {
1424
                    for ($ii = 0; $ii < $row['number_of_options']; $ii++) {
1425
                        $return .= str_replace(
1426
                            "\r\n",
1427
                            '  ',
1428
                            api_html_entity_decode(strip_tags($row['survey_question']), ENT_QUOTES)
1429
                        )
1430
                        .';';
1431
                    }
1432
                }
1433
            }
1434
        }
1435
1436
        $return .= "\n";
1437
        // Getting all the questions and options
1438
        $return .= ';';
1439
        // Show the fields names for user fields
1440
        if (!empty($extra_user_fields)) {
1441
            foreach ($extra_user_fields as &$field) {
1442
                if ($translate) {
1443
                    $field[3] = api_get_filtered_multilingual_HTML_string($field[3], $course['language']);
1444
                }
1445
                $return .= '"'
1446
                    .str_replace(
1447
                        "\r\n",
1448
                        '  ',
1449
                        api_html_entity_decode(strip_tags($field[3]), ENT_QUOTES)
1450
                    )
1451
                    .'";';
1452
            }
1453
        }
1454
1455
        $sql = "SELECT DISTINCT
1456
		            survey_question.iid question_id,
1457
		            survey_question.survey_id,
1458
		            survey_question.survey_question,
1459
		            survey_question.display,
1460
		            survey_question.sort,
1461
		            survey_question.type,
1462
                    survey_question_option.iid question_option_id,
1463
                    survey_question_option.option_text,
1464
                    survey_question_option.sort as option_sort
1465
				FROM $table_survey_question survey_question
1466
				LEFT JOIN $table_survey_question_option survey_question_option
1467
				ON
1468
				    survey_question.iid = survey_question_option.question_id
1469
				WHERE
1470
				    survey_question NOT LIKE '%{{%' AND
1471
				    survey_question.type <> 'pagebreak' AND
1472
				    survey_question.survey_id = $surveyId
1473
				ORDER BY survey_question.sort ASC, survey_question_option.sort ASC";
1474
        $result = Database::query($sql);
1475
        $possible_answers = [];
1476
        $possible_answers_type = [];
1477
        while ($row = Database::fetch_array($result)) {
1478
            // We show the options if
1479
            // 1. there is no question filter and the export button has not been clicked
1480
            // 2. there is a question filter but the question is selected for display
1481
            if ($translate) {
1482
                $row['option_text'] = api_get_filtered_multilingual_HTML_string($row['option_text'], $course['language']);
1483
            }
1484
            if (!(isset($_POST['submit_question_filter'])) || (
1485
                is_array($_POST['questions_filter']) &&
1486
                in_array($row['question_id'], $_POST['questions_filter'])
1487
            )
1488
            ) {
1489
                $row['option_text'] = str_replace(["\r", "\n"], ['', ''], $row['option_text']);
1490
                if (!$compact) {
1491
                    $return .= api_html_entity_decode(strip_tags($row['option_text']), ENT_QUOTES).';';
1492
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1493
                } else {
1494
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['option_text'];
1495
                }
1496
                $possible_answers_type[$row['question_id']] = $row['type'];
1497
            }
1498
        }
1499
1500
        $return .= "\n";
1501
1502
        // Getting all the answers of the users
1503
        $old_user = '';
1504
        $answers_of_user = [];
1505
        $sql = "SELECT * FROM $table_survey_answer
1506
		        WHERE
1507
		          survey_id = $surveyId
1508
		          ";
1509
        if (0 != $user_id) {
1510
            $user_id = (int) $user_id;
1511
            $sql .= " AND user = $user_id ";
1512
        }
1513
        $sql .= ' ORDER BY user ASC ';
1514
1515
        $questionIdList = array_keys($possible_answers_type);
1516
        $open_question_iterator = 1;
1517
        $result = Database::query($sql);
1518
        while ($row = Database::fetch_assoc($result)) {
1519
            if (!in_array($row['question_id'], $questionIdList)) {
1520
                continue;
1521
            }
1522
            if ($old_user != $row['user'] && '' != $old_user) {
1523
                $return .= self::export_complete_report_row(
1524
                    $survey_data,
1525
                    $possible_answers,
1526
                    $answers_of_user,
1527
                    $old_user,
1528
                    true,
1529
                    $compact
1530
                );
1531
                $answers_of_user = [];
1532
            }
1533
1534
            if ('open' === $possible_answers_type[$row['question_id']] ||
1535
                'comment' === $possible_answers_type[$row['question_id']]
1536
            ) {
1537
                $temp_id = 'open'.$open_question_iterator;
1538
                $answers_of_user[$row['question_id']][$temp_id] = $row;
1539
                $open_question_iterator++;
1540
            } else {
1541
                $answers_of_user[$row['question_id']][$row['option_id']] = $row;
1542
            }
1543
            $old_user = $row['user'];
1544
        }
1545
1546
        // This is to display the last user
1547
        $return .= self::export_complete_report_row(
1548
            $survey_data,
1549
            $possible_answers,
1550
            $answers_of_user,
1551
            $old_user,
1552
            true,
1553
            $compact
1554
        );
1555
1556
        return $return;
1557
    }
1558
1559
    /**
1560
     * Add a line to the csv file.
1561
     *
1562
     * @param array $survey_data               Basic survey data (we're mostly interested in the 'anonymous' index)
1563
     * @param array $possible_options          Possible answers
1564
     * @param array $answers_of_user           User's answers
1565
     * @param mixed $user                      User ID or user details as string - Used as a string in the result
1566
     *                                         string
1567
     * @param bool  $display_extra_user_fields Whether to display user fields or not
1568
     * @param bool  $compact                   Whether to show answers as different column values (true) or one column
1569
     *                                         per option (false, default)
1570
     *
1571
     * @return string One line of the csv file
1572
     *
1573
     * @author Patrick Cool <[email protected]>, Ghent University
1574
     *
1575
     * @version February 2007
1576
     */
1577
    public static function export_complete_report_row(
1578
        CSurvey $survey,
1579
        $possible_options,
1580
        $answers_of_user,
1581
        $user,
1582
        $display_extra_user_fields = false,
1583
        $compact = false
1584
    ) {
1585
        $return = '';
1586
        if (0 == $survey->getAnonymous()) {
1587
            if (0 !== intval($user)) {
1588
                $userInfo = api_get_user_info($user);
1589
                if (!empty($userInfo)) {
1590
                    $user_displayed = $userInfo['complete_name_with_username'];
1591
                } else {
1592
                    $user_displayed = '-';
1593
                }
1594
                $return .= $user_displayed.';';
1595
            } else {
1596
                $return .= $user.';';
1597
            }
1598
        } else {
1599
            $return .= '-;'; // The user column
1600
        }
1601
1602
        if ($display_extra_user_fields) {
1603
            // Show user fields data, if any, for this user
1604
            $user_fields_values = UserManager::get_extra_user_data(
1605
                $user,
1606
                false,
1607
                false,
1608
                false,
1609
                true
1610
            );
1611
            foreach ($user_fields_values as &$value) {
1612
                $return .= '"'.str_replace('"', '""', api_html_entity_decode(strip_tags($value), ENT_QUOTES)).'";';
1613
            }
1614
        }
1615
1616
        if (is_array($possible_options)) {
1617
            foreach ($possible_options as $question_id => $possible_option) {
1618
                if (is_array($possible_option) && count($possible_option) > 0) {
1619
                    foreach ($possible_option as $option_id => &$value) {
1620
                        // For each option of this question, look if it matches the user's answer
1621
                        $my_answer_of_user = !isset($answers_of_user[$question_id]) || isset($answers_of_user[$question_id]) && null == $answers_of_user[$question_id] ? [] : $answers_of_user[$question_id];
1622
                        $key = array_keys($my_answer_of_user);
1623
                        if (isset($key[0]) && 'open' === substr($key[0], 0, 4)) {
1624
                            // If this is an open type question (type starts by 'open'), take whatever answer is given
1625
                            $return .= '"'.
1626
                                str_replace(
1627
                                    '"',
1628
                                    '""',
1629
                                    api_html_entity_decode(
1630
                                        strip_tags(
1631
                                            $answers_of_user[$question_id][$key[0]]['option_id']
1632
                                        ),
1633
                                        ENT_QUOTES
1634
                                    )
1635
                                ).
1636
                                '";';
1637
                        } elseif (!empty($answers_of_user[$question_id][$option_id])) {
1638
                            //$return .= 'v';
1639
                            if ($compact) {
1640
                                // If we asked for a compact view, show only one column for the question
1641
                                // and fill it with the text of the selected option (i.e. "Yes") instead of an ID
1642
                                if (0 != $answers_of_user[$question_id][$option_id]['value']) {
1643
                                    $return .= $answers_of_user[$question_id][$option_id]['value'].";";
1644
                                } else {
1645
                                    $return .= '"'.
1646
                                        str_replace(
1647
                                            '"',
1648
                                            '""',
1649
                                            api_html_entity_decode(
1650
                                                strip_tags(
1651
                                                    $possible_option[$option_id]
1652
                                                ),
1653
                                                ENT_QUOTES
1654
                                            )
1655
                                        ).
1656
                                        '";';
1657
                                }
1658
                            } else {
1659
                                // If we don't want a compact view, show one column per possible option and mark a 'v'
1660
                                // or the defined value in the corresponding column if the user selected it
1661
                                if (0 != $answers_of_user[$question_id][$option_id]['value']) {
1662
                                    $return .= $answers_of_user[$question_id][$option_id]['value'].";";
1663
                                } else {
1664
                                    $return .= 'v;';
1665
                                }
1666
                            }
1667
                        } else {
1668
                            if (!$compact) {
1669
                                $return .= ';';
1670
                            }
1671
                        }
1672
                    }
1673
                }
1674
            }
1675
        }
1676
        $return .= "\n";
1677
1678
        return $return;
1679
    }
1680
1681
    public static function export_complete_report_xls(CSurvey $survey, $filename, $user_id = 0, $returnFile = false)
1682
    {
1683
        $course_id = api_get_course_int_id();
1684
        $user_id = (int) $user_id;
1685
        $surveyId = $survey->getIid();
1686
1687
        if (empty($course_id) || empty($surveyId)) {
1688
            return false;
1689
        }
1690
1691
        // Show extra fields blank space (enough for extra fields on next line)
1692
        // Show user fields section with a big th colspan that spans over all fields
1693
        $extra_user_fields = UserManager::get_extra_fields(
1694
            0,
1695
            0,
1696
            5,
1697
            'ASC',
1698
            false,
1699
            true
1700
        );
1701
        $list = [];
1702
        $num = count($extra_user_fields);
1703
        for ($i = 0; $i < $num; $i++) {
1704
            $list[0][] = '';
1705
        }
1706
1707
        $display_extra_user_fields = true;
1708
1709
        // Database table definitions
1710
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
1711
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
1712
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
1713
1714
        // First line (questions)
1715
        $sql = "SELECT
1716
                    questions.question_id,
1717
                    questions.type,
1718
                    questions.survey_question,
1719
                    count(options.iid) as number_of_options
1720
				FROM $table_survey_question questions
1721
				LEFT JOIN $table_survey_question_option options
1722
                ON
1723
                  questions.iid = options.question_id
1724
				WHERE
1725
				    survey_question NOT LIKE '%{{%' AND
1726
				    questions.type <> 'pagebreak' AND
1727
				    questions.survey_id = $surveyId
1728
				GROUP BY questions.question_id
1729
				ORDER BY questions.sort ASC";
1730
        $result = Database::query($sql);
1731
        $line = 1;
1732
        $column = 1;
1733
        while ($row = Database::fetch_array($result)) {
1734
            // We show the questions if
1735
            // 1. there is no question filter and the export button has not been clicked
1736
            // 2. there is a quesiton filter but the question is selected for display
1737
            if (!(isset($_POST['submit_question_filter'])) ||
1738
                (isset($_POST['submit_question_filter']) && is_array($_POST['questions_filter']) &&
1739
                in_array($row['question_id'], $_POST['questions_filter']))
1740
            ) {
1741
                // We do not show comment and pagebreak question types
1742
                if ('pagebreak' !== $row['type']) {
1743
                    if (0 == $row['number_of_options'] && ('open' === $row['type'] || 'comment' === $row['type'])) {
1744
                        $list[$line][$column] = api_html_entity_decode(
1745
                            strip_tags($row['survey_question']),
1746
                            ENT_QUOTES
1747
                        );
1748
                        $column++;
1749
                    } else {
1750
                        for ($ii = 0; $ii < $row['number_of_options']; $ii++) {
1751
                            $list[$line][$column] = api_html_entity_decode(
1752
                                strip_tags($row['survey_question']),
1753
                                ENT_QUOTES
1754
                            );
1755
                            $column++;
1756
                        }
1757
                    }
1758
                }
1759
            }
1760
        }
1761
1762
        $line++;
1763
        $column = 1;
1764
        // Show extra field values
1765
        if ($display_extra_user_fields) {
1766
            // Show the fields names for user fields
1767
            foreach ($extra_user_fields as &$field) {
1768
                $list[$line][$column] = api_html_entity_decode(strip_tags($field[3]), ENT_QUOTES);
1769
                $column++;
1770
            }
1771
        }
1772
1773
        // Getting all the questions and options (second line)
1774
        $sql = "SELECT
1775
                    survey_question.iid question_id,
1776
                    survey_question.survey_id,
1777
                    survey_question.survey_question,
1778
                    survey_question.display,
1779
                    survey_question.sort,
1780
                    survey_question.type,
1781
                    survey_question_option.iid question_option_id,
1782
                    survey_question_option.option_text,
1783
                    survey_question_option.sort as option_sort
1784
				FROM $table_survey_question survey_question
1785
				LEFT JOIN $table_survey_question_option survey_question_option
1786
				ON
1787
				    survey_question.iid = survey_question_option.question_id
1788
				WHERE
1789
				    survey_question NOT LIKE '%{{%' AND
1790
				    survey_question.type <> 'pagebreak' AND
1791
				    survey_question.survey_id = $surveyId
1792
				ORDER BY survey_question.sort ASC, survey_question_option.sort ASC";
1793
        $result = Database::query($sql);
1794
        $possible_answers = [];
1795
        $possible_answers_type = [];
1796
        while ($row = Database::fetch_array($result)) {
1797
            // We show the options if
1798
            // 1. there is no question filter and the export button has not been clicked
1799
            // 2. there is a quesiton filter but the question is selected for display
1800
            if (!isset($_POST['submit_question_filter']) ||
1801
                (isset($_POST['questions_filter']) && is_array($_POST['questions_filter']) &&
1802
                in_array($row['question_id'], $_POST['questions_filter']))
1803
            ) {
1804
                // We do not show comment and pagebreak question types
1805
                if ('pagebreak' !== $row['type']) {
1806
                    $list[$line][$column] = api_html_entity_decode(
1807
                        strip_tags($row['option_text']),
1808
                        ENT_QUOTES
1809
                    );
1810
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1811
                    $possible_answers_type[$row['question_id']] = $row['type'];
1812
                    $column++;
1813
                }
1814
            }
1815
        }
1816
1817
        // Getting all the answers of the users
1818
        $line++;
1819
        $column = 0;
1820
        $old_user = '';
1821
        $answers_of_user = [];
1822
        $sql = "SELECT * FROM $table_survey_answer
1823
                WHERE c_id = $course_id AND survey_id = $surveyId";
1824
        if (0 != $user_id) {
1825
            $sql .= " AND user='".$user_id."' ";
1826
        }
1827
        $sql .= ' ORDER BY user ASC';
1828
1829
        $open_question_iterator = 1;
1830
        $result = Database::query($sql);
1831
        while ($row = Database::fetch_array($result)) {
1832
            if ($old_user != $row['user'] && '' != $old_user) {
1833
                $return = self::export_complete_report_row_xls(
1834
                    $survey,
1835
                    $possible_answers,
1836
                    $answers_of_user,
1837
                    $old_user,
1838
                    true
1839
                );
1840
                foreach ($return as $elem) {
1841
                    $list[$line][$column] = $elem;
1842
                    $column++;
1843
                }
1844
                $answers_of_user = [];
1845
                $line++;
1846
                $column = 0;
1847
            }
1848
            if ('open' === $possible_answers_type[$row['question_id']] || 'comment' === $possible_answers_type[$row['question_id']]) {
1849
                $temp_id = 'open'.$open_question_iterator;
1850
                $answers_of_user[$row['question_id']][$temp_id] = $row;
1851
                $open_question_iterator++;
1852
            } else {
1853
                $answers_of_user[$row['question_id']][$row['option_id']] = $row;
1854
            }
1855
            $old_user = $row['user'];
1856
        }
1857
1858
        $return = self::export_complete_report_row_xls(
1859
            $survey,
1860
            $possible_answers,
1861
            $answers_of_user,
1862
            $old_user,
1863
            true
1864
        );
1865
1866
        // this is to display the last user
1867
        if (!empty($return)) {
1868
            foreach ($return as $elem) {
1869
                $list[$line][$column] = $elem;
1870
                $column++;
1871
            }
1872
        }
1873
1874
        Export::arrayToXls($list, $filename);
1875
1876
        return null;
1877
    }
1878
1879
    /**
1880
     * Add a line to the csv file.
1881
     *
1882
     * @param array Possible answers
1883
     * @param array User's answers
0 ignored issues
show
Documentation Bug introduced by
The doc comment User's at position 0 could not be parsed: Unknown type name 'User's' at position 0 in User's.
Loading history...
1884
     * @param mixed User ID or user details as string - Used as a string in the result string
1885
     * @param bool Whether to display user fields or not
1886
     *
1887
     * @return array
1888
     */
1889
    public static function export_complete_report_row_xls(
1890
        CSurvey $survey,
1891
        $possible_options,
1892
        $answers_of_user,
1893
        $user,
1894
        $display_extra_user_fields = false
1895
    ) {
1896
        $return = [];
1897
        if (0 == $survey->getAnonymous()) {
1898
            if (0 !== (int) $user) {
1899
                $userInfo = api_get_user_info($user);
1900
                if ($userInfo) {
1901
                    $user_displayed = $userInfo['complete_name_with_username'];
1902
                } else {
1903
                    $user_displayed = '-';
1904
                }
1905
                $return[] = $user_displayed;
1906
            } else {
1907
                $return[] = $user;
1908
            }
1909
        } else {
1910
            $return[] = '-'; // The user column
1911
        }
1912
1913
        if ($display_extra_user_fields) {
1914
            //show user fields data, if any, for this user
1915
            $user_fields_values = UserManager::get_extra_user_data(
1916
                $user,
1917
                false,
1918
                false,
1919
                false,
1920
                true
1921
            );
1922
            foreach ($user_fields_values as $value) {
1923
                $return[] = api_html_entity_decode(strip_tags($value), ENT_QUOTES);
1924
            }
1925
        }
1926
1927
        if (is_array($possible_options)) {
1928
            foreach ($possible_options as $question_id => &$possible_option) {
1929
                if (is_array($possible_option) && count($possible_option) > 0) {
1930
                    foreach ($possible_option as $option_id => &$value) {
1931
                        $my_answers_of_user = $answers_of_user[$question_id] ?? [];
1932
                        $key = array_keys($my_answers_of_user);
1933
                        if (isset($key[0]) && 'open' === substr($key[0], 0, 4)) {
1934
                            $return[] = api_html_entity_decode(
1935
                                strip_tags($answers_of_user[$question_id][$key[0]]['option_id']),
1936
                                ENT_QUOTES
1937
                            );
1938
                        } elseif (!empty($answers_of_user[$question_id][$option_id])) {
1939
                            if (0 != $answers_of_user[$question_id][$option_id]['value']) {
1940
                                $return[] = $answers_of_user[$question_id][$option_id]['value'];
1941
                            } else {
1942
                                $return[] = 'v';
1943
                            }
1944
                        } else {
1945
                            $return[] = '';
1946
                        }
1947
                    }
1948
                }
1949
            }
1950
        }
1951
1952
        return $return;
1953
    }
1954
1955
    /**
1956
     * This function displays the comparative report which
1957
     * allows you to compare two questions
1958
     * A comparative report creates a table where one question
1959
     * is on the x axis and a second question is on the y axis.
1960
     * In the intersection is the number of people who have
1961
     * answered positive on both options.
1962
     *
1963
     * @return string HTML code
1964
     *
1965
     * @author Patrick Cool <[email protected]>, Ghent University
1966
     *
1967
     * @version February 2007
1968
     */
1969
    public static function display_comparative_report()
1970
    {
1971
        // Allowed question types for comparative report
1972
        $allowed_question_types = [
1973
            'yesno',
1974
            'multiplechoice',
1975
            'multipleresponse',
1976
            'dropdown',
1977
            'percentage',
1978
            'score',
1979
        ];
1980
1981
        $surveyId = isset($_GET['survey_id']) ? (int) $_GET['survey_id'] : 0;
1982
1983
        // Getting all the questions
1984
        $questions = SurveyManager::get_questions($surveyId);
1985
1986
        // Actions bar
1987
        $actions = '<a href="'.api_get_path(
1988
                WEB_CODE_PATH
1989
            ).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq()
1990
            .'">'
1991
            .Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Reporting overview'))
1992
            .'</a>';
1993
        echo Display::toolbarAction('survey', [$actions]);
1994
1995
        // Displaying an information message that only the questions with predefined answers can be used in a comparative report
1996
        echo Display::return_message(get_lang('Only questions with predefined answers can be used'), 'normal', false);
1997
1998
        $xAxis = isset($_GET['xaxis']) ? Security::remove_XSS($_GET['xaxis']) : '';
1999
        $yAxis = isset($_GET['yaxis']) ? Security::remove_XSS($_GET['yaxis']) : '';
2000
2001
        $url = api_get_self().'?'.api_get_cidreq().'&action='.Security::remove_XSS($_GET['action'])
0 ignored issues
show
Bug introduced by
Are you sure Security::remove_XSS($_GET['action']) of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2001
        $url = api_get_self().'?'.api_get_cidreq().'&action='./** @scrutinizer ignore-type */ Security::remove_XSS($_GET['action'])
Loading history...
2002
            .'&survey_id='.$surveyId.'&xaxis='.$xAxis.'&y='.$yAxis;
0 ignored issues
show
Bug introduced by
Are you sure $xAxis of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2002
            .'&survey_id='.$surveyId.'&xaxis='./** @scrutinizer ignore-type */ $xAxis.'&y='.$yAxis;
Loading history...
Bug introduced by
Are you sure $yAxis of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2002
            .'&survey_id='.$surveyId.'&xaxis='.$xAxis.'&y='./** @scrutinizer ignore-type */ $yAxis;
Loading history...
2003
2004
        $form = new FormValidator('compare', 'get', $url);
2005
        $form->addHidden('action', Security::remove_XSS($_GET['action']));
2006
        $form->addHidden('survey_id', $surveyId);
2007
        $optionsX = ['----'];
2008
        $optionsY = ['----'];
2009
        $defaults = [];
2010
        foreach ($questions as $key => &$question) {
2011
            // Ignored tagged questions
2012
            if ($question) {
2013
                if (false !== strpos($question['question'], '{{')) {
2014
                    $question = null;
2015
                    continue;
2016
                }
2017
            }
2018
            if (is_array($allowed_question_types)) {
2019
                if (in_array($question['type'], $allowed_question_types)) {
2020
                    if (isset($_GET['xaxis']) && $_GET['xaxis'] == $question['question_id']) {
2021
                        $defaults['xaxis'] = $question['question_id'];
2022
                    }
2023
2024
                    if (isset($_GET['yaxis']) && $_GET['yaxis'] == $question['question_id']) {
2025
                        $defaults['yaxis'] = $question['question_id'];
2026
                    }
2027
2028
                    $optionsX[$question['question_id']] = api_substr(strip_tags($question['question']), 0, 90);
2029
                    $optionsY[$question['question_id']] = api_substr(strip_tags($question['question']), 0, 90);
2030
                }
2031
            }
2032
        }
2033
2034
        $form->addSelect('xaxis', get_lang('Select the question on the X axis'), $optionsX);
2035
        $form->addSelect('yaxis', get_lang('Select the question on the Y axis'), $optionsY);
2036
2037
        $form->addButtonSearch(get_lang('Compare questions'));
2038
        $form->setDefaults($defaults);
2039
        $form->display();
2040
2041
        // Getting all the information of the x axis
2042
        if (is_numeric($xAxis)) {
2043
            $question_x = SurveyManager::get_question($xAxis);
2044
        }
2045
2046
        // Getting all the information of the y axis
2047
        if (is_numeric($yAxis)) {
2048
            $question_y = SurveyManager::get_question($yAxis);
2049
        }
2050
2051
        if (is_numeric($xAxis) && is_numeric($yAxis) && $question_x && $question_y) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $question_y does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $question_x does not seem to be defined for all execution paths leading up to this point.
Loading history...
2052
            // Getting the answers of the two questions
2053
            $answers_x = self::get_answers_of_question_by_user($surveyId, $xAxis);
2054
            $answers_y = self::get_answers_of_question_by_user($surveyId, $yAxis);
2055
2056
            // Displaying the table
2057
            $tableHtml = '<table border="1" class="table table-hover table-striped data_table">';
2058
            $xOptions = [];
2059
            // The header
2060
            $tableHtml .= '<tr>';
2061
            for ($ii = 0; $ii <= count($question_x['answers']); $ii++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2062
                if (0 == $ii) {
2063
                    $tableHtml .= '<th>&nbsp;</th>';
2064
                } else {
2065
                    if ('score' == $question_x['type']) {
2066
                        for ($x = 1; $x <= $question_x['maximum_score']; $x++) {
2067
                            $tableHtml .= '<th>'.$question_x['answers'][($ii - 1)].'<br />'.$x.'</th>';
2068
                        }
2069
                        $x = '';
2070
                    } else {
2071
                        $tableHtml .= '<th>'.$question_x['answers'][($ii - 1)].'</th>';
2072
                    }
2073
                    $optionText = strip_tags($question_x['answers'][$ii - 1]);
2074
                    $optionText = html_entity_decode($optionText);
2075
                    array_push($xOptions, trim($optionText));
2076
                }
2077
            }
2078
            $tableHtml .= '</tr>';
2079
            $chartData = [];
2080
            // The main part
2081
            for ($ij = 0; $ij < count($question_y['answers']); $ij++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2082
                $currentYQuestion = strip_tags($question_y['answers'][$ij]);
2083
                $currentYQuestion = html_entity_decode($currentYQuestion);
2084
                // The Y axis is a scoring question type so we have more rows than the options (actually options * maximum score)
2085
                if ('score' == $question_y['type']) {
2086
                    for ($y = 1; $y <= $question_y['maximum_score']; $y++) {
2087
                        $tableHtml .= '<tr>';
2088
                        for ($ii = 0; $ii <= count($question_x['answers']); $ii++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2089
                            if ('score' == $question_x['type']) {
2090
                                for ($x = 1; $x <= $question_x['maximum_score']; $x++) {
2091
                                    if (0 == $ii) {
2092
                                        $tableHtml .= '<th>'.$question_y['answers'][($ij)].' '.$y.'</th>';
2093
                                        break;
2094
                                    } else {
2095
                                        $tableHtml .= '<td align="center">';
2096
                                        $votes = self::comparative_check(
2097
                                            $answers_x,
2098
                                            $answers_y,
2099
                                            $question_x['answersid'][($ii - 1)],
2100
                                            $question_y['answersid'][($ij)],
2101
                                            $x,
2102
                                            $y
2103
                                        );
2104
                                        $tableHtml .= $votes;
2105
                                        array_push(
2106
                                            $chartData,
2107
                                            [
2108
                                                'serie' => [$currentYQuestion, $xOptions[$ii - 1]],
2109
                                                'option' => $x,
2110
                                                'votes' => $votes,
2111
                                            ]
2112
                                        );
2113
                                        $tableHtml .= '</td>';
2114
                                    }
2115
                                }
2116
                            } else {
2117
                                if (0 == $ii) {
2118
                                    $tableHtml .= '<th>'.$question_y['answers'][$ij].' '.$y.'</th>';
2119
                                } else {
2120
                                    $tableHtml .= '<td align="center">';
2121
                                    $votes = self::comparative_check(
2122
                                        $answers_x,
2123
                                        $answers_y,
2124
                                        $question_x['answersid'][($ii - 1)],
2125
                                        $question_y['answersid'][($ij)],
2126
                                        0,
2127
                                        $y
2128
                                    );
2129
                                    $tableHtml .= $votes;
2130
                                    array_push(
2131
                                        $chartData,
2132
                                        [
2133
                                            'serie' => [$currentYQuestion, $xOptions[$ii - 1]],
2134
                                            'option' => $y,
2135
                                            'votes' => $votes,
2136
                                        ]
2137
                                    );
2138
                                    $tableHtml .= '</td>';
2139
                                }
2140
                            }
2141
                        }
2142
                        $tableHtml .= '</tr>';
2143
                    }
2144
                } else {
2145
                    // The Y axis is NOT a score question type so the number of rows = the number of options
2146
                    $tableHtml .= '<tr>';
2147
                    for ($ii = 0; $ii <= count($question_x['answers']); $ii++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2148
                        if ('score' == $question_x['type']) {
2149
                            for ($x = 1; $x <= $question_x['maximum_score']; $x++) {
2150
                                if (0 == $ii) {
2151
                                    $tableHtml .= '<th>'.$question_y['answers'][$ij].'</th>';
2152
                                    break;
2153
                                } else {
2154
                                    $tableHtml .= '<td align="center">';
2155
                                    $votes = self::comparative_check(
2156
                                        $answers_x,
2157
                                        $answers_y,
2158
                                        $question_x['answersid'][($ii - 1)],
2159
                                        $question_y['answersid'][($ij)],
2160
                                        $x,
2161
                                        0
2162
                                    );
2163
                                    $tableHtml .= $votes;
2164
                                    array_push(
2165
                                        $chartData,
2166
                                        [
2167
                                            'serie' => [$currentYQuestion, $xOptions[$ii - 1]],
2168
                                            'option' => $x,
2169
                                            'votes' => $votes,
2170
                                        ]
2171
                                    );
2172
                                    $tableHtml .= '</td>';
2173
                                }
2174
                            }
2175
                        } else {
2176
                            if (0 == $ii) {
2177
                                $tableHtml .= '<th>'.$question_y['answers'][($ij)].'</th>';
2178
                            } else {
2179
                                $tableHtml .= '<td align="center">';
2180
                                $votes = self::comparative_check(
2181
                                    $answers_x,
2182
                                    $answers_y,
2183
                                    $question_x['answersid'][($ii - 1)],
2184
                                    $question_y['answersid'][($ij)]
2185
                                );
2186
                                $tableHtml .= $votes;
2187
                                array_push(
2188
                                    $chartData,
2189
                                    [
2190
                                        'serie' => $xOptions[$ii - 1],
2191
                                        'option' => $currentYQuestion,
2192
                                        'votes' => $votes,
2193
                                    ]
2194
                                );
2195
                                $tableHtml .= '</td>';
2196
                            }
2197
                        }
2198
                    }
2199
                    $tableHtml .= '</tr>';
2200
                }
2201
            }
2202
            $tableHtml .= '</table>';
2203
            echo '<div id="chartContainer" class="col-md-12">';
2204
            echo self::drawChart($chartData, true);
2205
            echo '</div>';
2206
            echo $tableHtml;
2207
        }
2208
    }
2209
2210
    /**
2211
     * Get all the answers of a question grouped by user.
2212
     *
2213
     * @param int $survey_id   Survey ID
2214
     * @param int $question_id Question ID
2215
     *
2216
     * @return array Array containing all answers of all users, grouped by user
2217
     *
2218
     * @author Patrick Cool <[email protected]>, Ghent University
2219
     *
2220
     * @version February 2007 - Updated March 2008
2221
     */
2222
    public static function get_answers_of_question_by_user($survey_id, $question_id)
2223
    {
2224
        $course_id = api_get_course_int_id();
2225
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
2226
2227
        $sql = "SELECT * FROM $table_survey_answer
2228
                WHERE
2229
                  survey_id='".intval($survey_id)."' AND
2230
                  question_id='".intval($question_id)."'
2231
                ORDER BY USER ASC";
2232
        $result = Database::query($sql);
2233
        $return = [];
2234
        while ($row = Database::fetch_array($result)) {
2235
            if (0 == $row['value']) {
2236
                $return[$row['user']][] = $row['option_id'];
2237
            } else {
2238
                $return[$row['user']][] = $row['option_id'].'*'.$row['value'];
2239
            }
2240
        }
2241
2242
        return $return;
2243
    }
2244
2245
    /**
2246
     * Count the number of users who answer positively on both options.
2247
     *
2248
     * @param array All answers of the x axis
2249
     * @param array All answers of the y axis
2250
     * @param int x axis value (= the option_id of the first question)
2251
     * @param int y axis value (= the option_id of the second question)
2252
     *
2253
     * @return int Number of users who have answered positively to both options
2254
     *
2255
     * @author Patrick Cool <[email protected]>, Ghent University
2256
     *
2257
     * @version February 2007
2258
     */
2259
    public static function comparative_check(
2260
        $answers_x,
2261
        $answers_y,
2262
        $option_x,
2263
        $option_y,
2264
        $value_x = 0,
2265
        $value_y = 0
2266
    ) {
2267
        if (0 == $value_x) {
2268
            $check_x = $option_x;
2269
        } else {
2270
            $check_x = $option_x.'*'.$value_x;
2271
        }
2272
        if (0 == $value_y) {
2273
            $check_y = $option_y;
2274
        } else {
2275
            $check_y = $option_y.'*'.$value_y;
2276
        }
2277
2278
        $counter = 0;
2279
        if (is_array($answers_x)) {
2280
            foreach ($answers_x as $user => &$answers) {
2281
                // Check if the user has given $option_x as answer
2282
                if (in_array($check_x, $answers)) {
2283
                    // Check if the user has given $option_y as an answer
2284
                    if (!is_null($answers_y[$user]) &&
2285
                        in_array($check_y, $answers_y[$user])
2286
                    ) {
2287
                        $counter++;
2288
                    }
2289
                }
2290
            }
2291
        }
2292
2293
        return $counter;
2294
    }
2295
2296
    public static function saveInviteMail(CSurvey $survey, $content, $subject, $remind)
2297
    {
2298
        // Database table definition
2299
        if ($remind) {
2300
            $survey->setReminderMail($content);
2301
        } else {
2302
            $survey->setInviteMail($content);
2303
        }
2304
2305
        $survey->setMailSubject($subject);
2306
        $em = Database::getManager();
2307
        $em->persist($survey);
2308
        $em->flush();
2309
    }
2310
2311
    /**
2312
     * This function saves all the invitations of course users
2313
     * and additional users in the database
2314
     * and sends the invitations by email.
2315
     *
2316
     * @param int    $surveyId
2317
     * @param array  $users_array       Users array can be both a list of course uids AND a list of additional email
2318
     *                                  addresses
2319
     * @param string $invitation_title  title of the mail
2320
     * @param string $invitation_text   text of the mail has to contain a **link** string or
2321
     *                                  this will automatically be added to the end
2322
     * @param int    $reminder
2323
     * @param bool   $sendmail
2324
     * @param int    $remindUnAnswered
2325
     * @param bool   $isAdditionalEmail
2326
     * @param bool   $hideLink
2327
     *
2328
     * @author Patrick Cool <[email protected]>, Ghent University
2329
     * @author Julio Montoya - Adding auto-generated link support
2330
     *
2331
     * @version January 2007
2332
     */
2333
    public static function saveInvitations(
2334
        CSurvey $survey,
2335
        $users_array,
2336
        $invitation_title,
2337
        $invitation_text,
2338
        $reminder = 0,
2339
        $sendmail = false,
2340
        $remindUnAnswered = 0,
2341
        $isAdditionalEmail = false,
2342
        $hideLink = false
2343
    ) {
2344
        $surveyId = $survey->getIid();
2345
2346
        if (!is_array($users_array)) {
2347
            return 0;
2348
        }
2349
        $course = api_get_course_entity();
2350
        $session = api_get_session_entity();
2351
        $survey_invitations = self::get_invitations($surveyId);
2352
        $already_invited = self::get_invited_users($survey);
2353
2354
        // Remind unanswered is a special version of remind all reminder
2355
        $exclude_users = [];
2356
        if (1 == $remindUnAnswered) {
2357
            // Remind only unanswered users
2358
            $reminder = 1;
2359
            $exclude_users = SurveyManager::get_people_who_filled_survey($surveyId);
2360
        }
2361
2362
        $counter = 0; // Nr of invitations "sent" (if sendmail option)
2363
        $course_id = api_get_course_int_id();
2364
        $session_id = api_get_session_id();
2365
2366
        if (false == $isAdditionalEmail) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2367
            $result = AbstractResource::separateUsersGroups($users_array);
2368
            $groupList = $result['groups'];
2369
            $users_array = $result['users'];
2370
2371
            foreach ($groupList as $groupId) {
2372
                $group = api_get_group_entity($groupId);
2373
                $userGroupList = GroupManager::getStudents($groupId, true);
2374
                $userGroupIdList = array_column($userGroupList, 'user_id');
2375
                $users_array = array_merge($users_array, $userGroupIdList);
2376
2377
                /*$params = [
2378
                    'c_id' => $course_id,
2379
                    'session_id' => $session_id,
2380
                    'group_id' => $groupId,
2381
                    'survey_code' => $survey_data['code'],
2382
                ];*/
2383
2384
                $invitationExists = self::invitationExists(
2385
                    $course_id,
2386
                    $session_id,
2387
                    $groupId,
2388
                    $survey->getIid()
2389
                );
2390
                if (empty($invitationExists)) {
2391
                    self::saveInvitation(
2392
                        '',
2393
                        '',
2394
                        api_get_utc_datetime(time(), false, true),
2395
                        $survey,
2396
                        $course,
2397
                        $session,
2398
                        $group
2399
                    );
2400
                }
2401
            }
2402
        }
2403
2404
        $users_array = array_unique($users_array);
2405
        foreach ($users_array as $value) {
2406
            if (empty($value)) {
2407
                continue;
2408
            }
2409
2410
            // Skip user if reminding only unanswered people
2411
            if (in_array($value, $exclude_users)) {
2412
                continue;
2413
            }
2414
2415
            // Get the unique invitation code if we already have it
2416
            if (1 == $reminder && array_key_exists($value, $survey_invitations)) {
2417
                $invitation_code = $survey_invitations[$value]['invitation_code'];
2418
            } else {
2419
                $invitation_code = md5($value.microtime());
2420
            }
2421
            $new_user = false; // User not already invited
2422
            // Store the invitation if user_id not in $already_invited['course_users'] OR
2423
            // email is not in $already_invited['additional_users']
2424
            $addit_users_array = isset($already_invited['additional_users']) && !empty($already_invited['additional_users'])
2425
                    ? explode(';', $already_invited['additional_users'])
2426
                    : [];
2427
            $my_alredy_invited = $already_invited['course_users'] ?? [];
2428
2429
            $userId = 0;
2430
            if (is_string($value) && filter_var($value, FILTER_VALIDATE_EMAIL)) {
2431
                $userInfo = api_get_user_info_from_email($value);
2432
                if ($userInfo && isset($userInfo['id'])) {
2433
                    $userId = $userInfo['id'];
2434
                }
2435
            } elseif (is_numeric($value)) {
2436
                $userId = $value;
2437
            }
2438
2439
            if ($userId && !in_array($userId, $my_alredy_invited)) {
2440
                $new_user = true;
2441
                if (!array_key_exists($userId, $survey_invitations)) {
2442
                    self::saveInvitation(
2443
                        api_get_user_entity($userId),
2444
                        $invitation_code,
2445
                        api_get_utc_datetime(time(), null, true),
2446
                        $survey,
2447
                        $course,
2448
                        $session
2449
                    );
2450
                }
2451
            }
2452
2453
            // Send the email if checkboxed
2454
            if (($new_user || 1 == $reminder) && $sendmail) {
2455
                // Make a change for absolute url
2456
                if (isset($invitation_text)) {
2457
                    $invitation_text = api_html_entity_decode($invitation_text, ENT_QUOTES);
2458
                    $invitation_text = str_replace('src="../../', 'src="'.api_get_path(WEB_PATH), $invitation_text);
2459
                    $invitation_text = trim(stripslashes($invitation_text));
2460
                }
2461
                self::sendInvitationMail(
2462
                    $survey,
2463
                    $value,
2464
                    $course,
2465
                    $invitation_code,
2466
                    $invitation_title,
2467
                    $invitation_text,
2468
                    $hideLink
2469
                );
2470
                $counter++;
2471
            }
2472
        }
2473
2474
        return $counter; // Number of invitations sent
2475
    }
2476
2477
    public static function saveInvitation(
2478
        User $user,
2479
        $invitationCode,
2480
        $reminderDate,
2481
        CSurvey $survey,
2482
        Course $course,
2483
        SessionEntity $session = null,
2484
        CGroup $group = null
2485
    ): ?CSurveyInvitation {
2486
        $invitation = new CSurveyInvitation();
2487
        $invitation
2488
            ->setUser($user)
2489
            ->setInvitationCode($invitationCode)
2490
            ->setReminderDate($reminderDate)
2491
            ->setSurvey($survey)
2492
            ->setCourse($course)
2493
            ->setSession($session)
2494
            ->setGroup($group)
2495
        ;
2496
2497
        $em = Database::getManager();
2498
        $em->persist($invitation);
2499
        $em->flush();
2500
2501
        return $invitation;
2502
    }
2503
2504
    /**
2505
     * @param int $courseId
2506
     * @param int $sessionId
2507
     * @param int $groupId
2508
     * @param int $surveyId
2509
     *
2510
     * @return int
2511
     */
2512
    public static function invitationExists($courseId, $sessionId, $groupId, $surveyId)
2513
    {
2514
        $table = Database::get_course_table(TABLE_SURVEY_INVITATION);
2515
        $courseId = (int) $courseId;
2516
        $sessionId = (int) $sessionId;
2517
        $groupId = (int) $groupId;
2518
        $surveyId = (int) $surveyId;
2519
2520
        $sql = "SELECT iid FROM $table
2521
                WHERE
2522
                    c_id = $courseId AND
2523
                    session_id = $sessionId AND
2524
                    group_id = $groupId AND
2525
                    survey_id = $surveyId
2526
                ";
2527
        $result = Database::query($sql);
2528
2529
        return Database::num_rows($result);
2530
    }
2531
2532
    /**
2533
     * Send the invitation by mail.
2534
     *
2535
     * @param int invitedUser - the userId (course user) or emailaddress of additional user
2536
     * @param string $invitation_code - the unique invitation code for the URL
2537
     */
2538
    public static function sendInvitationMail(
2539
        CSurvey $survey,
2540
        $invitedUser,
2541
        Course $course,
2542
        $invitation_code,
2543
        $invitation_title,
2544
        $invitation_text,
2545
        $hideLink = false
2546
    ) {
2547
        $_user = api_get_user_info();
2548
        $sessionId = api_get_session_id();
2549
2550
        // Replacing the **link** part with a valid link for the user
2551
        $link = self::generateFillSurveyLink($survey, $invitation_code, $course, $sessionId);
2552
        if ($hideLink) {
2553
            $full_invitation_text = str_replace('**link**', '', $invitation_text);
2554
        } else {
2555
            $text_link = '<a href="'.$link.'">'.get_lang('Click here to answer the survey')."</a><br />\r\n<br />\r\n"
2556
                .get_lang('or copy paste the following url :')." <br /> \r\n <br /> \r\n ".$link;
2557
2558
            $replace_count = 0;
2559
            $full_invitation_text = api_str_ireplace('**link**', $text_link, $invitation_text, $replace_count);
2560
            if ($replace_count < 1) {
2561
                $full_invitation_text = $full_invitation_text."<br />\r\n<br />\r\n".$text_link;
0 ignored issues
show
Bug introduced by
Are you sure $full_invitation_text of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2561
                $full_invitation_text = /** @scrutinizer ignore-type */ $full_invitation_text."<br />\r\n<br />\r\n".$text_link;
Loading history...
2562
            }
2563
        }
2564
2565
        // Sending the mail
2566
        $sender_name = api_get_person_name($_user['firstName'], $_user['lastName'], null, PERSON_NAME_EMAIL_ADDRESS);
2567
        $sender_email = $_user['mail'];
2568
        $sender_user_id = api_get_user_id();
2569
2570
        $replyto = [];
2571
        if ('noreply' === api_get_setting('survey_email_sender_noreply')) {
2572
            $noreply = api_get_setting('noreply_email_address');
2573
            if (!empty($noreply)) {
2574
                $replyto['Reply-to'] = $noreply;
2575
                $sender_name = $noreply;
2576
                $sender_email = $noreply;
2577
                $sender_user_id = null;
2578
            }
2579
        }
2580
2581
        // Optionally: finding the e-mail of the course user
2582
        if (is_numeric($invitedUser)) {
2583
            MessageManager::send_message(
2584
                $invitedUser,
2585
                $invitation_title,
2586
                $full_invitation_text,
2587
                [],
2588
                [],
2589
                null,
2590
                null,
2591
                null,
2592
                null,
2593
                $sender_user_id,
2594
                true
2595
            );
2596
        } else {
2597
            @api_mail_html(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for api_mail_html(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

2597
            /** @scrutinizer ignore-unhandled */ @api_mail_html(

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2598
                '',
2599
                $invitedUser,
2600
                $invitation_title,
2601
                $full_invitation_text,
2602
                $sender_name,
2603
                $sender_email,
2604
                $replyto
2605
            );
2606
        }
2607
    }
2608
2609
    /**
2610
     * This function recalculates the number of users who have been invited and updates the survey table with this
2611
     * value.
2612
     */
2613
    public static function updateInvitedCount(CSurvey $survey, $courseId = 0, $sessionId = 0)
2614
    {
2615
        $surveyId = $survey->getIid();
2616
        $courseId = (int) $courseId;
2617
        $sessionId = (int) $sessionId;
2618
2619
        $courseId = $courseId ?: api_get_course_int_id();
2620
        $sessionId = $sessionId ?: api_get_session_id();
2621
        $sessionCondition = api_get_session_condition($sessionId);
2622
2623
        // Database table definition
2624
        $table_survey_invitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
2625
        $table_survey = Database::get_course_table(TABLE_SURVEY);
2626
2627
        // Counting the number of people that are invited
2628
        $sql = "SELECT count(user_id) as total
2629
                FROM $table_survey_invitation
2630
		        WHERE
2631
		            c_id = $courseId AND
2632
		            survey_id = '".$surveyId."' AND
2633
		            user_id <> ''
2634
		            $sessionCondition
2635
                ";
2636
        $result = Database::query($sql);
2637
        $row = Database::fetch_array($result);
2638
        $total_invited = $row['total'];
2639
2640
        // Updating the field in the survey table
2641
        $sql = "UPDATE $table_survey
2642
		        SET invited = '".Database::escape_string($total_invited)."'
2643
		        WHERE
2644
		            iid = '".$surveyId."'
2645
                ";
2646
        Database::query($sql);
2647
2648
        return $total_invited;
2649
    }
2650
2651
    /**
2652
     * This function gets all the invited users for a given survey code.
2653
     *
2654
     * @return array Array containing the course users and additional users (non course users)
2655
     */
2656
    public static function get_invited_users(CSurvey $survey, $course_code = '', $session_id = 0)
2657
    {
2658
        $session_id = (int) $session_id;
2659
        $surveyId = $survey->getIid();
2660
2661
        $course_code = Database::escape_string($course_code);
2662
        $course_id = api_get_course_int_id();
2663
2664
        if (!empty($course_code)) {
2665
            $course_info = api_get_course_info($course_code);
2666
            if ($course_info) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $course_info of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2667
                $course_id = $course_info['real_id'];
2668
            }
2669
        }
2670
2671
        if (empty($session_id)) {
2672
            $session_id = api_get_session_id();
2673
        }
2674
2675
        $table_survey_invitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
2676
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
2677
        $sessionCondition = api_get_session_condition($session_id);
2678
2679
        // Selecting all the invitations of this survey AND the additional emailaddresses (the left join)
2680
        $order_clause = api_sort_by_first_name() ? ' ORDER BY firstname, lastname' : ' ORDER BY lastname, firstname';
2681
        $sql = "SELECT user_id, group_id
2682
				FROM $table_survey_invitation as table_invitation
2683
				WHERE
2684
				    table_invitation.c_id = $course_id AND
2685
                    survey_id = ".$surveyId."
2686
                    $sessionCondition
2687
                ";
2688
2689
        $defaults = [];
2690
        $defaults['course_users'] = [];
2691
        $defaults['additional_users'] = ''; // Textarea
2692
        $defaults['users'] = []; // user and groups
2693
2694
        $result = Database::query($sql);
2695
        while ($row = Database::fetch_array($result)) {
2696
            if (is_numeric($row['user_id'])) {
2697
                $defaults['course_users'][] = $row['user_id'];
2698
                $defaults['users'][] = 'USER:'.$row['user_id'];
2699
            } else {
2700
                if (!empty($row['user_id'])) {
2701
                    $defaults['additional_users'][] = $row['user_id'];
2702
                }
2703
            }
2704
2705
            if (isset($row['group_id']) && !empty($row['group_id'])) {
2706
                $defaults['users'][] = 'GROUP:'.$row['group_id'];
2707
            }
2708
        }
2709
2710
        if (!empty($defaults['course_users'])) {
2711
            $user_ids = implode("','", $defaults['course_users']);
2712
            $sql = "SELECT id FROM $table_user WHERE id IN ('$user_ids') $order_clause";
2713
            $result = Database::query($sql);
2714
            $fixed_users = [];
2715
            while ($row = Database::fetch_array($result)) {
2716
                $fixed_users[] = $row['id'];
2717
            }
2718
            $defaults['course_users'] = $fixed_users;
2719
        }
2720
2721
        if (!empty($defaults['additional_users'])) {
2722
            $defaults['additional_users'] = implode(';', $defaults['additional_users']);
2723
        }
2724
2725
        return $defaults;
2726
    }
2727
2728
    /**
2729
     * Get all the invitations.
2730
     *
2731
     * @param int $surveyId
2732
     *
2733
     * @return array Database rows matching the survey code
2734
     *
2735
     * @author Patrick Cool <[email protected]>, Ghent University
2736
     *
2737
     * @version September 2007
2738
     */
2739
    public static function get_invitations($surveyId)
2740
    {
2741
        $course_id = api_get_course_int_id();
2742
        $sessionId = api_get_session_id();
2743
        // Database table definition
2744
        $table_survey_invitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
2745
2746
        $sql = "SELECT * FROM $table_survey_invitation
2747
		        WHERE
2748
		            c_id = $course_id AND
2749
                    session_id = $sessionId AND
2750
		            survey_id = '".(int) $surveyId."'";
2751
        $result = Database::query($sql);
2752
        $return = [];
2753
        while ($row = Database::fetch_array($result)) {
2754
            $return[$row['user_id']] = $row;
2755
        }
2756
2757
        return $return;
2758
    }
2759
2760
    /**
2761
     * This function displays the form for searching a survey.
2762
     *
2763
     * @author Patrick Cool <[email protected]>, Ghent University
2764
     *
2765
     * @version January 2007
2766
     *
2767
     * @todo consider moving this to surveymanager.inc.lib.php
2768
     */
2769
    public static function display_survey_search_form()
2770
    {
2771
        $url = api_get_path(WEB_CODE_PATH).'survey/survey_list.php?search=advanced&'.api_get_cidreq();
2772
        $form = new FormValidator('search', 'get', $url);
2773
        $form->addHeader(get_lang('Search a survey'));
2774
        $form->addText('keyword_title', get_lang('Title'));
2775
        $form->addText('keyword_code', get_lang('Course code'));
2776
        $form->addSelectLanguage('keyword_language', get_lang('Language'));
2777
        $form->addHidden('cid', api_get_course_int_id());
2778
        $form->addButtonSearch(get_lang('Search'), 'do_search');
2779
        $form->display();
2780
    }
2781
2782
    /**
2783
     * Show table only visible by DRH users.
2784
     */
2785
    public static function displaySurveyListForDrh()
2786
    {
2787
        $parameters = [];
2788
        $parameters['cidReq'] = api_get_course_id();
2789
2790
        // Create a sortable table with survey-data
2791
        $table = new SortableTable(
2792
            'surveys',
2793
            'get_number_of_surveys',
2794
            'get_survey_data_drh',
2795
            2
2796
        );
2797
        $table->set_additional_parameters($parameters);
2798
        $table->set_header(0, '', false);
2799
        $table->set_header(1, get_lang('Survey name'));
2800
        $table->set_header(2, get_lang('Survey code'));
2801
        $table->set_header(3, get_lang('Questions'));
2802
        $table->set_header(4, get_lang('Author'));
2803
        $table->set_header(5, get_lang('Available from'));
2804
        $table->set_header(6, get_lang('Until'));
2805
        $table->set_header(7, get_lang('Invite'));
2806
        $table->set_header(8, get_lang('Anonymous'));
2807
2808
        if ('true' === api_get_setting('survey.allow_mandatory_survey')) {
2809
            $table->set_header(9, get_lang('Mandatory?'));
2810
            $table->set_header(10, get_lang('Edit'), false, 'width="150"');
2811
            $table->set_column_filter(9, 'anonymous_filter');
2812
            $table->set_column_filter(10, 'modify_filter_drh');
2813
        } else {
2814
            $table->set_header(9, get_lang('Edit'), false, 'width="150"');
2815
            $table->set_column_filter(9, 'modify_filter_drh');
2816
        }
2817
2818
        $table->set_column_filter(8, 'anonymous_filter');
2819
        $table->display();
2820
    }
2821
2822
    /**
2823
     * This function displays the sortable table with all the surveys.
2824
     *
2825
     * @author Patrick Cool <[email protected]>, Ghent University
2826
     *
2827
     * @version January 2007
2828
     */
2829
    public static function display_survey_list()
2830
    {
2831
        $parameters = [];
2832
        $parameters['cid'] = api_get_course_int_id();
2833
        $parameters['sid'] = api_get_session_id();
2834
        $parameters['gid'] = api_get_group_id();
2835
        if (isset($_GET['do_search']) && $_GET['do_search']) {
2836
            $message = get_lang('Display search results').'<br />';
2837
            $message .= '<a href="'.api_get_self().'?'.api_get_cidreq().'">'.get_lang('Display all').'</a>';
2838
            echo Display::return_message($message, 'normal', false);
2839
        }
2840
2841
        // Create a sortable table with survey-data
2842
        $table = new SortableTable(
2843
            'surveys',
2844
            'get_number_of_surveys',
2845
            'get_survey_data',
2846
            2
2847
        );
2848
        $table->set_additional_parameters($parameters);
2849
        $table->set_header(0, '', false);
2850
        $table->set_header(1, get_lang('Survey name'));
2851
        $table->set_header(2, get_lang('Survey code'));
2852
        $table->set_header(3, get_lang('Questions'));
2853
        $table->set_header(4, get_lang('Author'));
2854
        $table->set_header(5, get_lang('Available from'));
2855
        $table->set_header(6, get_lang('Until'));
2856
        $table->set_header(7, get_lang('Invite'));
2857
        $table->set_header(8, get_lang('Anonymous'));
2858
2859
        if ('true' === api_get_setting('survey.allow_mandatory_survey')) {
2860
            $table->set_header(9, get_lang('Mandatory?'));
2861
            $table->set_header(10, get_lang('Edit'), false, 'width="150"');
2862
            $table->set_column_filter(8, 'anonymous_filter');
2863
            $table->set_column_filter(10, 'modify_filter');
2864
        } else {
2865
            $table->set_header(9, get_lang('Edit'), false, 'width="150"');
2866
            $table->set_column_filter(9, 'modify_filter');
2867
        }
2868
2869
        $table->set_column_filter(8, 'anonymous_filter');
2870
        $actions = [
2871
            'export_all' => get_lang('ExportResults'),
2872
            'export_by_class' => get_lang('ExportByClass'),
2873
            'send_to_tutors' => get_lang('SendToGroupTutors'),
2874
            'multiplicate' => get_lang('MultiplicateQuestions'),
2875
            'delete' => get_lang('DeleteSurvey'),
2876
        ];
2877
        $table->set_form_actions($actions);
2878
        $table->display();
2879
    }
2880
2881
    /**
2882
     * Survey list for coach.
2883
     */
2884
    public static function display_survey_list_for_coach()
2885
    {
2886
        $parameters = [];
2887
        $parameters['cidReq'] = api_get_course_id();
2888
        if (isset($_GET['do_search'])) {
2889
            $message = get_lang('Display search results').'<br />';
2890
            $message .= '<a href="'.api_get_self().'?'.api_get_cidreq().'">'.get_lang('Display all').'</a>';
2891
            echo Display::return_message($message, 'normal', false);
2892
        }
2893
2894
        // Create a sortable table with survey-data
2895
        $table = new SortableTable(
2896
            'surveys_coach',
2897
            'get_number_of_surveys_for_coach',
2898
            'get_survey_data_for_coach',
2899
            2
2900
        );
2901
        $table->set_additional_parameters($parameters);
2902
        $table->set_header(0, '', false);
2903
        $table->set_header(1, get_lang('Survey name'));
2904
        $table->set_header(2, get_lang('Survey code'));
2905
        $table->set_header(3, get_lang('Questions'));
2906
        $table->set_header(4, get_lang('Author'));
2907
        $table->set_header(5, get_lang('Available from'));
2908
        $table->set_header(6, get_lang('Until'));
2909
        $table->set_header(7, get_lang('Invite'));
2910
        $table->set_header(8, get_lang('Anonymous'));
2911
2912
        if ('true' === api_get_setting('survey.allow_mandatory_survey')) {
2913
            $table->set_header(9, get_lang('Edit'), false, 'width="130"');
2914
            $table->set_header(10, get_lang('Edit'), false, 'width="130"');
2915
            $table->set_column_filter(8, 'anonymous_filter');
2916
            $table->set_column_filter(10, 'modify_filter_for_coach');
2917
        } else {
2918
            $table->set_header(9, get_lang('Edit'), false, 'width="130"');
2919
            $table->set_column_filter(9, 'modify_filter_for_coach');
2920
        }
2921
2922
        $table->set_column_filter(8, 'anonymous_filter');
2923
        $table->display();
2924
    }
2925
2926
    /**
2927
     * Check if the hide_survey_edition configurations setting is enabled.
2928
     *
2929
     * @param string $surveyCode
2930
     *
2931
     * @return bool
2932
     */
2933
    public static function checkHideEditionToolsByCode($surveyCode)
2934
    {
2935
        $hideSurveyEdition = api_get_setting('survey.hide_survey_edition', true);
2936
2937
        if (empty($hideSurveyEdition) || 'false' === $hideSurveyEdition) {
2938
            return false;
2939
        }
2940
2941
        if (is_array($hideSurveyEdition)) {
2942
            if ('*' === $hideSurveyEdition['codes']) {
2943
                return true;
2944
            }
2945
2946
            if (in_array($surveyCode, $hideSurveyEdition['codes'])) {
2947
                return true;
2948
            }
2949
        }
2950
2951
        return false;
2952
    }
2953
2954
    /**
2955
     * This function changes the modify column of the sortable table.
2956
     *
2957
     * @param int  $survey_id the id of the survey
2958
     * @param bool $drh
2959
     *
2960
     * @return string html code that are the actions that can be performed on any survey
2961
     *
2962
     * @author Patrick Cool <[email protected]>, Ghent University
2963
     *
2964
     * @version January 2007
2965
     */
2966
    public static function modify_filter($survey_id, $url_params, $row)
2967
    {
2968
        $repo = Container::getSurveyRepository();
2969
        /** @var CSurvey|null $survey */
2970
        $survey = $repo->find($survey_id);
2971
2972
        if (null === $survey) {
2973
            return '';
2974
        }
2975
2976
        $hideSurveyEdition = self::checkHideEditionToolsByCode($survey->getCode());
2977
2978
        if ($hideSurveyEdition) {
2979
            return '';
2980
        }
2981
2982
        $survey_id = $survey->getIid();
2983
        $actions = [];
2984
        $hideReportingButton = ('true' === api_get_setting('survey.hide_survey_reporting_button'));
2985
        $codePath = api_get_path(WEB_CODE_PATH);
2986
        $params = [];
2987
        parse_str(api_get_cidreq(), $params);
2988
2989
        $reportingLink = Display::url(
2990
            Display::getMdiIcon(ToolIcon::TRACKING, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Reporting')),
2991
            $codePath.'survey/reporting.php?'.http_build_query($params + ['survey_id' => $survey_id])
2992
        );
2993
2994
        if (!$row['can_edit']) {
2995
            return $hideReportingButton ? '-' : $reportingLink;
2996
        }
2997
2998
        $type = $survey->getSurveyType();
2999
3000
        // Coach can see that only if the survey is in his session
3001
        if (api_is_allowed_to_edit() || api_is_element_in_the_session(TOOL_SURVEY, $survey_id)) {
3002
            $configUrl = $codePath.'survey/create_new_survey.php?'.
3003
                http_build_query($params + ['action' => 'edit', 'survey_id' => $survey_id]);
3004
            $editUrl = $codePath.'survey/survey.php?'.
3005
                http_build_query($params + ['survey_id' => $survey_id]);
3006
            if (3 == $type) {
3007
                $configUrl = $codePath.'survey/edit_meeting.php?'.
3008
                    http_build_query($params + ['action' => 'edit', 'survey_id' => $survey_id]);
3009
            }
3010
3011
            $actions[] = Display::url(
3012
                Display::getMdiIcon(ActionIcon::EDIT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Edit')),
3013
                $editUrl
3014
            );
3015
            $actions[] = Display::url(
3016
                Display::getMdiIcon(ToolIcon::SETTINGS, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Configure')),
3017
                $configUrl
3018
            );
3019
3020
            if (SurveyManager::survey_generation_hash_available()) {
3021
                $actions[] = Display::url(
3022
                    Display::getMdiIcon(ToolIcon::LINK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Generate survey access link')),
3023
                    $codePath.'survey/generate_link.php?'.http_build_query($params + ['survey_id' => $survey_id])
3024
                );
3025
            }
3026
3027
            if (3 != $type) {
3028
                $actions[] = Display::url(
3029
                    Display::getMdiIcon(ActionIcon::COPY_CONTENT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Copy survey')),
3030
                    $codePath.'survey/copy_survey.php?'.http_build_query($params + ['survey_id' => $survey_id])
3031
                );
3032
3033
                $actions[] = Display::url(
3034
                    Display::getMdiIcon(ObjectIcon::MULTI_ELEMENT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Duplicate survey')),
3035
                    $codePath.'survey/survey_list.php?'
3036
                    .http_build_query($params + ['action' => 'copy_survey', 'survey_id' => $survey_id])
3037
                );
3038
3039
                $actions[] = Display::url(
3040
                    Display::getMdiIcon('view-grid-plus-outline', 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Multiplicate questions')),
3041
                    $codePath.'survey/survey_list.php?'
3042
                    .http_build_query($params + ['action' => 'multiplicate', 'survey_id' => $survey_id])
3043
                );
3044
3045
                $actions[] = Display::url(
3046
                    Display::getMdiIcon('view-grid-plus-outline', 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, get_lang('RemoveMultiplicate questions')),
3047
                    $codePath.'survey/survey_list.php?'
3048
                    .http_build_query($params + ['action' => 'remove_multiplicate', 'survey_id' => $survey_id])
3049
                );
3050
3051
                $warning = addslashes(api_htmlentities(get_lang('Empty survey').'?', ENT_QUOTES));
3052
                $actions[] = Display::url(
3053
                    Display::getMdiIcon(ActionIcon::RESET, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Empty survey')),
3054
                    $codePath.'survey/survey_list.php?'
3055
                    .http_build_query($params + ['action' => 'empty', 'survey_id' => $survey_id]),
3056
                    [
3057
                        'onclick' => "javascript: if (!confirm('".$warning."')) return false;",
3058
                    ]
3059
                );
3060
            }
3061
        }
3062
3063
        if (3 != $type) {
3064
            $actions[] = Display::url(
3065
                Display::getMdiIcon(ActionIcon::PREVIEW_CONTENT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Preview')),
3066
                $codePath.'survey/preview.php?'.http_build_query($params + ['survey_id' => $survey_id])
3067
            );
3068
        }
3069
3070
        $actions[] = Display::url(
3071
            Display::getMdiIcon(StateIcon::MAIL_NOTIFICATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Publish')),
3072
            $codePath.'survey/survey_invite.php?'.http_build_query($params + ['survey_id' => $survey_id])
3073
        );
3074
3075
        $extraFieldValue = new ExtraFieldValue('survey');
3076
        $groupData = $extraFieldValue->get_values_by_handler_and_field_variable($survey_id, 'group_id');
3077
        if ($groupData && !empty($groupData['value'])) {
3078
            $actions[] = Display::url(
3079
                Display::getMdiIcon(ObjectIcon::TEACHER, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('SendToGroupTutors')),
3080
                $codePath.'survey/survey_list.php?action=send_to_tutors&'.http_build_query($params + ['survey_id' => $survey_id])
3081
            );
3082
        }
3083
3084
        if (3 != $type) {
3085
            $actions[] = $hideReportingButton ? null : $reportingLink;
3086
        }
3087
3088
        if (api_is_allowed_to_edit() ||
3089
            api_is_element_in_the_session(TOOL_SURVEY, $survey_id)
3090
        ) {
3091
            $actions[] = self::getAdditionalTeacherActions($survey_id);
3092
3093
            $warning = addslashes(api_htmlentities(get_lang('Delete survey').'?', ENT_QUOTES));
3094
            $actions[] = Display::url(
3095
                Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')),
3096
                $codePath.'survey/survey_list.php?'
3097
                .http_build_query($params + ['action' => 'delete', 'survey_id' => $survey_id]),
3098
                [
3099
                    'onclick' => "javascript: if (!confirm('".$warning."')) return false;",
3100
                ]
3101
            );
3102
        }
3103
3104
        return implode(PHP_EOL, $actions);
3105
    }
3106
3107
    /**
3108
     * Get the additional actions added in survey_additional_teacher_modify_actions configuration.
3109
     *
3110
     * @param int $surveyId
3111
     * @param int $iconSize
3112
     *
3113
     * @return string
3114
     */
3115
    public static function getAdditionalTeacherActions($surveyId, $iconSize = ICON_SIZE_SMALL)
3116
    {
3117
        $additionalActions = api_get_setting('survey.survey_additional_teacher_modify_actions', true) ?: [];
3118
3119
        if (empty($additionalActions) || ('false' === $additionalActions)) {
3120
            return '';
3121
        }
3122
3123
        $action = '';
3124
        if (is_array($additionalActions)) {
3125
            $actions = [];
3126
            foreach ($additionalActions as $additionalAction) {
3127
                $actions[] = call_user_func(
3128
                    $additionalAction,
3129
                    ['survey_id' => $surveyId, 'icon_size' => $iconSize]
3130
                );
3131
            }
3132
            $action = implode(PHP_EOL, $actions);
3133
        }
3134
3135
3136
        return $action;
3137
    }
3138
3139
    /**
3140
     * @param int $survey_id
3141
     *
3142
     * @return string
3143
     */
3144
    public static function modify_filter_for_coach($survey_id)
3145
    {
3146
        $survey_id = (int) $survey_id;
3147
        $actions = [];
3148
        $codePath = api_get_path(WEB_CODE_PATH);
3149
        $params = [];
3150
        parse_str(api_get_cidreq(), $params);
3151
        $actions[] = Display::url(
3152
            Display::getMdiIcon(ActionIcon::PREVIEW_CONTENT, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Preview')),
3153
            $codePath.'survey/preview.php?'.http_build_query($params + ['survey_id' => $survey_id])
3154
        );
3155
        $actions[] = Display::url(
3156
            Display::getMdiIcon(StateIcon::MAIL_NOTIFICATION, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Publish')),
3157
            $codePath.'survey/survey_invite.php?'.http_build_query($params + ['survey_id' => $survey_id])
3158
        );
3159
        $warning = addslashes(api_htmlentities(get_lang('Empty survey').'?', ENT_QUOTES));
3160
        $actions[] = Display::url(
3161
            Display::getMdiIcon(ActionIcon::RESET, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Empty survey')),
3162
            $codePath.'survey/survey_list.php?'
3163
                .http_build_query($params + ['action' => 'empty', 'survey_id' => $survey_id]),
3164
            [
3165
                'onclick' => "javascript: if(!confirm('".$warning."')) return false;",
3166
            ]
3167
        );
3168
3169
        return implode(PHP_EOL, $actions);
3170
    }
3171
3172
    /**
3173
     * Returns "yes" when given parameter is one, "no" for any other value.
3174
     *
3175
     * @param int Whether anonymous or not
3176
     *
3177
     * @return string "Yes" or "No" in the current language
3178
     */
3179
    public static function anonymous_filter($anonymous)
3180
    {
3181
        if (1 == $anonymous) {
3182
            return get_lang('Yes');
3183
        }
3184
3185
        return get_lang('No');
3186
    }
3187
3188
    public static function survey_search_restriction()
3189
    {
3190
        if (isset($_GET['do_search'])) {
3191
            if ('' != $_GET['keyword_title']) {
3192
                $search_term[] = 'title like "%" \''.Database::escape_string($_GET['keyword_title']).'\' "%"';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$search_term was never initialized. Although not strictly required by PHP, it is generally a good practice to add $search_term = array(); before regardless.
Loading history...
3193
            }
3194
            if ('' != $_GET['keyword_code']) {
3195
                $search_term[] = 'code =\''.Database::escape_string($_GET['keyword_code']).'\'';
3196
            }
3197
            if ('%' != $_GET['keyword_language']) {
3198
                $search_term[] = 'lang =\''.Database::escape_string($_GET['keyword_language']).'\'';
3199
            }
3200
            $my_search_term = (null == $search_term) ? [] : $search_term;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $search_term does not seem to be defined for all execution paths leading up to this point.
Loading history...
3201
            $search_restriction = implode(' AND ', $my_search_term);
3202
3203
            return $search_restriction;
3204
        } else {
3205
            return false;
3206
        }
3207
    }
3208
3209
    public static function get_number_of_surveys()
3210
    {
3211
        $repo = Container::getSurveyRepository();
3212
        $course = api_get_course_entity();
3213
        $session = api_get_session_entity();
3214
        $qb = $repo->findAllByCourse($course, $session);
3215
3216
        return $repo->getCount($qb);
3217
3218
        /*$table_survey = Database::get_course_table(TABLE_SURVEY);
3219
        $course_id = api_get_course_int_id();
3220
3221
        $search_restriction = self::survey_search_restriction();
3222
        if ($search_restriction) {
3223
            $search_restriction = 'WHERE c_id = '.$course_id.' AND '.$search_restriction;
3224
        } else {
3225
            $search_restriction = "WHERE c_id = $course_id";
3226
        }
3227
        $sql = "SELECT count(iid) AS total_number_of_items
3228
                FROM $table_survey $search_restriction";
3229
        $res = Database::query($sql);
3230
        $obj = Database::fetch_object($res);
3231
3232
        return $obj->total_number_of_items;*/
3233
    }
3234
3235
    /**
3236
     * @return int
3237
     */
3238
    public static function get_number_of_surveys_for_coach()
3239
    {
3240
        $repo = Container::getSurveyRepository();
3241
        $course = api_get_course_entity();
3242
        $session = api_get_session_entity();
3243
        $qb = $repo->findAllByCourse($course, $session);
3244
3245
        return $repo->getCount($qb);
3246
3247
        /*$survey_tree = new SurveyTree();
3248
        return count($survey_tree->surveylist);*/
3249
    }
3250
3251
    /**
3252
     * This function gets all the survey data that is to be displayed in the sortable table.
3253
     *
3254
     * @param int    $from
3255
     * @param int    $number_of_items
3256
     * @param int    $column
3257
     * @param string $direction
3258
     * @param bool   $isDrh
3259
     *
3260
     * @return array
3261
     *
3262
     * @author Patrick Cool <[email protected]>, Ghent University
3263
     * @author Julio Montoya <[email protected]>, Beeznest - Adding intvals
3264
     *
3265
     * @version January 2007
3266
     */
3267
    public static function get_survey_data(
3268
        int    $from,
3269
        int    $number_of_items,
3270
        int    $column,
3271
        string $direction,
3272
        bool $isDrh = false
3273
    ): array {
3274
        $repo = Container::getSurveyRepository();
3275
        $course = api_get_course_entity();
3276
        $sessionId = api_get_session_id();
3277
        $session = api_get_session_entity();
3278
3279
        $qb = $repo->findAllByCourse($course, $session);
3280
        /** @var CSurvey[] $surveys */
3281
        $surveys = $qb->getQuery()->getResult();
3282
3283
        $mandatoryAllowed = ('true' === api_get_setting('survey.allow_mandatory_survey'));
3284
3285
        $from = (int) $from;
3286
        $number_of_items = (int) $number_of_items;
3287
        $column = (int) $column;
3288
        if (!in_array(strtolower($direction), ['asc', 'desc'])) {
3289
            $direction = 'asc';
3290
        }
3291
3292
        $efv = new ExtraFieldValue('survey');
3293
        $list = [];
3294
3295
        if ($sessionId > 0) {
3296
            foreach ($surveys as $survey) {
3297
                $surveySession = $survey->getFirstResourceLink()->getSession();
3298
                if ($surveySession && $surveySession->getId() == $sessionId) {
3299
                    $list[] = self::prepare_survey_array($survey, $efv, $mandatoryAllowed, $isDrh);
3300
                }
3301
            }
3302
3303
            $lpRepo = Container::getLpRepository();
3304
            $lpsInSessionQb = $lpRepo->findAllByCourse($course, $session);
3305
            $lpsInSession = $lpsInSessionQb->getQuery()->getResult();
3306
3307
            foreach ($lpsInSession as $lpInSession) {
3308
                $lpInSessionSession = $lpInSession->getFirstResourceLink()->getSession();
3309
3310
                if ($lpInSessionSession && $lpInSessionSession->getId() == $sessionId) {
3311
                    foreach ($lpInSession->getItems() as $sessionItem) {
3312
                        if ($sessionItem->getItemType() === 'survey') {
3313
                            $itemId = (int) $sessionItem->getPath();
3314
                            $survey = $repo->find($itemId);
3315
                            if ($survey && $survey->getFirstResourceLink()->getSession() == null) {
3316
                                $list[] = self::prepare_survey_array($survey, $efv, $mandatoryAllowed, $isDrh, false);
3317
                            }
3318
                        }
3319
                    }
3320
                }
3321
            }
3322
        } else {
3323
            foreach ($surveys as $survey) {
3324
                $list[] = self::prepare_survey_array($survey, $efv, $mandatoryAllowed, $isDrh);
3325
            }
3326
        }
3327
3328
        return $list;
3329
    }
3330
3331
3332
    private static function prepare_survey_array($survey, $efv, $mandatoryAllowed, $isDrh, $canEdit = true) {
3333
        $array = [];
3334
        $surveyId = $survey->getIid();
3335
        $array[0] = $surveyId;
3336
        $type = $survey->getSurveyType();
3337
3338
        $title = $survey->getTitle();
3339
        if (self::checkHideEditionToolsByCode($survey->getCode())) {
3340
            $array[1] = $title;
3341
        } else {
3342
            // Doodle
3343
            if (3 == $type) {
3344
                $array[1] = Display::url(
3345
                    $title,
3346
                    api_get_path(WEB_CODE_PATH).'survey/meeting.php?survey_id='.$surveyId.'&'.api_get_cidreq()
3347
                );
3348
            } else {
3349
                $array[1] = Display::url(
3350
                    $title,
3351
                    api_get_path(WEB_CODE_PATH).'survey/survey.php?survey_id='.$surveyId.'&'.api_get_cidreq()
3352
                );
3353
            }
3354
        }
3355
3356
        $array[2] = $survey->getCode();
3357
        $array[3] = $survey->getQuestions()->count();
3358
        $array[4] = UserManager::formatUserFullName($survey->getCreator());
3359
3360
        $array[5] = '';
3361
        $from = $survey->getAvailFrom() ? $survey->getAvailFrom()->format('Y-m-d H:i:s') : null;
3362
        if (null !== $from) {
3363
            $array[5] = api_convert_and_format_date(
3364
                $from,
3365
                DATE_TIME_FORMAT_LONG
3366
            );
3367
        }
3368
3369
        $array[6] = '';
3370
        $till = $survey->getAvailTill() ? $survey->getAvailTill()->format('Y-m-d H:i:s') : null;
3371
        if (null !== $till) {
3372
            $array[6] = api_convert_and_format_date(
3373
                $till,
3374
                DATE_TIME_FORMAT_LONG
3375
            );
3376
        }
3377
3378
        $array[7] = Display::url(
3379
                $survey->getAnswered(),
3380
                api_get_path(WEB_CODE_PATH).'survey/survey_invitation.php?view=answered&survey_id='.$surveyId.'&'.api_get_cidreq()
3381
            ).' / '.
3382
            Display::url(
3383
                $survey->getInvited(),
3384
                api_get_path(WEB_CODE_PATH).'survey/survey_invitation.php?view=invited&survey_id='.$surveyId.'&'.api_get_cidreq()
3385
            );
3386
3387
        $array[8] = $survey->getAnonymous();
3388
        if ($mandatoryAllowed) {
3389
            $efvMandatory = $efv->get_values_by_handler_and_field_variable(
3390
                $surveyId,
3391
                'is_mandatory'
3392
            );
3393
3394
            $array[9] = $efvMandatory ? $efvMandatory['value'] : 0;
3395
            $array[10] = $surveyId;
3396
        } else {
3397
            $array[9] = $surveyId;
3398
        }
3399
3400
        if ($isDrh) {
3401
            $array[1] = $title;
3402
            $array[7] = strip_tags($survey->getInvited());
3403
        }
3404
3405
        $array['can_edit'] = $canEdit;
3406
3407
        return $array;
3408
    }
3409
3410
    /**
3411
     * @param $from
3412
     * @param $number_of_items
3413
     * @param $column
3414
     * @param $direction
3415
     *
3416
     * @return array
3417
     */
3418
    public static function get_survey_data_for_coach($from, $number_of_items, $column, $direction)
3419
    {
3420
        $mandatoryAllowed = ('true' === api_get_setting('survey.allow_mandatory_survey'));
3421
        $repo = Container::getSurveyRepository();
3422
        $qb = $repo->findAllByCourse(
3423
            api_get_course_entity(),
3424
            api_get_session_entity(),
3425
            null,
3426
            null,
3427
            api_get_user_entity()
3428
        );
3429
3430
        /** @var CSurvey[] $surveys */
3431
        $surveys = $qb->getQuery()->getResult();
3432
        $list = [];
3433
        foreach ($surveys as $survey) {
3434
            $list[] = $survey->getIid();
3435
        }
3436
        $list_condition = '';
3437
        if (count($list) > 0) {
3438
            $list_condition = " AND survey.iid IN (".implode(',', $list).") ";
3439
        }
3440
        $from = (int) $from;
3441
        $number_of_items = (int) $number_of_items;
3442
        $column = (int) $column;
3443
        if (!in_array(strtolower($direction), ['asc', 'desc'])) {
3444
            $direction = 'asc';
3445
        }
3446
3447
        $table_survey = Database::get_course_table(TABLE_SURVEY);
3448
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
3449
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3450
        $course_id = api_get_course_int_id();
3451
        $efv = new ExtraFieldValue('survey');
3452
3453
        $sql = "
3454
            SELECT
3455
            survey.iid AS col0,
3456
                survey.title AS col1,
3457
                survey.code AS col2,
3458
                count(survey_question.iid) AS col3,
3459
        "
3460
            .(api_is_western_name_order()
3461
                ? "CONCAT(user.firstname, ' ', user.lastname)"
3462
                : "CONCAT(user.lastname, ' ', user.firstname)")
3463
            ."	AS col4,
3464
                survey.avail_from AS col5,
3465
                survey.avail_till AS col6,
3466
                CONCAT('<a href=\"survey_invitation.php?view=answered&survey_id=',survey.iid,'\">',survey.answered,'</a> / <a href=\"survey_invitation.php?view=invited&survey_id=',survey.iid,'\">',survey.invited, '</a>') AS col7,
3467
                survey.anonymous AS col8,
3468
                survey.iid AS col9
3469
            FROM $table_survey survey
3470
            LEFT JOIN $table_survey_question survey_question
3471
            ON (survey.iid = survey_question.survey_id),
3472
            $table_user user
3473
            WHERE survey.author = user.id AND survey.c_id = $course_id $list_condition
3474
        ";
3475
        $sql .= ' GROUP BY survey.iid';
3476
        $sql .= " ORDER BY col$column $direction ";
3477
        $sql .= " LIMIT $from,$number_of_items";
3478
3479
        $res = Database::query($sql);
3480
        $surveys = [];
3481
        while ($survey = Database::fetch_array($res)) {
3482
            $survey['col5'] = api_convert_and_format_date(
3483
                $survey['col5'],
3484
                DATE_TIME_FORMAT_LONG
3485
            );
3486
            $survey['col6'] = api_convert_and_format_date(
3487
                $survey['col6'],
3488
                DATE_TIME_FORMAT_LONG
3489
            );
3490
3491
            if ($mandatoryAllowed) {
3492
                $survey['col10'] = $survey['col9'];
3493
                $efvMandatory = $efv->get_values_by_handler_and_field_variable(
3494
                    $survey['col9'],
3495
                    'is_mandatory'
3496
                );
3497
                $survey['col9'] = $efvMandatory['value'];
3498
            }
3499
            $surveys[] = $survey;
3500
        }
3501
3502
        return $surveys;
3503
    }
3504
3505
    /**
3506
     * Display all the active surveys for the given course user.
3507
     *
3508
     * @param int $user_id
3509
     *
3510
     * @author Patrick Cool <[email protected]>, Ghent University
3511
     *
3512
     * @version April 2007
3513
     */
3514
    public static function getSurveyList($user_id)
3515
    {
3516
        $course = api_get_course_entity();
3517
        $_course = api_get_course_info();
3518
        $course_id = $_course['real_id'];
3519
        $user_id = (int) $user_id;
3520
        $sessionId = api_get_session_id();
3521
        $mandatoryAllowed = ('true' === api_get_setting('survey.allow_mandatory_survey'));
3522
3523
        // Database table definitions
3524
        $table_survey_invitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
3525
        $table_survey = Database::get_course_table(TABLE_SURVEY);
3526
3527
        echo '<table id="list-survey" class="table">';
3528
        echo '<thead>';
3529
        echo '<tr>';
3530
        echo '	<th>'.get_lang('Survey name').'</th>';
3531
        echo '	<th class="text-center">'.get_lang('Anonymous').'</th>';
3532
        if ($mandatoryAllowed) {
3533
            echo '<th class="text-center">'.get_lang('Mandatory?').'</th>';
3534
        }
3535
        echo '</tr>';
3536
        echo '</thead>';
3537
        echo '<tbody>';
3538
3539
        /** @var \DateTime $now */
3540
        $now = api_get_utc_datetime(null, false, true);
3541
        $filterDate = $now->format('Y-m-d H:i');
3542
        $sessionCondition = api_get_session_condition($sessionId, true, false, 'survey_invitation.session_id');
3543
3544
        $sql = "SELECT
3545
                    survey_invitation.answered,
3546
                    survey_invitation.invitation_code,
3547
                    survey_invitation.session_id,
3548
                    survey.title,
3549
                    survey.visible_results,
3550
                    survey.iid,
3551
                    survey.anonymous
3552
                FROM $table_survey survey
3553
                INNER JOIN
3554
                $table_survey_invitation survey_invitation
3555
                ON (
3556
                    survey.iid = survey_invitation.survey_id
3557
                )
3558
				WHERE
3559
                    survey_invitation.user_id = $user_id AND
3560
                    survey.avail_from <= '$filterDate' AND
3561
                    survey.avail_till >= '$filterDate' AND
3562
                    survey_invitation.c_id = $course_id
3563
                    $sessionCondition
3564
				";
3565
        $result = Database::query($sql);
3566
        $efv = new ExtraFieldValue('survey');
3567
        $surveyIds = [];
3568
        $repo = Container::getSurveyRepository();
3569
        while ($row = Database::fetch_assoc($result)) {
3570
            $surveyId = $row['iid'];
3571
            if (in_array($surveyId, $surveyIds)) {
3572
                continue;
3573
            }
3574
3575
            /** @var CSurvey $survey */
3576
            $survey = $repo->find($surveyId);
3577
3578
            echo '<tr>';
3579
            if (0 == $survey->getAnswered()) {
3580
                echo '<td>';
3581
                $url = self::generateFillSurveyLink($survey, $row['invitation_code'], $course, $row['session_id']);
3582
                $icon = Display::getMdiIcon(ToolIcon::SURVEY, 'ch-tool-icon', null, ICON_SIZE_TINY, get_lang('ClickHereToAnswerTheSurvey'));
3583
                echo '<a href="'.$url.'">
3584
                    '.$icon
3585
                    .$row['title']
3586
                    .'</a></td>';
3587
            } else {
3588
                $isDrhOfCourse = CourseManager::isUserSubscribedInCourseAsDrh(
3589
                    $user_id,
3590
                    $_course
3591
                );
3592
                $icon = Display::getMdiIcon(ObjectIcon::SURVEY, 'ch-tool-icon-disabled', null, ICON_SIZE_TINY, get_lang('SurveysDone'));
3593
                $showLink = (!api_is_allowed_to_edit(false, true) || $isDrhOfCourse)
3594
                    && SURVEY_VISIBLE_TUTOR != $row['visible_results'];
3595
3596
                echo '<td>';
3597
                echo $showLink
3598
                    ? Display::url(
3599
                        $icon.PHP_EOL.$row['title'],
3600
                        api_get_path(WEB_CODE_PATH).'survey/reporting.php?'.api_get_cidreq().'&'.http_build_query([
3601
                            'action' => 'questionreport',
3602
                            'survey_id' => $surveyId,
3603
                        ])
3604
                    )
3605
                    : $icon.PHP_EOL.$row['title'];
3606
                echo '</td>';
3607
            }
3608
            echo '<td class="text-center">';
3609
            echo 1 == $row['anonymous'] ? get_lang('Yes') : get_lang('No');
3610
            echo '</td>';
3611
            if ($mandatoryAllowed) {
3612
                $efvMandatory = $efv->get_values_by_handler_and_field_variable(
3613
                    $row['survey_id'],
3614
                    'is_mandatory'
3615
                );
3616
                echo '<td class="text-center">'.($efvMandatory['value'] ? get_lang('Yes') : get_lang('No')).'</td>';
3617
            }
3618
            echo '</tr>';
3619
            $surveyIds[] = $surveyId;
3620
        }
3621
        echo '</tbody>';
3622
        echo '</table>';
3623
    }
3624
3625
    /**
3626
     * Creates a multi array with the user fields that we can show.
3627
     * We look the visibility with the api_get_setting function
3628
     * The username is always NOT able to change it.
3629
     *
3630
     * @author Julio Montoya Armas <[email protected]>, Chamilo: Personality Test modification
3631
     *
3632
     * @return array array[value_name][name], array[value_name][visibilty]
3633
     */
3634
    public static function make_field_list()
3635
    {
3636
        //	LAST NAME and FIRST NAME
3637
        $field_list_array = [];
3638
        $field_list_array['lastname']['name'] = get_lang('Last name');
3639
        $field_list_array['firstname']['name'] = get_lang('First name');
3640
3641
        if ('true' != api_get_setting('profile', 'name')) {
3642
            $field_list_array['firstname']['visibility'] = 0;
3643
            $field_list_array['lastname']['visibility'] = 0;
3644
        } else {
3645
            $field_list_array['firstname']['visibility'] = 1;
3646
            $field_list_array['lastname']['visibility'] = 1;
3647
        }
3648
3649
        $field_list_array['username']['name'] = get_lang('Username');
3650
        $field_list_array['username']['visibility'] = 0;
3651
3652
        //	OFFICIAL CODE
3653
        $field_list_array['official_code']['name'] = get_lang('OfficialCourse code');
3654
3655
        if ('true' != api_get_setting('profile', 'officialcode')) {
3656
            $field_list_array['official_code']['visibility'] = 1;
3657
        } else {
3658
            $field_list_array['official_code']['visibility'] = 0;
3659
        }
3660
3661
        // EMAIL
3662
        $field_list_array['email']['name'] = get_lang('e-mail');
3663
        if ('true' != api_get_setting('profile', 'email')) {
3664
            $field_list_array['email']['visibility'] = 1;
3665
        } else {
3666
            $field_list_array['email']['visibility'] = 0;
3667
        }
3668
3669
        // PHONE
3670
        $field_list_array['phone']['name'] = get_lang('Phone');
3671
        if ('true' != api_get_setting('profile', 'phone')) {
3672
            $field_list_array['phone']['visibility'] = 0;
3673
        } else {
3674
            $field_list_array['phone']['visibility'] = 1;
3675
        }
3676
        //	LANGUAGE
3677
        $field_list_array['language']['name'] = get_lang('Language');
3678
        if ('true' != api_get_setting('profile', 'language')) {
3679
            $field_list_array['language']['visibility'] = 0;
3680
        } else {
3681
            $field_list_array['language']['visibility'] = 1;
3682
        }
3683
3684
        // EXTRA FIELDS
3685
        $extra = UserManager::get_extra_fields(0, 50, 5, 'ASC');
3686
3687
        foreach ($extra as $id => $field_details) {
3688
            if (0 == $field_details[6]) {
3689
                continue;
3690
            }
3691
            switch ($field_details[2]) {
3692
                case UserManager::USER_FIELD_TYPE_TEXT:
3693
                    $field_list_array['extra_'.$field_details[1]]['name'] = $field_details[3];
3694
                    if (0 == $field_details[7]) {
3695
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 0;
3696
                    } else {
3697
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 1;
3698
                    }
3699
                    break;
3700
                case UserManager::USER_FIELD_TYPE_TEXTAREA:
3701
                    $field_list_array['extra_'.$field_details[1]]['name'] = $field_details[3];
3702
                    if (0 == $field_details[7]) {
3703
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 0;
3704
                    } else {
3705
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 1;
3706
                    }
3707
                    break;
3708
                case UserManager::USER_FIELD_TYPE_RADIO:
3709
                    $field_list_array['extra_'.$field_details[1]]['name'] = $field_details[3];
3710
                    if (0 == $field_details[7]) {
3711
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 0;
3712
                    } else {
3713
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 1;
3714
                    }
3715
                    break;
3716
                case UserManager::USER_FIELD_TYPE_SELECT:
3717
                    $get_lang_variables = false;
3718
                    if (in_array(
3719
                        $field_details[1],
3720
                        ['mail_notify_message', 'mail_notify_invitation', 'mail_notify_group_message']
3721
                    )
3722
                    ) {
3723
                        $get_lang_variables = true;
3724
                    }
3725
3726
                    if ($get_lang_variables) {
3727
                        $field_list_array['extra_'.$field_details[1]]['name'] = get_lang($field_details[3]);
3728
                    } else {
3729
                        $field_list_array['extra_'.$field_details[1]]['name'] = $field_details[3];
3730
                    }
3731
3732
                    if (0 == $field_details[7]) {
3733
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 0;
3734
                    } else {
3735
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 1;
3736
                    }
3737
                    break;
3738
                case UserManager::USER_FIELD_TYPE_SELECT_MULTIPLE:
3739
                    $field_list_array['extra_'.$field_details[1]]['name'] = $field_details[3];
3740
                    if (0 == $field_details[7]) {
3741
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 0;
3742
                    } else {
3743
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 1;
3744
                    }
3745
                    break;
3746
                case UserManager::USER_FIELD_TYPE_DATE:
3747
                    $field_list_array['extra_'.$field_details[1]]['name'] = $field_details[3];
3748
                    if (0 == $field_details[7]) {
3749
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 0;
3750
                    } else {
3751
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 1;
3752
                    }
3753
                    break;
3754
                case UserManager::USER_FIELD_TYPE_DATETIME:
3755
                    $field_list_array['extra_'.$field_details[1]]['name'] = $field_details[3];
3756
                    if (0 == $field_details[7]) {
3757
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 0;
3758
                    } else {
3759
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 1;
3760
                    }
3761
                    break;
3762
                case UserManager::USER_FIELD_TYPE_DOUBLE_SELECT:
3763
                    $field_list_array['extra_'.$field_details[1]]['name'] = $field_details[3];
3764
                    if (0 == $field_details[7]) {
3765
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 0;
3766
                    } else {
3767
                        $field_list_array['extra_'.$field_details[1]]['visibility'] = 1;
3768
                    }
3769
                    break;
3770
                case UserManager::USER_FIELD_TYPE_DIVIDER:
3771
                    //$form->addElement('static',$field_details[1], '<br /><strong>'.$field_details[3].'</strong>');
3772
                    break;
3773
            }
3774
        }
3775
3776
        return $field_list_array;
3777
    }
3778
3779
    /**
3780
     * Display survey question chart.
3781
     *
3782
     * @param array  $chartData
3783
     * @param bool   $hasSerie         Tells if the chart has a serie. False by default
3784
     * @param string $chartContainerId
3785
     *
3786
     * @return string (direct output)
3787
     */
3788
    public static function drawChart($chartData, $hasSerie = false, $chartContainerId = 'chartContainer', $loadLibs = true)
3789
    {
3790
        $htmlChart = '';
3791
        //if (api_browser_support('svg')) {
3792
        if (true) {
3793
            $serie = [];
3794
            $order = [];
3795
            $data = '';
3796
            foreach ($chartData as $chartDataElement) {
3797
                $data .= '{"';
3798
                $option = str_replace(["\n", "\r"], '', $chartDataElement['option']);
3799
                $serieValue = isset($chartDataElement['serie']) ? $chartDataElement['serie'] : null;
3800
                if (!$hasSerie) {
3801
                    $data .= get_lang("Option").'":"'.$option.'", "';
3802
                    array_push($order, $option);
3803
                } else {
3804
                    if (!is_array($serieValue)) {
3805
                        $data .=
3806
                            get_lang("Option").'":"'.$serieValue.'", "'.
3807
                            get_lang("Score").'":"'.$option.'", "';
3808
                        array_push($serie, $serieValue);
3809
                    } else {
3810
                        $data .=
3811
                            get_lang("Serie").'":"'.$serieValue[0].'", "'.
3812
                            get_lang("Option").'":"'.$serieValue[1].'", "'.
3813
                            get_lang("Score").'":"'.$option.'", "';
3814
                    }
3815
                }
3816
                $data .= get_lang("Votes").'":"'.$chartDataElement['votes'].'"},';
3817
                rtrim($data, ",");
3818
            }
3819
            if ($loadLibs) {
3820
                $htmlChart .= api_get_js('d3/d3.v3.5.4.min.js');
3821
                $htmlChart .= api_get_js('dimple.v2.1.2.min.js');
3822
            }
3823
3824
            $htmlChart .= '
3825
            <script>
3826
                var svg = dimple.newSvg("#'.$chartContainerId.'", 600, 400);
3827
                var data = ['.$data.'];
3828
                var myChart = new dimple.chart(svg, data);
3829
                myChart.setBounds(50, 30, 550, 300);
3830
                var yAxis = myChart.addMeasureAxis("y", "'.get_lang('Votes').'");
3831
                yAxis.fontSize = "14px";
3832
            ';
3833
            if (!$hasSerie) {
3834
                $htmlChart .= '
3835
                    var xAxisCategory = myChart.addCategoryAxis("x", "'.get_lang("Option").'");
3836
                    xAxisCategory.fontSize = "14px";
3837
                    xAxisCategory.addOrderRule('.json_encode($order).');
3838
                    myChart.addSeries("'.get_lang("Option").'", dimple.plot.bar);';
3839
            } else {
3840
                if (!is_array($chartDataElement['serie'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $chartDataElement seems to be defined by a foreach iteration on line 3796. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
3841
                    $serie = array_values(array_unique($serie));
3842
                    $htmlChart .= '
3843
                        var xAxisCategory =
3844
                        myChart.addCategoryAxis("x", ["'.get_lang('Option').'","'.get_lang("Score").'"]);
3845
                        xAxisCategory.addOrderRule('.json_encode($serie).');
3846
                        xAxisCategory.addGroupOrderRule("'.get_lang('Score').'");
3847
3848
                        myChart.addSeries("'.get_lang('Option').'", dimple.plot.bar);';
3849
                } else {
3850
                    $htmlChart .= '
3851
                        myChart.addCategoryAxis("x", ["'.get_lang('Option').'","'.get_lang("Score").'"]);
3852
                        myChart.addSeries("'.get_lang('Serie').'", dimple.plot.bar);';
3853
                }
3854
            }
3855
            $htmlChart .= 'myChart.draw();';
3856
            $htmlChart .= '</script>';
3857
        }
3858
3859
        return $htmlChart;
3860
    }
3861
3862
    /**
3863
     * Set a flag to the current survey as answered by the current user.
3864
     *
3865
     * @param string $surveyCode The survey code
3866
     * @param int    $courseId   The course ID
3867
     */
3868
    public static function flagSurveyAsAnswered($surveyCode, $courseId)
3869
    {
3870
        $currentUserId = api_get_user_id();
3871
        $flag = sprintf('%s-%s-%d', $courseId, $surveyCode, $currentUserId);
3872
3873
        if (!isset($_SESSION['filled_surveys'])) {
3874
            $_SESSION['filled_surveys'] = [];
3875
        }
3876
3877
        $_SESSION['filled_surveys'][] = $flag;
3878
    }
3879
3880
    /**
3881
     * Check whether a survey was answered by the current user.
3882
     *
3883
     * @param string $surveyCode The survey code
3884
     * @param int    $courseId   The course ID
3885
     *
3886
     * @return bool
3887
     */
3888
    public static function isSurveyAnsweredFlagged($surveyCode, $courseId)
3889
    {
3890
        $currentUserId = api_get_user_id();
3891
        $flagToCheck = sprintf('%s-%s-%d', $courseId, $surveyCode, $currentUserId);
3892
3893
        if (!isset($_SESSION['filled_surveys'])) {
3894
            return false;
3895
        }
3896
3897
        if (!is_array($_SESSION['filled_surveys'])) {
3898
            return false;
3899
        }
3900
3901
        foreach ($_SESSION['filled_surveys'] as $flag) {
3902
            if ($flagToCheck != $flag) {
3903
                continue;
3904
            }
3905
3906
            return true;
3907
        }
3908
3909
        return false;
3910
    }
3911
3912
    /**
3913
     * Check if the current survey has answers.
3914
     *
3915
     * @param int $surveyId
3916
     *
3917
     * @return bool return true if the survey has answers, false otherwise
3918
     */
3919
    public static function checkIfSurveyHasAnswers(int $surveyId): bool
3920
    {
3921
        $tableSurveyAnswer = Database::get_course_table(TABLE_SURVEY_ANSWER);
3922
3923
        if (empty($surveyId)) {
3924
            return false;
3925
        }
3926
3927
        $sql = "SELECT * FROM $tableSurveyAnswer
3928
                WHERE
3929
                    survey_id = '".$surveyId."'
3930
                ORDER BY iid, user ASC";
3931
        $result = Database::query($sql);
3932
        $response = Database::affected_rows($result);
3933
3934
        return $response > 0;
3935
    }
3936
3937
    /**
3938
     * @param int $surveyId
3939
     * @param int $courseId
3940
     * @param int $sessionId
3941
     *
3942
     * @return array
3943
     */
3944
    public static function getSentInvitations($surveyId, $courseId, $sessionId = 0)
3945
    {
3946
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
3947
        $tblSurveyInvitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
3948
3949
        $sessionCondition = api_get_session_condition($sessionId);
3950
        $surveyId = (int) $surveyId;
3951
        $courseId = (int) $courseId;
3952
3953
        $sql = "SELECT survey_invitation.*, user.firstname, user.lastname, user.email
3954
                FROM $tblSurveyInvitation survey_invitation
3955
                LEFT JOIN $tblUser user
3956
                ON (survey_invitation.user_id = user.id AND survey_invitation.c_id = $courseId)
3957
                WHERE
3958
                    survey_invitation.survey_id = $surveyId AND
3959
                    survey_invitation.c_id = $courseId
3960
                    $sessionCondition";
3961
3962
        $query = Database::query($sql);
3963
3964
        return Database::store_result($query);
3965
    }
3966
3967
    /**
3968
     * @param string $code      invitation code
3969
     * @param int    $sessionId
3970
     *
3971
     * @return string
3972
     */
3973
    public static function generateFillSurveyLink(CSurvey $survey, $invitationCode, Course $course, $sessionId = 0)
3974
    {
3975
        $invitationCode = Security::remove_XSS($invitationCode);
3976
        $sessionId = (int) $sessionId;
3977
3978
        $params = [
3979
            'iid' => $survey->getIid(),
3980
            'invitationcode' => $invitationCode,
3981
            'cid' => $course->getId(),
3982
            'course' => $course->getCode(),
3983
            'sid' => $sessionId,
3984
            'language' => $course->getCourseLanguage(),
3985
        ];
3986
3987
        return api_get_path(WEB_CODE_PATH).'survey/fillsurvey.php?'.http_build_query($params);
3988
    }
3989
}
3990