Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Library of functions and constants for module questionnaire.
19
 * @package mod_questionnaire
20
 * @copyright  2016 Mike Churchward (mike.churchward@poetopensource.org)
21
 * @author     Mike Churchward
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
/** This may no longer be needed. */
26
define('QUESTIONNAIRE_RESETFORM_RESET', 'questionnaire_reset_data_');
27
 
28
/** This may no longer be needed. */
29
define('QUESTIONNAIRE_RESETFORM_DROP', 'questionnaire_drop_questionnaire_');
30
 
31
/**
32
 * Library supports implementation.
33
 * @param string $feature
34
 * @return bool|null
35
 */
36
function questionnaire_supports($feature) {
37
    switch($feature) {
38
        case FEATURE_BACKUP_MOODLE2:
39
            return true;
40
        case FEATURE_COMPLETION_TRACKS_VIEWS:
41
            return false;
42
        case FEATURE_COMPLETION_HAS_RULES:
43
            return true;
44
        case FEATURE_GRADE_HAS_GRADE:
45
            return false;
46
        case FEATURE_GRADE_OUTCOMES:
47
            return false;
48
        case FEATURE_GROUPINGS:
49
            return true;
50
        case FEATURE_GROUPS:
51
            return true;
52
        case FEATURE_MOD_INTRO:
53
            return true;
54
        case FEATURE_SHOW_DESCRIPTION:
55
            return true;
56
        case FEATURE_MOD_PURPOSE:
57
            return MOD_PURPOSE_COMMUNICATION;
58
        default:
59
            return null;
60
    }
61
}
62
 
63
/**
64
 * Return any extra capabilities.
65
 * @return array all other caps used in module
66
 */
67
function questionnaire_get_extra_capabilities() {
68
    return array('moodle/site:accessallgroups');
69
}
70
 
71
/**
72
 * Implementation of get_instance.
73
 * @param int $questionnaireid
74
 * @return false|mixed|stdClass
75
 */
76
function questionnaire_get_instance($questionnaireid) {
77
    global $DB;
78
    return $DB->get_record('questionnaire', array('id' => $questionnaireid));
79
}
80
 
81
/**
82
 * Implementation of add_instance.
83
 * @param stdClass $questionnaire
84
 * @return bool|int
85
 */
86
function questionnaire_add_instance($questionnaire) {
87
    // Given an object containing all the necessary data,
88
    // (defined by the form in mod.html) this function
89
    // will create a new instance and return the id number
90
    // of the new instance.
91
    global $DB, $CFG;
92
    require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php');
93
    require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');
94
 
95
    $copyfiles = false;
96
 
97
    // Check the realm and set it to the survey if it's set.
98
    if (empty($questionnaire->sid)) {
99
        // Create a new survey.
100
        $course = get_course($questionnaire->course);
101
        $cm = new stdClass();
102
        $qobject = new questionnaire($course, $cm, 0, $questionnaire);
103
 
104
        if ($questionnaire->create == 'new-0') {
105
            $sdata = new stdClass();
106
            $sdata->name = $questionnaire->name;
107
            $sdata->realm = 'private';
108
            $sdata->title = $questionnaire->name;
109
            $sdata->subtitle = '';
110
            $sdata->info = '';
111
            $sdata->theme = ''; // Theme is deprecated.
112
            $sdata->thanks_page = '';
113
            $sdata->thank_head = '';
114
            $sdata->thank_body = '';
115
            $sdata->email = '';
116
            $sdata->feedbacknotes = '';
117
            $sdata->courseid = $course->id;
118
            if (!($sid = $qobject->survey_update($sdata))) {
119
                throw new \moodle_exception('couldnotcreatenewsurvey', 'mod_questionnaire');
120
            }
121
        } else {
122
            $copyid = explode('-', $questionnaire->create);
123
            $copyrealm = $copyid[0];
124
            $copyid = $copyid[1];
125
            if (empty($qobject->survey)) {
126
                $qobject->add_survey($copyid);
127
                $qobject->add_questions($copyid);
128
            }
129
            // New questionnaires created as "use public" should not create a new survey instance.
130
            if ($copyrealm == 'public') {
131
                $sid = $copyid;
132
            } else {
133
                $sid = $qobject->sid = $qobject->survey_copy($course->id);
134
                // All new questionnaires should be created as "private".
135
                // Even if they are *copies* of public or template questionnaires.
136
                $DB->set_field('questionnaire_survey', 'realm', 'private', array('id' => $sid));
137
 
138
                // Need to copy any files from the old questionnaire instance to the new one.
139
                $questionnaire->copyid = $copyid;
140
            }
141
            // If the survey has dependency data, need to set the questionnaire to allow dependencies.
142
            if ($DB->count_records('questionnaire_dependency', ['surveyid' => $sid]) > 0) {
143
                $questionnaire->navigate = 1;
144
            }
145
        }
146
        $questionnaire->sid = $sid;
147
    }
148
 
149
    $questionnaire->timemodified = time();
150
 
151
    if ($questionnaire->resume == '1') {
152
        $questionnaire->resume = 1;
153
    } else {
154
        $questionnaire->resume = 0;
155
    }
156
 
157
    if (!$questionnaire->id = $DB->insert_record("questionnaire", $questionnaire)) {
158
        return false;
159
    }
160
 
161
    questionnaire_set_events($questionnaire);
162
 
163
    $completiontimeexpected = !empty($questionnaire->completionexpected) ? $questionnaire->completionexpected : null;
164
    \core_completion\api::update_completion_date_event($questionnaire->coursemodule, 'questionnaire',
165
        $questionnaire->id, $completiontimeexpected);
166
 
167
    return $questionnaire->id;
168
}
169
 
170
/**
171
 * Given an object containing all the necessary data, (defined by the form in mod.html) this function will update an existing
172
 * instance with new data.
173
 * @param stdClass $questionnaire
174
 * @return bool
175
 */
176
function questionnaire_update_instance($questionnaire) {
177
    global $DB, $CFG;
178
    require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');
179
 
180
    // Check the realm and set it to the survey if its set.
181
    if (!empty($questionnaire->sid) && !empty($questionnaire->realm)) {
182
        $DB->set_field('questionnaire_survey', 'realm', $questionnaire->realm, array('id' => $questionnaire->sid));
183
    }
184
 
185
    $questionnaire->timemodified = time();
186
    $questionnaire->id = $questionnaire->instance;
187
 
188
    if ($questionnaire->resume == '1') {
189
        $questionnaire->resume = 1;
190
    } else {
191
        $questionnaire->resume = 0;
192
    }
193
 
194
    // Get existing grade item.
195
    questionnaire_grade_item_update($questionnaire);
196
 
197
    questionnaire_set_events($questionnaire);
198
 
199
    $completiontimeexpected = !empty($questionnaire->completionexpected) ? $questionnaire->completionexpected : null;
200
    \core_completion\api::update_completion_date_event($questionnaire->coursemodule, 'questionnaire',
201
        $questionnaire->id, $completiontimeexpected);
202
 
203
    return $DB->update_record("questionnaire", $questionnaire);
204
}
205
 
