SurveyUtil::displayCompleteReport()   F
last analyzed

Complexity

Conditions 61
Paths > 20000

Size

Total Lines 237
Code Lines 153

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 61
eloc 153
c 1
b 0
f 0
nc 10245313
nop 6
dl 0
loc 237
rs 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
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\Enums\ActionIcon;
10
use Chamilo\CoreBundle\Enums\ObjectIcon;
11
use Chamilo\CoreBundle\Enums\StateIcon;
12
use Chamilo\CoreBundle\Enums\ToolIcon;
13
use Chamilo\CoreBundle\Framework\Container;
14
use Chamilo\CourseBundle\Entity\CGroup;
15
use Chamilo\CourseBundle\Entity\CSurvey;
16
use Chamilo\CourseBundle\Entity\CSurveyAnswer;
17
use Chamilo\CourseBundle\Entity\CSurveyInvitation;
18
use Chamilo\CourseBundle\Entity\CSurveyQuestion;
19
use Chamilo\CourseBundle\Entity\CSurveyQuestionOption;
20
use ChamiloSession as Session;
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, $lpItemId = 0): void
82
    {
83
        $sessionId = api_get_session_id();
84
        // table definition
85
        $table = Database::get_course_table(TABLE_SURVEY_ANSWER);
86
87
        $lpItemCondition = '';
88
        if (!empty($lpItemId)) {
89
            $lpItemCondition = " AND c_lp_item_id = $lpItemId";
90
        }
91
        $sessionCondition = '';
92
        if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
93
            $sessionCondition = api_get_session_condition($sessionId);
94
        }
95
96
        $sql = "DELETE FROM $table
97
				WHERE
98
                    user = '".Database::escape_string($user)."' AND
99
                    survey_id = '".intval($survey_id)."' AND
100
                    question_id = '".intval($question_id)."'
101
                    $sessionCondition
102
                    $lpItemCondition";
103
        Database::query($sql);
104
    }
105
106
    public static function saveAnswer(
107
        $user,
108
        CSurvey $survey,
109
        CSurveyQuestion $question,
110
        $optionId,
111
        $optionValue,
112
        $otherOption = '',
113
        $lpItemId = 0
114
    ): bool {
115
116
        // Make the survey anonymous
117
        if (1 == $survey->getAnonymous()) {
118
            $surveyUser = Session::read('surveyuser');
119
            if (empty($surveyUser)) {
120
                $user = md5($user.time());
121
                Session::write('surveyuser', $user);
122
            } else {
123
                $user = Session::read('surveyuser');
124
            }
125
        }
126
127
        if (!empty($otherOption)) {
128
            $optionId = $optionId.'@:@'.$otherOption;
129
        }
130
131
        $sessionId = api_get_session_id();
132
        $answer = new CSurveyAnswer();
133
        $answer
134
            ->setUser($user)
135
            ->setSurvey($survey)
136
            ->setQuestion($question)
137
            ->setOptionId($optionId)
138
            ->setValue((int) $optionValue)
139
            ->setLpItemId((int) $lpItemId)
140
            ->setSessionId($sessionId ?: null)
141
        ;
142
143
        $em = Database::getManager();
144
        $em->persist($answer);
145
        $em->flush();
146
147
        $insertId = $answer->getIid();
148
        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...
149
            return true;
150
        }
151
152
        return false;
153
    }
154
155
    /**
156
     * This function checks the parameters that are used in this page.
157
     *
158
     * @return string $people_filled The header, an error and the footer if any parameter fails, else it returns true
159
     *
160
     * @author Patrick Cool <[email protected]>, Ghent University
161
     *
162
     * @version February 2007
163
     */
164
    public static function check_parameters($people_filled)
165
    {
166
        $error = false;
167
168
        // Getting the survey data
169
        $survey_data = SurveyManager::get_survey($_GET['survey_id']);
170
171
        // $_GET['survey_id'] has to be numeric
172
        if (!is_numeric($_GET['survey_id'])) {
173
            $error = get_lang('Unknown survey id');
174
        }
175
176
        // $_GET['action']
177
        $allowed_actions = [
178
            'overview',
179
            'questionreport',
180
            'userreport',
181
            'comparativereport',
182
            'completereport',
183
            'deleteuserreport',
184
        ];
185
        if (isset($_GET['action']) && !in_array($_GET['action'], $allowed_actions)) {
186
            $error = get_lang('Action not allowed');
187
        }
188
189
        // User report
190
        if (isset($_GET['action']) && 'userreport' == $_GET['action']) {
191
            if (0 == $survey_data['anonymous']) {
192
                //TODO fix that this may trigger 'PHP Warning:  Undefined array key "anonymous"'
193
                foreach ($people_filled as $key => &$value) {
194
                    $people_filled_userids[] = $value['invited_user'];
195
                }
196
            } else {
197
                $people_filled_userids = $people_filled;
198
            }
199
200
            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...
201
                $error = get_lang('Unknow user');
202
            }
203
        }
204
205
        // Question report
206
        if (isset($_GET['action']) && 'questionreport' == $_GET['action']) {
207
            if (isset($_GET['question']) && !is_numeric($_GET['question'])) {
208
                $error = get_lang('Unknown question');
209
            }
210
        }
211
212
        if ($error) {
213
            $tool_name = get_lang('Reporting');
214
            Display::addFlash(
215
                Display::return_message(
216
                    get_lang('Error').': '.$error,
217
                    'error',
218
                    false
219
                )
220
            );
221
            Display::display_header($tool_name);
222
            Display::display_footer();
223
            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...
224
        }
225
226
        return true;
227
    }
228
229
    public static function handleReportingActions(CSurvey $survey, array $people_filled = [])
230
    {
231
        $action = $_GET['action'] ?? '';
232
233
        switch ($action) {
234
            case 'questionreport':
235
                self::display_question_report($survey);
236
                break;
237
            case 'userreport':
238
                self::displayUserReport($survey, $people_filled);
239
                break;
240
            case 'comparativereport':
241
                self::display_comparative_report();
242
                break;
243
            case 'completereport':
244
                // Render once per unique (survey_id, lp_item_id) pair to avoid duplicates.
245
                $answered = SurveyManager::getInvitationsAnswered(
246
                    $survey->getCode(),
247
                    api_get_course_int_id(),
248
                    api_get_session_id()
249
                );
250
251
                $rendered = [];
252
                $showNames = ($survey->getAnonymous() === '0');
253
254
                // If no invitations answered, still show a single consolidated report.
255
                if (empty($answered)) {
256
                    echo self::displayCompleteReport($survey, 0, true, true, $showNames, 0);
257
                    break;
258
                }
259
260
                foreach ($answered as $inv) {
261
                    $s = $inv->getSurvey();
262
                    if (!$s) {
263
                        continue;
264
                    }
265
                    $key = $s->getIid().':'.(int) $inv->getLpItemId();
266
                    if (isset($rendered[$key])) {
267
                        continue; // already rendered this bucket
268
                    }
269
                    $rendered[$key] = true;
270
271
                    echo self::displayCompleteReport(
272
                        $s,
273
                        0,               // all users
274
                        true,            // action bar
275
                        true,            // filters
276
                        $showNames,      // extra user fields only if not anonymous
277
                        (int) $inv->getLpItemId()
278
                    );
279
                }
280
                break;
281
            case 'deleteuserreport':
282
                self::delete_user_report($survey->getIid(), $_GET['user']);
283
                break;
284
        }
285
    }
286
287
    /**
288
     * This function deletes the report of an user who wants to retake the survey.
289
     *
290
     * @param int $survey_id
291
     * @param int $user_id
292
     *
293
     * @author Christian Fasanando Flores <[email protected]>
294
     *
295
     * @version November 2008
296
     */
297
    public static function delete_user_report($survey_id, $user_id)
298
    {
299
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
300
        $table_survey_invitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
301
        $table_survey = Database::get_course_table(TABLE_SURVEY);
302
303
        $course_id = api_get_course_int_id();
304
        $survey_id = (int) $survey_id;
305
        $user_id = Database::escape_string($user_id);
306
307
        if (!empty($survey_id) && !empty($user_id)) {
308
309
            $sessionCondition = '';
310
            if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
311
                $sessionId = api_get_session_id();
312
                $sessionCondition = api_get_session_condition($sessionId);
313
            }
314
315
            // delete data from survey_answer by user_id and survey_id
316
            $sql = "DELETE FROM $table_survey_answer
317
			        WHERE c_id = $course_id AND survey_id = '".$survey_id."' AND user = '".$user_id."' $sessionCondition";
318
            Database::query($sql);
319
            // update field answered from survey_invitation by user_id and survey_id
320
            $sql = "UPDATE $table_survey_invitation SET answered = '0'
321
			        WHERE
322
			            c_id = $course_id AND
323
			            survey_id = (
324
                            SELECT iid FROM $table_survey
325
                            WHERE
326
                                iid = '".$survey_id."'
327
                        ) AND
328
			            user_id = '".$user_id."'  $sessionCondition";
329
            $result = Database::query($sql);
330
        }
331
332
        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...
333
            $message = get_lang('The user\'s answers to the survey have been successfully removed.').'<br />
334
					<a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action=userreport&survey_id=' .$survey_id.'&'.api_get_cidreq().'">'.get_lang('Go back').'</a>';
335
            echo Display::return_message($message, 'confirmation', false);
336
        }
337
    }
338
339
    /**
340
     * @return string
341
     */
342
    public static function displayUserReportForm(CSurvey $survey, array $people_filled = [])
