Passed
Push — master ( 20f08f...fae2d8 )
by Yannick
19:58 queued 11:53
created

SurveyManager::emptySurveyFromId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 20
nc 2
nop 1
dl 0
loc 30
rs 9.6
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Framework\Container;
6
use Chamilo\CourseBundle\Entity\CSurvey;
7
use Chamilo\CourseBundle\Entity\CSurveyInvitation;
8
use Chamilo\CourseBundle\Entity\CSurveyQuestion;
9
use Chamilo\CourseBundle\Entity\CSurveyQuestionOption;
10
11
/**
12
 * Class SurveyManager.
13
 *
14
 * @author Patrick Cool <[email protected]>, Ghent University:
15
 * cleanup, refactoring and rewriting large parts (if not all) of the code
16
 * @author Julio Montoya <[email protected]>, Personality Test modification
17
 * and rewriting large parts of the code
18
 * @author cfasanando
19
 *
20
 * @todo move this file to inc/lib
21
 * @todo use consistent naming for the functions (save vs store for instance)
22
 */
23
class SurveyManager
24
{
25
    /**
26
     * @param $code
27
     *
28
     * @return string
29
     */
30
    public static function generate_unique_code(string $code, ?int $surveyId = null)
31
    {
32
        if (empty($code)) {
33
            return false;
34
        }
35
        $course_id = api_get_course_int_id();
36
        $table = Database::get_course_table(TABLE_SURVEY);
37
        $code = Database::escape_string($code);
38
        $num = 0;
39
        $new_code = $code;
40
        while (true) {
41
42
            if (isset($surveyId)) {
43
                $sql = "SELECT * FROM $table
44
                    WHERE code = '$new_code' AND iid = $surveyId";
45
            } else {
46
                $sql = "SELECT * FROM $table
47
                    WHERE code = '$new_code'";
48
            }
49
50
            $result = Database::query($sql);
51
            if (Database::num_rows($result)) {
52
                $num++;
53
                $new_code = $code.$num;
54
            } else {
55
                break;
56
            }
57
        }
58
59
        return $code.$num;
60
    }
61
62
    /**
63
     * Deletes all survey invitations of a user.
64
     *
65
     * @param int $user_id
66
     *
67
     * @return bool
68
     * @assert ('') === false
69
     */
70
    public static function delete_all_survey_invitations_by_user($user_id)
71
    {
72
        $user_id = (int) $user_id;
73
        if (empty($user_id)) {
74
            return false;
75
        }
76
        $table_survey_invitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
77
        $table_survey = Database::get_course_table(TABLE_SURVEY);
78
79
        $sql = "SELECT iid, survey_id
80
                FROM $table_survey_invitation WHERE user_id = '$user_id' AND c_id <> 0 ";
81
        $result = Database::query($sql);
82
        while ($row = Database::fetch_assoc($result)) {
83
            $survey_invitation_id = $row['iid'];
84
            $surveyId = $row['survey_id'];
85
            $sql2 = "DELETE FROM $table_survey_invitation
86
                     WHERE iid = '$survey_invitation_id' AND c_id <> 0";
87
            if (Database::query($sql2)) {
88
                $sql3 = "UPDATE $table_survey SET
89
                            invited = invited - 1
90
                         WHERE  iid = $surveyId";
91
                Database::query($sql3);
92
            }
93
        }
94
    }
95
96
    /**
97
     * @param string $course_code
98
     * @param int    $session_id
99
     *
100
     * @return array
101
     * @assert ('') === false
102
     */
103
    public static function get_surveys($courseCode, int $sessionId = 0)
104
    {
105
        $courseId = api_get_course_int_id();
106
        $repo = Container::getSurveyRepository();
107
        $course = api_get_course_entity($courseId);
108
        $session = api_get_session_entity($sessionId);
109
110
        $qb = $repo->getResourcesByCourse($course, $session, null, null, true, true);
111
112
        return $qb->getQuery()->getArrayResult();
113
    }
114
115
    /**
116
     * Retrieves all the survey information.
117
     *
118
     * @param int $survey_id the id of the survey
119
     * @param int $shared    this parameter determines if
120
     *                       we have to get the information of a survey from the central (shared) database or from the
121
     *                       course database
122
     * @param string course code optional
123
     *
124
     * @author Patrick Cool <[email protected]>, Ghent University
125
     *
126
     * @version February 2007
127
     * @assert ('') === false
128
     *
129
     * @return array
130
     *
131
     * @todo this is the same function as in create_new_survey.php
132
     */
133
    public static function get_survey(
134
        $survey_id,
135
        $shared = 0,
136
        $course_code = '',
137
        $simple_return = false
138
    ) {
139
        $courseInfo = api_get_course_info();
140
        $survey_id = (int) $survey_id;
141
        $table_survey = Database::get_course_table(TABLE_SURVEY);
142
143
        if (empty($courseInfo)) {
144
            return [];
145
        }
146
        $sql = "SELECT * FROM $table_survey
147
                WHERE iid = $survey_id";
148
149
        $result = Database::query($sql);
150
        $return = [];
151
152
        if (Database::num_rows($result) > 0) {
153
            $return = Database::fetch_assoc($result);
154
            if ($simple_return) {
155
                return $return;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $return also could return the type boolean which is incompatible with the documented return type array.
Loading history...
156
            }
157
            // We do this (temporarily) to have the array match the quickform elements immediately
158
            // idealiter the fields in the db match the quickform fields
159
            $return['survey_code'] = $return['code'];
160
            $return['survey_title'] = $return['title'];
161
            $return['survey_subtitle'] = $return['subtitle'];
162
            $return['survey_language'] = $return['lang'];
163
            $return['start_date'] = $return['avail_from'];
164
            $return['end_date'] = $return['avail_till'];
165
            $return['survey_share'] = $return['is_shared'];
166
            $return['survey_introduction'] = $return['intro'];
167
            $return['survey_thanks'] = $return['surveythanks'];
168
            $return['survey_type'] = $return['survey_type'];
169
            $return['one_question_per_page'] = $return['one_question_per_page'];
170
            $return['show_form_profile'] = $return['show_form_profile'];
171
            $return['input_name_list'] = isset($return['input_name_list']) ? $return['input_name_list'] : null;
172
            $return['shuffle'] = $return['shuffle'];
173
            $return['parent_id'] = $return['parent_id'];
174
            $return['survey_version'] = $return['survey_version'];
175
            $return['anonymous'] = $return['anonymous'];
176
            $return['c_id'] = isset($return['c_id']) ? $return['c_id'] : 0;
177
            $return['session_id'] = isset($return['session_id']) ? $return['session_id'] : 0;
178
        }
179
180
        return $return;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $return also could return the type boolean which is incompatible with the documented return type array.
Loading history...
181
    }
182
183
    /**
184
     * @param string $code
185
     *
186
     * @return string
187
     */
188
    public static function generateSurveyCode($code)
189
    {
190
        return strtolower(CourseManager::generate_course_code($code));
191
    }
192
193
    /**
194
     * This function stores a survey in the database.
195
     *
196
     * @param array $values
197
     *
198
     * @return array $return the type of return message that has to be displayed and the message in it
199
     *
200
     * @author Patrick Cool <[email protected]>, Ghent University
201
     *
202
     * @version February 2007
203
     */
204
    public static function store_survey($values)
205
    {
206
        $session_id = api_get_session_id();
207
        $courseCode = api_get_course_id();
208
        $table_survey = Database::get_course_table(TABLE_SURVEY);
209
        $shared_survey_id = 0;
210
        $displayQuestionNumber = isset($values['display_question_number']);
211
        $repo = Container::getSurveyRepository();
212
213
        if (!isset($values['survey_id'])) {
214
            // Check if the code doesn't soon exists in this language
215
            $sql = 'SELECT 1 FROM '.$table_survey.'
216
			        WHERE
217
			            code = "'.Database::escape_string($values['survey_code']).'" AND
218
			            lang = "'.Database::escape_string($values['survey_language']).'"';
219
            $rs = Database::query($sql);
220
            if (Database::num_rows($rs) > 0) {
221
                Display::addFlash(
222
                    Display::return_message(
223
                        get_lang('This survey code soon exists in this language'),
224
                        'error'
225
                    )
226
                );
227
                $return['type'] = 'error';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.
Loading history...
228
                $return['id'] = isset($values['survey_id']) ? $values['survey_id'] : 0;
229
230
                return $return;
231
            }
232
233
            if (!isset($values['anonymous'])) {
234
                $values['anonymous'] = 0;
235
            }
236
237
            $values['anonymous'] = (int) $values['anonymous'];
238
239
            $survey = new CSurvey();
240
            $extraParams = [];
241
            if (0 == $values['anonymous']) {
242
                // Input_name_list
243
                $values['show_form_profile'] = isset($values['show_form_profile']) ? $values['show_form_profile'] : 0;
244
                $survey->setShowFormProfile($values['show_form_profile']);
245
246
                if (1 == $values['show_form_profile']) {
247
                    // Input_name_list
248
                    $fields = explode(',', $values['input_name_list']);
249
                    $field_values = '';
250
                    foreach ($fields as &$field) {
251
                        if ('' != $field) {
252
                            if ('' == $values[$field]) {
253
                                $values[$field] = 0;
254
                            }
255
                            $field_values .= $field.':'.$values[$field].'@';
256
                        }
257
                    }
258
                    $extraParams['form_fields'] = $field_values;
259
                } else {
260
                    $extraParams['form_fields'] = '';
261
                }
262
                $survey->setFormFields($extraParams['form_fields']);
263
            }
264
265
            $extraParams['one_question_per_page'] = isset($values['one_question_per_page']) ? $values['one_question_per_page'] : 0;
266
            $extraParams['shuffle'] = isset($values['shuffle']) ? $values['shuffle'] : 0;
267
268
            if (1 == $values['survey_type']) {
269
                $survey
270
                    ->setSurveyType(1)
271
                    ->setShuffle($values['shuffle'])
272
                    ->setOneQuestionPerPage($values['one_question_per_page'])
273
                ;
274
                // Logic for versioning surveys
275
                if (!empty($values['parent_id'])) {
276
                    $parentId = (int) $values['parent_id'];
277
                    $sql = 'SELECT survey_version
278
                            FROM '.$table_survey.'
279
					        WHERE
280
					            parent_id = '.$parentId.'
281
                            ORDER BY survey_version DESC
282
                            LIMIT 1';
283
                    $rs = Database::query($sql);
284
                    if (0 === Database::num_rows($rs)) {
285
                        $sql = 'SELECT survey_version FROM '.$table_survey.'
286
						        WHERE
287
						            iid = '.$parentId;
288
                        $rs = Database::query($sql);
289
                        $getversion = Database::fetch_assoc($rs);
290
                        if (empty($getversion['survey_version'])) {
291
                            $versionValue = ++$getversion['survey_version'];
292
                        } else {
293
                            $versionValue = $getversion['survey_version'];
294
                        }
295
                    } else {
296
                        $row = Database::fetch_assoc($rs);
297
                        $pos = api_strpos($row['survey_version'], '.');
298
                        if (false === $pos) {
299
                            $row['survey_version'] = $row['survey_version'] + 1;
300
                            $versionValue = $row['survey_version'];
301
                        } else {
302
                            $getlast = explode('\.', $row['survey_version']);
303
                            $lastversion = array_pop($getlast);
304
                            $lastversion = $lastversion + 1;
305
                            $add = implode('.', $getlast);
306
                            if ('' != $add) {
307
                                $insertnewversion = $add.'.'.$lastversion;
308
                            } else {
309
                                $insertnewversion = $lastversion;
310
                            }
311
                            $versionValue = $insertnewversion;
312
                        }
313
                    }
314
                    $survey->setSurveyVersion($versionValue);
315
                }
316
            }
317
318
            $course = api_get_course_entity();
319
            $session = api_get_session_entity();
320
321
            $survey
322
                ->setCode(self::generateSurveyCode($values['survey_code']))
323
                ->setTitle($values['survey_title'])
324
                ->setSubtitle($values['survey_title'])
325
                ->setLang($values['survey_language'])
326
                ->setAvailFrom(new \DateTime($values['start_date']))
327
                ->setAvailTill(new \DateTime($values['end_date']))
328
                ->setIsShared($shared_survey_id)
329
                ->setTemplate('template')
330
                ->setIntro($values['survey_introduction'])
331
                ->setSurveyThanks($values['survey_thanks'])
332
                ->setDisplayQuestionNumber($displayQuestionNumber)
333
                ->setAnonymous((string) $values['anonymous'])
334
                ->setVisibleResults((int) $values['visible_results'])
335
                ->setSurveyType((int) ($values['survey_type'] ?? 1))
336
                ->setParent($course)
337
                ->addCourseLink($course, $session)
338
            ;
339
340
            if (isset($values['parent_id']) && !empty($values['parent_id'])) {
341
                $parent = $repo->find($values['parent_id']);
342
                $survey->setSurveyParent($parent);
343
            }
344
345
            $repo->create($survey);
346
347
            $survey_id = $survey->getIid();
348
            if ($survey_id > 0) {
349
                Event::addEvent(
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

349
                Event::/** @scrutinizer ignore-call */ 
350
                       addEvent(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
350
                    LOG_SURVEY_CREATED,
351
                    LOG_SURVEY_ID,
352
                    $survey_id,
353
                    null,
354
                    api_get_user_id(),
355
                    api_get_course_int_id(),
356
                    api_get_session_id()
357
                );
358
            }
359
360
            if (1 == $values['survey_type'] && !empty($values['parent_id'])) {
361
                self::copy_survey($values['parent_id'], $survey_id);
362
            }
363
364
            Display::addFlash(
365
                Display::return_message(
366
                    get_lang('The survey has been created succesfully'),
367
                    'success'
368
                )
369
            );
370
            $return['id'] = $survey_id;
371
        } else {
372
            // Check whether the code doesn't soon exists in this language
373
            $sql = 'SELECT 1 FROM '.$table_survey.'
374
			        WHERE
375
			            code = "'.Database::escape_string($values['survey_code']).'" AND
376
			            lang = "'.Database::escape_string($values['survey_language']).'" AND
377
			            iid !='.intval($values['survey_id']);
378
            $rs = Database::query($sql);
379
            if (Database::num_rows($rs) > 0) {
380
                Display::addFlash(
381
                    Display::return_message(
382
                        get_lang('This survey code soon exists in this language'),
383
                        'error'
384
                    )
385
                );
386
                $return['type'] = 'error';
387
                $return['id'] = isset($values['survey_id']) ? $values['survey_id'] : 0;
388
389
                return $return;
390
            }
391
392
            if (!isset($values['anonymous'])
393
                || (isset($values['anonymous']) && '' == $values['anonymous'])
394
            ) {
395
                $values['anonymous'] = 0;
396
            }
397
398
            /** @var CSurvey $survey */
399
            $survey = $repo->find($values['survey_id']);
400
401
            $survey->setOneQuestionPerPage(isset($values['one_question_per_page']) ? $values['one_question_per_page'] : 0);
402
            $survey->setShuffle(isset($values['shuffle']) ? $values['shuffle'] : 0);
403
404
            if (0 == $values['anonymous']) {
405
                $survey->setShowFormProfile(isset($values['show_form_profile']) ? $values['show_form_profile'] : 0);
406
                $isFormProfile = isset($values['show_form_profile']) ? $values['show_form_profile'] : 0;
407
                if (1 == $isFormProfile) {
408
                    $fields = explode(',', $values['input_name_list']);
409
                    $field_values = '';
410
                    foreach ($fields as &$field) {
411
                        if ('' != $field) {
412
                            if (!isset($values[$field]) ||
413
                                (isset($values[$field]) && '' == $values[$field])
414
                            ) {
415
                                $values[$field] = 0;
416
                            }
417
                            $field_values .= $field.':'.$values[$field].'@';
418
                        }
419
                    }
420
                    $survey->setFormFields($field_values);
421
                } else {
422
                    $survey->setFormFields('');
423
                }
424
            } else {
425
                $survey->setFormFields('');
426
                $survey->setShowFormProfile(0);
427
            }
428
429
            $survey
430
                ->setTitle($values['survey_title'])
431
                ->setSubtitle($values['survey_title'])
432
                ->setLang($values['survey_language'])
433
                ->setAvailFrom(new \DateTime($values['start_date']))
434
                ->setAvailTill(new \DateTime($values['end_date']))
435
                ->setIsShared($shared_survey_id)
436
                ->setTemplate('template')
437
                ->setIntro($values['survey_introduction'])
438
                ->setSurveyThanks($values['survey_thanks'])
439
                ->setAnonymous((string) $values['anonymous'])
440
                ->setVisibleResults((int) $values['visible_results'])
441
                ->setDisplayQuestionNumber($displayQuestionNumber)
442
            ;
443
444
            $repo->update($survey);
445
            /*
446
            // Update into item_property (update)
447
            api_item_property_update(
448
                api_get_course_info(),
449
                TOOL_SURVEY,
450
                $values['survey_id'],
451
                'SurveyUpdated',
452
                api_get_user_id()
453
            );*/
454
455
            Display::addFlash(
456
                Display::return_message(
457
                    get_lang('The survey has been updated succesfully'),
458
                    'confirmation'
459
                )
460
            );
461
462
            $return['id'] = $values['survey_id'];
463
        }
464
465
        $survey_id = (int) $return['id'];
466
467
        // Gradebook
468
        $gradebook_option = false;
469
        if (isset($values['survey_qualify_gradebook'])) {
470
            $gradebook_option = $values['survey_qualify_gradebook'] > 0;
471
        }
472
473
        $gradebook_link_type = 8;
474
        $link_info = GradebookUtils::isResourceInCourseGradebook(
475
            api_get_course_int_id(),
476
            $gradebook_link_type,
477
            $survey_id,
478
            $session_id
479
        );
480
481
        $gradebook_link_id = isset($link_info['id']) ? $link_info['id'] : false;
482
483
        if ($gradebook_option) {
484
            if ($survey_id > 0) {
485
                $title_gradebook = ''; // Not needed here.
486
                $description_gradebook = ''; // Not needed here.
487
                $survey_weight = floatval($_POST['survey_weight']);
488
                $max_score = 1;
489
490
                if (!$gradebook_link_id && isset($values['category_id'])) {
491
                    GradebookUtils::add_resource_to_course_gradebook(
492
                        $values['category_id'],
493
                        api_get_course_int_id(),
494
                        $gradebook_link_type,
495
                        $survey_id,
496
                        $title_gradebook,
497
                        $survey_weight,
498
                        $max_score,
499
                        $description_gradebook,
500
                        1,
501
                        $session_id
502
                    );
503
                } else {
504
                    GradebookUtils::updateResourceFromCourseGradebook(
505
                        $gradebook_link_id,
506
                        api_get_course_int_id(),
507
                        $survey_weight
508
                    );
509
                }
510
            }
511
        } else {
512
            // Delete everything of the gradebook for this $linkId
513
            GradebookUtils::remove_resource_from_course_gradebook($gradebook_link_id);
514
        }
515
516
        return $return;
517
    }
518
519
    public static function deleteSurvey(CSurvey $survey)
520
    {
521
        $repo = Container::getSurveyRepository();
522
        $surveyId = $survey->getIid();
523
        $repo->delete($survey);
524
525
        $table_survey = Database::get_course_table(TABLE_SURVEY);
526
        $table_survey_question_group = Database::get_course_table(TABLE_SURVEY_QUESTION_GROUP);
527
528
        Event::addEvent(
529
            LOG_SURVEY_DELETED,
530
            LOG_SURVEY_ID,
531
            $surveyId,
532
            null,
533
            api_get_user_id(),
534
            api_get_course_int_id(),
535
            api_get_session_id()
536
        );
537
538
        /*// Deleting groups of this survey
539
        $sql = "DELETE FROM $table_survey_question_group
540
                WHERE c_id = $course_id AND iid='".$survey_id."'";
541
        Database::query($sql);*/
542
543
        // Deleting the questions of the survey
544
        self::delete_all_survey_questions($surveyId, false);
545
546
        // Update into item_property (delete)
547
        /*api_item_property_update(
548
            $course_info,
549
            TOOL_SURVEY,
550
            $survey_id,
551
            'SurveyDeleted',
552
            api_get_user_id()
553
        );*/
554
555
        SkillModel::deleteSkillsFromItem($surveyId, ITEM_TYPE_SURVEY);
556
557
        return true;
558
    }
559
560
    /**
561
     * Copy given survey to a new (optional) given survey ID.
562
     *
563
     * @param int $survey_id
564
     * @param int $new_survey_id
565
     * @param int $targetCourseId
566
     *
567
     * @return bool
568
     */
569
    public static function copy_survey($survey_id, $new_survey_id = null, $targetCourseId = null)
570
    {
571
        $course_id = api_get_course_int_id();
572
        if (!$targetCourseId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $targetCourseId of type integer|null is loosely compared to false; 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...
573
            $targetCourseId = $course_id;
574
        }
575
576
        // Database table definitions
577
        $table_survey = Database::get_course_table(TABLE_SURVEY);
578
        $table_survey_question_group = Database::get_course_table(TABLE_SURVEY_QUESTION_GROUP);
579
        $table_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
580
        $table_survey_options = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
581
        $survey_id = (int) $survey_id;
582
        $repo = Container::getSurveyRepository();
583
584
        // Get groups
585
        $survey_data = self::get_survey($survey_id, 0, null, true);
586
        if (empty($survey_data)) {
587
            return true;
588
        }
589
590
        if (empty($new_survey_id)) {
591
            $params = $survey_data;
592
            $params['code'] = self::generate_unique_code($params['code']);
593
            $params['c_id'] = $targetCourseId;
594
            unset($params['survey_id']);
595
            $params['session_id'] = api_get_session_id();
596
            $params['title'] = $params['title'].' '.get_lang('Copy');
597
            unset($params['iid']);
598
            $params['invited'] = 0;
599
            $params['answered'] = 0;
600
601
            $course = api_get_course_entity();
602
            $session = api_get_session_entity();
603
            $survey = new CSurvey();
604
            $survey->setShowFormProfile($params['show_form_profile']);
605
            $survey->setFormFields($params['form_fields']);
606
            $survey
607
                ->setSurveyType($params['survey_type'])
608
                ->setShuffle($params['shuffle'])
609
                ->setOneQuestionPerPage($params['one_question_per_page'])
610
            ;
611
            $survey->setSurveyVersion($params['survey_version']);
612
            $survey
613
                ->setCode($params['code'])
614
                ->setTitle($params['title'])
615
                ->setSubtitle($params['subtitle'])
616
                ->setLang($params['lang'])
617
                ->setAvailFrom(new \DateTime($params['avail_from']))
618
                ->setAvailTill(new \DateTime($params['avail_till']))
619
                ->setIsShared($params['is_shared'])
620
                ->setTemplate($params['template'])
621
                ->setIntro($params['intro'])
622
                ->setSurveyThanks($params['surveythanks'])
623
                ->setAnonymous($params['anonymous'])
624
                ->setVisibleResults($params['visible_results'])
625
                ->setSurveyType($params['survey_type'])
626
                ->setParent($course)
627
                ->addCourseLink($course, $session)
628
            ;
629
630
            if (isset($params['parent_id']) && !empty($params['parent_id'])) {
631
                $parent = $repo->find($params['parent_id']);
632
                $survey->setSurveyParent($parent);
633
            }
634
635
            $repo->create($survey);
636
            $new_survey_id = $survey->getIid();
637
        } else {
638
            $new_survey_id = (int) $new_survey_id;
639
        }
640
641
        /*$sql = "SELECT * FROM $table_survey_question_group
642
                WHERE iid = $survey_id";
643
644
        $res = Database::query($sql);
645
        while ($row = Database::fetch_assoc($res)) {
646
            $params = [
647
                'c_id' => $targetCourseId,
648
                'name' => $row['title'],
649
                'description' => $row['description'],
650
                'survey_id' => $new_survey_id,
651
            ];
652
653
            $insertId = Database::insert($table_survey_question_group, $params);
654
655
            $sql = "UPDATE $table_survey_question_group SET id = iid
656
                    WHERE iid = $insertId";
657
            Database::query($sql);
658
659
            $group_id[$row['id']] = $insertId;
660
        }*/
661
662
        // Get questions
663
        $sql = "SELECT * FROM $table_survey_question
664
                WHERE survey_id = $survey_id";
665
666
        $res = Database::query($sql);
667
        while ($row = Database::fetch_assoc($res)) {
668
            $params = [
669
                'survey_id' => $new_survey_id,
670
                'survey_question' => $row['survey_question'],
671
                'survey_question_comment' => $row['survey_question_comment'],
672
                'type' => $row['type'],
673
                'display' => $row['display'],
674
                'sort' => $row['sort'],
675
                'shared_question_id' => $row['shared_question_id'],
676
                'max_value' => $row['max_value'],
677
                'survey_group_pri' => $row['survey_group_pri'],
678
                'survey_group_sec1' => $row['survey_group_sec1'],
679
                'survey_group_sec2' => $row['survey_group_sec2'],
680
            ];
681
682
            if (isset($row['is_required'])) {
683
                $params['is_required'] = $row['is_required'];
684
            }
685
686
            $insertId = Database::insert($table_survey_question, $params);
687
            if ($insertId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $insertId of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false 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...
688
                /*$sql = "UPDATE $table_survey_question SET question_id = iid WHERE iid = $insertId";
689
                Database::query($sql);*/
690
                $question_id[$row['iid']] = $insertId;
691
            }
692
        }
693
694
        // Get questions options
695
        $sql = "SELECT * FROM $table_survey_options
696
                WHERE survey_id='".$survey_id."'";
697
698
        $res = Database::query($sql);
699
        while ($row = Database::fetch_assoc($res)) {
700
            $params = [
701
                'question_id' => $question_id[$row['question_id']],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $question_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
702
                'survey_id' => $new_survey_id,
703
                'option_text' => $row['option_text'],
704
                'sort' => $row['sort'],
705
                'value' => $row['value'],
706
            ];
707
            $insertId = Database::insert($table_survey_options, $params);
708
        }
709
710
        return $new_survey_id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $new_survey_id also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
711
    }
712
713
    /**
714
     * This function duplicates a survey (and also all the question in that survey.
715
     *
716
     * @param int $surveyId id of the survey that has to be duplicated
717
     * @param int $courseId id of the course which survey has to be duplicated
718
     *
719
     * @return true
720
     *
721
     * @author Eric Marguin <[email protected]>, Elixir Interactive
722
     *
723
     * @version October 2007
724
     */
725
    public static function empty_survey($surveyId, $courseId = 0)
726
    {
727
        // Database table definitions
728
        $table_survey_invitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
729
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
730
        $table_survey = Database::get_course_table(TABLE_SURVEY);
731
732
        $courseId = (int) $courseId;
733
        $courseId = empty($courseId) ? api_get_course_int_id() : $courseId;
734
        $surveyId = (int) $surveyId;
735
736
        $datas = self::get_survey($surveyId);
737
        $session_where = '';
738
        if (0 != api_get_session_id()) {
739
            $session_where = ' AND session_id = "'.api_get_session_id().'" ';
740
        }
741
742
        $sql = 'DELETE FROM '.$table_survey_invitation.'
743
		        WHERE
744
		            survey_id = "'.$surveyId.'" '.$session_where.' ';
745
        Database::query($sql);
746
747
        $sql = 'DELETE FROM '.$table_survey_answer.'
748
		        WHERE
749
		        survey_id = "'.$surveyId.'" '.$session_where.' ';
750
        Database::query($sql);
751
752
        $sql = 'UPDATE '.$table_survey.' SET invited=0, answered=0
753
		        WHERE iid ='.$surveyId;
754
        Database::query($sql);
755
756
        Event::addEvent(
757
            LOG_SURVEY_CLEAN_RESULTS,
758
            LOG_SURVEY_ID,
759
            $surveyId,
760
            null,
761
            api_get_user_id(),
762
            api_get_course_int_id(),
763
            api_get_session_id()
764
        );
765
766
        return true;
767
    }
768
769
    /**
770
     * Updates c_survey.answered: number of people who have taken the survey (=filled at least one question).
771
     */
772
    public static function updateSurveyAnswered(CSurvey $survey, $user, $lpItemId = 0): void
773
    {
774
        $em = Database::getManager();
775
        $surveyId = $survey->getIid();
776
        $courseId = api_get_course_int_id();
777
        $sessionId = api_get_session_id();
778
779
        $survey->setAnswered($survey->getAnswered() + 1);
780
        $em->persist($survey);
781
        $em->flush();
782
783
        $lpItemCondition = '';
784
        if (!empty($lpItemId)) {
785
            $lpItemCondition = " AND c_lp_item_id = $lpItemId";
786
        }
787
        $sessionCondition = '';
788
        if (true === api_get_configuration_value('show_surveys_base_in_sessions')) {
789
            $sessionCondition = api_get_session_condition($sessionId);
790
        }
791
792
        $table = Database::get_course_table(TABLE_SURVEY_INVITATION);
793
        // Storing that the user has finished the survey.
794
        $sql = "UPDATE $table
795
                SET
796
                    answered_at = '".api_get_utc_datetime()."',
797
                    answered = 1
798
                WHERE
799
                    session_id = $sessionId AND
800
                    user_id ='".Database::escape_string($user)."' AND
801
                    survey_id ='".$surveyId."'
802
                    $sessionCondition
803
                    $lpItemCondition";
804
        Database::query($sql);
805
    }
806
807
    /**
808
     * This function return the "icon" of the question type.
809
     *
810
     * @param string $type
811
     *
812
     * @author Patrick Cool <[email protected]>, Ghent University
813
     *
814
     * @version February 2007
815
     */
816
    public static function icon_question($type)
817
    {
818
        // the possible question types
819
        $possible_types = [
820
            'personality',
821
            'yesno',
822
            'multiplechoice',
823
            'multipleresponse',
824
            'open',
825
            'dropdown',
826
            'comment',
827
            'pagebreak',
828
            'percentage',
829
            'score',
830
        ];
831
832
        // the images array
833
        $icon_question = [
834
            'yesno' => 'thumbs-up-down',
835
            'personality' => 'thumbs-up-down',
836
            'multiplechoice' => 'format-list-bulleted',
837
            'multipleresponse' => 'format-list-bulleted-square',
838
            'open' => 'form-textarea',
839
            'dropdown' => 'form-dropdown',
840
            'percentage' => 'percent-box-outline',
841
            'score' => 'format-annotation-plus',
842
            'comment' => 'format-align-top',
843
            'pagebreak' => 'format-page-break',
844
        ];
845
846
        if (in_array($type, $possible_types)) {
847
            return $icon_question[$type];
848
        }
849
850
        return false;
851
    }
852
853
    /**
854
     * This function retrieves all the information of a question.
855
     *
856
     * @param int  $question_id the id of the question
857
     * @param bool $shared
858
     *
859
     * @return array
860
     *
861
     * @author Patrick Cool <[email protected]>, Ghent University
862
     *
863
     * @version January 2007
864
     *
865
     * @todo one sql call should do the trick
866
     */
867
    public static function get_question($question_id)
868
    {
869
        $tbl_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
870
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
871
        $course_id = api_get_course_int_id();
872
        $question_id = (int) $question_id;
873
874
        if (empty($question_id)) {
875
            return [];
876
        }
877
878
        $sql = "SELECT * FROM $tbl_survey_question
879
                WHERE iid = $question_id
880
                ORDER BY `sort` ";
881
882
        $sqlOption = "  SELECT * FROM $table_survey_question_option
883
                        WHERE question_id='".$question_id."'
884
                        ORDER BY `sort` ";
885
        // Getting the information of the question
886
        $result = Database::query($sql);
887
        $row = Database::fetch_assoc($result);
888
889
        $return['survey_id'] = $row['survey_id'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.
Loading history...
890
        $return['parent_id'] = isset($row['parent_id']) ? $row['parent_id'] : 0;
891
        $return['parent_option_id'] = isset($row['parent_option_id']) ? $row['parent_option_id'] : 0;
892
        $return['question_id'] = $row['iid'];
893
        $return['type'] = $row['type'];
894
        $return['question'] = $row['survey_question'];
895
        $return['horizontalvertical'] = $row['display'];
896
        $return['shared_question_id'] = $row['shared_question_id'];
897
        $return['maximum_score'] = $row['max_value'];
898
        $return['is_required'] = $row['is_required'];
899
900
        if (0 != $row['survey_group_pri']) {
901
            $return['assigned'] = $row['survey_group_pri'];
902
            $return['choose'] = 1;
903
        } else {
904
            $return['assigned1'] = $row['survey_group_sec1'];
905
            $return['assigned2'] = $row['survey_group_sec2'];
906
            $return['choose'] = 2;
907
        }
908
909
        // Getting the information of the question options
910
        $result = Database::query($sqlOption);
911
        $counter = 0;
912
        while ($row = Database::fetch_assoc($result)) {
913
            /** @todo this should be renamed to options instead of answers */
914
            $return['answers'][] = $row['option_text'];
915
            $return['values'][] = $row['value'];
916
            $return['answer_data'][$counter]['data'] = $row['option_text'];
917
            $return['answer_data'][$counter]['iid'] = $row['iid'];
918
            /** @todo this can be done more elegantly (used in reporting) */
919
            $return['answersid'][] = $row['iid'];
920
            $counter++;
921
        }
922
923
        return $return;
924
    }
925
926
    /**
927
     * This function gets all the question of any given survey.
928
     *
929
     * @param int $surveyId the id of the survey
930
     * @param int $courseId
931
     *
932
     * @return array containing all the questions of the survey
933
     *
934
     * @author Patrick Cool <[email protected]>, Ghent University
935
     *
936
     * @version February 2007
937
     *
938
     * @todo one sql call should do the trick
939
     */
940
    public static function get_questions($surveyId, $courseId = 0)
941
    {
942
        $tbl_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
943
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
944
945
        $courseId = (int) $courseId;
946
        $surveyId = (int) $surveyId;
947
948
        // Getting the information of the question
949
        $sql = "SELECT * FROM $tbl_survey_question
950
		        WHERE survey_id= $surveyId ";
951
        $result = Database::query($sql);
952
        $questions = [];
953
        while ($row = Database::fetch_assoc($result)) {
954
            $questionId = $row['iid'];
955
            $questions[$questionId]['survey_id'] = $surveyId;
956
            $questions[$questionId]['question_id'] = $questionId;
957
            $questions[$questionId]['type'] = $row['type'];
958
            $questions[$questionId]['question'] = $row['survey_question'];
959
            $questions[$questionId]['horizontalvertical'] = $row['display'];
960
            $questions[$questionId]['maximum_score'] = $row['max_value'];
961
            $questions[$questionId]['sort'] = $row['sort'];
962
            $questions[$questionId]['survey_question_comment'] = $row['survey_question_comment'];
963
964
            // Getting the information of the question options
965
            $sql = "SELECT * FROM $table_survey_question_option
966
		             WHERE survey_id= $surveyId  AND question_id = $questionId";
967
            $resultOptions = Database::query($sql);
968
            while ($rowOption = Database::fetch_assoc($resultOptions)) {
969
                $questions[$questionId]['answers'][] = $rowOption['option_text'];
970
            }
971
        }
972
973
        return $questions;
974
    }
975
976
    public static function saveQuestion(CSurvey $survey, array $form_content, bool $showMessage = true, array $dataFromDatabase = [])
977
    {
978
        $surveyId = $survey->getIid();
979
980
        $error = false;
981
        $message = '';
982
        if (strlen($form_content['question']) > 1) {
983
            // Checks length of the question
984
            $empty_answer = false;
985
            if (1 == $survey->getSurveyType()) {
986
                if (empty($form_content['choose'])) {
987
                    return 'PleaseChooseACondition';
988
                }
989
990
                if ((2 == $form_content['choose']) &&
991
                    ($form_content['assigned1'] == $form_content['assigned2'])
992
                ) {
993
                    return 'ChooseDifferentCategories';
994
                }
995
            }
996
997
            if ('percentage' !== $form_content['type']) {
998
                if (isset($form_content['answers'])) {
999
                    for ($i = 0; $i < count($form_content['answers']); $i++) {
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...
1000
                        if (strlen($form_content['answers'][$i]) < 1) {
1001
                            $empty_answer = true;
1002
                            break;
1003
                        }
1004
                    }
1005
                }
1006
            }
1007
1008
            if ('score' === $form_content['type']) {
1009
                if (strlen($form_content['maximum_score']) < 1) {
1010
                    $empty_answer = true;
1011
                }
1012
            }
1013
1014
            $em = Database::getManager();
1015
            $course_id = api_get_course_int_id();
1016
            if (!$empty_answer) {
1017
                // Table definitions
1018
                $tbl_survey_question = Database::get_course_table(TABLE_SURVEY_QUESTION);
1019
1020
                // Getting all the information of the survey
1021
                $survey_data = self::get_survey($surveyId);
1022
1023
                // Storing a new question
1024
                if (empty($form_content['question_id']) || !is_numeric($form_content['question_id'])) {
1025
                    // Finding the max sort order of the questions in the given survey
1026
                    $sql = "SELECT max(sort) AS max_sort
1027
					        FROM $tbl_survey_question
1028
                            WHERE survey_id = $surveyId ";
1029
                    $result = Database::query($sql);
1030
                    $row = Database::fetch_assoc($result);
1031
                    $max_sort = $row['max_sort'];
1032
1033
                    $question = new CSurveyQuestion();
1034
1035
                    // Some variables defined for survey-test type
1036
                    if (isset($_POST['choose'])) {
1037
                        if (1 == $_POST['choose']) {
1038
                            $question->setSurveyGroupPri($_POST['assigned']);
1039
                        } elseif (2 == $_POST['choose']) {
1040
                            $question->setSurveyGroupSec1($_POST['assigned1']);
1041
                            $question->setSurveyGroupSec2($_POST['assigned2']);
1042
                        }
1043
                    }
1044
1045
                    $question
1046
                        ->setSurveyQuestionComment($form_content['question_comment'] ?? '')
1047
                        ->setMaxValue($form_content['maximum_score'] ?? 0)
1048
                        ->setDisplay($form_content['horizontalvertical'] ?? '')
1049
                        //->setCId($course_id)
1050
                        ->setSurvey($survey)
1051
                        ->setSurveyQuestion($form_content['question'])
1052
                        ->setType($form_content['type'])
1053
                        ->setSort($max_sort + 1)
1054
                        ->setSharedQuestionId((int) $form_content['shared_question_id'])
1055
                    ;
1056
1057
                    $question->setIsMandatory(isset($form_content['is_required']));
1058
1059
                    $params['parent_id'] = 0;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
1060
                    $params['parent_option_id'] = 0;
1061
                    if (isset($form_content['parent_id']) &&
1062
                        isset($form_content['parent_option_id']) &&
1063
                        !empty($form_content['parent_id']) &&
1064
                        !empty($form_content['parent_option_id'])
1065
                    ) {
1066
                        $params['parent_id'] = $form_content['parent_id'];
1067
                        $params['parent_option_id'] = $form_content['parent_option_id'];
1068
                    }
1069
1070
                    $em->persist($question);
1071
                    $em->flush();
1072
1073
                    $question_id = $question->getIid();
1074
                    if ($question_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $question_id 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...
1075
                        /*$sql = "UPDATE $tbl_survey_question SET question_id = $question_id
1076
                                WHERE iid = $question_id";
1077
                        Database::query($sql);*/
1078
                        $form_content['question_id'] = $question_id;
1079
                        $message = 'The question has been added.';
1080
                        $error = false;
1081
                    }
1082
                } else {
1083
                    $repo = $em->getRepository(CSurveyQuestion::class);
1084
                    $repoOption = $em->getRepository(CSurveyQuestionOption::class);
1085
                    /** @var CSurveyQuestion $question */
1086
                    $question = $repo->find($form_content['question_id']);
1087
1088
                    if (isset($_POST['choose'])) {
1089
                        if (1 == $_POST['choose']) {
1090
                            $question->setSurveyGroupPri($_POST['assigned']);
1091
                            $question->setSurveyGroupSec1(0);
1092
                            $question->setSurveyGroupSec2(0);
1093
                        } elseif (2 == $_POST['choose']) {
1094
                            $question->setSurveyGroupPri(0);
1095
                            $question->setSurveyGroupSec1($_POST['assigned1']);
1096
                            $question->setSurveyGroupSec2($_POST['assigned2']);
1097
                        }
1098
                    }
1099
1100
                    $maxScore = isset($form_content['maximum_score']) ? (int) $form_content['maximum_score'] : 0;
1101
                    $questionComment = $form_content['question_comment'] ?? '';
1102
                    $question
1103
                        ->setSurveyQuestionComment($questionComment)
1104
                        ->setSurveyQuestion($form_content['question'])
1105
                        ->setDisplay($form_content['horizontalvertical'])
1106
                        ->setMaxValue($maxScore)
1107
                    ;
1108
1109
                    $question->isMandatory(isset($form_content['is_required']));
1110
                    $params['parent_id'] = 0;
1111
                    $params['parent_option_id'] = 0;
1112
                    if (isset($form_content['parent_id']) &&
1113
                        isset($form_content['parent_option_id']) &&
1114
                        !empty($form_content['parent_id']) &&
1115
                        !empty($form_content['parent_option_id'])
1116
                    ) {
1117
                        $question->setParent($repo->find($form_content['parent_id']));
1118
                        $question->setParentOption($repoOption->find($form_content['parent_option_id']));
1119
                    }
1120
1121
                    $em->persist($question);
1122
                    $em->flush();
1123
                    $message = 'QuestionUpdated';
1124
                    $error = false;
1125
                }
1126
                // Storing the options of the question
1127
                self::saveQuestionOptions($survey, $question, $form_content, $dataFromDatabase);
1128
            } else {
1129
                $message = 'PleasFillAllAnswer';
1130
                $error = true;
1131
            }
1132
        } else {
1133
            $message = 'PleaseEnterAQuestion';
1134
            $error = true;
1135
        }
1136
1137
        if ($showMessage) {
1138
            if (!empty($message)) {
1139
                Display::addFlash(Display::return_message(get_lang($message)));
1140
            }
1141
        }
1142
        $result = [
1143
            'error' => $error,
1144
            'message' => $message,
1145
        ];
1146
1147
        return $result;
1148
    }
1149
1150
    /**
1151
     * Moves a survey question within the ordered list.
1152
     *
1153
     * @param string $direction The direction to move the question ('moveup' or 'movedown').
1154
     * @param int    $surveyQuestionId The ID of the survey question to move.
1155
     * @param int    $surveyId The ID of the survey to which the question belongs.
1156
     */
1157
    public static function moveSurveyQuestion(string $direction, int $surveyQuestionId, int $surveyId): void
1158
    {
1159
        $em = Database::getManager();
1160
        $repo = $em->getRepository(CSurveyQuestion::class);
1161
        $sortDirection = $direction === 'moveup' ? 'DESC' : 'ASC';
1162
        $questions = $repo->findBy(['survey' => $surveyId], ['sort' => $sortDirection]);
1163
1164
        $found = false;
1165
        foreach ($questions as $question) {
1166
            if ($found) {
1167
                $secondQuestion = $question;
1168
                $found = false;
1169
                break;
1170
            }
1171
            if ($question->getIid() == $surveyQuestionId) {
1172
                $found = true;
1173
                $firstQuestion = $question;
1174
            }
1175
        }
1176
1177
        if (isset($firstQuestion) && isset($secondQuestion)) {
1178
            $tempSort = $firstQuestion->getSort();
1179
            $firstQuestion->setSort($secondQuestion->getSort());
1180
            $secondQuestion->setSort($tempSort);
1181
1182
            $em->persist($firstQuestion);
1183
            $em->persist($secondQuestion);
1184
            $em->flush();
1185
        }
1186
    }
1187
1188
    /**
1189
     * This function deletes all the questions of a given survey
1190
     * This function is normally only called when a survey is deleted.
1191
     *
1192
     * @param int $survey_id the id of the survey that has to be deleted
1193
     *
1194
     * @return bool
1195
     *
1196
     * @author Patrick Cool <[email protected]>, Ghent University
1197
     *
1198
     * @version January 2007
1199
     */
1200
    public static function delete_all_survey_questions($survey_id, $shared = false)
1201
    {
1202
        $course_id = api_get_course_int_id();
1203
        $survey_id = (int) $survey_id;
1204
1205
        /*$sql = "DELETE FROM $table_survey_question
1206
                WHERE $course_condition survey_id = '".$survey_id."'";
1207
1208
        // Deleting the survey questions
1209
        Database::query($sql);*/
1210
1211
        // Deleting all the options of the questions of the survey
1212
        //self::delete_all_survey_questions_options($survey_id, $shared);
1213
1214
        // Deleting all the answers on this survey
1215
        //self::delete_all_survey_answers($survey_id);
1216
1217
        return true;
1218
    }
1219
1220
    public static function deleteQuestion($questionId)
1221
    {
1222
        $questionId = (int) $questionId;
1223
        if (empty($questionId)) {
1224
            return false;
1225
        }
1226
1227
        $em = Database::getManager();
1228
        $repo = Container::getSurveyQuestionRepository();
1229
        $question = $repo->find($questionId);
1230
        if ($question) {
1231
            $em->remove($question);
1232
            $em->flush();
1233
1234
            return true;
1235
        }
1236
1237
        return false;
1238
    }
1239
1240
    public static function saveQuestionOptions(CSurvey $survey, CSurveyQuestion $question, $form_content, $dataFromDatabase = [])
1241
    {
1242
        $course_id = api_get_course_int_id();
1243
        $type = $form_content['type'];
1244
1245
        // A percentage question type has options 1 -> 100
1246
        if ('percentage' === $type) {
1247
            for ($i = 1; $i < 101; $i++) {
1248
                $form_content['answers'][] = $i;
1249
            }
1250
        }
1251
        $em = Database::getManager();
1252
1253
        // Table definition
1254
        $table = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
1255
1256
        // We are editing a question so we first have to remove all the existing options from the database
1257
        $optionsToDelete = [];
1258
        if (isset($dataFromDatabase['answer_data'])) {
1259
            foreach ($dataFromDatabase['answer_data'] as $data) {
1260
                if ('other' === $data['data'] && 'multiplechoiceother' === $type) {
1261
                    continue;
1262
                }
1263
1264
                if (!in_array($data['iid'], $form_content['answersid'])) {
1265
                    $optionsToDelete[] = $data['iid'];
1266
                }
1267
            }
1268
        }
1269
1270
        if (!empty($optionsToDelete)) {
1271
            foreach ($optionsToDelete as $iid) {
1272
                $iid = (int) $iid;
1273
                $sql = "DELETE FROM $table
1274
			            WHERE
1275
			                iid = $iid AND
1276
                            question_id = '".intval($form_content['question_id'])."'
1277
                            ";
1278
                Database::query($sql);
1279
            }
1280
        }
1281
1282
        $counter = 1;
1283
        if (isset($form_content['answers']) && is_array($form_content['answers'])) {
1284
            for ($i = 0; $i < count($form_content['answers']); $i++) {
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...
1285
                $values = isset($form_content['values']) ? (int) $form_content['values'][$i] : 0;
1286
                $answerId = 0;
1287
                if (isset($form_content['answersid']) && isset($form_content['answersid'][$i])) {
1288
                    $answerId = $form_content['answersid'][$i];
1289
                }
1290
                if (empty($answerId)) {
1291
                    $option = new CSurveyQuestionOption();
1292
                    $option
1293
                        ->setQuestion($question)
1294
                        ->setOptionText($form_content['answers'][$i])
1295
                        ->setSurvey($survey)
1296
                        ->setValue($values)
1297
                        ->setSort($counter)
1298
                    ;
1299
                    $em->persist($option);
1300
                    $em->flush();
1301
                    $insertId = $option->getIid();
1302
                    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...
1303
                        $counter++;
1304
                    }
1305
                } else {
1306
                    $repo = $em->getRepository(CSurveyQuestionOption::class);
1307
                    /** @var CSurveyQuestionOption $option */
1308
                    $option = $repo->find($answerId);
1309
                    if ($option) {
1310
                        $option
1311
                            ->setOptionText($form_content['answers'][$i])
1312
                            ->setValue($values)
1313
                            ->setSort($counter)
1314
                        ;
1315
                        $em->persist($option);
1316
                        $em->flush();
1317
                    }
1318
                    $counter++;
1319
                }
1320
            }
1321
        }
1322
1323
        if ('multiplechoiceother' === $type) {
1324
            if (empty($dataFromDatabase['answer_data'])) {
1325
                $params = [
1326
                    'question_id' => $form_content['question_id'],
1327
                    'survey_id' => $form_content['survey_id'],
1328
                    'option_text' => 'other',
1329
                    'value' => 0,
1330
                    'sort' => $counter,
1331
                ];
1332
                Database::insert($table, $params);
1333
            } else {
1334
                $params = [
1335
                    'option_text' => 'other',
1336
                    'value' => 0,
1337
                    'sort' => $counter,
1338
                ];
1339
                Database::update(
1340
                    $table,
1341
                    $params,
1342
                    [
1343
                        'question_id = ? AND survey_id = ? AND option_text = ?' => [
1344
                            $form_content['question_id'],
1345
                            $form_content['survey_id'],
1346
                            'other',
1347
                        ],
1348
                    ]
1349
                );
1350
            }
1351
        }
1352
    }
1353
1354
    /**
1355
     * This function deletes all the options of the questions of a given survey
1356
     * This function is normally only called when a survey is deleted.
1357
     *
1358
     * @param int $survey_id the id of the survey that has to be deleted
1359
     *
1360
     * @return true
1361
     *
1362
     * @author Patrick Cool <[email protected]>, Ghent University
1363
     *
1364
     * @version January 2007
1365
     */
1366
    public static function delete_all_survey_questions_options($survey_id, $shared = false)
1367
    {
1368
        // Table definitions
1369
        $table_survey_question_option = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
1370
        $course_id = api_get_course_int_id();
1371
        $course_condition = " c_id = $course_id AND ";
1372
        $sql = "DELETE FROM $table_survey_question_option
1373
                WHERE $course_condition survey_id='".intval($survey_id)."'";
1374
1375
        // Deleting the options of the survey questions
1376
        Database::query($sql);
1377
1378
        return true;
1379
    }
1380
1381
    /**
1382
     * SURVEY ANSWERS FUNCTIONS.
1383
     */
1384
1385
    /**
1386
     * This function deletes all the answers anyone has given on this survey
1387
     * This function is normally only called when a survey is deleted.
1388
     *
1389
     * @param $survey_id the id of the survey that has to be deleted
1390
     *
1391
     * @return true
1392
     *
1393
     * @todo write the function
1394
     *
1395
     * @author Patrick Cool <[email protected]>, Ghent University
1396
     *
1397
     * @version January 2007,december 2008
1398
     */
1399
    public static function delete_all_survey_answers($survey_id)
1400
    {
1401
        $course_id = api_get_course_int_id();
1402
        $table = Database::get_course_table(TABLE_SURVEY_ANSWER);
1403
        $survey_id = (int) $survey_id;
1404
        $sql = "DELETE FROM $table
1405
                WHERE c_id = $course_id AND survey_id = $survey_id";
1406
        Database::query($sql);
1407
1408
        return true;
1409
    }
1410
1411
    /**
1412
     * This function gets all the persons who have filled the survey.
1413
     *
1414
     * @param int $survey_id
1415
     *
1416
     * @return array
1417
     *
1418
     * @author Patrick Cool <[email protected]>, Ghent University
1419
     *
1420
     * @version February 2007
1421
     */
1422
    public static function get_people_who_filled_survey(
1423
        $survey_id,
1424
        $all_user_info = false,
1425
        $course_id = null
1426
    ) {
1427
        // Database table definition
1428
        $table_survey_answer = Database::get_course_table(TABLE_SURVEY_ANSWER);
1429
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1430
1431
        // Variable initialisation
1432
        $return = [];
1433
1434
        if (empty($course_id)) {
1435
            $course_id = api_get_course_int_id();
1436
        } else {
1437
            $course_id = (int) $course_id;
1438
        }
1439
1440
        $survey_id = (int) $survey_id;
1441
1442
        if ($all_user_info) {
1443
            $order_clause = api_sort_by_first_name()
1444
                ? ' ORDER BY user.firstname, user.lastname'
1445
                : ' ORDER BY user.lastname, user.firstname';
1446
            $sql = "SELECT DISTINCT
1447
			            answered_user.user as invited_user,
1448
			            user.firstname,
1449
			            user.lastname,
1450
			            user.id as user_id
1451
                    FROM $table_survey_answer answered_user
1452
                    LEFT JOIN $table_user as user
1453
                    ON answered_user.user = user.id
1454
                    WHERE
1455
                        survey_id= '".$survey_id."' ".
1456
                $order_clause;
1457
        } else {
1458
            $sql = "SELECT DISTINCT user FROM $table_survey_answer
1459
			        WHERE survey_id= '".$survey_id."'  ";
1460
1461
            if ('true' === api_get_setting('survey.survey_anonymous_show_answered')) {
1462
                $tblInvitation = Database::get_course_table(TABLE_SURVEY_INVITATION);
1463
                $tblSurvey = Database::get_course_table(TABLE_SURVEY);
1464
1465
                $sql = "SELECT i.user_id FROM $tblInvitation i
1466
                    INNER JOIN $tblSurvey s
1467
                    ON i.survey_code = s.code
1468
                    WHERE i.answered IS TRUE AND s.iid = $survey_id";
1469
            }
1470
        }
1471
1472
        $res = Database::query($sql);
1473
        while ($row = Database::fetch_assoc($res)) {
1474
            if ($all_user_info) {
1475
                $userInfo = api_get_user_info($row['user_id']);
1476
                $row['user_info'] = $userInfo;
1477
                $return[] = $row;
1478
            } else {
1479
                $return[] = $row['user'];
1480
            }
1481
        }
1482
1483
        return $return;
1484
    }
1485
1486
    /**
1487
     * @return bool
1488
     */
1489
    public static function survey_generation_hash_available()
1490
    {
1491
        if (extension_loaded('mcrypt')) {
1492
            return true;
1493
        }
1494
1495
        return false;
1496
    }
1497
1498
    /**
1499
     * @param int $survey_id
1500
     * @param int $course_id
1501
     * @param int $session_id
1502
     * @param int $group_id
1503
     *
1504
     * @return string
1505
     */
1506
    public static function generate_survey_hash(int $survey_id, int $course_id, int $session_id, int $group_id): string
1507
    {
1508
        $securityKey = api_get_env_variable('kernel.secret');
1509
1510
        return hash(
1511
            'sha512',
1512
            $securityKey.'_'.$course_id.'_'.$session_id.'_'.$group_id.'_'.$survey_id
1513
        );
1514
    }
1515
1516
    /**
1517
     * @param int    $survey_id
1518
     * @param int    $course_id
1519
     * @param int    $session_id
1520
     * @param int    $group_id
1521
     * @param string $hash
1522
     *
1523
     * @return bool
1524
     */
1525
    public static function validate_survey_hash($survey_id, $course_id, $session_id, $group_id, $hash)
1526
    {
1527
        $generatedHash = self::generate_survey_hash($survey_id, $course_id, $session_id, $group_id);
1528
        if ($generatedHash == $hash) {
1529
            return true;
1530
        }
1531
1532
        return false;
1533
    }
1534
1535
    /**
1536
     * @param int $survey_id
1537
     * @param int $course_id
1538
     * @param int $session_id
1539
     * @param int $group_id
1540
     *
1541
     * @return string
1542
     */
1543
    public static function generate_survey_link(
1544
        $survey_id,
1545
        $course_id,
1546
        $session_id,
1547
        $group_id
1548
    ) {
1549
        $code = self::generate_survey_hash(
1550
            $survey_id,
1551
            $course_id,
1552
            $session_id,
1553
            $group_id
1554
        );
1555
1556
        return api_get_path(WEB_CODE_PATH).'survey/link.php?h='.$code.'&i='.$survey_id.'&c='.intval($course_id).'&s='
1557
            .intval($session_id).'&g='.$group_id;
1558
    }
1559
1560
    /**
1561
     * Check if the current user has mandatory surveys no-answered
1562
     * and redirect to fill the first found survey.
1563
     */
1564
    public static function protectByMandatory()
1565
    {
1566
        if (false !== strpos($_SERVER['SCRIPT_NAME'], 'fillsurvey.php')) {
1567
            return;
1568
        }
1569
1570
        $userId = api_get_user_id();
1571
        $courseId = api_get_course_int_id();
1572
        $sessionId = api_get_session_id();
1573
1574
        if (!$userId) {
1575
            return;
1576
        }
1577
1578
        if (!$courseId) {
1579
            return;
1580
        }
1581
1582
        try {
1583
            /** @var CSurveyInvitation $invitation */
1584
            $invitation = Database::getManager()
1585
                ->createQuery("
1586
                    SELECT i FROM ChamiloCourseBundle:CSurveyInvitation i
1587
                    INNER JOIN ChamiloCourseBundle:CSurvey s
1588
                        WITH (s.code = i.surveyCode AND s.cId = i.cId AND s.sessionId = i.sessionId)
1589
                    INNER JOIN ChamiloCoreBundle:ExtraFieldValues efv WITH efv.itemId = s.iid
1590
                    INNER JOIN ChamiloCoreBundle:ExtraField ef WITH efv.field = ef.id
1591
                    WHERE
1592
                        i.answered = 0 AND
1593
                        i.cId = :course AND
1594
                        i.user = :user AND
1595
                        i.sessionId = :session AND
1596
                        :now BETWEEN s.availFrom AND s.availTill AND
1597
                        ef.variable = :variable AND
1598
                        efv.field_value = 1 AND
1599
                        s.surveyType != 3
1600
                    ORDER BY s.availTill ASC
1601
                ")
1602
                ->setMaxResults(1)
1603
                ->setParameters([
1604
                    'course' => $courseId,
1605
                    'user' => $userId,
1606
                    'session' => $sessionId,
1607
                    'now' => new DateTime('UTC', new DateTimeZone('UTC')),
1608
                    'variable' => 'is_mandatory',
1609
                ])
1610
                ->getSingleResult();
1611
        } catch (Exception $e) {
1612
            $invitation = null;
1613
        }
1614
1615
        if (!$invitation) {
1616
            return;
1617
        }
1618
1619
        Display::addFlash(
1620
            Display::return_message(
1621
                get_lang(
1622
                    'A mandatory survey is waiting your answer. To enter the course, you must first complete the survey.'
1623
                ),
1624
                'warning'
1625
            )
1626
        );
1627
1628
        $url = SurveyUtil::generateFillSurveyLink(
1629
            $invitation->getInvitationCode(),
1630
            api_get_course_info(),
1631
            api_get_session_id()
1632
        );
1633
1634
        header('Location: '.$url);
1635
        exit;
1636
    }
1637
1638
    /**
1639
     * This function empty surveys (invitations and answers).
1640
     *
1641
     * @param int $surveyId id of the survey to empty
1642
     *
1643
     * @return bool
1644
     */
1645
    public static function emptySurveyFromId($surveyId)
1646
    {
1647
        // Database table definitions
1648
        $surveyInvitationTable = Database:: get_course_table(TABLE_SURVEY_INVITATION);
1649
        $surveyAnswerTable = Database:: get_course_table(TABLE_SURVEY_ANSWER);
1650
        $surveyTable = Database:: get_course_table(TABLE_SURVEY);
1651
        $surveyId = (int) $surveyId;
1652
        $surveyData = self::get_survey($surveyId);
1653
        if (empty($surveyData)) {
1654
            return false;
1655
        }
1656
1657
        $surveyCode = $surveyData['survey_code'];
1658
        $courseId = (int) $surveyData['c_id'];
1659
        $sessionId = (int) $surveyData['session_id'];
1660
1661
        $sql = "DELETE FROM $surveyInvitationTable
1662
                WHERE session_id = $sessionId AND c_id = $courseId AND survey_code = '$surveyCode' ";
1663
        Database::query($sql);
1664
1665
        $sql = "DELETE FROM $surveyAnswerTable
1666
                WHERE survey_id = $surveyId AND c_id = $courseId ";
1667
        Database::query($sql);
1668
1669
        $sql = "UPDATE $surveyTable
1670
                SET invited = 0, answered = 0
1671
                WHERE iid = $surveyId AND c_id = $courseId AND session_id = $sessionId ";
1672
        Database::query($sql);
1673
1674
        return true;
1675
    }
1676
1677
    /**
1678
     * Copy survey specifying course ID and session ID where will be copied.
1679
     *
1680
     * @param int $surveyId
1681
     * @param int $targetCourseId  target course id
1682
     * @param int $targetSessionId target session id
1683
     *
1684
     * @return bool|int when fails or return the new survey id
1685
     */
1686
    public static function copySurveySession($surveyId, $targetCourseId, $targetSessionId)
1687
    {
1688
        // Database table definitions
1689
        $surveyTable = Database::get_course_table(TABLE_SURVEY);
1690
        $surveyQuestionGroupTable = Database::get_course_table(TABLE_SURVEY_QUESTION_GROUP);
1691
        $surveyQuestionTable = Database::get_course_table(TABLE_SURVEY_QUESTION);
1692
        $surveyOptionsTable = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
1693
        $surveyId = (int) $surveyId;
1694
        $targetCourseId = (int) $targetCourseId;
1695
        $targetSessionId = (int) $targetSessionId;
1696
1697
        $surveyData = self::get_survey($surveyId, 0, '', true);
1698
        if (empty($surveyData) || empty($targetCourseId)) {
1699
            return false;
1700
        }
1701
1702
        $originalCourseId = $surveyData['c_id'];
1703
        $originalSessionId = $surveyData['session_id'];
1704
1705
        $surveyData['code'] = self::generate_unique_code($surveyData['code']);
1706
        $surveyData['c_id'] = $targetCourseId;
1707
        $surveyData['session_id'] = $targetSessionId;
1708
        // Add a "Copy" suffix if copied inside the same course
1709
        if ($targetCourseId == $originalCourseId) {
1710
            $surveyData['title'] = $surveyData['title'].' '.get_lang('Copy');
1711
        }
1712
        unset($surveyData['iid']);
1713
        unset($surveyData['id']);
1714
1715
        $newSurveyId = Database::insert($surveyTable, $surveyData);
1716
1717
        if ($newSurveyId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $newSurveyId of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false 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...
1718
            $sql = "SELECT * FROM $surveyQuestionGroupTable
1719
                    WHERE c_id = $originalCourseId AND survey_id = $surveyId";
1720
            $res = Database::query($sql);
1721
            while ($row = Database::fetch_assoc($res)) {
1722
                $params = [
1723
                    'c_id' => $targetCourseId,
1724
                    'name' => $row['title'],
1725
                    'description' => $row['description'],
1726
                    'survey_id' => $newSurveyId,
1727
                ];
1728
                $insertId = Database::insert($surveyQuestionGroupTable, $params);
1729
                if ($insertId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $insertId of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false 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...
1730
                    $sql = "UPDATE $surveyQuestionGroupTable SET id = iid WHERE iid = $insertId";
1731
                    Database::query($sql);
1732
                    $group_id[$row['id']] = $insertId;
1733
                }
1734
            }
1735
1736
            // Get questions
1737
            $sql = "SELECT * FROM $surveyQuestionTable
1738
                    WHERE c_id = $originalCourseId AND survey_id = $surveyId";
1739
            $res = Database::query($sql);
1740
            while ($row = Database::fetch_assoc($res)) {
1741
                $params = [
1742
                    'c_id' => $targetCourseId,
1743
                    'survey_id' => $newSurveyId,
1744
                    'survey_question' => $row['survey_question'],
1745
                    'survey_question_comment' => $row['survey_question_comment'],
1746
                    'type' => $row['type'],
1747
                    'display' => $row['display'],
1748
                    'sort' => $row['sort'],
1749
                    'shared_question_id' => $row['shared_question_id'],
1750
                    'max_value' => $row['max_value'],
1751
                    'survey_group_pri' => $row['survey_group_pri'],
1752
                    'survey_group_sec1' => $row['survey_group_sec1'],
1753
                    'survey_group_sec2' => $row['survey_group_sec2'],
1754
                ];
1755
1756
                if (isset($row['is_required'])) {
1757
                    $params['is_required'] = $row['is_required'];
1758
                }
1759
1760
                $insertId = Database::insert($surveyQuestionTable, $params);
1761
                if ($insertId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $insertId of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false 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...
1762
                    /*$sql = "UPDATE $surveyQuestionTable
1763
                            SET question_id = iid
1764
                            WHERE iid = $insertId";
1765
                    Database::query($sql);*/
1766
1767
                    $question_id[$row['iid']] = $insertId;
1768
                }
1769
            }
1770
1771
            // Get questions options
1772
            $sql = "SELECT * FROM $surveyOptionsTable
1773
                    WHERE survey_id = $surveyId AND c_id = $originalCourseId";
1774
1775
            $res = Database::query($sql);
1776
            while ($row = Database::fetch_assoc($res)) {
1777
                $params = [
1778
                    'c_id' => $targetCourseId,
1779
                    'question_id' => $question_id[$row['question_id']],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $question_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
1780
                    'survey_id' => $newSurveyId,
1781
                    'option_text' => $row['option_text'],
1782
                    'sort' => $row['sort'],
1783
                    'value' => $row['value'],
1784
                ];
1785
                $insertId = Database::insert($surveyOptionsTable, $params);
1786
                if ($insertId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $insertId of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false 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...
1787
                    $sql = "UPDATE $surveyOptionsTable SET question_option_id = $insertId WHERE iid = $insertId";
1788
                    Database::query($sql);
1789
                }
1790
            }
1791
1792
            return $newSurveyId;
1793
        }
1794
1795
        return false;
1796
    }
1797
1798
    /**
1799
     * Copy/duplicate one question (into the same survey).
1800
     * Note: Relies on the question iid to find all necessary info.
1801
     *
1802
     * @param int $questionId
1803
     *
1804
     * @return int The new question's iid, or 0 on error
1805
     */
1806
    public static function copyQuestion($questionId)
1807
    {
1808
        if (empty($questionId)) {
1809
            return 0;
1810
        }
1811
        $questionId = (int) $questionId;
1812
        $questionTable = Database::get_course_table(TABLE_SURVEY_QUESTION);
1813
        $optionsTable = Database::get_course_table(TABLE_SURVEY_QUESTION_OPTION);
1814
1815
        // Get questions
1816
        $sql = "SELECT * FROM $questionTable WHERE iid = $questionId";
1817
        $res = Database::query($sql);
1818
        if (false == $res) {
1819
            // Could not find this question
1820
            return 0;
1821
        }
1822
        $row = Database::fetch_assoc($res);
1823
        $params = [
1824
            //'c_id' => $row['c_id'],
1825
            'survey_id' => $row['survey_id'],
1826
            'survey_question' => trim($row['survey_question']),
1827
            'survey_question_comment' => $row['survey_question_comment'],
1828
            'type' => $row['type'],
1829
            'display' => $row['display'],
1830
            'shared_question_id' => $row['shared_question_id'],
1831
            'max_value' => $row['max_value'],
1832
            'survey_group_pri' => $row['survey_group_pri'],
1833
            'survey_group_sec1' => $row['survey_group_sec1'],
1834
            'survey_group_sec2' => $row['survey_group_sec2'],
1835
        ];
1836
        if (isset($row['is_required'])) {
1837
            $params['is_required'] = $row['is_required'];
1838
        }
1839
        // Get question position
1840
        $sqlSort = "SELECT max(sort) as sort FROM $questionTable
1841
                    WHERE survey_id = ".$row['survey_id'];
1842
        $resSort = Database::query($sqlSort);
1843
        $rowSort = Database::fetch_assoc($resSort);
1844
        $params['sort'] = $rowSort['sort'] + 1;
1845
        // Insert the new question
1846
        $insertId = Database::insert($questionTable, $params);
1847
        if (false == $insertId) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $insertId of type false|integer against false; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
1848
            return 0;
1849
        }
1850
1851
        // Get questions options
1852
        $sql = "SELECT * FROM $optionsTable WHERE question_id = $questionId";
1853
        $res = Database::query($sql);
1854
        while ($row = Database::fetch_assoc($res)) {
1855
            $params = [
1856
                //'c_id' => $row['c_id'],
1857
                'question_id' => $insertId,
1858
                'survey_id' => $row['survey_id'],
1859
                'option_text' => $row['option_text'],
1860
                'sort' => $row['sort'],
1861
                'value' => $row['value'],
1862
            ];
1863
            Database::insert($optionsTable, $params);
1864
        }
1865
1866
        return $insertId;
1867
    }
1868
1869
    /**
1870
     * @param array $surveyData
1871
     *
1872
     * @return bool
1873
     */
1874
    public static function removeMultiplicateQuestions(CSurvey $survey, $courseId)
1875
    {
1876
        $surveyId = $survey->getIid();
1877
        $courseId = (int) $courseId;
1878
1879
        if (empty($surveyId) || empty($courseId)) {
1880
            return false;
1881
        }
1882
1883
        $questions = $survey->getQuestions();
1884
        foreach ($questions as $question) {
1885
            // Questions marked with "geneated" were created using the "multiplicate" feature.
1886
            if ('generated' === $question->getSurveyQuestionComment()) {
1887
                self::deleteQuestion($question->getIid());
1888
            }
1889
        }
1890
    }
1891
1892
    /**
1893
     * @return bool
1894
     */
1895
    public static function multiplicateQuestions(CSurvey $survey, $courseId)
1896
    {
1897
        $surveyId = $survey->getIid();
1898
1899
        if (empty($surveyId)) {
1900
            return false;
1901
        }
1902
1903
        $questions = self::get_questions($surveyId);
1904
1905
        if (empty($questions)) {
1906
            return false;
1907
        }
1908
1909
        $extraFieldValue = new ExtraFieldValue('survey');
1910
        $groupData = $extraFieldValue->get_values_by_handler_and_field_variable($surveyId, 'group_id');
1911
        $groupId = null;
1912
        if ($groupData && !empty($groupData['value'])) {
1913
            $groupId = (int) $groupData['value'];
1914
        }
1915
1916
        if (null === $groupId) {
1917
            $obj = new UserGroupModel();
1918
            $options['where'] = [' usergroup.course_id = ? ' => $courseId];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.
Loading history...
1919
            $classList = $obj->getUserGroupInCourse($options);
1920
1921
            $classToParse = [];
1922
            foreach ($classList as $class) {
1923
                $users = $obj->get_users_by_usergroup($class['id']);
1924
                if (empty($users)) {
1925
                    continue;
1926
                }
1927
                $classToParse[] = [
1928
                    'name' => $class['name'],
1929
                    'users' => $users,
1930
                ];
1931
            }
1932
            self::parseMultiplicateUserList($classToParse, $questions, $courseId, $survey, true);
1933
        } else {
1934
            $groupInfo = GroupManager::get_group_properties($groupId);
1935
            if (!empty($groupInfo)) {
1936
                $users = GroupManager::getStudents($groupInfo['iid'], true);
1937
                if (!empty($users)) {
1938
                    $users = array_column($users, 'id');
1939
                    self::parseMultiplicateUserList(
1940
                        [
1941
                            [
1942
                                'name' => $groupInfo['name'],
1943
                                'users' => $users,
1944
                            ],
1945
                        ],
1946
                        $questions,
1947
                        $courseId,
1948
                        $survey,
1949
                        false
1950
                    );
1951
                }
1952
            }
1953
        }
1954
1955
        return true;
1956
    }
1957
1958
    public static function parseMultiplicateUserList($itemList, $questions, $courseId, CSurvey $survey, $addClassNewPage = false)
1959
    {
1960
        if (empty($itemList) || empty($questions)) {
1961
            return false;
1962
        }
1963
1964
        $surveyId = $survey->getIid();
1965
        $classTag = '{{class_name}}';
1966
        $studentTag = '{{student_full_name}}';
1967
        $classCounter = 0;
1968
1969
        $newQuestionList = [];
1970
        foreach ($questions as $question) {
1971
            $newQuestionList[$question['sort']] = $question;
1972
        }
1973
        ksort($newQuestionList);
1974
1975
        $order = ('true' === api_get_setting('survey.survey_duplicate_order_by_name'));
1976
        foreach ($itemList as $class) {
1977
            $className = $class['name'];
1978
            $users = $class['users'];
1979
            $userInfoList = [];
1980
            foreach ($users as $userId) {
1981
                $userInfoList[] = api_get_user_info($userId);
1982
            }
1983
1984
            if ($order) {
1985
                usort(
1986
                    $userInfoList,
1987
                    function ($a, $b) {
1988
                        return $a['lastname'] > $b['lastname'];
1989
                    }
1990
                );
1991
            }
1992
1993
            foreach ($newQuestionList as $question) {
1994
                $text = $question['question'];
1995
                if (false !== strpos($text, $classTag)) {
1996
                    $replacedText = str_replace($classTag, $className, $text);
1997
                    $values = [
1998
                        'c_id' => $courseId,
1999
                        'question_comment' => 'generated',
2000
                        'type' => $question['type'],
2001
                        'display' => $question['horizontalvertical'],
2002
                        'horizontalvertical' => $question['horizontalvertical'],
2003
                        'question' => $replacedText,
2004
                        'survey_id' => $surveyId,
2005
                        'question_id' => 0,
2006
                        'shared_question_id' => 0,
2007
                        'answers' => $question['answers'] ?? null,
2008
                    ];
2009
                    self::saveQuestion($survey, $values, false);
2010
                    $classCounter++;
2011
                    continue;
2012
                }
2013
2014
                foreach ($userInfoList as $userInfo) {
2015
                    if (false !== strpos($text, $studentTag)) {
2016
                        $replacedText = str_replace($studentTag, $userInfo['complete_name'], $text);
2017
                        $values = [
2018
                            'c_id' => $courseId,
2019
                            'question_comment' => 'generated',
2020
                            'type' => $question['type'],
2021
                            'display' => $question['horizontalvertical'],
2022
                            'maximum_score' => $question['maximum_score'],
2023
                            'question' => $replacedText,
2024
                            'survey_id' => $surveyId,
2025
                            'question_id' => 0,
2026
                            'shared_question_id' => 0,
2027
                        ];
2028
2029
                        $answers = [];
2030
                        if (!empty($question['answers'])) {
2031
                            foreach ($question['answers'] as $answer) {
2032
                                $replacedText = str_replace($studentTag, $userInfo['complete_name'], $answer);
2033
                                $answers[] = $replacedText;
2034
                            }
2035
                        }
2036
                        $values['answers'] = $answers;
2037
                        self::saveQuestion($survey, $values, false);
2038
                    }
2039
                }
2040
2041
                if ($addClassNewPage && $classCounter < count($itemList)) {
2042
                    // Add end page
2043
                    $values = [
2044
                        'c_id' => $courseId,
2045
                        'question_comment' => 'generated',
2046
                        'type' => 'pagebreak',
2047
                        'display' => 'horizontal',
2048
                        'question' => get_lang('Question for next class'),
2049
                        'survey_id' => $surveyId,
2050
                        'question_id' => 0,
2051
                        'shared_question_id' => 0,
2052
                    ];
2053
                    self::saveQuestion($survey, $values, false);
2054
                }
2055
            }
2056
        }
2057
2058
        return true;
2059
    }
2060
2061
    public static function hasDependency(CSurvey $survey)
2062
    {
2063
        $surveyId = $survey->getIid();
2064
2065
        $table = Database::get_course_table(TABLE_SURVEY_QUESTION);
2066
        $sql = "SELECT COUNT(iid) count FROM $table
2067
                WHERE
2068
                    survey_id = $surveyId AND
2069
                    parent_option_id <> 0
2070
                LIMIT 1
2071
                ";
2072
        $result = Database::query($sql);
2073
        $row = Database::fetch_array($result);
2074
2075
        if ($row) {
2076
            return $row['count'] > 0;
2077
        }
2078
2079
        return false;
2080
    }
2081
2082
    /**
2083
     * @return int
2084
     */
2085
    public static function getCountPages(CSurvey $survey)
2086
    {
2087
        $surveyId = $survey->getIid();
2088
2089
        $table = Database::get_course_table(TABLE_SURVEY_QUESTION);
2090
2091
        // pagebreak
2092
        $sql = "SELECT COUNT(iid) FROM $table
2093
                WHERE
2094
                    survey_question NOT LIKE '%{{%' AND
2095
                    type = 'pagebreak' AND
2096
                    survey_id = $surveyId";
2097
        $result = Database::query($sql);
2098
        $numberPageBreaks = Database::result($result, 0, 0);
2099
2100
        // No pagebreak
2101
        $sql = "SELECT COUNT(iid) FROM $table
2102
                WHERE
2103
                    survey_question NOT LIKE '%{{%' AND
2104
                    type != 'pagebreak' AND
2105
                    survey_id = $surveyId";
2106
        $result = Database::query($sql);
2107
        $countOfQuestions = Database::result($result, 0, 0);
2108
2109
        if (1 == $survey->getOneQuestionPerPage()) {
2110
            if (!empty($countOfQuestions)) {
2111
                return $countOfQuestions;
2112
            }
2113
2114
            return 1;
2115
        }
2116
2117
        if (empty($numberPageBreaks)) {
2118
            return 1;
2119
        }
2120
2121
        return $numberPageBreaks + 1;
2122
    }
2123
2124
    /**
2125
     * Check whether this survey has ended. If so, display message and exit this script.
2126
     */
2127
    public static function checkTimeAvailability(?CSurvey $survey): void
2128
    {
2129
        if (null === $survey) {
2130
            api_not_allowed(true);
2131
        }
2132
2133
        $utcZone = new DateTimeZone('UTC');
2134
        $startDate = $survey->getAvailFrom();
2135
        $endDate = $survey->getAvailTill();
2136
        $currentDate = new DateTime('now', $utcZone);
2137
        $currentDate->modify('today');
2138
2139
        $returnMessage = false;
2140
        if ($currentDate < $startDate) {
2141
            $returnMessage =  Display:: return_message(
2142
                get_lang('This survey is not yet available. Please try again later. Thank you.'),
2143
                'warning',
2144
                false
2145
            );
2146
        }
2147
2148
        if ($currentDate > $endDate) {
2149
            $returnMessage = Display:: return_message(
2150
                get_lang('Sorry, this survey is not available anymore. Thank you for trying.'),
2151
                'warning',
2152
                false
2153
            );
2154
        }
2155
2156
        if (false !== $returnMessage) {
2157
            $content = Display::page_header($survey->getTitle());
2158
            $content .= $returnMessage;
2159
            $template = new Template();
2160
            $template->assign('actions', Display::toolbarAction('toolbar', []));
2161
            $template->assign('content', $content);
2162
            $template->display_one_col_template();
2163
            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...
2164
        }
2165
    }
2166
2167
    public static function getUserInvitationsForSurveyInCourse(
2168
        int    $userId,
2169
        string $surveyCode,
2170
        int    $courseId,
2171
        int    $sessionId = 0,
2172
        int    $groupId = 0,
2173
        int    $lpItemId = 0
2174
    ): array {
2175
2176
        $em = Database::getManager();
2177
        $invitationRepo = $em->getRepository(CSurveyInvitation::class);
2178
        $surveyRepo = $em->getRepository(CSurvey::class);
2179
        $survey = $surveyRepo->findBy(['code' => $surveyCode]);
2180
2181
        $criteria = [
2182
            'user' => api_get_user_entity($userId),
2183
            'course' => api_get_course_entity($courseId),
2184
            'session' => api_get_session_entity($sessionId),
2185
            'group' => api_get_group_entity($groupId),
2186
            'survey' => $survey,
2187
        ];
2188
2189
        if (is_int($lpItemId) && $lpItemId > 0) {
2190
            $criteria['lpItemId'] = $lpItemId;
2191
        }
2192
2193
        return $invitationRepo->findBy(
2194
            $criteria,
2195
            ['invitationDate' => 'DESC']
2196
        );
2197
    }
2198
2199
2200
    /**
2201
     * @param array $userInfo
2202
     * @param int   $answered (1 = answered 0 = not answered)
2203
     *
2204
     * @return string
2205
     */
2206
    public static function surveyReport($userInfo, $answered = 0)
2207
    {
2208
        $userId = isset($userInfo['user_id']) ? (int) $userInfo['user_id'] : 0;
2209
        $answered = (int) $answered;
2210
2211
        if (empty($userId)) {
2212
            return '';
2213
        }
2214
2215
        $em = Database::getManager();
2216
        $repo = $em->getRepository(CSurveyInvitation::class);
2217
        $repoSurvey = $em->getRepository(CSurvey::class);
2218
        $invitations = $repo->findBy(['user' => $userId, 'answered' => $answered]);
2219
        $mainUrl = api_get_path(WEB_CODE_PATH).'survey/survey.php?';
2220
        $content = '';
2221
2222
        if (empty($answered)) {
2223
            $content .= Display::page_subheader(get_lang('Unanswered'));
2224
        } else {
2225
            $content .= Display::page_subheader(get_lang('Answered'));
2226
        }
2227
2228
        if (!empty($invitations)) {
2229
            $table = new HTML_Table(['class' => 'table']);
2230
            $table->setHeaderContents(0, 0, get_lang('Survey name'));
2231
            $table->setHeaderContents(0, 1, get_lang('Course'));
2232
2233
            if (empty($answered)) {
2234
                $table->setHeaderContents(0, 2, get_lang('Survey').' - '.get_lang('End Date'));
2235
            }
2236
2237
            // Not answered
2238
            /** @var CSurveyInvitation $invitation */
2239
            $row = 1;
2240
            foreach ($invitations as $invitation) {
2241
                $course = $invitation->getCourse();
2242
                $courseId = $course->getId();
2243
                $courseCode = $course->getCode();
2244
                $sessionId = $invitation->getSession() ? $invitation->getSession()->getId() : 0;
2245
2246
                if (!empty($answered)) {
2247
                    // check if user is subscribed to the course/session
2248
                    if (empty($sessionId)) {
2249
                        $subscribe = CourseManager::is_user_subscribed_in_course($userId, $courseCode);
2250
                    } else {
2251
                        $subscribe = CourseManager::is_user_subscribed_in_course(
2252
                            $userId,
2253
                            $courseCode,
2254
                            true,
2255
                            $sessionId
2256
                        );
2257
                    }
2258
2259
                    // User is not subscribe skip!
2260
                    if (empty($subscribe)) {
2261
                        continue;
2262
                    }
2263
                }
2264
2265
                $survey = $invitation->getSurvey();
2266
                if (null === $survey) {
2267
                    continue;
2268
                }
2269
2270
                $url = $mainUrl.'survey_id='.$survey->getIid().'&cid='.$courseId.'&sid='.$sessionId;
2271
                $title = $survey->getTitle();
2272
                $title = Display::url($title, $url);
2273
                $courseTitle = $course->getTitle();
2274
                if (!empty($sessionId)) {
2275
                    $courseTitle .= ' ('.$invitation->getSession()->getTitle().')';
2276
                }
2277
2278
                $surveyData = self::get_survey($survey->getIid(), 0, $courseCode);
2279
                $table->setCellContents($row, 0, $title);
2280
                $table->setCellContents($row, 1, $courseTitle);
2281
2282
                if (empty($answered)) {
2283
                    $table->setHeaderContents(
2284
                        $row,
2285
                        2,
2286
                        api_get_local_time(
2287
                            $survey->getAvailTill(),
2288
                            null,
2289
                            null,
2290
                            true,
2291
                            false
2292
                        )
2293
                    );
2294
                }
2295
2296
                if (!empty($answered) && 0 == $survey->getAnonymous()) {
2297
                    $answers = SurveyUtil::displayCompleteReport(
2298
                        $surveyData,
2299
                        $userId,
2300
                        false,
2301
                        false,
2302
                        false
2303
                    );
2304
                    $table->setCellContents(++$row, 0, $answers);
2305
                    $table->setCellContents(++$row, 1, '');
2306
                }
2307
2308
                $row++;
2309
            }
2310
            $content .= $table->toHtml();
2311
        } else {
2312
            $content .= Display::return_message(get_lang('No data available'));
2313
        }
2314
2315
        return $content;
2316
    }
2317
2318
    public static function sendToTutors(CSurvey $survey)
2319
    {
2320
        $surveyId = $survey->getIid();
2321
2322
        $extraFieldValue = new ExtraFieldValue('survey');
2323
        $groupData = $extraFieldValue->get_values_by_handler_and_field_variable($surveyId, 'group_id');
2324
        if ($groupData && !empty($groupData['value'])) {
2325
            $groupInfo = GroupManager::get_group_properties($groupData['value']);
2326
            if ($groupInfo) {
2327
                $tutors = GroupManager::getTutors($groupInfo);
2328
                if (!empty($tutors)) {
2329
                    SurveyUtil::saveInviteMail(
2330
                        $survey,
2331
                        ' ',
2332
                        ' ',
2333
                        false
2334
                    );
2335
2336
                    foreach ($tutors as $tutor) {
2337
                        $subject = sprintf(get_lang('GroupSurveyX'), $groupInfo['name']);
2338
                        $content = sprintf(
2339
                            get_lang('HelloXGroupX'),
2340
                            $tutor['complete_name'],
2341
                            $groupInfo['name']
2342
                        );
2343
2344
                        SurveyUtil::saveInvitations(
2345
                            $survey,
2346
                            ['users' => $tutor['user_id']],
2347
                            $subject,
2348
                            $content,
2349
                            false,
2350
                            true,
2351
                            false,
2352
                            true
2353
                        );
2354
                    }
2355
                    Display::addFlash(Display::return_message(get_lang('Updated'), 'confirmation', false));
2356
                }
2357
                SurveyUtil::updateInvitedCount($survey);
2358
2359
                return true;
2360
            }
2361
        }
2362
2363
        return false;
2364
    }
2365
2366
    public static function getInvitationsAnswered(
2367
        $surveyCode,
2368
        $courseId,
2369
        $sessionId = 0
2370
    ): array
2371
    {
2372
        $invitationRepo = Database::getManager()->getRepository(CSurveyInvitation::class);
2373
2374
        return $invitationRepo->findBy(
2375
            [
2376
                'cId' => $courseId,
2377
                'sessionId' => $sessionId,
2378
                'answered' => true,
2379
                'surveyCode' => $surveyCode,
2380
            ],
2381
            ['invitationDate' => 'DESC']
2382
        );
2383
    }
2384
}
2385