206
/**
207
 * Given an ID of an instance of this module, this function will permanently delete the instance and any data that depends on it.
208
 * @param int $id
209
 * @return bool
210
 */
211
function questionnaire_delete_instance($id) {
212
    global $DB, $CFG;
213
    require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');
214
 
215
    if (! $questionnaire = $DB->get_record('questionnaire', array('id' => $id))) {
216
        return false;
217
    }
218
 
219
    $result = true;
220
 
221
    if ($events = $DB->get_records('event', array("modulename" => 'questionnaire', "instance" => $questionnaire->id))) {
222
        foreach ($events as $event) {
223
            $event = calendar_event::load($event);
224
            $event->delete();
225
        }
226
    }
227
 
228
    if (! $DB->delete_records('questionnaire', array('id' => $questionnaire->id))) {
229
        $result = false;
230
    }
231
 
232
    if ($survey = $DB->get_record('questionnaire_survey', array('id' => $questionnaire->sid))) {
233
        // If this survey is owned by this course, delete all of the survey records and responses.
234
        if ($survey->courseid == $questionnaire->course) {
235
            $result = $result && questionnaire_delete_survey($questionnaire->sid, $questionnaire->id);
236
        }
237
    }
238
 
239
    return $result;
240
}
241
 
242
/**
243
 * Add a get_coursemodule_info function in case any questionnaire type wants to add 'extra' information
244
 * for the course (see resource).
245
 *
246
 * Given a course_module object, this function returns any "extra" information that may be needed
247
 * when printing this activity in a course listing.  See get_array_of_activities() in course/lib.php.
248
 *
249
 * @param stdClass $coursemodule The coursemodule object (record).
250
 * @return cached_cm_info An object on information that the courses
251
 *                        will know about (most noticeably, an icon).
252
 */
253
function questionnaire_get_coursemodule_info($coursemodule) {
254
    global $DB;
255
 
256
    $questionnaire = $DB->get_record('questionnaire',
257
        array('id' => $coursemodule->instance), 'id, name, intro, introformat,
258
             completionsubmit');
259
    if (!$questionnaire) {
260
        return null;
261
    }
262
 
263
    $info = new cached_cm_info();
264
    $info->customdata = (object)[];
265
    // Populate the custom completion rules as key => value pairs, but only if the completion mode is 'automatic'.
266
    if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) {
267
        $info->customdata->customcompletionrules['completionsubmit'] = $questionnaire->completionsubmit;
268
    }
269
    return $info;
270
}
271
 
272
/**
273
 * Return a small object with summary information about what a user has done with a given particular instance of this module.
274
 * Used for user activity reports.
275
 * $return->time = the time they did it
276
 * $return->info = a short text description.
277
 * $course and $mod are unused, but API requires them. Suppress PHPMD warning.
278
 * @param stdClass $course
279
 * @param stdClass $user
280
 * @param stdClass $mod
281
 * @param stdClass $questionnaire
282
 * @return stdClass
283
 */
284
function questionnaire_user_outline($course, $user, $mod, $questionnaire) {
285
    global $CFG;
286
    require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');
287
 
288
    $result = new stdClass();
289
    if ($responses = questionnaire_get_user_responses($questionnaire->id, $user->id, true)) {
290
        $n = count($responses);
291
        if ($n == 1) {
292
            $result->info = $n.' '.get_string("response", "questionnaire");
293
        } else {
294
            $result->info = $n.' '.get_string("responses", "questionnaire");
295
        }
296
        $lastresponse = array_pop($responses);
297
        $result->time = $lastresponse->submitted;
298
    } else {
299
        $result->info = get_string("noresponses", "questionnaire");
300
    }
301
    return $result;
302
}
303
 
304
/**
305
 * Print a detailed representation of what a  user has done with a given particular instance of this module, for user
306
 * activity reports.
307
 * $course and $mod are unused, but API requires them. Suppress PHPMD warning.
308
 * @param stdClass $course
309
 * @param stdClass $user
310
 * @param stdClass $mod
311
 * @param stdClass $questionnaire
312
 * @return bool
313
 */
314
function questionnaire_user_complete($course, $user, $mod, $questionnaire) {
315
    global $CFG;
316
    require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');
317
 
318
    if ($responses = questionnaire_get_user_responses($questionnaire->id, $user->id, false)) {
319
        foreach ($responses as $response) {
320
            if ($response->complete == 'y') {
321
                echo get_string('submitted', 'questionnaire').' '.userdate($response->submitted).'<br />';
322
            } else {
323
                echo get_string('attemptstillinprogress', 'questionnaire').' '.userdate($response->submitted).'<br />';
324
            }
325
        }
326
    } else {
327
        print_string('noresponses', 'questionnaire');
328
    }
329
 
330
    return true;
331
}
332
 
333
/**
334
 * Given a course and a time, this module should find recent activity that has occurred in questionnaire activities and print it
335
 * out.
336
 * Return true if there was output, or false is there was none.
337
 * $course, $isteacher and $timestart are unused, but API requires them. Suppress PHPMD warning.
338
 * @param stdClass $course
339
 * @param bool $isteacher
340
 * @param int $timestart
341
 * @return false
342
 */
343
function questionnaire_print_recent_activity($course, $isteacher, $timestart) {
344
    return false;  // True if anything was printed, otherwise false.
345
}
346
 
347
/**
348
 * Must return an array of grades for a given instance of this module, indexed by user.  It also returns a maximum allowed grade.
349
 * $questionnaireid is unused, but API requires it. Suppress PHPMD warning.
350
 * @param int $questionnaireid
351
 * @return null
352
 */
353
function questionnaire_grades($questionnaireid) {
354
    return null;
355
}
356
 
357
/**
358
 * Return grade for given user or all users.
359
 *
360
 * @param stdClass $questionnaire
361
 * @param int $userid optional user id, 0 means all users
362
 * @return array array of grades, false if none
363
 */
364
function questionnaire_get_user_grades($questionnaire, $userid=0) {
365
    global $DB;
366
    $params = array();
367
    $usersql = '';
368
    if (!empty($userid)) {
369
        $usersql = "AND u.id = ?";
370
        $params[] = $userid;
371
    }
372
 
373
    $sql = "SELECT r.id, u.id AS userid, r.grade AS rawgrade, r.submitted AS dategraded, r.submitted AS datesubmitted
374
            FROM {user} u, {questionnaire_response} r
375
            WHERE u.id = r.userid AND r.questionnaireid = $questionnaire->id AND r.complete = 'y' $usersql";
376
    return $DB->get_records_sql($sql, $params) ?? [];
377
}
378
 
379
/**
380
 * Update grades by firing grade_updated event.
381
 * $nullifnone is unused, but API requires it. Suppress PHPMD warning.
382
 * @param stdClass $questionnaire
383
 * @param int $userid
384
 * @param bool $nullifnone
385
 */