343
    {
344
        $surveyId = $survey->getIid();
345
        // Step 1: selection of the user
346
        echo "<script>
347
        function jumpMenu(targ,selObj,restore) {
348
            eval(targ+\".location='\"+selObj.options[selObj.selectedIndex].value+\"'\");
349
            if (restore) selObj.selectedIndex=0;
350
        }
351
		</script>";
352
        echo get_lang('Select user who filled the survey').'<br />';
353
        echo '<select name="user" onchange="jumpMenu(\'parent\',this,0)">';
354
        echo '<option value="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='
355
            .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'">'
356
            .get_lang('User').'</option>';
357
358
        foreach ($people_filled as $key => &$person) {
359
            if (0 == $survey->getAnonymous()) {
360
                $name = $person['user_info']['complete_name_with_username'];
361
                $id = $person['user_id'];
362
                if ('' == $id) {
363
                    $id = $person['invited_user'];
364
                    $name = $person['invited_user'];
365
                }
366
            } else {
367
                $name = get_lang('Anonymous').' '.($key + 1);
368
                $id = $person;
369
            }
370
            echo '<option value="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='
371
                .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&user='
372
                .Security::remove_XSS($id).'&'.api_get_cidreq().'" ';
373
            if (isset($_GET['user']) && $_GET['user'] == $id) {
374
                echo 'selected="selected"';
375
            }
376
            echo '>'.$name.'</option>';
377
        }
378
        echo '</select>';
379
    }
380
381
    /**
382
     * @param int   $userId
383
     * @param array $survey_data
384
     * @param bool  $addMessage
385
     */
386
    public static function displayUserReportAnswers($userId, CSurvey $survey, $addMessage = true)
387
    {
388
        // Database table definitions
389
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
390
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
391
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
392
        $course_id = api_get_course_int_id();
393
        $surveyId = $survey->getIid();
394
        $userId = Database::escape_string($userId);
395
396
        $content = '';
397
        // Step 2: displaying the survey and the answer of the selected users
398
        if (!empty($userId)) {
399
            if ($addMessage) {
400
                $content .= Display::return_message(
401
                    get_lang('This screen displays an exact copy of the form as it was filled by the user'),
402
                    'normal',
403
                    false
404
                );
405
            }
406
407
            // Getting all the questions and options
408
            $sql = "SELECT
409
			            survey_question.iid question_id,
410
			            survey_question.survey_id,
411
			            survey_question.survey_question,
412
			            survey_question.display,
413
			            survey_question.max_value,
414
			            survey_question.sort,
415
			            survey_question.type,
416
                        survey_question_option.iid question_option_id,
417
                        survey_question_option.option_text,
418
                        survey_question_option.sort as option_sort
419
					FROM $table_survey_question survey_question
420
					LEFT JOIN $table_survey_question_option survey_question_option
421
					ON
422
					    survey_question.iid = survey_question_option.question_id
423
					WHERE
424
					    survey_question NOT LIKE '%{{%' AND
425
					    survey_question.survey_id = '".$surveyId."'
426
					ORDER BY survey_question.sort, survey_question_option.sort ASC";
427
            $result = Database::query($sql);
428
            while ($row = Database::fetch_assoc($result)) {
429
                if ('pagebreak' !== $row['type']) {
430
                    $questions[$row['sort']]['question_id'] = $row['question_id'];
431
                    $questions[$row['sort']]['survey_id'] = $row['survey_id'];
432
                    $questions[$row['sort']]['survey_question'] = $row['survey_question'];
433
                    $questions[$row['sort']]['display'] = $row['display'];
434
                    $questions[$row['sort']]['type'] = $row['type'];
435
                    $questions[$row['sort']]['maximum_score'] = $row['max_value'];
436
                    $questions[$row['sort']]['options'][$row['question_option_id']] = $row['option_text'];
437
                }
438
            }
439
440
            $sessionCondition = '';
441
            if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
442
                $sessionId = api_get_session_id();
443
                $sessionCondition = api_get_session_condition($sessionId);
444
            }
445
446
            // Getting all the answers of the user
447
            $sql = "SELECT * FROM $table_survey_answer
448
			        WHERE
449
                        survey_id = '".$surveyId."' AND
450
                        user = '".$userId."' $sessionCondition";
451
            $result = Database::query($sql);
452
            while ($row = Database::fetch_assoc($result)) {
453
                $answers[$row['question_id']][] = $row['option_id'];
454
                $all_answers[$row['question_id']][] = $row;
455
            }
456
457
            // Displaying all the questions
458
            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...
459
                // If the question type is a scoring then we have to format the answers differently
460
                switch ($question['type']) {
461
                    case 'score':
462
                        $finalAnswer = [];
463
                        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...
464
                            foreach ($all_answers[$question['question_id']] as $key => &$answer_array) {
465
                                $finalAnswer[$answer_array['option_id']] = $answer_array['value'];
466
                            }
467
                        }
468
                        break;
469
                    case 'multipleresponse':
470
                        $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...
471
                        break;
472
                    default:
473
                        $finalAnswer = '';
474
                        if (isset($all_answers[$question['question_id']])) {
475
                            $finalAnswer = $all_answers[$question['question_id']][0]['option_id'];
476
                        }
477
                        break;
478
                }
479
480
                $display = survey_question::createQuestion($question['type']);
481
                $url = api_get_self();
482
                $form = new FormValidator('question', 'post', $url);
483
                $form->addHtml('<div class="survey_question_wrapper"><div class="survey_question">');
484
                $form->addHtml($question['survey_question']);
485
                $display->render($form, $question, $finalAnswer);
486
                $form->addHtml('</div></div>');
487
                $content .= $form->returnForm();
488
            }
489
        }
490
491
        return $content;
492
    }
493
494
    /**
495
     * This function displays the user report which is basically nothing more
496
     * than a one-page display of all the questions
497
     * of the survey that is filled with the answers of the person who filled the survey.
498
     *
499
     * @return string html code of the one-page survey with the answers of the selected user
500
     *
501
     * @author Patrick Cool <[email protected]>, Ghent University
502
     *
503
     * @version February 2007 - Updated March 2008
504
     */
505
    public static function displayUserReport(CSurvey $survey, $people_filled, $addActionBar = true)