386
function questionnaire_update_grades($questionnaire=null, $userid=0, $nullifnone=true) {
387
    global $CFG, $DB;
388
 
389
    if (!function_exists('grade_update')) { // Workaround for buggy PHP versions.
390
        require_once($CFG->libdir.'/gradelib.php');
391
    }
392
 
393
    if ($questionnaire != null) {
394
        if ($graderecs = questionnaire_get_user_grades($questionnaire, $userid)) {
395
            $grades = array();
396
            foreach ($graderecs as $v) {
397
                if (!isset($grades[$v->userid])) {
398
                    $grades[$v->userid] = new stdClass();
399
                    if ($v->rawgrade == -1) {
400
                        $grades[$v->userid]->rawgrade = null;
401
                    } else {
402
                        $grades[$v->userid]->rawgrade = $v->rawgrade;
403
                    }
404
                    $grades[$v->userid]->userid = $v->userid;
405
                } else if (isset($grades[$v->userid]) && ($v->rawgrade > $grades[$v->userid]->rawgrade)) {
406
                    $grades[$v->userid]->rawgrade = $v->rawgrade;
407
                }
408
            }
409
            questionnaire_grade_item_update($questionnaire, $grades);
410
        } else {
411
            questionnaire_grade_item_update($questionnaire);
412
        }
413
 
414
    } else {
415
        $sql = "SELECT q.*, cm.idnumber as cmidnumber, q.course as courseid
416
                  FROM {questionnaire} q, {course_modules} cm, {modules} m
417
                 WHERE m.name='questionnaire' AND m.id=cm.module AND cm.instance=q.id";
418
        if ($rs = $DB->get_recordset_sql($sql)) {
419
            foreach ($rs as $questionnaire) {
420
                if ($questionnaire->grade != 0) {
421
                    questionnaire_update_grades($questionnaire);
422
                } else {
423
                    questionnaire_grade_item_update($questionnaire);
424
                }
425
            }
426
            $rs->close();
427
        }
428
    }
429
}
430
 
431
/**
432
 * Create grade item for given questionnaire
433
 *
434
 * @param stdClass $questionnaire object with extra cmidnumber
435
 * @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
436
 * @return int 0 if ok, error code otherwise
437
 */
438
function questionnaire_grade_item_update($questionnaire, $grades = null) {
439
    global $CFG;
440
    if (!function_exists('grade_update')) { // Workaround for buggy PHP versions.
441
        require_once($CFG->libdir.'/gradelib.php');
442
    }
443
 
444
    if (!isset($questionnaire->courseid)) {
445
        $questionnaire->courseid = $questionnaire->course;
446
    }
447
 
448
    if ($questionnaire->cmidnumber != '') {
449
        $params = array('itemname' => $questionnaire->name, 'idnumber' => $questionnaire->cmidnumber);
450
    } else {
451
        $params = array('itemname' => $questionnaire->name);
452
    }
453
 
454
    if ($questionnaire->grade > 0) {
455
        $params['gradetype'] = GRADE_TYPE_VALUE;
456
        $params['grademax'] = $questionnaire->grade;
457
        $params['grademin'] = 0;
458
 
459
    } else if ($questionnaire->grade < 0) {
460
        $params['gradetype'] = GRADE_TYPE_SCALE;
461
        $params['scaleid'] = -$questionnaire->grade;
462
 
463
    } else if ($questionnaire->grade == 0) { // No Grade..be sure to delete the grade item if it exists.
464
        $grades = null;
465
        $params = array('deleted' => 1);
466
 
467
    } else {
468
        $params = null; // Allow text comments only.
469
    }
470
 
471
    if ($grades === 'reset') {
472
        $params['reset'] = true;
473
        $grades = null;
474
    }
475
 
476
    return grade_update('mod/questionnaire', $questionnaire->courseid, 'mod', 'questionnaire',
477
                    $questionnaire->id, 0, $grades, $params);
478
}
479
 
480
/**
481
 * This function returns if a scale is being used by one questionnaire
482
 * it it has support for grading and scales. Commented code should be
483
 * modified if necessary. See forum, glossary or journal modules
484
 * as reference.
485
 * @param int $questionnaireid
486
 * @param int $scaleid
487
 * @return boolean True if the scale is used by any questionnaire
488
 *
489
 * Function parameters are unused, but API requires them. Suppress PHPMD warning.
490
 */
491
function questionnaire_scale_used ($questionnaireid, $scaleid) {
492
    return false;
493
}
494
 
495
/**
496
 * Checks if scale is being used by any instance of questionnaire
497
 *
498
 * This is used to find out if scale used anywhere
499
 * @param int $scaleid
500
 * @return boolean True if the scale is used by any questionnaire
501
 *
502
 * Function parameters are unused, but API requires them. Suppress PHPMD warning.
503
 */
504
function questionnaire_scale_used_anywhere($scaleid) {
505
    return false;
506
}
507
 
508
/**
509
 * Serves the questionnaire attachments. Implements needed access control ;-)
510
 *
511
 * @param stdClass $course
512
 * @param stdClass $cm
513
 * @param stdClass $context
514
 * @param string $filearea
515
 * @param array $args
516
 * @param bool $forcedownload
517
 * @return bool false if file not found, does not return if found - justsend the file
518
 *
519
 * $forcedownload is unused, but API requires it. Suppress PHPMD warning.
520
 */
521
function questionnaire_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
522
    global $DB;
523
 
524
    if ($context->contextlevel != CONTEXT_MODULE) {
525
        return false;
526
    }
527
 
528
    require_course_login($course, true, $cm);
529
 
530
    $fileareas = ['intro', 'info', 'thankbody', 'question', 'feedbacknotes', 'sectionheading', 'feedback'];
531
    if (!in_array($filearea, $fileareas)) {
532
        return false;
533
    }
534
 
535
    $componentid = (int)array_shift($args);
536
 
537
    if ($filearea == 'question') {
538
        if (!$DB->record_exists('questionnaire_question', ['id' => $componentid])) {
539
            return false;
540
        }
541
    } else if ($filearea == 'sectionheading') {
542
        if (!$DB->record_exists('questionnaire_fb_sections', ['id' => $componentid])) {
543
            return false;
544
        }
545
    } else if ($filearea == 'feedback') {
546
        if (!$DB->record_exists('questionnaire_feedback', ['id' => $componentid])) {
547
            return false;
548
        }
549
    } else {
550
        if (!$DB->record_exists('questionnaire_survey', ['id' => $componentid])) {
551
            return false;
552
        }
553
    }
554
 
555
    if (!$DB->record_exists('questionnaire', ['id' => $cm->instance])) {
556
        return false;
557
    }
558
 
559
    $fs = get_file_storage();
560
    $relativepath = implode('/', $args);
561
    $fullpath = "/$context->id/mod_questionnaire/$filearea/$componentid/$relativepath";
562
    if (!($file = $fs->get_file_by_hash(sha1($fullpath))) || $file->is_directory()) {
563
        return false;
564
    }
565
 
566
    // Finally send the file.
567
    send_stored_file($file, 0, 0, true); // Download MUST be forced - security!
568
}
569
/**
570
 * Adds module specific settings to the settings block
571
 *
572
 * @param settings_navigation $settings The settings navigation object
573
 * @param navigation_node $questionnairenode The node to add module settings to
574
 *
575
 * $settings is unused, but API requires it. Suppress PHPMD warning.
576
 */
577
function questionnaire_extend_settings_navigation(settings_navigation $settings, navigation_node $questionnairenode) {
578
    global $DB, $USER, $CFG;
579
 
580
    $individualresponse = optional_param('individualresponse', false, PARAM_INT);
581
    $rid = optional_param('rid', false, PARAM_INT); // Response id.
582
    $currentgroupid = optional_param('group', 0, PARAM_INT); // Group id.
583
 
584
    require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php');
585
 
586
    $cm = $settings->get_page()->cm;
587
    $context = $cm->context;
588
    $cmid = $cm->id;
589
    $course = $settings->get_page()->course;
590
 
591
    if (! $questionnaire = $DB->get_record("questionnaire", array("id" => $cm->instance))) {
592
        throw new \moodle_exception('invalidcoursemodule', 'mod_questionnaire');
593
    }
594
 
595
    $courseid = $course->id;
596
    $questionnaire = new questionnaire($course, $cm, 0, $questionnaire);
597
 
598
    if ($owner = $DB->get_field('questionnaire_survey', 'courseid', ['id' => $questionnaire->sid])) {
599
        $owner = (trim($owner) == trim($courseid));
600
    } else {
601
        $owner = true;
602
    }
603
 
604
    // On view page, currentgroupid is not yet sent as an optional_param, so get it.
605
    $groupmode = groups_get_activity_groupmode($cm, $course);
606
    if ($groupmode > 0 && $currentgroupid == 0) {
607
        $currentgroupid = groups_get_activity_group($questionnaire->cm);
608
        if (!groups_is_member($currentgroupid, $USER->id)) {
609
            $currentgroupid = 0;
610
        }
611
    }
612
 
613
    // We want to add these new nodes after the Edit settings node, and before the
614
    // Locally assigned roles node. Of course, both of those are controlled by capabilities.
615
    $keys = $questionnairenode->get_children_key_list();
616
    $beforekey = null;
617
    $i = array_search('modedit', $keys);
618
    if (($i === false) && array_key_exists(0, $keys)) {
619
        $beforekey = $keys[0];
620
    } else if (array_key_exists($i + 1, $keys)) {
621
        $beforekey = $keys[$i + 1];
622
    }
623
 
624
    if (has_capability('mod/questionnaire:manage', $context) && $owner) {
625
        $url = '/mod/questionnaire/qsettings.php';
626
        $node = navigation_node::create(get_string('advancedsettings'),
627
            new moodle_url($url, array('id' => $cmid)),
628
            navigation_node::TYPE_SETTING, null, 'advancedsettings',
629
            new pix_icon('t/edit', ''));
630
        $questionnairenode->add_node($node, $beforekey);
631
    }
632
 
633
    if (has_capability('mod/questionnaire:editquestions', $context) && $owner) {
634
        $url = '/mod/questionnaire/questions.php';
635
        $node = navigation_node::create(get_string('questions', 'questionnaire'),
636
            new moodle_url($url, array('id' => $cmid)),
637
            navigation_node::TYPE_SETTING, null, 'questions',
638
            new pix_icon('t/edit', ''));
639
        $questionnairenode->add_node($node, $beforekey);
640
    }
641
 
642
    if (has_capability('mod/questionnaire:editquestions', $context) && $owner) {
643
        $url = '/mod/questionnaire/feedback.php';
644
        $node = navigation_node::create(get_string('feedback', 'questionnaire'),
645
            new moodle_url($url, array('id' => $cmid)),
646
            navigation_node::TYPE_SETTING, null, 'feedback',
647
            new pix_icon('t/edit', ''));
648
        $questionnairenode->add_node($node, $beforekey);
649
    }
650
 
651
    if (has_capability('mod/questionnaire:preview', $context)) {
652
        $url = '/mod/questionnaire/preview.php';
653
        $node = navigation_node::create(get_string('preview_label', 'questionnaire'),
654
            new moodle_url($url, array('id' => $cmid)),
655
            navigation_node::TYPE_SETTING, null, 'preview',
656
            new pix_icon('t/preview', ''));
657
        $questionnairenode->add_node($node, $beforekey);
658
    }
659
 
660
    if ($questionnaire->user_can_take($USER->id)) {
661
        $url = '/mod/questionnaire/complete.php';
662
        if ($questionnaire->user_has_saved_response($USER->id)) {
663
            $args = ['id' => $cmid, 'resume' => 1];
664
            $text = get_string('resumesurvey', 'questionnaire');
665
        } else {
666
            $args = ['id' => $cmid];
667
            $text = get_string('answerquestions', 'questionnaire');
668
        }
669
        $node = navigation_node::create($text, new moodle_url($url, $args),
670
            navigation_node::TYPE_SETTING, null, '', new pix_icon('i/info', 'answerquestions'));
671
        $questionnairenode->add_node($node, $beforekey);
672
    }
673
    $usernumresp = $questionnaire->count_submissions($USER->id);
674
 
675
    if ($questionnaire->capabilities->readownresponses && ($usernumresp > 0)) {
676
        $url = '/mod/questionnaire/myreport.php';
677
 
678
        if ($usernumresp > 1) {
679
            $urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,
680
                'byresponse' => 0, 'action' => 'summary', 'group' => $currentgroupid);
681
            $node = navigation_node::create(get_string('yourresponses', 'questionnaire'),
682
                new moodle_url($url, $urlargs), navigation_node::TYPE_SETTING, null, 'yourresponses');
683
            $myreportnode = $questionnairenode->add_node($node, $beforekey);
684
 
685
            $urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,
686
                'byresponse' => 0, 'action' => 'summary', 'group' => $currentgroupid);
687
            $myreportnode->add(get_string('summary', 'questionnaire'), new moodle_url($url, $urlargs));
688
 
689
            $urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,
690
                'byresponse' => 1, 'action' => 'vresp', 'group' => $currentgroupid);
691
            $byresponsenode = $myreportnode->add(get_string('viewindividualresponse', 'questionnaire'),
692
                new moodle_url($url, $urlargs));
693
 
694
            $urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,
695
                'byresponse' => 0, 'action' => 'vall', 'group' => $currentgroupid);
696
            $myreportnode->add(get_string('myresponses', 'questionnaire'), new moodle_url($url, $urlargs));