506
    {
507
        $surveyId = $survey->getIid();
508
        $reportingUrl = api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq();
509
510
        // Actions bar
511
        if ($addActionBar) {
512
            $actions = '<a href="'.$reportingUrl.'">'.
513
                Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Reporting overview'))
514
                .'</a>';
515
            if (isset($_GET['user'])) {
516
                if (api_is_allowed_to_edit()) {
517
                    // The delete link
518
                    $actions .= '<a
519
                        href="'.$reportingUrl.'&action=deleteuserreport&user='.Security::remove_XSS($_GET['user']).'" >'.
520
                        Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Delete')).'</a>';
521
                }
522
523
                // Export the user report
524
                $actions .= '<a href="javascript: void(0);" onclick="document.form1a.submit();">'
525
                    .Display::getMdiIcon(ActionIcon::EXPORT_CSV, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('CSV export')).'</a> ';
526
                $actions .= '<a href="javascript: void(0);" onclick="document.form1b.submit();">'
527
                    .Display::getMdiIcon(ActionIcon::EXPORT_SPREADSHEET, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Excel export')).'</a> ';
528
                $actions .= '<form id="form1a" name="form1a" method="post" action="'.api_get_self().'?action='
529
                    .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'&user_id='
530
                    .Security::remove_XSS($_GET['user']).'">';
531
                $actions .= '<input type="hidden" name="export_report" value="export_report">';
532
                $actions .= '<input type="hidden" name="export_format" value="csv">';
533
                $actions .= '</form>';
534
                $actions .= '<form id="form1b" name="form1b" method="post" action="'.api_get_self().'?action='
535
                    .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'&user_id='
536
                    .Security::remove_XSS($_GET['user']).'">';
537
                $actions .= '<input type="hidden" name="export_report" value="export_report">';
538
                $actions .= '<input type="hidden" name="export_format" value="xls">';
539
                $actions .= '</form>';
540
                $actions .= '<form id="form2" name="form2" method="post" action="'.api_get_self().'?action='
541
                    .Security::remove_XSS($_GET['action']).'&survey_id='.$surveyId.'&'.api_get_cidreq().'">';
542
            }
543
            echo Display::toolbarAction('survey', [$actions]);
544
        }
545
546
        echo self::displayUserReportForm($survey, $people_filled);
547
        if (isset($_GET['user'])) {
548
            echo self::displayUserReportAnswers($_GET['user'], $survey);
549
        }
550
    }
551
552
    /**
553
     * This function displays the report by question.
554
     *
555
     * It displays a table with all the options of the question and the number of users who have answered positively on
556
     * the option. The number of users who answered positive on a given option is expressed in an absolute number, in a
557
     * percentage of the total and graphically using bars By clicking on the absolute number you get a list with the
558
     * persons who have answered this. You can then click on the name of the person and you will then go to the report
559
     * by user where you see all the answers of that user.
560
     *
561
     * @return string html code that displays the report by question
562
     *
563
     * @todo allow switching between horizontal and vertical.
564
     * @todo multiple response: percentage are probably not OK
565
     * @todo the question and option text have to be shortened and should expand when the user clicks on it.
566
     * @todo the pagebreak and comment question types should not be shown => removed from $survey_data before
567
     *
568
     * @author Patrick Cool <[email protected]>, Ghent University
569
     *
570
     * @version February 2007 - Updated March 2008
571
     */
572
    public static function display_question_report(CSurvey $survey)
573
    {
574
        $singlePage = isset($_GET['single_page']) ? (int) $_GET['single_page'] : 0;
575
        $offset = !isset($_GET['question']) ? 0 : (int) $_GET['question'];
576
        $currentQuestion = isset($_GET['question']) ? (int) $_GET['question'] : 0;
577
        $surveyId = $survey->getIid();
578
        $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : '';
579
        $course_id = api_get_course_int_id();
580
581
        $sessionCondition = '';
582
        if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
583
            $sessionId = api_get_session_id();
584
            $sessionCondition = api_get_session_condition($sessionId);
585
        }
586
587
        // DB tables
588
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
589
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
590
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
591
592
        // Toolbar
593
        $actions = '<a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq().'">'.
594
            Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Reporting overview')).
595
            '</a>';
596
        $actions .= Display::url(
597
            Display::getMdiIcon(ActionIcon::EXPORT_PDF, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Export to PDF')),
598
            'javascript: void(0);',
599
            ['onclick' => 'exportToPdf();']
600
        );
601
602
        echo Display::toolbarAction('survey', [$actions]);
603
        echo '<div class="ch-survey-report">';
604
605
        // Survey header table (used for PDF too)
606
        $fromUntil = sprintf(
607
            get_lang('From %s until %s'),
608
            api_get_local_time($survey->getAvailFrom()),
609
            api_get_local_time($survey->getAvailTill())
610
        );
611
        $max = 80;
612
        $data = [
613
            get_lang('Survey title') => cut(strip_tags($survey->getTitle()), $max),
614
            get_lang('Survey subtitle') => cut(strip_tags($survey->getSubtitle()), $max),
615
            get_lang('Dates') => $fromUntil,
616
            get_lang('Survey introduction') => cut(strip_tags($survey->getIntro()), $max),
617
        ];
618
619
        $table = new HTML_Table(['id' => 'pdf_table', 'class' => 'table table-bordered table-sm sr-table']);
620
621
        $row = 0;
622
        foreach ($data as $label => $item) {
623
            $table->setCellContents($row, 0, $label);
624
            $table->setCellContents($row, 1, $item);
625
            $row++;
626
        }
627
628
        // Question list
629
        $questions = $survey->getQuestions();
630
        $numberOfQuestions = 0;
631
        foreach ($questions as $q) {
632
            if ('pagebreak' !== $q->getType()) {
633
                $numberOfQuestions++;
634
            }
635
        }
636
637
        $newQuestionList = [];
638
        if ($numberOfQuestions > 0) {
639
            if (!$singlePage) {
640
                // Pagination (question numbers)
641
                echo '<div id="question_report_questionnumbers" class="pagination">';
642
                if (0 != $currentQuestion) {
643
                    echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='.$action.'&'
644
                        .api_get_cidreq().'&survey_id='.$surveyId.'&question='.($offset - 1).'">'
645
                        .get_lang('Previous question').'</a></li>';
646
                }
647
648
                for ($i = 1; $i <= $numberOfQuestions; $i++) {
649
                    if ($offset != $i - 1) {
650
                        echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='.$action.'&'
651
                            .api_get_cidreq().'&survey_id='.$surveyId.'&question='.($i - 1).'">'.$i.'</a></li>';
652
                    } else {
653
                        echo '<li class="disabled"><a href="#">'.$i.'</a></li>';
654
                    }
655
                }
656
                if ($currentQuestion < ($numberOfQuestions - 1)) {
657
                    echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?action='.$action.'&'
658
                        .api_get_cidreq().'&survey_id='.$surveyId.'&question='.($offset + 1).'">'
659
                        .get_lang('Next question').'</a></li>';
660
                }
661
                echo '</div>';
662
            }
663
664
            // Build list (skip tagged/pagebreak)
665
            foreach ($questions as $q) {
666
                if (strpos($q->getSurveyQuestion(), '%{{%') && 'pagebreak' !== $q->getType()) {
667
                    continue;
668
                }
669
                $newQuestionList[$q->getIid()] = $q;
670
            }
671
        }
672
673
        // Pretty container
674
        echo '<div class="sr-container">';
675
676
        // Survey info card
677
        // Survey info card (pretty, keeps #pdf_table for PDF export)
678
        echo '<div class="sr-card">';
679
        echo '  <div class="sr-card__header">'.get_lang('Survey').'</div>';
680
        echo '  <div class="sr-card__body">';
681
682
        echo '    <table id="pdf_table" class="table table-bordered table-striped table-sm sr-table sr-summary-table">';
683
        echo '      <tbody>';
684
        echo '        <tr>';
685
        echo '          <th scope="row">'.get_lang('Survey title').'</th>';
686
        echo '          <td>'.cut(strip_tags($survey->getTitle()), $max).'</td>';
687
        echo '        </tr>';
688
        echo '        <tr>';
689
        echo '          <th scope="row">'.get_lang('Survey subtitle').'</th>';
690
        echo '          <td>'.cut(strip_tags($survey->getSubtitle()), $max).'</td>';
691
        echo '        </tr>';
692
        echo '        <tr>';
693
        echo '          <th scope="row">'.get_lang('Dates').'</th>';
694
        echo '          <td>'.$fromUntil.'</td>';
695
        echo '        </tr>';
696
        echo '        <tr>';
697
        echo '          <th scope="row">'.get_lang('Survey introduction').'</th>';
698
        echo '          <td>'.cut(strip_tags($survey->getIntro()), $max).'</td>';
699
        echo '        </tr>';
700
        echo '      </tbody>';
701
        echo '    </table>';
702
703
        echo '  </div>';
704
        echo '</div>';
705
706
        echo '<div id="question_results">';
707
        /** @var CSurveyQuestion $question */
708
        foreach ($newQuestionList as $question) {
709
            $chartData = [];
710
            $options = [];
711
            $questionId = $question->getIid();
712
            $type = $question->getType();
713
714
            // Card wrapper per question
715
            echo '<div class="sr-card">';
716
            echo '<div class="sr-card__header">'.get_lang('Question').'</div>';
717
            echo '<div class="sr-card__body">';
718
719
            echo '<div class="title-question">'.strip_tags($question->getSurveyQuestion()).'</div>';
720
721
            if ('score' === $type) {
722
                // Score-type
723
                self::display_question_report_score($survey, $question, $offset);
724
            } elseif ('open' === $type || 'comment' === $type) {
725
                // Open/comment as list
726
                echo '<div class="open-question sr-block"><ul>';
727
                $sql = "SELECT * FROM $table_survey_answer
728
                    WHERE survey_id= $surveyId AND question_id = $questionId ";
729
                $result = Database::query($sql);
730
                while ($row = Database::fetch_assoc($result)) {
731
                    echo '<li>'.api_htmlentities($row['option_id'], ENT_QUOTES).'</li>';
732
                }
733
                echo '</ul></div>';
734
            } else {
735
                // Choice-based questions
736
                $sql = "SELECT * FROM $table_survey_question_option
737
                    WHERE survey_id = $surveyId AND question_id = $questionId
738
                    ORDER BY sort ASC";
739
                $result = Database::query($sql);
740
                while ($row = Database::fetch_assoc($result)) {
741
                    $options[$row['iid']] = $row;
742
                }
743
744
                $sql = "SELECT *, count(iid) as total
745
                    FROM $table_survey_answer
746
                    WHERE survey_id = $surveyId AND question_id = $questionId
747
                    GROUP BY option_id, value";
748
                $result = Database::query($sql);
749
                $number_of_answers = [];
750
                $data = [];
751
                while ($row = Database::fetch_assoc($result)) {
752
                    if (!isset($number_of_answers[$row['question_id']])) {
753
                        $number_of_answers[$row['question_id']] = 0;
754
                    }
755
                    $number_of_answers[$row['question_id']] += $row['total'];
756
                    if ('multiplechoiceother' === $type) {
757
                        $parts = ch_multiplechoiceother::decodeOptionValue($row['option_id']);
758
                        $row['option_id'] = $parts[0];
759
                    }
760
                    $data[$row['option_id']] = $row;
761
                }
762
763
                foreach ($options as $opt) {
764
                    $optionText = strip_tags($opt['option_text']);
765
                    $optionText = html_entity_decode($optionText);
766
                    $votes = isset($data[$opt['iid']]['total']) ? $data[$opt['iid']]['total'] : 0;
767
                    $chartData[] = ['option' => $optionText, 'votes' => $votes];
768
                }
769
770
                $chartContainerId = 'chartContainer'.$questionId;
771
                echo '<div id="'.$chartContainerId.'" class="sr-block" style="text-align:center;">';
772
                echo self::drawChart($chartData, false, $chartContainerId, false);
773
                echo '</div>';
774
775
                // Table (bordered/striped/compact)
776
                echo '<table class="display-survey table table-bordered table-striped table-sm sr-table sr-block" id="table_'.$chartContainerId.'">';
777
                echo '  <thead><tr>
778
                        <th style="width:50%">&nbsp;</th>
779
                        <th style="width:10%">'.get_lang('Absolute total').'</th>
780
                        <th style="width:10%">'.get_lang('Percentage').'</th>
781
                        <th style="width:30%">'.get_lang('Graphic').'</th>
782
                    </tr></thead><tbody>';
783
784
                if (is_array($options)) {
785
                    foreach ($options as $key => $value) {
786
                        if ('multiplechoiceother' === $type && 'other' === $value['option_text']) {
787
                            $value['option_text'] = get_lang('Please specify:');
788
                        }
789
                        $absolute_number = isset($data[$value['iid']]) ? $data[$value['iid']]['total'] : 0;
790
                        if ('percentage' === $type && empty($absolute_number)) {
791
                            continue;
792
                        }
793
                        $qid = $value['question_id'] ?? null;
794
                        $totalAnswers = $qid && isset($number_of_answers[$qid]) ? $number_of_answers[$qid] : 0;
795
                        $percentage = $totalAnswers ? ($absolute_number / $totalAnswers * 100) : 0;
796
797
                        echo '<tr>';
798
                        echo '<td>'.$value['option_text'].'</td>';
799
                        echo '<td>';
800
                        if (0 != $absolute_number) {
801
                            echo '<a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?'.api_get_cidreq().'&action='.$action
802
                                .'&survey_id='.$surveyId.'&question='.$offset.'&viewoption='.$value['iid'].'">'.$absolute_number.'</a>';
803
                        } else {
804
                            echo '0';
805
                        }
806
                        echo '</td>';
807
                        echo '<td>'.round($percentage, 2).' %</td>';
808
                        echo '<td>';
809
                        // Simple progress bar (no external JS/CSS needed)
810
                        $w = max(0, min(100, round($percentage, 2)));
811
                        echo '<div style="background:#eef2ff;border:1px solid #c7d2fe;height:10px;position:relative;">'
812
                            .'<div style="height:10px;width:'.$w.'%;background:#93c5fd;"></div>'
813
                            .'</div>';
814
                        echo '</td>';
815
                        echo '</tr>';
816
                    }
817
                }
818
819
                $optionResult = '0';
820
                if (isset($option['question_id']) && isset($number_of_answers[$option['question_id']])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $option does not exist. Did you maybe mean $optionText?
Loading history...
821
                    $optionResult = $number_of_answers[$option['question_id']] ?: '0';
822
                }
823
824
                echo '</tbody><tfoot><tr>
825
                    <td><b>'.get_lang('Total').'</b></td>
826
                    <td><b>'.$optionResult.'</b></td>
827
                    <td>&nbsp;</td>
828
                    <td>&nbsp;</td>
829
                </tr></tfoot>';
830
                echo '</table>';
831
            }
832
833
            echo '</div>';  // body
834
            echo '</div>';  // card
835
        }
836
        echo '</div>';      // #question_results
837
        echo '</div>';      // .sr-container
838
839
        // People who chose specific option
840
        if (isset($_GET['viewoption'])) {
841
            echo '<div class="sr-container">';
842
            echo '<div class="sr-card"><div class="sr-card__header">'.get_lang('Details').'</div><div class="sr-card__body">';
843
            echo '<div class="answered-people">';
844
            echo '<h4>'.get_lang('People who have chosen this answer').': '
845
                .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 708. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
846
847
            if (is_numeric($_GET['value'])) {
848
                $sql_restriction = "AND value='".Database::escape_string($_GET['value'])."'";
849
            } else {
850
                $sql_restriction = '';
851
            }
852
853
            $sql = "SELECT user FROM $table_survey_answer
854
                WHERE c_id = $course_id
855
                  AND option_id = '".Database::escape_string($_GET['viewoption'])."'
856
                  $sql_restriction $sessionCondition";
857
            $result = Database::query($sql);
858
            echo '<ul>';
859
            while ($row = Database::fetch_assoc($result)) {
860
                $user_info = api_get_user_info($row['user']);
861
                echo '<li><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?'.api_get_cidreq().'&action=userreport&survey_id='
862
                    .$surveyId.'&user='.$row['user'].'">'.$user_info['complete_name_with_username'].'</a></li>';
863
            }
864
            echo '</ul>';
865
            echo '</div></div></div></div>';
866
        }
867
        echo '</div>';
868
    }
869
870
    /**
871
     * Display score data about a survey question.
872
     *
873
     * @param    int    The offset of results shown
874
     */
875
    public static function display_question_report_score(CSurvey $survey, CSurveyQuestion $question, $offset)
876
    {
877
        $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : '';
878
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
879
        $surveyId = $survey->getIid();
880
        $questionId = $question->getIid();
881
        $options = $survey->getOptions();
882
883
        $sessionCondition = '';
884
        if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
885
            $sessionId = api_get_session_id();
886
            $sessionCondition = api_get_session_condition($sessionId);
887
        }
888
889
        foreach ($options as $opt) {
890
            $options[$opt->getIid()] = $opt;
891
        }
892
893
        // Answers summary
894
        $sql = "SELECT *, count(iid) as total
895
            FROM $table_survey_answer
896
            WHERE survey_id= $surveyId AND question_id = '".$questionId."'
897
            $sessionCondition
898
            GROUP BY option_id, value";
899
        $result = Database::query($sql);
900
        $number_of_answers = 0;
901
        $data = [];
902
        while ($row = Database::fetch_array($result)) {
903
            $number_of_answers += $row['total'];
904
            $data[$row['option_id']][$row['value']] = $row;
905
        }
906
907
        // Chart data
908
        $chartData = [];
909
        /** @var CSurveyQuestionOption $option */
910
        foreach ($options as $option) {
911
            $optionId = $option->getIid();
912
            $optionText = html_entity_decode(strip_tags($option->getOptionText()));
913
            for ($i = 1; $i <= $question->getMaxValue(); $i++) {
914
                $votes = isset($data[$optionId][$i]) ? $data[$optionId][$i]['total'] : 0;
915
                $chartData[] = ['serie' => $optionText, 'option' => $i, 'votes' => $votes];
916
            }
917
        }
918
919
        echo '<div class="sr-container">';
920
        echo '<div id="chartContainer" class="col-md-12 sr-block">';
921
        echo self::drawChart($chartData, true);
922
        echo '</div>';
923
924
        // Table (styled)
925
        echo '<table class="table table-bordered table-striped table-sm sr-table">';
926
        echo '  <thead><tr>
927
                <th>&nbsp;</th>
928
                <th>'.get_lang('Score').'</th>
929
                <th>'.get_lang('Absolute total').'</th>
930
                <th>'.get_lang('Percentage').'</th>
931
                <th>'.get_lang('Graphic').'</th>
932
            </tr></thead><tbody>';
933
934
        foreach ($options as $key => $value) {
935
            $optionId = $value->getIid();
936
            for ($i = 1; $i <= $question->getMaxValue(); $i++) {
937
                $absolute_number = isset($data[$optionId][$i]) ? $data[$optionId][$i]['total'] : 0;
938
939
                $percentage = 0;
940
                $size = 0;
941
                if (!empty($number_of_answers)) {
942
                    $percentage = round($absolute_number / $number_of_answers * 100, 2);
943
                    $size = $percentage; // 0..100
944
                }
945
946
                echo '<tr>';
947
                echo '<td>'.$value->getOptionText().'</td>';
948
                echo '<td>'.$i.'</td>';
949
                echo '<td><a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?'.api_get_cidreq().'&action='.$action
950
                    .'&survey_id='.$surveyId.'&question='.Security::remove_XSS($offset)
951
                    .'&viewoption='.$optionId.'&value='.$i.'">'.$absolute_number.'</a></td>';
952
                echo '<td>'.$percentage.' %</td>';
953
                echo '<td>';
954
                echo '<div style="background:#eef2ff;border:1px solid #c7d2fe;height:10px;position:relative;">'
955
                    .'<div style="height:10px;width:'.$size.'%;background:#93c5fd;"></div>'
956
                    .'</div>';
957
                echo '</td>';
958
                echo '</tr>';
959
            }
960
        }
961
962
        echo '  </tbody><tfoot><tr>
963
                <td colspan="5"><b>'.get_lang('Total').': '.$number_of_answers.'</b></td>
964
            </tr></tfoot>';
965
        echo '</table>';
966
        echo '</div>';
967
    }
968
969
    /**
970
     * This functions displays the complete reporting.
971
     *
972
     * @param int  $userId
973
     * @param bool $addActionBar
974
     * @param bool $addFilters
975
     * @param bool $addExtraFields
976
     *
977
     * @return string
978
     */
979
    public static function displayCompleteReport(
980
        CSurvey $survey,
981
                $userId = 0,
982
                $addActionBar = true,
983
                $addFilters = true,
984
                $addExtraFields = true,
985
                $lpItemId = 0
986
    ) {
987
        // DB tables
988
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
989
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
990
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
991
992
        $surveyId = $survey->getIid();
993
        $course_id = api_get_course_int_id();
994
        if (empty($surveyId) || empty($course_id)) {
995
            return '';
996
        }
997
998
        $sessionCondition = '';
999
        if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
1000
            $sessionId = api_get_session_id();
1001
            $sessionCondition = api_get_session_condition($sessionId);
1002
        }