697
            if ($questionnaire->capabilities->downloadresponses) {
698
                $urlargs = array('instance' => $questionnaire->id, 'user' => $USER->id,
699
                    'action' => 'dwnpg', 'group' => $currentgroupid);
700
                $myreportnode->add(get_string('downloadtextformat', 'questionnaire'),
701
                    new moodle_url('/mod/questionnaire/report.php', $urlargs));
702
            }
703
        } else {
704
            $urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,
705
                'byresponse' => 1, 'action' => 'vresp', 'group' => $currentgroupid);
706
            $node = navigation_node::create(get_string('yourresponse', 'questionnaire'),
707
                new moodle_url($url, $urlargs), navigation_node::TYPE_SETTING, null, 'yourresponse');
708
            $myreportnode = $questionnairenode->add_node($node, $beforekey);
709
        }
710
    }
711
 
712
    // If questionnaire is set to separate groups, prevent user who is not member of any group
713
    // and is not a non-editing teacher to view All responses.
714
    if ($questionnaire->can_view_all_responses($usernumresp)) {
715
 
716
        $url = '/mod/questionnaire/report.php';
717
        $node = navigation_node::create(get_string('viewallresponses', 'questionnaire'),
718
            new moodle_url($url, array('instance' => $questionnaire->id, 'action' => 'vall')),
719
            navigation_node::TYPE_SETTING, null, 'vall');
720
        $reportnode = $questionnairenode->add_node($node, $beforekey);
721
 
722
        if ($questionnaire->capabilities->viewsingleresponse) {
723
            $summarynode = $reportnode->add(get_string('summary', 'questionnaire'),
724
                new moodle_url('/mod/questionnaire/report.php',
725
                    array('instance' => $questionnaire->id, 'action' => 'vall')));
726
        } else {
727
            $summarynode = $reportnode;
728
        }
729
        $summarynode->add(get_string('order_default', 'questionnaire'),
730
            new moodle_url('/mod/questionnaire/report.php',
731
                array('instance' => $questionnaire->id, 'action' => 'vall', 'group' => $currentgroupid)));
732
        $summarynode->add(get_string('order_ascending', 'questionnaire'),
733
            new moodle_url('/mod/questionnaire/report.php',
734
                array('instance' => $questionnaire->id, 'action' => 'vallasort', 'group' => $currentgroupid)));
735
        $summarynode->add(get_string('order_descending', 'questionnaire'),
736
            new moodle_url('/mod/questionnaire/report.php',
737
                array('instance' => $questionnaire->id, 'action' => 'vallarsort', 'group' => $currentgroupid)));
738
 
739
        if ($questionnaire->capabilities->deleteresponses) {
740
            $summarynode->add(get_string('deleteallresponses', 'questionnaire'),
741
                new moodle_url('/mod/questionnaire/report.php',
742
                    array('instance' => $questionnaire->id, 'action' => 'delallresp', 'group' => $currentgroupid)));
743
        }
744
 
745
        if ($questionnaire->capabilities->downloadresponses) {
746
            $summarynode->add(get_string('downloadtextformat', 'questionnaire'),
747
                new moodle_url('/mod/questionnaire/report.php',
748
                    array('instance' => $questionnaire->id, 'action' => 'dwnpg', 'group' => $currentgroupid)));
749
        }
750
        if ($questionnaire->capabilities->viewsingleresponse) {
751
            $byresponsenode = $reportnode->add(get_string('viewbyresponse', 'questionnaire'),
752
                new moodle_url('/mod/questionnaire/report.php',
753
                    array('instance' => $questionnaire->id, 'action' => 'vresp', 'byresponse' => 1, 'group' => $currentgroupid)));
754
 
755
            $byresponsenode->add(get_string('view', 'questionnaire'),
756
                new moodle_url('/mod/questionnaire/report.php',
757
                    array('instance' => $questionnaire->id, 'action' => 'vresp', 'byresponse' => 1, 'group' => $currentgroupid)));
758
 
759
            if ($individualresponse) {
760
                $byresponsenode->add(get_string('deleteresp', 'questionnaire'),
761
                    new moodle_url('/mod/questionnaire/report.php',
762
                        array('instance' => $questionnaire->id, 'action' => 'dresp', 'byresponse' => 1,
763
                            'rid' => $rid, 'group' => $currentgroupid, 'individualresponse' => 1)));
764
            }
765
        }
766
    }
767
 
768
    $canviewgroups = true;
769
    $groupmode = groups_get_activity_groupmode($cm, $course);
770
    if ($groupmode == 1) {
771
        $canviewgroups = groups_has_membership($cm, $USER->id);
772
    }
773
    $canviewallgroups = has_capability('moodle/site:accessallgroups', $context);
774
    if ($questionnaire->capabilities->viewsingleresponse && ($canviewallgroups || $canviewgroups)) {
775
        $url = '/mod/questionnaire/show_nonrespondents.php';
776
        $node = navigation_node::create(get_string('show_nonrespondents', 'questionnaire'),
777
            new moodle_url($url, array('id' => $cmid)),
778
            navigation_node::TYPE_SETTING, null, 'nonrespondents');
779
        $questionnairenode->add_node($node, $beforekey);
780
 
781
    }
782
}
783
 
784
// Any other questionnaire functions go here.  Each of them must have a name that
785
// starts with questionnaire_.
786
 
787
/**
788
 * Return the view actions.
789
 * @return string[]
790
 */
791
function questionnaire_get_view_actions() {
792
    return array('view', 'view all');
793
}
794
 
795
/**
796
 * Return the post actions.
797
 * @return string[]
798
 */
799
function questionnaire_get_post_actions() {
800
    return array('submit', 'update');
801
}
802
 
803
/**
804
 * Return the recent activity.
805
 * @param array $activities
806
 * @param int $index
807
 * @param int $timestart
808
 * @param int $courseid
809
 * @param int $cmid
810
 * @param int $userid
811
 * @param int $groupid
812
 * @return mixed|void
813
 */
814
function questionnaire_get_recent_mod_activity(&$activities, &$index, $timestart,
815
                                               $courseid, $cmid, $userid = 0, $groupid = 0) {
816
 
817
    global $CFG, $COURSE, $USER, $DB;
818
    require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');
819
    require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php');
820
 
821
    if ($COURSE->id == $courseid) {
822
        $course = $COURSE;
823
    } else {
824
        $course = $DB->get_record('course', ['id' => $courseid]);
825
    }
826
 
827
    $modinfo = get_fast_modinfo($course);
828
 
829
    $cm = $modinfo->cms[$cmid];
830
    $questionnaire = $DB->get_record('questionnaire', ['id' => $cm->instance]);
831
    $questionnaire = new questionnaire($course, $cm, 0, $questionnaire);
832
 
833
    $context = context_module::instance($cm->id);
834
    $grader = has_capability('mod/questionnaire:viewsingleresponse', $context);
835
 
836
    // If this is a copy of a public questionnaire whose original is located in another course,
837
    // current user (teacher) cannot view responses.
838
    if ($grader) {
839
        // For a public questionnaire, look for the original public questionnaire that it is based on.
840
        if (!$questionnaire->survey_is_public_master()) {
841
            // For a public questionnaire, look for the original public questionnaire that it is based on.
842
            $originalquestionnaire = $DB->get_record('questionnaire',
843
                ['sid' => $questionnaire->survey->id, 'course' => $questionnaire->survey->courseid]);
844
            $cmoriginal = get_coursemodule_from_instance("questionnaire", $originalquestionnaire->id,
845
                $questionnaire->survey->courseid);
846
            $contextoriginal = context_course::instance($questionnaire->survey->courseid, MUST_EXIST);
847
            if (!has_capability('mod/questionnaire:viewsingleresponse', $contextoriginal)) {
848
                $tmpactivity = new stdClass();
849
                $tmpactivity->type = 'questionnaire';
850
                $tmpactivity->cmid = $cm->id;
851
                $tmpactivity->cannotview = true;
852
                $tmpactivity->anonymous = false;
853
                $activities[$index++] = $tmpactivity;
854
                return $activities;
855
            }
856
        }
857
    }
858
 
859
    if ($userid) {
860
        $userselect = "AND u.id = :userid";
861
        $params['userid'] = $userid;
862
    } else {
863
        $userselect = '';
864
    }
865
 
866
    if ($groupid) {
867
        $groupselect = 'AND gm.groupid = :groupid';
868
        $groupjoin = 'JOIN {groups_members} gm ON  gm.userid=u.id';
869
        $params['groupid'] = $groupid;
870
    } else {
871
        $groupselect = '';
872
        $groupjoin = '';
873
    }
874
 
875
    $params['timestart'] = $timestart;
876
    $params['questionnaireid'] = $questionnaire->id;
877
 
878
    $ufields = user_picture::fields('u', null, 'useridagain');
879
    if (!$attempts = $DB->get_records_sql("
880
                    SELECT qr.*,
881
                    {$ufields}
882
                    FROM {questionnaire_response} qr
883
                    JOIN {user} u ON u.id = qr.userid
884
                    $groupjoin
885
                    WHERE qr.submitted > :timestart
886
                    AND qr.questionnaireid = :questionnaireid
887
                    $userselect
888
                    $groupselect
889
                    ORDER BY qr.submitted ASC", $params)) {
890
        return;
891
    }
892
 
893
    $accessallgroups = has_capability('moodle/site:accessallgroups', $context);
894
    $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
895
    $groupmode = groups_get_activity_groupmode($cm, $course);
896
 
897
    $usersgroups = null;
898
    $aname = format_string($cm->name, true);
899
    $userattempts = array();
900
    foreach ($attempts as $attempt) {
901
        if ($questionnaire->respondenttype != 'anonymous') {
902
            if (!isset($userattempts[$attempt->lastname])) {
903
                $userattempts[$attempt->lastname] = 1;
904
            } else {
905
                $userattempts[$attempt->lastname]++;
906
            }
907
        }
908
        if ($attempt->userid != $USER->id) {
909
            if (!$grader) {
910
                // View complete individual responses permission required.
911
                continue;
912
            }
913
 
914
            if (($groupmode == SEPARATEGROUPS) && !$accessallgroups) {
915
                if ($usersgroups === null) {
916
                    $usersgroups = groups_get_all_groups($course->id,
917
                    $attempt->userid, $cm->groupingid);
918
                    if (is_array($usersgroups)) {
919
                        $usersgroups = array_keys($usersgroups);
920
                    } else {
921
                         $usersgroups = array();
922
                    }
923
                }
924
                if (!array_intersect($usersgroups, $modinfo->groups[$cm->id])) {
925
                    continue;
926
                }
927
            }
928
        }
929
 
930
        $tmpactivity = new stdClass();
931
 
932
        $tmpactivity->type = 'questionnaire';
933
        $tmpactivity->cmid = $cm->id;
934
        $tmpactivity->cminstance = $cm->instance;
935
        // Current user is admin - or teacher enrolled in original public course.
936
        if (isset($cmoriginal)) {
937
            $tmpactivity->cminstance = $cmoriginal->instance;
938
        }
939
        $tmpactivity->cannotview = false;
940
        $tmpactivity->anonymous = false;
941
        $tmpactivity->name = $aname;
942
        $tmpactivity->sectionnum = $cm->sectionnum;
943
        $tmpactivity->timestamp = $attempt->submitted;
944
        $tmpactivity->groupid = $groupid;
945
        if (isset($userattempts[$attempt->lastname])) {
946
            $tmpactivity->nbattempts = $userattempts[$attempt->lastname];
947
        }
948
 
949
        $tmpactivity->content = new stdClass();
950
        $tmpactivity->content->attemptid = $attempt->id;
951
 
952
        $userfields = explode(',', user_picture::fields());
953
        $tmpactivity->user = new stdClass();
954
        foreach ($userfields as $userfield) {
955
            if ($userfield == 'id') {
956
                $tmpactivity->user->{$userfield} = $attempt->userid;
957
            } else {
958
                if (!empty($attempt->{$userfield})) {
959
                    $tmpactivity->user->{$userfield} = $attempt->{$userfield};
960
                } else {
961
                    $tmpactivity->user->{$userfield} = null;
962
                }
963
            }
964
        }
965
        if ($questionnaire->respondenttype != 'anonymous') {
966
            $tmpactivity->user->fullname = fullname($attempt, $viewfullnames);
967
        } else {
968
            $tmpactivity->user = '';
969
            unset ($tmpactivity->user);
970
            $tmpactivity->anonymous = true;
971
        }
972
        $activities[$index++] = $tmpactivity;
973
    }
974
}
975
 
976
/**
977
 * Prints all users who have completed a specified questionnaire since a given time
978
 *
979
 * @param stdClass $activity
980
 * @param int $courseid
981
 * @param string $detail not used but needed for compability
982
 * @param array $modnames
983
 * @return void Output is echo'd
984
 *
985
 * $details and $modenames are unused, but API requires them. Suppress PHPMD warning.
986
 */
987
function questionnaire_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
988
    global $OUTPUT;
989
 
990
    // If the questionnaire is "anonymous", then $activity->user won't have been set, so do not display respondent info.
991
    if ($activity->anonymous) {
992
        $stranonymous = ' ('.get_string('anonymous', 'questionnaire').')';
993
        $activity->nbattempts = '';
994
    } else {
995
        $stranonymous = '';
996
    }
997
    // Current user cannot view responses to public questionnaire.
998
    if ($activity->cannotview) {
999
        $strcannotview = get_string('cannotviewpublicresponses', 'questionnaire');
1000
    }
1001
    echo html_writer::start_tag('div');
1002
    echo html_writer::start_tag('span', array('class' => 'clearfix',
1003
                    'style' => 'margin-top:0px; background-color: white; display: inline-block;'));
1004
 
1005
    if (!$activity->anonymous && !$activity->cannotview) {
1006
        echo html_writer::tag('div', $OUTPUT->user_picture($activity->user, array('courseid' => $courseid)),
1007
                        array('style' => 'float: left; padding-right: 10px;'));
1008
    }
1009
    if (!$activity->cannotview) {
1010
        echo html_writer::start_tag('div');
1011
        echo html_writer::start_tag('div');
1012
 
1013
        $urlparams = array('action' => 'vresp', 'instance' => $activity->cminstance,
1014
                        'group' => $activity->groupid, 'rid' => $activity->content->attemptid, 'individualresponse' => 1);
1015
 
1016
        $context = context_module::instance($activity->cmid);
1017
        if (has_capability('mod/questionnaire:viewsingleresponse', $context)) {
1018
            $report = 'report.php';
1019
        } else {
1020
            $report = 'myreport.php';
1021
        }
1022
        echo html_writer::tag('a', get_string('response', 'questionnaire').' '.$activity->nbattempts.$stranonymous,
1023
                        array('href' => new moodle_url('/mod/questionnaire/'.$report, $urlparams)));
1024
        echo html_writer::end_tag('div');
1025
    } else {
1026
        echo html_writer::start_tag('div');
1027
        echo html_writer::start_tag('div');
1028
        echo html_writer::tag('div', $strcannotview);
1029
        echo html_writer::end_tag('div');
1030
    }
1031
    if (!$activity->anonymous  && !$activity->cannotview) {
1032
        $url = new moodle_url('/user/view.php', array('course' => $courseid, 'id' => $activity->user->id));
1033
        $name = $activity->user->fullname;
1034
        $link = html_writer::link($url, $name);
1035
        echo html_writer::start_tag('div', array('class' => 'user'));
1036
        echo $link .' - '. userdate($activity->timestamp);
1037
        echo html_writer::end_tag('div');
1038
    }
1039
 
1040
    echo html_writer::end_tag('div');
1041
    echo html_writer::end_tag('span');
1042
    echo html_writer::end_tag('div');
1043
 
1044
    return;
1045
}
1046
 