1003
        $lpItemCondition = '';
1004
        if (!empty($lpItemId)) {
1005
            $lpItemCondition = " AND c_lp_item_id = $lpItemId";
1006
        }
1007
1008
        $action = isset($_GET['action']) ? Security::remove_XSS($_GET['action']) : '';
1009
        $content = '';
1010
1011
        // Optional LP header
1012
        if (!empty($lpItemId)) {
1013
            $tableLp = Database::get_course_table(TABLE_LP_MAIN);
1014
            $tableLpItem = Database::get_course_table(TABLE_LP_ITEM);
1015
            $sql = "SELECT l.name, li.title
1016
                FROM $tableLpItem li
1017
                INNER JOIN $tableLp l ON l.iid = li.lp_id AND l.c_id = li.c_id
1018
                WHERE li.c_id = $course_id AND li.iid = $lpItemId";
1019
            $rs = Database::query($sql);
1020
            if (Database::num_rows($rs) > 0) {
1021
                $row = Database::fetch_assoc($rs);
1022
                $content .= '<div class="sr-container"><div class="sr-card"><div class="sr-card__header">'
1023
                    .get_lang('Learning path')
1024
                    .'</div><div class="sr-card__body"><h3 style="margin:0">'.$row['name'].' : '.$row['title'].'</h3></div></div></div>';
1025
            }
1026
        }
1027
1028
        // Toolbar