1047
/**
1048
 * Prints questionnaire summaries on 'My home' page
1049
 *
1050
 * Prints questionnaire name, due date and attempt information on
1051
 * questionnaires that have a deadline that has not already passed
1052
 * and it is available for taking.
1053
 *
1054
 * @param array $courses An array of course objects to get questionnaire instances from
1055
 * @param array $htmlarray Store overview output array( course ID => 'questionnaire' => HTML output )
1056
 * @return void
1057
 */
1058
function questionnaire_print_overview($courses, &$htmlarray) {
1059
    global $USER, $CFG, $DB, $OUTPUT;
1060
 
1061
    require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');
1062
 
1063
    if (!$questionnaires = get_all_instances_in_courses('questionnaire', $courses)) {
1064
        return;
1065
    }
1066
 
1067
    // Get Necessary Strings.
1068
    $strquestionnaire = get_string('modulename', 'questionnaire');
1069
    $strnotattempted = get_string('noattempts', 'questionnaire');
1070
    $strattempted = get_string('attempted', 'questionnaire');
1071
    $strsavedbutnotsubmitted = get_string('savedbutnotsubmitted', 'questionnaire');
1072
 
1073
    $now = time();
1074
    foreach ($questionnaires as $questionnaire) {
1075
 
1076
        // The questionnaire has a deadline.
1077
        if (($questionnaire->closedate != 0)
1078
                        // And it is before the deadline has been met.
1079
                        && ($questionnaire->closedate >= $now)
1080
                        // And the questionnaire is available.
1081
                        && (($questionnaire->opendate == 0) || ($questionnaire->opendate <= $now))) {
1082
            if (!$questionnaire->visible) {
1083
                $class = ' class="dimmed"';
1084
            } else {
1085
                $class = '';
1086
            }
1087
            $str = $OUTPUT->box("$strquestionnaire:
1088
                            <a$class href=\"$CFG->wwwroot/mod/questionnaire/view.php?id=$questionnaire->coursemodule\">".
1089
                            format_string($questionnaire->name).'</a>', 'name');
1090
 
1091
            // Deadline.
1092
            $str .= $OUTPUT->box(get_string('closeson', 'questionnaire', userdate($questionnaire->closedate)), 'info');
1093
            $attempts = $DB->get_records('questionnaire_response',
1094
                ['questionnaireid' => $questionnaire->id, 'userid' => $USER->id, 'complete' => 'y']) ?? [];
1095
            $nbattempts = count($attempts);
1096
 
1097
            // Do not display a questionnaire as due if it can only be sumbitted once and it has already been submitted!
1098
            if ($nbattempts != 0 && $questionnaire->qtype == QUESTIONNAIREONCE) {
1099
                continue;
1100
            }
1101
 
1102
            // Attempt information.
1103
            if (has_capability('mod/questionnaire:manage', context_module::instance($questionnaire->coursemodule))) {
1104
                // Number of user attempts.
1105
                $attempts = $DB->count_records('questionnaire_response',
1106
                    ['questionnaireid' => $questionnaire->id, 'complete' => 'y']);
1107
                $str .= $OUTPUT->box(get_string('numattemptsmade', 'questionnaire', $attempts), 'info');
1108
            } else {
1109
                if ($responses = questionnaire_get_user_responses($questionnaire->id, $USER->id, false)) {
1110
                    foreach ($responses as $response) {
1111
                        if ($response->complete == 'y') {
1112
                            $str .= $OUTPUT->box($strattempted, 'info');
1113
                            break;
1114
                        } else {
1115
                            $str .= $OUTPUT->box($strsavedbutnotsubmitted, 'info');
1116
                        }
1117
                    }
1118
                } else {
1119
                    $str .= $OUTPUT->box($strnotattempted, 'info');
1120
                }
1121
            }
1122
            $str = $OUTPUT->box($str, 'questionnaire overview');
1123
 
1124
            if (empty($htmlarray[$questionnaire->course]['questionnaire'])) {
1125
                $htmlarray[$questionnaire->course]['questionnaire'] = $str;
1126
            } else {
1127
                $htmlarray[$questionnaire->course]['questionnaire'] .= $str;
1128
            }
1129
        }
1130
    }
1131
}
1132
 
1133
 
1134
/**
1135
 * Implementation of the function for printing the form elements that control
1136
 * whether the course reset functionality affects the questionnaire.
1137
 *
1138
 * @param stdClass $mform the course reset form that is being built.
1139
 */
1140
function questionnaire_reset_course_form_definition($mform) {
1141
    $mform->addElement('header', 'questionnaireheader', get_string('modulenameplural', 'questionnaire'));
1142
    $mform->addElement('advcheckbox', 'reset_questionnaire',
1143
                    get_string('removeallquestionnaireattempts', 'questionnaire'));
1144
}
1145
 