1029
        if ($addActionBar) {
1030
            $actions = '<a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq().'">'
1031
                .Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Reporting overview'), [], ICON_SIZE_MEDIUM)
1032
                .'</a>';
1033
            $actions .= '<a class="survey_export_link" href="javascript: void(0);" onclick="document.form1a.submit();">'
1034
                .Display::getMdiIcon(ActionIcon::EXPORT_CSV, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('CSV export')).'</a>';
1035
            $actions .= '<a class="survey_export_link" href="javascript: void(0);" onclick="document.form1b.submit();">'
1036
                .Display::getMdiIcon(ActionIcon::EXPORT_SPREADSHEET, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Excel export')).'</a>';
1037
            $actions .= '<a class="survey_export_link" href="javascript: void(0);" onclick="document.form1c.submit();">'
1038
                .Display::getMdiIcon(ActionIcon::EXPORT_ARCHIVE, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Export as compact CSV')).'</a>';
1039
1040
            $content .= Display::toolbarAction('survey', [$actions]);
1041
1042
            // Export forms
1043
            $content .= '<form id="form1a" name="form1a" method="post" action="'.api_get_self().'?action='.$action.'&survey_id='.$surveyId.'&'.api_get_cidreq().'">
1044
            <input type="hidden" name="export_report" value="export_report">
1045
            <input type="hidden" name="export_format" value="csv"></form>';
1046
            $content .= '<form id="form1b" name="form1b" method="post" action="'.api_get_self().'?action='.$action.'&survey_id='.$surveyId.'&'.api_get_cidreq().'">
1047
            <input type="hidden" name="export_report" value="export_report">
1048
            <input type="hidden" name="export_format" value="xls"></form>';
1049
            $content .= '<form id="form1c" name="form1c" method="post" action="'.api_get_self().'?action='.$action.'&survey_id='.$surveyId.'&'.api_get_cidreq().'">
1050
            <input type="hidden" name="export_report" value="export_report">
1051
            <input type="hidden" name="export_format" value="csv-compact"></form>';
1052
        }
1053
1054
        // Open card + form + table
1055
        $content .= '<div class="sr-container"><div class="sr-card"><div class="sr-card__header">'
1056
            .get_lang('Complete report').'</div><div class="sr-card__body">';
1057
1058
        $content .= '<form id="form2" name="form2" method="post" action="'.api_get_self().'?action='.$action.'&survey_id='.$surveyId.'&'.api_get_cidreq().'">';
1059
        $content .= '<table class="table table-bordered table-striped table-hover table-sm data_table sr-table">';
1060
1061
        // Header row (filters + profile attrs)
1062
        $content .= '<tr><th>';
1063
        if ($addFilters) {
1064
            if ((isset($_POST['submit_question_filter']) && $_POST['submit_question_filter']) ||
1065
                (isset($_POST['export_report']) && $_POST['export_report'])
1066
            ) {
1067
                $content .= '<button class="cancel" type="submit" name="reset_question_filter" value="'.get_lang('Reset filter').'">'
1068
                    .get_lang('Reset filter').'</button>';
1069
            }
1070
            $content .= '<button class="save" type="submit" name="submit_question_filter" value="'.get_lang('Filter').'">'
1071
                .get_lang('Filter').'</button>';
1072
            $content .= '</th>';
1073
        }
1074
1075
        $display_extra_user_fields = false;
1076
        if ($addExtraFields) {
1077
            if (!(isset($_POST['submit_question_filter']) && $_POST['submit_question_filter'] ||
1078
                    isset($_POST['export_report']) && $_POST['export_report']) ||
1079
                !empty($_POST['fields_filter'])
1080
            ) {
1081
                $extra_user_fields = UserManager::get_extra_fields(0, 0, 5, 'ASC', false, true);
1082
                $num = count($extra_user_fields);
1083
                if ($num > 0) {
1084
                    $content .= '<th '.($num > 0 ? ' colspan="'.$num.'"' : '').'>';
1085
                    $content .= '<label>';
1086
                    if ($addFilters) {
1087
                        $content .= '<input type="checkbox" name="fields_filter" value="1" checked="checked"/> ';
1088
                    }
1089
                    $content .= get_lang('Profile attributes');
1090
                    $content .= '</label></th>';
1091
                    $display_extra_user_fields = true;
1092
                }
1093
            }
1094
        }
1095
1096
        // Questions headers
1097
        $sql = "SELECT
1098
              q.iid question_id, q.type, q.survey_question, count(o.iid) as number_of_options
1099
            FROM $table_survey_question q
1100
            LEFT JOIN $table_survey_question_option o ON q.iid = o.question_id
1101
            WHERE survey_question NOT LIKE '%{{%' AND q.survey_id = '".$surveyId."'
1102
            GROUP BY q.iid ORDER BY q.sort ASC";
1103
        $result = Database::query($sql);
1104
        $questions = [];
1105
        while ($row = Database::fetch_array($result)) {
1106
            if (!(isset($_POST['submit_question_filter']) && $_POST['submit_question_filter']) ||
1107
                (is_array($_POST['questions_filter']) && in_array($row['question_id'], $_POST['questions_filter']))
1108
            ) {
1109
                if ('pagebreak' !== $row['type']) {
1110
                    $content .= ' <th'.(($row['number_of_options'] > 0 && 'percentage' !== $row['type']) ? ' colspan="'.$row['number_of_options'].'"' : '').'>';
1111
                    $content .= '<label>';
1112
                    if ($addFilters) {
1113
                        $content .= '<input type="checkbox" name="questions_filter[]" value="'.$row['question_id'].'" checked="checked"/>';
1114
                    }
1115
                    $content .= $row['survey_question'];
1116
                    $content .= '</label></th>';
1117
                }
1118
            }
1119
            $questions[$row['question_id']] = $row;
1120
        }
1121
        $content .= '</tr>';
1122
1123
        // Second header row (user + extra fields + options)
1124
        $content .= '<tr><th>&nbsp;</th>';
1125
        if (!(isset($_POST['submit_question_filter']) && $_POST['submit_question_filter'] ||
1126
                isset($_POST['export_report']) && $_POST['export_report']) || !empty($_POST['fields_filter'])
1127
        ) {
1128
            if ($addExtraFields) {
1129
                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...
1130
                    $content .= '<th>'.$field[3].'</th>';
1131
                }
1132
            }
1133
        }
1134
1135
        $sql = "SELECT
1136
                sq.iid question_id, sq.survey_id, sq.survey_question, sq.display, sq.sort, sq.type,
1137
                sqo.iid question_option_id, sqo.option_text, sqo.sort as option_sort
1138
            FROM $table_survey_question sq
1139
            LEFT JOIN $table_survey_question_option sqo ON sq.iid = sqo.question_id
1140
            WHERE survey_question NOT LIKE '%{{%' AND sq.survey_id = $surveyId
1141
            ORDER BY sq.sort ASC, sqo.sort ASC";
1142
        $result = Database::query($sql);
1143
1144
        $display_percentage_header = 1;
1145
        $possible_answers = [];
1146
        while ($row = Database::fetch_array($result)) {
1147
            if (!(isset($_POST['submit_question_filter']) && $_POST['submit_question_filter']) ||
1148
                (is_array($_POST['questions_filter']) && in_array($row['question_id'], $_POST['questions_filter']))
1149
            ) {
1150
                if ('open' == $row['type'] || 'comment' == $row['type']) {
1151
                    $content .= '<th>&nbsp;-&nbsp;</th>';
1152
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1153
                    $display_percentage_header = 1;
1154
                } elseif ('percentage' == $row['type'] && $display_percentage_header) {
1155
                    $content .= '<th>&nbsp;%&nbsp;</th>';
1156
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1157
                    $display_percentage_header = 0;
1158
                } elseif ('percentage' == $row['type']) {
1159
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1160
                } elseif ('pagebreak' != $row['type'] && 'percentage' != $row['type']) {
1161
                    $content .= '<th>'.$row['option_text'].'</th>';
1162
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1163
                    $display_percentage_header = 1;
1164
                }
1165
            }
1166
        }
1167
        $content .= '</tr>';
1168
1169
        // User filter
1170
        $userCondition = '';
1171
        if (!empty($userId)) {
1172
            $userId = (int) $userId;
1173
            $userCondition = " AND user = $userId ";
1174
        }
1175
1176
        // Collect answers grouped by user
1177
        $old_user = '';
1178
        $answers_of_user = [];
1179
        $sql = "SELECT * FROM $table_survey_answer
1180
            WHERE survey_id = $surveyId
1181
              $userCondition
1182
              $sessionCondition
1183
              $lpItemCondition
1184
            ORDER BY iid, user ASC";
1185
        $result = Database::query($sql);
1186
        $i = 1;
1187
        while ($row = Database::fetch_array($result)) {
1188
            if ($old_user != $row['user'] && '' != $old_user) {
1189
                $userParam = (0 != $survey->getAnonymous()) ? $i : $old_user;
1190
                if (0 != $survey->getAnonymous()) { $i++; }
1191
                $content .= self::display_complete_report_row(
1192
                    $survey, $possible_answers, $answers_of_user, $userParam, $questions, $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 = (0 != $survey->getAnonymous()) ? $i : $old_user;
1208
        $content .= self::display_complete_report_row(
1209
            $survey, $possible_answers, $answers_of_user, $userParam, $questions, $display_extra_user_fields
1210
        );
1211
1212
        $content .= '</table></form>';
1213
        $content .= '</div></div></div>'; // close card+container
1214
1215
        return $content;
1216
    }
1217
1218
    /**
1219
     * Return user answers in a row.
1220
     *
1221
     * @return string
1222
     */
1223
    public static function display_complete_report_row(
1224
        CSurvey $survey,
1225
        $possible_options,
1226
        $answers_of_user,
1227
        $user,
1228
        $questions,
1229
        $display_extra_user_fields = false
1230
    ) {
1231
        $user = Security::remove_XSS($user);
1232
        $surveyId = $survey->getIid();
1233
1234
        if (empty($surveyId)) {
1235
            return '';
1236
        }
1237
1238
        $content = '<tr>';
1239
        $url = api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq();
1240
        if (0 == $survey->getAnonymous()) {
1241
            if (0 !== (int) $user) {
1242
                $userInfo = api_get_user_info($user);
1243
                $user_displayed = '-';
1244
                if (!empty($userInfo)) {
1245
                    $user_displayed = $userInfo['complete_name_with_username'];
1246
                }
1247
1248
                $content .= '<th>
1249
                    <a href="'.$url.'&action=userreport&user='.$user.'">'
1250
                        .$user_displayed.'
1251
                    </a>
1252
                    </th>'; // the user column
1253
            } else {
1254
                $content .= '<th>'.$user.'</th>'; // the user column
1255
            }
1256
        } else {
1257
            $content .= '<th>'.get_lang('Anonymous').' '.$user.'</th>';
1258
        }
1259
1260
        if ($display_extra_user_fields) {
1261
            // Show user fields data, if any, for this user
1262
            $user_fields_values = UserManager::get_extra_user_data(
1263
                $user,
1264
                false,
1265
                false,
1266
                false,
1267
                true
1268
            );
1269
            foreach ($user_fields_values as &$value) {
1270
                $content .= '<td align="center">'.$value.'</td>';
1271
            }
1272
        }
1273
1274
        if (is_array($possible_options)) {
1275
            foreach ($possible_options as $question_id => &$possible_option) {
1276
                if ('open' === $questions[$question_id]['type'] || 'comment' === $questions[$question_id]['type']) {
1277
                    $content .= '<td align="center">';
1278
                    if (isset($answers_of_user[$question_id]) && isset($answers_of_user[$question_id]['0'])) {
1279
                        $content .= $answers_of_user[$question_id]['0']['option_id'];
1280
                    }
1281
                    $content .= '</td>';
1282
                } else {
1283
                    foreach ($possible_option as $option_id => $value) {
1284
                        if ('multiplechoiceother' === $questions[$question_id]['type']) {
1285
                            foreach ($answers_of_user[$question_id] as $key => $newValue) {
1286
                                $parts = ch_multiplechoiceother::decodeOptionValue($key);
1287
                                if (isset($parts[0])) {
1288
                                    $data = $answers_of_user[$question_id][$key];
1289
                                    unset($answers_of_user[$question_id][$key]);
1290
                                    $newKey = $parts[0];
1291
                                    $answers_of_user[$question_id][$newKey] = $data;
1292
                                }
1293
                            }
1294
                        }
1295
                        if ('percentage' === $questions[$question_id]['type']) {
1296
                            if (!empty($answers_of_user[$question_id][$option_id])) {
1297
                                $content .= "<td align='center'>";
1298
                                $content .= $answers_of_user[$question_id][$option_id]['value'];
1299
                                $content .= "</td>";
1300
                            }
1301
                        } else {
1302
                            $content .= '<td align="center">';
1303
                            if (!empty($answers_of_user[$question_id][$option_id])) {
1304
                                if (0 != $answers_of_user[$question_id][$option_id]['value']) {
1305
                                    $content .= $answers_of_user[$question_id][$option_id]['value'];
1306
                                } else {
1307
                                    $content .= 'v';
1308
                                }
1309
                            }
1310
                        }
1311
                    }
1312
                }
1313
            }
1314
        }
1315
1316
        $content .= '</tr>';
1317
1318
        return $content;
1319
    }
1320
1321
    /**
1322
     * Quite similar to display_complete_report(), returns an HTML string
1323
     * that can be used in a csv file.
1324
     *
1325
     * @param array $survey_data The basic survey data as initially obtained by SurveyManager::get_survey()
1326
     * @param int   $user_id     The ID of the user asking for the report
1327
     * @param bool  $compact     Whether to present the long (v marks with multiple columns per question) or compact
1328
     *                           (one column per question) answers format
1329
     *
1330
     * @todo consider merging this function with display_complete_report
1331
     *
1332
     * @throws Exception
1333
     *
1334
     * @return string The contents of a csv file
1335
     *
1336
     * @author Patrick Cool <[email protected]>, Ghent University
1337
     *
1338
     * @version February 2007
1339
     */
1340
    public static function export_complete_report($survey_data, $user_id = 0, $compact = false)
1341
    {
1342
        $surveyId = isset($_GET['survey_id']) ? (int) $_GET['survey_id'] : 0;
1343
1344
        if (empty($surveyId)) {
1345
            return false;
1346
        }
1347
1348
        $course = api_get_course_info();
1349
        $course_id = $course['real_id'];
1350
1351
        $sessionCondition = '';
1352
        if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
1353
            $sessionId = api_get_session_id();
1354
            $sessionCondition = api_get_session_condition($sessionId);
1355
        }
1356
1357
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
1358
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
1359
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
1360
1361
        $translate = false;
1362
        if ('true' === api_get_setting('editor.translate_html')) {
1363
            $translate = true;
1364
        }
1365
1366
        // The first column
1367
        $return = ';';
1368
1369
        // Show extra fields blank space (enough for extra fields on next line)
1370
        $extra_user_fields = UserManager::get_extra_fields(
1371
            0,
1372
            0,
1373
            5,
1374
            'ASC',
1375
            false,
1376
            true
1377
        );
1378
1379
        $num = count($extra_user_fields);
1380
        $return .= str_repeat(';', $num);
1381
1382
        $sql = "SELECT
1383
                    questions.iid,
1384
                    questions.type,
1385
                    questions.survey_question,
1386
                    count(options.iid) as number_of_options
1387
				FROM $table_survey_question questions
1388
                LEFT JOIN $table_survey_question_option options
1389
				ON
1390
				  questions.iid = options.question_id
1391
				WHERE
1392
				    survey_question NOT LIKE '%{{%' AND
1393
				    questions.type <> 'pagebreak' AND
1394
				    questions.survey_id = $surveyId
1395
				GROUP BY questions.iid
1396
				ORDER BY questions.sort ASC";
1397
1398
        $result = Database::query($sql);
1399
        while ($row = Database::fetch_array($result)) {
1400
            if ($translate) {
1401
                $row['survey_question'] = api_get_filtered_multilingual_HTML_string($row['survey_question'], $course['language']);
1402
            }
1403
            // We show the questions if
1404
            // 1. there is no question filter and the export button has not been clicked
1405
            // 2. there is a quesiton filter but the question is selected for display
1406
            if (!(isset($_POST['submit_question_filter'])) ||
1407
                (isset($_POST['submit_question_filter']) &&
1408
                    is_array($_POST['questions_filter']) &&
1409
                    in_array($row['iid'], $_POST['questions_filter']))
1410
            ) {
1411
                if (0 == $row['number_of_options'] || $compact) {
1412
                    $return .= str_replace(
1413
                        "\r\n",
1414
                        '  ',
1415
                        api_html_entity_decode(strip_tags($row['survey_question']), ENT_QUOTES)
1416
                    )
1417
                    .';';
1418
                } else {
1419
                    for ($ii = 0; $ii < $row['number_of_options']; $ii++) {
1420
                        $return .= str_replace(
1421
                            "\r\n",
1422
                            '  ',
1423
                            api_html_entity_decode(strip_tags($row['survey_question']), ENT_QUOTES)
1424
                        )
1425
                        .';';
1426
                    }
1427
                }
1428
            }
1429
        }
1430
1431
        $return .= "\n";
1432
        // Getting all the questions and options
1433
        $return .= ';';
1434
        // Show the fields names for user fields
1435
        if (!empty($extra_user_fields)) {
1436
            foreach ($extra_user_fields as &$field) {
1437
                if ($translate) {
1438
                    $field[3] = api_get_filtered_multilingual_HTML_string($field[3], $course['language']);
1439
                }
1440
                $return .= '"'
1441
                    .str_replace(
1442
                        "\r\n",
1443
                        '  ',
1444
                        api_html_entity_decode(strip_tags($field[3]), ENT_QUOTES)
1445
                    )
1446
                    .'";';
1447
            }
1448
        }
1449
1450
        $sql = "SELECT DISTINCT
1451
		            survey_question.iid question_id,
1452
		            survey_question.survey_id,
1453
		            survey_question.survey_question,
1454
		            survey_question.display,
1455
		            survey_question.sort,
1456
		            survey_question.type,
1457
                    survey_question_option.iid question_option_id,
1458
                    survey_question_option.option_text,
1459
                    survey_question_option.sort as option_sort
1460
				FROM $table_survey_question survey_question
1461
				LEFT JOIN $table_survey_question_option survey_question_option
1462
				ON
1463
				    survey_question.iid = survey_question_option.question_id
1464
				WHERE
1465
				    survey_question NOT LIKE '%{{%' AND
1466
				    survey_question.type <> 'pagebreak' AND
1467
				    survey_question.survey_id = $surveyId
1468
				ORDER BY survey_question.sort ASC, survey_question_option.sort ASC";
1469
        $result = Database::query($sql);
1470
        $possible_answers = [];
1471
        $possible_answers_type = [];
1472
        while ($row = Database::fetch_array($result)) {
1473
            // We show the options if
1474
            // 1. there is no question filter and the export button has not been clicked
1475
            // 2. there is a question filter but the question is selected for display
1476
            if ($translate) {
1477
                $row['option_text'] = api_get_filtered_multilingual_HTML_string($row['option_text'], $course['language']);
1478
            }
1479
            if (!(isset($_POST['submit_question_filter'])) || (
1480
                is_array($_POST['questions_filter']) &&
1481
                in_array($row['question_id'], $_POST['questions_filter'])
1482
            )
1483
            ) {
1484
                $row['option_text'] = str_replace(["\r", "\n"], ['', ''], $row['option_text']);
1485
                if (!$compact) {
1486
                    $return .= api_html_entity_decode(strip_tags($row['option_text']), ENT_QUOTES).';';
1487
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1488
                } else {
1489
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['option_text'];
1490
                }
1491
                $possible_answers_type[$row['question_id']] = $row['type'];
1492
            }
1493
        }
1494
1495
        $return .= "\n";
1496
1497
        // Getting all the answers of the users
1498
        $old_user = '';
1499
        $answers_of_user = [];
1500
        $sql = "SELECT * FROM $table_survey_answer
1501
		        WHERE
1502
		          survey_id = $surveyId
1503
		          $sessionCondition
1504
		          ";
1505
        if (0 != $user_id) {
1506
            $user_id = (int) $user_id;
1507
            $sql .= " AND user = $user_id ";
1508
        }
1509
        $sql .= ' ORDER BY user ASC ';
1510
1511
        $questionIdList = array_keys($possible_answers_type);
1512
        $open_question_iterator = 1;
1513
        $result = Database::query($sql);
1514
        while ($row = Database::fetch_assoc($result)) {
1515
            if (!in_array($row['question_id'], $questionIdList)) {
1516
                continue;
1517
            }
1518
            if ($old_user != $row['user'] && '' != $old_user) {
1519
                $return .= self::export_complete_report_row(
1520
                    $survey_data,
1521
                    $possible_answers,
1522
                    $answers_of_user,
1523
                    $old_user,
1524
                    true,
1525
                    $compact
1526
                );
1527
                $answers_of_user = [];
1528
            }
1529
1530
            if ('open' === $possible_answers_type[$row['question_id']] ||
1531
                'comment' === $possible_answers_type[$row['question_id']]
1532
            ) {
1533
                $temp_id = 'open'.$open_question_iterator;
1534
                $answers_of_user[$row['question_id']][$temp_id] = $row;
1535
                $open_question_iterator++;
1536
            } else {
1537
                $answers_of_user[$row['question_id']][$row['option_id']] = $row;
1538
            }
1539
            $old_user = $row['user'];
1540
        }
1541
1542
        // This is to display the last user
1543
        $return .= self::export_complete_report_row(
1544
            $survey_data,
1545
            $possible_answers,
1546
            $answers_of_user,
1547
            $old_user,
1548
            true,
1549
            $compact
1550
        );
1551
1552
        return $return;
1553
    }
1554
1555
    /**
1556
     * Add a line to the csv file.
1557
     *
1558
     * @param array $survey_data               Basic survey data (we're mostly interested in the 'anonymous' index)
1559
     * @param array $possible_options          Possible answers
1560
     * @param array $answers_of_user           User's answers
1561
     * @param mixed $user                      User ID or user details as string - Used as a string in the result
1562
     *                                         string
1563
     * @param bool  $display_extra_user_fields Whether to display user fields or not
1564
     * @param bool  $compact                   Whether to show answers as different column values (true) or one column
1565
     *                                         per option (false, default)
1566
     *
1567
     * @return string One line of the csv file
1568
     *
1569
     * @author Patrick Cool <[email protected]>, Ghent University
1570
     *
1571
     * @version February 2007
1572
     */
1573
    public static function export_complete_report_row(
1574
        CSurvey $survey,
1575
        $possible_options,
1576
        $answers_of_user,
1577
        $user,
1578
        $display_extra_user_fields = false,
1579
        $compact = false
1580
    ) {
1581
        $return = '';
1582
        if (0 == $survey->getAnonymous()) {
1583
            if (0 !== intval($user)) {
1584
                $userInfo = api_get_user_info($user);
1585
                if (!empty($userInfo)) {
1586
                    $user_displayed = $userInfo['complete_name_with_username'];
1587
                } else {
1588
                    $user_displayed = '-';
1589
                }
1590
                $return .= $user_displayed.';';
1591
            } else {
1592
                $return .= $user.';';
1593
            }
1594
        } else {
1595
            $return .= '-;'; // The user column
1596
        }
1597
1598
        if ($display_extra_user_fields) {
1599
            // Show user fields data, if any, for this user
1600
            $user_fields_values = UserManager::get_extra_user_data(
1601
                $user,
1602
                false,
1603
                false,
1604
                false,
1605
                true
1606
            );
1607
            foreach ($user_fields_values as &$value) {
1608
                $return .= '"'.str_replace('"', '""', api_html_entity_decode(strip_tags($value), ENT_QUOTES)).'";';
1609
            }
1610
        }
1611
1612
        if (is_array($possible_options)) {
1613
            foreach ($possible_options as $question_id => $possible_option) {
1614
                if (is_array($possible_option) && count($possible_option) > 0) {
1615
                    foreach ($possible_option as $option_id => &$value) {
1616
                        // For each option of this question, look if it matches the user's answer
1617
                        $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];
1618
                        $key = array_keys($my_answer_of_user);
1619
                        if (isset($key[0]) && 'open' === substr($key[0], 0, 4)) {
1620
                            // If this is an open type question (type starts by 'open'), take whatever answer is given
1621
                            $return .= '"'.
1622
                                str_replace(
1623
                                    '"',
1624
                                    '""',
1625
                                    api_html_entity_decode(
1626
                                        strip_tags(
1627
                                            $answers_of_user[$question_id][$key[0]]['option_id']
1628
                                        ),
1629
                                        ENT_QUOTES
1630
                                    )
1631
                                ).
1632
                                '";';
1633
                        } elseif (!empty($answers_of_user[$question_id][$option_id])) {
1634
                            //$return .= 'v';
1635
                            if ($compact) {
1636
                                // If we asked for a compact view, show only one column for the question
1637
                                // and fill it with the text of the selected option (i.e. "Yes") instead of an ID
1638
                                if (0 != $answers_of_user[$question_id][$option_id]['value']) {
1639
                                    $return .= $answers_of_user[$question_id][$option_id]['value'].";";
1640
                                } else {
1641
                                    $return .= '"'.
1642
                                        str_replace(
1643
                                            '"',
1644
                                            '""',
1645
                                            api_html_entity_decode(
1646
                                                strip_tags(
1647
                                                    $possible_option[$option_id]
1648
                                                ),
1649
                                                ENT_QUOTES
1650
                                            )
1651
                                        ).
1652
                                        '";';
1653
                                }
1654
                            } else {
1655
                                // If we don't want a compact view, show one column per possible option and mark a 'v'
1656
                                // or the defined value in the corresponding column if the user selected it
1657
                                if (0 != $answers_of_user[$question_id][$option_id]['value']) {
1658
                                    $return .= $answers_of_user[$question_id][$option_id]['value'].";";
1659
                                } else {
1660
                                    $return .= 'v;';
1661
                                }
1662
                            }
1663
                        } else {
1664
                            if (!$compact) {
1665
                                $return .= ';';
1666
                            }
1667
                        }
1668
                    }
1669
                }
1670
            }
1671
        }
1672
        $return .= "\n";
1673
1674
        return $return;
1675
    }
1676
1677
    public static function export_complete_report_xls(CSurvey $survey, $filename, $user_id = 0, $returnFile = false)
1678
    {
1679
        $course_id = api_get_course_int_id();
1680
        $user_id = (int) $user_id;
1681
        $surveyId = $survey->getIid();
1682
1683
        if (empty($course_id) || empty($surveyId)) {
1684
            return false;
1685
        }
1686
1687
        // Show extra fields blank space (enough for extra fields on next line)
1688
        // Show user fields section with a big th colspan that spans over all fields
1689
        $extra_user_fields = UserManager::get_extra_fields(
1690
            0,
1691
            0,
1692
            5,
1693
            'ASC',
1694
            false,
1695
            true
1696
        );
1697
        $list = [];
1698
        $num = count($extra_user_fields);
1699
        for ($i = 0; $i < $num; $i++) {
1700
            $list[0][] = '';
1701
        }
1702
1703
        $display_extra_user_fields = true;
1704
1705
        // Database table definitions
1706
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
1707
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
1708
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
1709
1710
        // First line (questions)
1711
        $sql = "SELECT
1712
                    questions.question_id,
1713
                    questions.type,
1714
                    questions.survey_question,
1715
                    count(options.iid) as number_of_options
1716
				FROM $table_survey_question questions
1717
				LEFT JOIN $table_survey_question_option options
1718
                ON
1719
                  questions.iid = options.question_id
1720
				WHERE
1721
				    survey_question NOT LIKE '%{{%' AND
1722
				    questions.type <> 'pagebreak' AND
1723
				    questions.survey_id = $surveyId
1724
				GROUP BY questions.question_id
1725
				ORDER BY questions.sort ASC";
1726
        $result = Database::query($sql);
1727
        $line = 1;
1728
        $column = 1;
1729
        while ($row = Database::fetch_array($result)) {
1730
            // We show the questions if
1731
            // 1. there is no question filter and the export button has not been clicked
1732
            // 2. there is a quesiton filter but the question is selected for display
1733
            if (!(isset($_POST['submit_question_filter'])) ||
1734
                (isset($_POST['submit_question_filter']) && is_array($_POST['questions_filter']) &&
1735
                in_array($row['question_id'], $_POST['questions_filter']))
1736
            ) {
1737
                // We do not show comment and pagebreak question types
1738
                if ('pagebreak' !== $row['type']) {
1739
                    if (0 == $row['number_of_options'] && ('open' === $row['type'] || 'comment' === $row['type'])) {
1740
                        $list[$line][$column] = api_html_entity_decode(
1741
                            strip_tags($row['survey_question']),
1742
                            ENT_QUOTES
1743
                        );
1744
                        $column++;
1745
                    } else {
1746
                        for ($ii = 0; $ii < $row['number_of_options']; $ii++) {
1747
                            $list[$line][$column] = api_html_entity_decode(
1748
                                strip_tags($row['survey_question']),
1749
                                ENT_QUOTES
1750
                            );
1751
                            $column++;
1752
                        }
1753
                    }
1754
                }
1755
            }
1756
        }
1757
1758
        $line++;
1759
        $column = 1;
1760
        // Show extra field values
1761
        if ($display_extra_user_fields) {
1762
            // Show the fields names for user fields
1763
            foreach ($extra_user_fields as &$field) {
1764
                $list[$line][$column] = api_html_entity_decode(strip_tags($field[3]), ENT_QUOTES);
1765
                $column++;
1766
            }
1767
        }
1768
1769
        // Getting all the questions and options (second line)
1770
        $sql = "SELECT
1771
                    survey_question.iid question_id,
1772
                    survey_question.survey_id,
1773
                    survey_question.survey_question,
1774
                    survey_question.display,
1775
                    survey_question.sort,
1776
                    survey_question.type,
1777
                    survey_question_option.iid question_option_id,
1778
                    survey_question_option.option_text,
1779
                    survey_question_option.sort as option_sort
1780
				FROM $table_survey_question survey_question
1781
				LEFT JOIN $table_survey_question_option survey_question_option
1782
				ON
1783
				    survey_question.iid = survey_question_option.question_id
1784
				WHERE
1785
				    survey_question NOT LIKE '%{{%' AND
1786
				    survey_question.type <> 'pagebreak' AND
1787
				    survey_question.survey_id = $surveyId
1788
				ORDER BY survey_question.sort ASC, survey_question_option.sort ASC";
1789
        $result = Database::query($sql);
1790
        $possible_answers = [];
1791
        $possible_answers_type = [];
1792
        while ($row = Database::fetch_array($result)) {
1793
            // We show the options if
1794
            // 1. there is no question filter and the export button has not been clicked
1795
            // 2. there is a quesiton filter but the question is selected for display
1796
            if (!isset($_POST['submit_question_filter']) ||
1797
                (isset($_POST['questions_filter']) && is_array($_POST['questions_filter']) &&
1798
                in_array($row['question_id'], $_POST['questions_filter']))
1799
            ) {
1800
                // We do not show comment and pagebreak question types
1801
                if ('pagebreak' !== $row['type']) {
1802
                    $list[$line][$column] = api_html_entity_decode(
1803
                        strip_tags($row['option_text']),
1804
                        ENT_QUOTES
1805
                    );
1806
                    $possible_answers[$row['question_id']][$row['question_option_id']] = $row['question_option_id'];
1807
                    $possible_answers_type[$row['question_id']] = $row['type'];
1808
                    $column++;
1809
                }
1810
            }
1811
        }
1812
1813
        // Getting all the answers of the users
1814
        $line++;
1815
        $column = 0;
1816
        $old_user = '';
1817
        $answers_of_user = [];
1818
        $sql = "SELECT * FROM $table_survey_answer
1819
                WHERE c_id = $course_id AND survey_id = $surveyId";
1820
        if (0 != $user_id) {
1821
            $sql .= " AND user='".$user_id."' ";
1822
        }
1823
        $sql .= ' ORDER BY user ASC';
1824
1825
        $open_question_iterator = 1;
1826
        $result = Database::query($sql);
1827
        while ($row = Database::fetch_array($result)) {
1828
            if ($old_user != $row['user'] && '' != $old_user) {
1829
                $return = self::export_complete_report_row_xls(
1830
                    $survey,
1831
                    $possible_answers,
1832
                    $answers_of_user,
1833
                    $old_user,
1834
                    true
1835
                );
1836
                foreach ($return as $elem) {
1837
                    $list[$line][$column] = $elem;
1838
                    $column++;
1839
                }
1840
                $answers_of_user = [];
1841
                $line++;
1842
                $column = 0;
1843
            }
1844
            if ('open' === $possible_answers_type[$row['question_id']] || 'comment' === $possible_answers_type[$row['question_id']]) {
1845
                $temp_id = 'open'.$open_question_iterator;
1846
                $answers_of_user[$row['question_id']][$temp_id] = $row;
1847
                $open_question_iterator++;
1848
            } else {
1849
                $answers_of_user[$row['question_id']][$row['option_id']] = $row;
1850
            }
1851
            $old_user = $row['user'];
1852
        }
1853
1854
        $return = self::export_complete_report_row_xls(
1855
            $survey,
1856
            $possible_answers,
1857
            $answers_of_user,
1858
            $old_user,
1859
            true
1860
        );
1861
1862
        // this is to display the last user
1863
        if (!empty($return)) {
1864
            foreach ($return as $elem) {
1865
                $list[$line][$column] = $elem;
1866
                $column++;
1867
            }
1868
        }
1869
1870
        Export::arrayToXls($list, $filename);
1871
1872
        return null;
1873
    }
1874
1875
    /**
1876
     * Add a line to the csv file.
1877
     *
1878
     * @param array Possible answers
1879
     * @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...
1880
     * @param mixed User ID or user details as string - Used as a string in the result string
1881
     * @param bool Whether to display user fields or not
1882
     *
1883
     * @return array
1884
     */
1885
    public static function export_complete_report_row_xls(
1886
        CSurvey $survey,
1887
        $possible_options,
1888
        $answers_of_user,
1889
        $user,
1890
        $display_extra_user_fields = false
1891
    ) {
1892
        $return = [];
1893
        if (0 == $survey->getAnonymous()) {
1894
            if (0 !== (int) $user) {
1895
                $userInfo = api_get_user_info($user);
1896
                if ($userInfo) {
1897
                    $user_displayed = $userInfo['complete_name_with_username'];
1898
                } else {
1899
                    $user_displayed = '-';
1900
                }
1901
                $return[] = $user_displayed;
1902
            } else {
1903
                $return[] = $user;
1904
            }
1905
        } else {
1906
            $return[] = '-'; // The user column
1907
        }
1908
1909
        if ($display_extra_user_fields) {
1910
            //show user fields data, if any, for this user
1911
            $user_fields_values = UserManager::get_extra_user_data(
1912
                $user,
1913
                false,
1914
                false,
1915
                false,
1916
                true
1917
            );
1918
            foreach ($user_fields_values as $value) {
1919
                $return[] = api_html_entity_decode(strip_tags($value), ENT_QUOTES);
1920
            }
1921
        }
1922
1923
        if (is_array($possible_options)) {
1924
            foreach ($possible_options as $question_id => &$possible_option) {
1925
                if (is_array($possible_option) && count($possible_option) > 0) {
1926
                    foreach ($possible_option as $option_id => &$value) {
1927
                        $my_answers_of_user = $answers_of_user[$question_id] ?? [];
1928
                        $key = array_keys($my_answers_of_user);
1929
                        if (isset($key[0]) && 'open' === substr($key[0], 0, 4)) {
1930
                            $return[] = api_html_entity_decode(
1931
                                strip_tags($answers_of_user[$question_id][$key[0]]['option_id']),
1932
                                ENT_QUOTES
1933
                            );
1934
                        } elseif (!empty($answers_of_user[$question_id][$option_id])) {
1935
                            if (0 != $answers_of_user[$question_id][$option_id]['value']) {
1936
                                $return[] = $answers_of_user[$question_id][$option_id]['value'];
1937
                            } else {
1938
                                $return[] = 'v';
1939
                            }
1940
                        } else {
1941
                            $return[] = '';
1942
                        }
1943
                    }
1944
                }
1945
            }
1946
        }
1947
1948
        return $return;
1949
    }
1950
1951
    /**
1952
     * This function displays the comparative report which
1953
     * allows you to compare two questions
1954
     * A comparative report creates a table where one question
1955
     * is on the x axis and a second question is on the y axis.
1956
     * In the intersection is the number of people who have
1957
     * answered positive on both options.
1958
     *
1959
     * @return string HTML code
1960
     *
1961
     * @author Patrick Cool <[email protected]>, Ghent University
1962
     *
1963
     * @version February 2007
1964
     */
1965
    public static function display_comparative_report()
1966
    {
1967
        $allowed_question_types = ['yesno','multiplechoice','multipleresponse','dropdown','percentage','score'];
1968
        $surveyId = isset($_GET['survey_id']) ? (int) $_GET['survey_id'] : 0;
1969
1970
        $questions = SurveyManager::get_questions($surveyId);
1971
1972
        // Toolbar
1973
        $actions = '<a href="'.api_get_path(WEB_CODE_PATH).'survey/reporting.php?survey_id='.$surveyId.'&'.api_get_cidreq().'">'
1974
            .Display::getMdiIcon(ActionIcon::BACK, 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Back to').' '.get_lang('Reporting overview'))
1975
            .'</a>';
1976
        echo Display::toolbarAction('survey', [$actions]);
1977
1978
        echo Display::return_message(get_lang('Only questions with predefined answers can be used'), 'normal', false);
1979
1980
        $xAxis = isset($_GET['xaxis']) ? Security::remove_XSS($_GET['xaxis']) : '';
1981
        $yAxis = isset($_GET['yaxis']) ? Security::remove_XSS($_GET['yaxis']) : '';
1982
1983
        $url = api_get_path(WEB_CODE_PATH).'survey/reporting.php?'.api_get_cidreq().'&action='.Security::remove_XSS($_GET['action'])
1984
            .'&survey_id='.$surveyId.'&xaxis='.$xAxis.'&y='.$yAxis;
1985
1986
        $form = new FormValidator('compare', 'get', $url);
1987
        $form->addHidden('action', Security::remove_XSS($_GET['action']));
1988
        $form->addHidden('survey_id', $surveyId);
1989
        $form->addHidden('cid', api_get_course_int_id());
1990
        $form->addHidden('sid', api_get_session_id());
1991
        $optionsX = ['----'];
1992
        $optionsY = ['----'];
1993
        $defaults = [];
1994
        foreach ($questions as $key => &$question) {
1995
            if ($question && false !== strpos($question['question'], '{{')) {
1996
                $question = null;
1997
                continue;
1998
            }
1999
            if (in_array($question['type'], $allowed_question_types)) {
2000
                if (isset($_GET['xaxis']) && $_GET['xaxis'] == $question['question_id']) {
2001
                    $defaults['xaxis'] = $question['question_id'];
2002
                }
2003
                if (isset($_GET['yaxis']) && $_GET['yaxis'] == $question['question_id']) {
2004
                    $defaults['yaxis'] = $question['question_id'];
2005
                }
2006
                $optionsX[$question['question_id']] = api_substr(strip_tags($question['question']), 0, 90);
2007
                $optionsY[$question['question_id']] = api_substr(strip_tags($question['question']), 0, 90);
2008
            }
2009
        }
2010
        $form->addSelect('xaxis', get_lang('Select the question on the X axis'), $optionsX);
2011
        $form->addSelect('yaxis', get_lang('Select the question on the Y axis'), $optionsY);
2012
        $form->addButtonSearch(get_lang('Compare questions'));
2013
        $form->setDefaults($defaults);
2014
        $form->display();
2015
2016
        if (is_numeric($xAxis)) { $question_x = SurveyManager::get_question($xAxis); }
2017
        if (is_numeric($yAxis)) { $question_y = SurveyManager::get_question($yAxis); }
2018
2019
        if (is_numeric($xAxis) && is_numeric($yAxis) && !empty($question_x) && !empty($question_y)) {
2020
            $answers_x = self::get_answers_of_question_by_user($surveyId, $xAxis);
2021
            $answers_y = self::get_answers_of_question_by_user($surveyId, $yAxis);
2022
2023
            $tableHtml = '<table class="table table-bordered table-striped table-sm sr-table">';
2024
            $xOptions = [];
2025
2026
            // Header
2027
            $tableHtml .= '<thead><tr>';
2028
            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...
2029
                if (0 == $ii) {
2030
                    $tableHtml .= '<th>&nbsp;</th>';
2031
                } else {
2032
                    if ('score' == $question_x['type']) {
2033
                        for ($x = 1; $x <= $question_x['maximum_score']; $x++) {
2034
                            $tableHtml .= '<th>'.$question_x['answers'][($ii - 1)].'<br />'.$x.'</th>';
2035
                        }
2036
                    } else {
2037
                        $tableHtml .= '<th>'.$question_x['answers'][($ii - 1)].'</th>';
2038
                    }
2039
                    $optionText = html_entity_decode(strip_tags($question_x['answers'][$ii - 1]));
2040
                    $xOptions[] = trim($optionText);
2041
                }
2042
            }
2043
            $tableHtml .= '</tr></thead><tbody>';
2044
2045
            $chartData = [];
2046
            // Body
2047
            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...
2048
                $currentYQuestion = html_entity_decode(strip_tags($question_y['answers'][$ij]));
2049
                if ('score' == $question_y['type']) {
2050
                    for ($y = 1; $y <= $question_y['maximum_score']; $y++) {
2051
                        $tableHtml .= '<tr>';
2052
                        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...
2053
                            if ('score' == $question_x['type']) {
2054
                                for ($x = 1; $x <= $question_x['maximum_score']; $x++) {
2055
                                    if (0 == $ii) {
2056
                                        $tableHtml .= '<th>'.$question_y['answers'][($ij)].' '.$y.'</th>';
2057
                                        break;
2058
                                    } else {
2059
                                        $votes = self::comparative_check(
2060
                                            $answers_x, $answers_y,
2061
                                            $question_x['answersid'][($ii - 1)],
2062
                                            $question_y['answersid'][($ij)],
2063
                                            $x, $y
2064
                                        );
2065
                                        $tableHtml .= '<td align="center">'.$votes.'</td>';
2066
                                        $chartData[] = ['serie' => [$currentYQuestion, $xOptions[$ii - 1]], 'option' => $x, 'votes' => $votes];
2067
                                    }
2068
                                }
2069
                            } else {
2070
                                if (0 == $ii) {
2071
                                    $tableHtml .= '<th>'.$question_y['answers'][$ij].' '.$y.'</th>';
2072
                                } else {
2073
                                    $votes = self::comparative_check(
2074
                                        $answers_x, $answers_y,
2075
                                        $question_x['answersid'][($ii - 1)],
2076
                                        $question_y['answersid'][($ij)],
2077
                                        0, $y
2078
                                    );
2079
                                    $tableHtml .= '<td align="center">'.$votes.'</td>';
2080
                                    $chartData[] = ['serie' => [$currentYQuestion, $xOptions[$ii - 1]], 'option' => $y, 'votes' => $votes];
2081
                                }
2082
                            }
2083
                        }
2084
                        $tableHtml .= '</tr>';
2085
                    }
2086
                } else {
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].'</th>';
2093
                                    break;
2094
                                } else {
2095
                                    $votes = self::comparative_check(
2096
                                        $answers_x, $answers_y,
2097
                                        $question_x['answersid'][($ii - 1)],
2098
                                        $question_y['answersid'][($ij)],
2099
                                        $x, 0
2100
                                    );
2101
                                    $tableHtml .= '<td align="center">'.$votes.'</td>';
2102
                                    $chartData[] = ['serie' => [$currentYQuestion, $xOptions[$ii - 1]], 'option' => $x, 'votes' => $votes];
2103
                                }
2104
                            }
2105
                        } else {
2106
                            if (0 == $ii) {
2107
                                $tableHtml .= '<th>'.$question_y['answers'][($ij)].'</th>';
2108
                            } else {
2109
                                $votes = self::comparative_check(
2110
                                    $answers_x, $answers_y,
2111
                                    $question_x['answersid'][($ii - 1)],
2112
                                    $question_y['answersid'][($ij)]
2113
                                );
2114
                                $tableHtml .= '<td align="center">'.$votes.'</td>';
2115
                                $chartData[] = ['serie' => $xOptions[$ii - 1], 'option' => $currentYQuestion, 'votes' => $votes];
2116
                            }
2117
                        }
2118
                    }
2119
                    $tableHtml .= '</tr>';
2120
                }
2121
            }
2122
            $tableHtml .= '</tbody></table>';
2123
2124
            echo '<div class="sr-container"><div class="sr-card"><div class="sr-card__header">'
2125
                .get_lang('Comparative report').'</div><div class="sr-card__body">';
2126
            echo '<div id="chartContainer" class="sr-block">'.self::drawChart($chartData, true).'</div>';
2127
            echo $tableHtml;
2128
            echo '</div></div></div>';
2129
        }
2130
    }
2131
2132
    /**
2133
     * Get all the answers of a question grouped by user.
2134
     *
2135
     * @param int $survey_id   Survey ID
2136
     * @param int $question_id Question ID
2137
     *
2138
     * @return array Array containing all answers of all users, grouped by user
2139
     *
2140
     * @author Patrick Cool <[email protected]>, Ghent University
2141
     *
2142
     * @version February 2007 - Updated March 2008
2143
     */