1146
/**
1147
 * Course reset form defaults.
1148
 * @param stdClass $course
1149
 * @return array the defaults.
1150
 *
1151
 * Function parameters are unused, but API requires them. Suppress PHPMD warning.
1152
 */
1153
function questionnaire_reset_course_form_defaults($course) {
1154
    return array('reset_questionnaire' => 1);
1155
}
1156
 
1157
/**
1158
 * Actual implementation of the reset course functionality, delete all the
1159
 * questionnaire responses for course $data->courseid.
1160
 *
1161
 * @param stdClass $data the data submitted from the reset course.
1162
 * @return array status array
1163
 */
1164
function questionnaire_reset_userdata($data) {
1165
    global $CFG, $DB;
1166
    require_once($CFG->libdir . '/questionlib.php');
1167
    require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');
1168
 
1169
    $componentstr = get_string('modulenameplural', 'questionnaire');
1170
    $status = array();
1171
 
1172
    if (!empty($data->reset_questionnaire)) {
1173
        $surveys = questionnaire_get_survey_list($data->courseid, '');
1174
 
1175
        // Delete responses.
1176
        foreach ($surveys as $survey) {
1177
            // Get all responses for this questionnaire.
1178
            $sql = "SELECT qr.id, qr.questionnaireid, qr.submitted, qr.userid, q.sid
1179
                 FROM {questionnaire} q
1180
                 INNER JOIN {questionnaire_response} qr ON q.id = qr.questionnaireid
1181
                 WHERE q.sid = ?
1182
                 ORDER BY qr.id";
1183
            $resps = $DB->get_records_sql($sql, [$survey->id]);
1184
            if (!empty($resps)) {
1185
                $questionnaire = $DB->get_record("questionnaire", ["sid" => $survey->id, "course" => $survey->courseid]);
1186
                $questionnaire->course = $DB->get_record("course", array("id" => $questionnaire->course));
1187
                foreach ($resps as $response) {
1188
                    questionnaire_delete_response($response, $questionnaire);
1189
                }
1190
            }
1191
            // Remove this questionnaire's grades (and feedback) from gradebook (if any).
1192
            $select = "itemmodule = 'questionnaire' AND iteminstance = ".$survey->qid;
1193
            $fields = 'id';
1194
            if ($itemid = $DB->get_record_select('grade_items', $select, null, $fields)) {
1195
                $itemid = $itemid->id;
1196
                $DB->delete_records_select('grade_grades', 'itemid = '.$itemid);
1197
 
1198
            }
1199
        }
1200
        $status[] = array(
1201
                        'component' => $componentstr,
1202
                        'item' => get_string('deletedallresp', 'questionnaire'),
1203
                        'error' => false);
1204
 
1205
        $status[] = array(
1206
                        'component' => $componentstr,
1207
                        'item' => get_string('gradesdeleted', 'questionnaire'),
1208
                        'error' => false);
1209
    }
1210
    return $status;
1211
}
1212
 
1213
/**
1214
 * Obtains the automatic completion state for this questionnaire based on the condition
1215
 * in questionnaire settings.
1216
 *
1217
 * @param object $cm Course-module
1218
 * @param int $userid User ID
1219
 * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
1220
 * @return bool True if completed, false if not, $type if conditions not set.
1221
 *
1222
 */
1223
function questionnaire_get_completion_state($cm, $userid, $type) {
1224
    global $DB;
1225
 
1226
    // Get questionnaire details.
1227
    $questionnaire = $DB->get_record('questionnaire', array('id' => $cm->instance), '*', MUST_EXIST);
1228
 
1229
    // If completion option is enabled, evaluate it and return true/false.
1230
    if ($questionnaire->completionsubmit) {
1231
        $params = ['userid' => $userid, 'questionnaireid' => $questionnaire->id, 'complete' => 'y'];
1232
        return $DB->record_exists('questionnaire_response', $params);
1233
    } else {
1234
        // Completion option is not enabled so just return $type.
1235
        return $type;
1236
    }
1237
}
1238
 
1239
/**
1240
 * This function receives a calendar event and returns the action associated with it, or null if there is none.
1241
 *
1242
 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
1243
 * is not displayed on the block.
1244
 *
1245
 * @param calendar_event $event
1246
 * @param \core_calendar\action_factory $factory
1247
 * @return \core_calendar\local\event\entities\action_interface|null
1248
 */
1249
function mod_questionnaire_core_calendar_provide_event_action(calendar_event $event,
1250
                                                            \core_calendar\action_factory $factory) {
1251
    $cm = get_fast_modinfo($event->courseid)->instances['questionnaire'][$event->instance];
1252
 
1253
    $completion = new \completion_info($cm->get_course());
1254
 
1255
    $completiondata = $completion->get_data($cm, false);
1256
 
1257
    if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {
1258
        return null;
1259
    }
1260
 
1261
    return $factory->create_instance(
1262
            get_string('view'),
1263
            new \moodle_url('/mod/questionnaire/view.php', ['id' => $cm->id]),
1264
            1,
1265
            true
1266
    );
1267
}
1268
 
1269
/**
1270
 * Called after the activity and module have been created. Use this to copy any images if the questionnaire was created from another
1271
 * questionnaire survey.
1272
 *
1273
 * @param stdClass $data
1274
 * @param stdClass $course
1275
 * @throws coding_exception
1276
 */
1277
function mod_questionnaire_coursemodule_edit_post_actions($data, $course) {
1278
    global $DB;
1279
 
1280
    if (!empty($data->copyid)) {
1281
        $cm = (object)['id' => $data->coursemodule];
1282
        $questionnaire = new questionnaire($course, $cm, 0, $data);
1283
        $oldquestionnaireid = $DB->get_field('questionnaire', 'id', ['sid' => $data->copyid]);
1284
        $oldcm = get_coursemodule_from_instance('questionnaire', $oldquestionnaireid);
1285
        $oldquestionnaire = new questionnaire($course, $oldcm, $oldquestionnaireid, null);
1286
        $oldcontext = context_module::instance($oldcm->id);
1287
        $newcontext = context_module::instance($data->coursemodule);
1288
        $areas = $questionnaire->get_all_file_areas();
1289
        $oldareas = $oldquestionnaire->get_all_file_areas();
1290
        $fs = new \mod_questionnaire\file_storage();
1291
        foreach ($areas as $area => $ids) {
1292
            if (is_array($ids)) {
1293
                $oldid = current($oldareas[$area]);
1294
                foreach ($ids as $id) {
1295
                    $fs->copy_area_files_to_new_context($oldcontext->id, $newcontext->id, 'mod_questionnaire', $area, $oldid, $id);
1296
                    $oldid = next($oldareas[$area]);
1297
                }
1298
            } else {
1299
                $fs->copy_area_files_to_new_context($oldcontext->id, $newcontext->id, 'mod_questionnaire', $area,
1300
                    $oldareas[$area], $ids);
1301
            }
1302
        }
1303
    }
1304
 
1305
    return $data;
1306
}