2144
    public static function get_answers_of_question_by_user($survey_id, $question_id, $lpItemId = 0)
2145
    {
2146
        $course_id = api_get_course_int_id();
2147
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
2148
2149
        $sessionCondition = '';
2150
        if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
2151
            $sessionId = api_get_session_id();
2152
            $sessionCondition = api_get_session_condition($sessionId);
2153
        }
2154
        $lpItemCondition = '';
2155
        if (!empty($lpItemId)) {
2156
            $lpItemCondition = " AND c_lp_item_id = $lpItemId";
2157
        }
2158
2159
        $sql = "SELECT * FROM $table_survey_answer
2160
                WHERE
2161
                  survey_id='".intval($survey_id)."' AND
2162
                  question_id='".intval($question_id)."'
2163
                  $sessionCondition
2164
                  $lpItemCondition
2165
                ORDER BY USER ASC";
2166
        $result = Database::query($sql);
2167
        $return = [];
2168
        while ($row = Database::fetch_array($result)) {
2169
            if (0 == $row['value']) {
2170
                $return[$row['user']][] = $row['option_id'];
2171
            } else {
2172
                $return[$row['user']][] = $row['option_id'].'*'.$row['value'];
2173
            }
2174
        }
2175
2176
        return $return;
2177
    }
2178
2179
    /**
2180
     * Count the number of users who answer positively on both options.
2181
     *
2182
     * @param array All answers of the x axis
2183
     * @param array All answers of the y axis
2184
     * @param int x axis value (= the option_id of the first question)
2185
     * @param int y axis value (= the option_id of the second question)
2186
     *
2187
     * @return int Number of users who have answered positively to both options
2188
     *
2189
     * @author Patrick Cool <[email protected]>, Ghent University
2190
     *
2191
     * @version February 2007
2192
     */
2193
    public static function comparative_check(
2194
        $answers_x,
2195
        $answers_y,
2196
        $option_x,
2197
        $option_y,
2198
        $value_x = 0,
2199
        $value_y = 0
2200
    ) {
2201
        if (0 == $value_x) {
2202
            $check_x = $option_x;
2203
        } else {
2204
            $check_x = $option_x.'*'.$value_x;
2205
        }
2206
        if (0 == $value_y) {
2207
            $check_y = $option_y;
2208
        } else {
2209
            $check_y = $option_y.'*'.$value_y;
2210
        }
2211
2212
        $counter = 0;
2213
        if (is_array($answers_x)) {
2214
            foreach ($answers_x as $user => &$answers) {
2215
                // Check if the user has given $option_x as answer
2216
                if (in_array($check_x, $answers)) {
2217
                    // Check if the user has given $option_y as an answer
2218
                    if (!is_null($answers_y[$user]) &&
2219
                        in_array($check_y, $answers_y[$user])
2220
                    ) {
2221
                        $counter++;
2222
                    }
2223
                }
2224
            }
2225
        }
2226
2227
        return $counter;
2228
    }
2229
2230
    public static function saveInviteMail(CSurvey $survey, $content, $subject, $remind)
2231
    {
2232
        // Database table definition
2233
        if ($remind) {
2234
            $survey->setReminderMail($content);
2235
        } else {
2236
            $survey->setInviteMail($content);
2237
        }
2238
2239
        $survey->setMailSubject($subject);
2240
        $em = Database::getManager();
2241
        $em->persist($survey);
2242
        $em->flush();
2243
    }
2244
2245
    /**
2246
     * This function saves all the invitations of course users
2247
     * and additional users in the database
2248
     * and sends the invitations by email.
2249
     *
2250
     * @param int    $surveyId
2251
     * @param array  $users_array       Users array can be both a list of course uids AND a list of additional email
2252
     *                                  addresses
2253
     * @param string $invitation_title  title of the mail
2254
     * @param string $invitation_text   text of the mail has to contain a **link** string or
2255
     *                                  this will automatically be added to the end
2256
     * @param int    $reminder
2257
     * @param bool   $sendmail
2258
     * @param int    $remindUnAnswered
2259
     * @param bool   $isAdditionalEmail
2260
     * @param bool   $hideLink
2261
     *
2262
     * @author Patrick Cool <[email protected]>, Ghent University
2263
     * @author Julio Montoya - Adding auto-generated link support
2264
     *
2265
     * @version January 2007
2266
     */
2267
    public static function saveInvitations(
2268
        CSurvey $survey,
2269
        $users_array,
2270
        $invitation_title,
2271
        $invitation_text,
2272
        $reminder = 0,
2273
        $sendmail = false,
2274
        $remindUnAnswered = 0,
2275
        $isAdditionalEmail = false,
2276
        $hideLink = false
2277
    ) {
2278
        $surveyId = $survey->getIid();
2279
2280
        if (!is_array($users_array)) {
2281
            return 0;
2282
        }
2283
        $course = api_get_course_entity();
2284
        $session = api_get_session_entity();
2285
        $survey_invitations = self::get_invitations($surveyId);
2286
        $already_invited = self::get_invited_users($survey);
2287
2288
        // Remind unanswered is a special version of remind all reminder
2289
        $exclude_users = [];
2290
        if (1 == $remindUnAnswered) {
2291
            // Remind only unanswered users
2292
            $reminder = 1;
2293
            $exclude_users = SurveyManager::get_people_who_filled_survey($surveyId);
2294
        }
2295
2296
        $counter = 0; // Nr of invitations "sent" (if sendmail option)
2297
        $course_id = api_get_course_int_id();
2298
        $session_id = api_get_session_id();
2299
2300
        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...
2301
            $result = AbstractResource::separateUsersGroups($users_array);
2302
            $groupList = $result['groups'];
2303
            $users_array = $result['users'];
2304
2305
            foreach ($groupList as $groupId) {
2306
                $group = api_get_group_entity($groupId);
2307
                $userGroupList = GroupManager::getStudents($groupId, true);
2308
                $userGroupIdList = array_column($userGroupList, 'user_id');
2309
                $users_array = array_merge($users_array, $userGroupIdList);
2310
2311
                /*$params = [
2312
                    'c_id' => $course_id,
2313
                    'session_id' => $session_id,
2314
                    'group_id' => $groupId,
2315
                    'survey_code' => $survey_data['code'],
2316
                ];*/
2317
2318
                $invitationExists = self::invitationExists(
2319
                    $course_id,
2320
                    $session_id,
2321
                    $groupId,
2322
                    $survey->getIid()
2323
                );
2324
                if (empty($invitationExists)) {
2325
                    self::saveInvitation(
2326
                        '',
2327
                        '',
2328
                        api_get_utc_datetime(time(), false, true),
2329
                        $survey,
2330
                        $course,
2331
                        $session,
2332
                        $group
2333
                    );
2334
                }
2335
            }
2336
        }
2337
2338
        $users_array = array_unique($users_array);
2339
        foreach ($users_array as $value) {
2340
            if (empty($value)) {
2341
                continue;
2342
            }
2343
2344
            // Skip user if reminding only unanswered people
2345
            if (in_array($value, $exclude_users)) {
2346
                continue;
2347
            }
2348
2349
            // Get the unique invitation code if we already have it
2350
            if (1 == $reminder && array_key_exists($value, $survey_invitations)) {
2351
                $invitation_code = $survey_invitations[$value]['invitation_code'];
2352
            } else {
2353
                $invitation_code = md5($value.microtime());
2354
            }
2355
            $new_user = false; // User not already invited
2356
            // Store the invitation if user_id not in $already_invited['course_users'] OR
2357
            // email is not in $already_invited['additional_users']
2358
            $addit_users_array = isset($already_invited['additional_users']) && !empty($already_invited['additional_users'])
2359
                    ? explode(';', $already_invited['additional_users'])
2360
                    : [];
2361
            $my_alredy_invited = $already_invited['course_users'] ?? [];
2362
2363
            $userId = 0;
2364
            if (is_string($value) && filter_var($value, FILTER_VALIDATE_EMAIL)) {
2365
                $userInfo = api_get_user_info_from_email($value);
2366
                if ($userInfo && isset($userInfo['id'])) {
2367
                    $userId = $userInfo['id'];
2368
                }
2369
            } elseif (is_numeric($value)) {
2370
                $userId = $value;
2371
            }
2372
2373
            if ($userId && !in_array($userId, $my_alredy_invited)) {
2374
                $new_user = true;
2375
                if (!array_key_exists($userId, $survey_invitations)) {
2376
                    self::saveInvitation(
2377
                        api_get_user_entity($userId),
2378
                        $invitation_code,
2379
                        api_get_utc_datetime(time(), null, true),
2380
                        $survey,
2381
                        $course,
2382
                        $session
2383
                    );
2384
                }
2385
            }
2386
2387
            // Send the email if checkboxed
2388
            if (($new_user || 1 == $reminder) && $sendmail) {
2389
                // Make a change for absolute url
2390
                if (isset($invitation_text)) {
2391
                    $invitation_text = api_html_entity_decode($invitation_text, ENT_QUOTES);
2392
                    $invitation_text = str_replace('src="../../', 'src="'.api_get_path(WEB_PATH), $invitation_text);
2393
                    $invitation_text = trim(stripslashes($invitation_text));
2394
                }
2395
                self::sendInvitationMail(
2396
                    $survey,
2397
                    $value,
2398
                    $course,
2399
                    $invitation_code,
2400
                    $invitation_title,
2401
                    $invitation_text,
2402
                    $hideLink
2403
                );
2404
                $counter++;
2405
            }
2406
        }
2407
2408
        return $counter; // Number of invitations sent
2409
    }
2410
2411
    public static function saveInvitation(
2412
        User $user,
2413
        $invitationCode,
2414
        $reminderDate,
2415
        CSurvey $survey,
2416
        Course $course,
2417
        SessionEntity $session = null,
2418
        CGroup $group = null
2419
    ): ?CSurveyInvitation {
2420
        $invitation = new CSurveyInvitation();
2421
        $invitation
2422
            ->setUser($user)
2423
            ->setInvitationCode($invitationCode)
2424
            ->setReminderDate($reminderDate)
2425
            ->setSurvey($survey)
2426
            ->setCourse($course)
2427
            ->setSession($session)
2428
            ->setGroup($group)
2429
        ;
2430
2431
        $em = Database::getManager();
2432
        $em->persist($invitation);
2433
        $em->flush();
2434
2435
        return $invitation;
2436
    }
2437
2438
    /**
2439
     * @param int $courseId
2440
     * @param int $sessionId
2441
     * @param int $groupId
2442
     * @param int $surveyId
2443
     *
2444
     * @return int
2445
     */
2446
    public static function invitationExists($courseId, $sessionId, $groupId, $surveyId)
2447
    {
2448
        $table = Database::get_course_table(TABLE_SURVEY_INVITATION);
2449
        $courseId = (int) $courseId;
2450
        $sessionId = (int) $sessionId;
2451
        $groupId = (int) $groupId;
2452
        $surveyId = (int) $surveyId;
2453
2454
        $sql = "SELECT iid FROM $table
2455
                WHERE
2456
                    c_id = $courseId AND
2457
                    session_id = $sessionId AND
2458
                    group_id = $groupId AND
2459
                    survey_id = $surveyId
2460
                ";
2461
        $result = Database::query($sql);
2462
2463
        return Database::num_rows($result);
2464
    }
2465
2466
    /**
2467
     * Send the invitation by mail.
2468
     *
2469
     * @param int invitedUser - the userId (course user) or emailaddress of additional user
2470
     * @param string $invitation_code - the unique invitation code for the URL
2471
     */
2472
    public static function sendInvitationMail(
2473
        CSurvey $survey,
2474
        $invitedUser,
2475
        Course $course,
2476
        $invitation_code,
2477
        $invitation_title,
2478
        $invitation_text,
2479
        $hideLink = false
2480
    ) {
2481
        $_user = api_get_user_info();
2482
        $sessionId = api_get_session_id();
2483
2484
        // Replacing the **link** part with a valid link for the user
2485
        $link = self::generateFillSurveyLink($survey, $invitation_code, $course, $sessionId);
2486
        if ($hideLink) {
2487
            $full_invitation_text = str_replace('**link**', '', $invitation_text);
2488
        } else {
2489
            $text_link = '<a href="'.$link.'">'.get_lang('Click here to answer the survey')."</a><br />\r\n<br />\r\n"
2490
                .get_lang('or copy paste the following url :')." <br /> \r\n <br /> \r\n ".$link;
2491
2492
            $replace_count = 0;
2493
            $full_invitation_text = api_str_ireplace('**link**', $text_link, $invitation_text, $replace_count);
2494
            if ($replace_count < 1) {
2495
                $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

2495
                $full_invitation_text = /** @scrutinizer ignore-type */ $full_invitation_text."<br />\r\n<br />\r\n".$text_link;
Loading history...
2496
            }
2497
        }
2498
2499
        // Sending the mail
2500
        $sender_user_id = api_get_user_id();
2501
2502
        if ('noreply' === api_get_setting('survey_email_sender_noreply')) {
2503
            $sender_user_id = null;
2504
        }
2505
2506
        // Optionally: finding the e-mail of the course user
2507
        if (is_numeric($invitedUser)) {
2508
            MessageManager::send_message(
2509
                $invitedUser,
2510
                $invitation_title,
2511
                $full_invitation_text,
2512
                [],
2513
                [],
2514
                null,
2515
                null,
2516
                null,
2517
                null,
2518
                $sender_user_id,
2519
                true
2520
            );
2521
        } else {
2522
            @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

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