MDL-53972 feedback: Final deprecation in mod/feedback/lib.php
[moodle.git] / mod / feedback / lib.php
CommitLineData
32be99dc 1<?php
32be99dc 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 feedback
19 * includes the main-part of feedback-functions
20 *
9714baa1 21 * @package mod_feedback
32be99dc 22 * @copyright Andreas Grabs
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
952c6e26
SB
26defined('MOODLE_INTERNAL') || die();
27
32be99dc 28/** Include eventslib.php */
3b120e46 29require_once($CFG->libdir.'/eventslib.php');
9c515cdc
AA
30// Include forms lib.
31require_once($CFG->libdir.'/formslib.php');
c70ad9f7 32
c70ad9f7 33define('FEEDBACK_ANONYMOUS_YES', 1);
34define('FEEDBACK_ANONYMOUS_NO', 2);
35define('FEEDBACK_MIN_ANONYMOUS_COUNT_IN_GROUP', 2);
36define('FEEDBACK_DECIMAL', '.');
37define('FEEDBACK_THOUSAND', ',');
38define('FEEDBACK_RESETFORM_RESET', 'feedback_reset_data_');
39define('FEEDBACK_RESETFORM_DROP', 'feedback_drop_feedback_');
40define('FEEDBACK_MAX_PIX_LENGTH', '400'); //max. Breite des grafischen Balkens in der Auswertung
8a23b38c 41define('FEEDBACK_DEFAULT_PAGE_COUNT', 20);
c70ad9f7 42
90e8330f
SL
43// Event types.
44define('FEEDBACK_EVENT_TYPE_OPEN', 'open');
45define('FEEDBACK_EVENT_TYPE_CLOSE', 'close');
46
06ca6cc9
TH
47/**
48 * Returns all other caps used in module.
49 *
50 * @return array
51 */
52function feedback_get_extra_capabilities() {
53 return array('moodle/site:accessallgroups');
54}
55
29081d02 56/**
32be99dc 57 * @uses FEATURE_GROUPS
58 * @uses FEATURE_GROUPINGS
32be99dc 59 * @uses FEATURE_MOD_INTRO
60 * @uses FEATURE_COMPLETION_TRACKS_VIEWS
61 * @uses FEATURE_GRADE_HAS_GRADE
62 * @uses FEATURE_GRADE_OUTCOMES
29081d02 63 * @param string $feature FEATURE_xx constant for requested feature
64 * @return mixed True if module supports feature, null if doesn't know
65 */
66function feedback_supports($feature) {
67 switch($feature) {
42f103be 68 case FEATURE_GROUPS: return true;
69 case FEATURE_GROUPINGS: return true;
dc5c2bd9 70 case FEATURE_MOD_INTRO: return true;
29081d02 71 case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
8a41eb4f 72 case FEATURE_COMPLETION_HAS_RULES: return true;
42f103be 73 case FEATURE_GRADE_HAS_GRADE: return false;
74 case FEATURE_GRADE_OUTCOMES: return false;
cb3dcd78 75 case FEATURE_BACKUP_MOODLE2: return true;
3e4c2435 76 case FEATURE_SHOW_DESCRIPTION: return true;
42f103be 77
29081d02 78 default: return null;
79 }
80}
3b120e46 81
c70ad9f7 82/**
32be99dc 83 * this will create a new instance and return the id number
84 * of the new instance.
85 *
86 * @global object
87 * @param object $feedback the object given by mod_feedback_mod_form
88 * @return int
89 */
c70ad9f7 90function feedback_add_instance($feedback) {
c18269c7 91 global $DB;
c70ad9f7 92
93 $feedback->timemodified = time();
94 $feedback->id = '';
95
9c5bc7a5 96 if (empty($feedback->site_after_submit)) {
c184660d 97 $feedback->site_after_submit = '';
98 }
c70ad9f7 99
100 //saving the feedback in db
a8f3a651 101 $feedbackid = $DB->insert_record("feedback", $feedback);
37b86a39 102
103 $feedback->id = $feedbackid;
104
c70ad9f7 105 feedback_set_events($feedback);
37b86a39 106
abd2899c
AG
107 if (!isset($feedback->coursemodule)) {
108 $cm = get_coursemodule_from_id('feedback', $feedback->id);
109 $feedback->coursemodule = $cm->id;
110 }
2c979976 111 $context = context_module::instance($feedback->coursemodule);
abd2899c 112
9ffbdafc
AG
113 if (!empty($feedback->completionexpected)) {
114 \core_completion\api::update_completion_date_event($feedback->coursemodule, 'feedback', $feedback->id,
115 $feedback->completionexpected);
116 }
117
abd2899c
AG
118 $editoroptions = feedback_get_editor_options();
119
120 // process the custom wysiwyg editor in page_after_submit
121 if ($draftitemid = $feedback->page_after_submit_editor['itemid']) {
122 $feedback->page_after_submit = file_save_draft_area_files($draftitemid, $context->id,
123 'mod_feedback', 'page_after_submit',
124 0, $editoroptions,
125 $feedback->page_after_submit_editor['text']);
126
127 $feedback->page_after_submitformat = $feedback->page_after_submit_editor['format'];
128 }
129 $DB->update_record('feedback', $feedback);
130
c70ad9f7 131 return $feedbackid;
132}
133
134/**
32be99dc 135 * this will update a given instance
136 *
137 * @global object
138 * @param object $feedback the object given by mod_feedback_mod_form
139 * @return boolean
140 */
c70ad9f7 141function feedback_update_instance($feedback) {
c18269c7 142 global $DB;
c70ad9f7 143
144 $feedback->timemodified = time();
145 $feedback->id = $feedback->instance;
146
9c5bc7a5 147 if (empty($feedback->site_after_submit)) {
c184660d 148 $feedback->site_after_submit = '';
149 }
c70ad9f7 150
151 //save the feedback into the db
9d749339 152 $DB->update_record("feedback", $feedback);
c70ad9f7 153
154 //create or update the new events
155 feedback_set_events($feedback);
9ffbdafc
AG
156 $completionexpected = (!empty($feedback->completionexpected)) ? $feedback->completionexpected : null;
157 \core_completion\api::update_completion_date_event($feedback->coursemodule, 'feedback', $feedback->id, $completionexpected);
37b86a39 158
2c979976 159 $context = context_module::instance($feedback->coursemodule);
abd2899c
AG
160
161 $editoroptions = feedback_get_editor_options();
162
163 // process the custom wysiwyg editor in page_after_submit
164 if ($draftitemid = $feedback->page_after_submit_editor['itemid']) {
165 $feedback->page_after_submit = file_save_draft_area_files($draftitemid, $context->id,
166 'mod_feedback', 'page_after_submit',
167 0, $editoroptions,
168 $feedback->page_after_submit_editor['text']);
169
170 $feedback->page_after_submitformat = $feedback->page_after_submit_editor['format'];
171 }
172 $DB->update_record('feedback', $feedback);
173
c70ad9f7 174 return true;
175}
176
a59ff6b0
AG
177/**
178 * Serves the files included in feedback items like label. Implements needed access control ;-)
179 *
485ec594
AG
180 * There are two situations in general where the files will be sent.
181 * 1) filearea = item, 2) filearea = template
182 *
d2b7803e
DC
183 * @package mod_feedback
184 * @category files
185 * @param stdClass $course course object
186 * @param stdClass $cm course module object
187 * @param stdClass $context context object
188 * @param string $filearea file area
189 * @param array $args extra arguments
190 * @param bool $forcedownload whether or not force download
261cbbac 191 * @param array $options additional options affecting the file serving
a59ff6b0
AG
192 * @return bool false if file not found, does not return if found - justsend the file
193 */
261cbbac 194function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
a59ff6b0
AG
195 global $CFG, $DB;
196
abd2899c
AG
197 if ($filearea === 'item' or $filearea === 'template') {
198 $itemid = (int)array_shift($args);
199 //get the item what includes the file
200 if (!$item = $DB->get_record('feedback_item', array('id'=>$itemid))) {
201 return false;
202 }
203 $feedbackid = $item->feedback;
204 $templateid = $item->template;
32d2ddab 205 }
22ca3e49 206
abd2899c
AG
207 if ($filearea === 'page_after_submit' or $filearea === 'item') {
208 if (! $feedback = $DB->get_record("feedback", array("id"=>$cm->instance))) {
485ec594
AG
209 return false;
210 }
a59ff6b0 211
abd2899c
AG
212 $feedbackid = $feedback->id;
213
214 //if the filearea is "item" so we check the permissions like view/complete the feedback
485ec594
AG
215 $canload = false;
216 //first check whether the user has the complete capability
9c5bc7a5 217 if (has_capability('mod/feedback:complete', $context)) {
485ec594
AG
218 $canload = true;
219 }
22ca3e49 220
485ec594 221 //now we check whether the user has the view capability
9c5bc7a5 222 if (has_capability('mod/feedback:view', $context)) {
485ec594
AG
223 $canload = true;
224 }
22ca3e49 225
485ec594
AG
226 //if the feedback is on frontpage and anonymous and the fullanonymous is allowed
227 //so the file can be loaded too.
9c5bc7a5 228 if (isset($CFG->feedback_allowfullanonymous)
485ec594
AG
229 AND $CFG->feedback_allowfullanonymous
230 AND $course->id == SITEID
231 AND $feedback->anonymous == FEEDBACK_ANONYMOUS_YES ) {
232 $canload = true;
233 }
234
9c5bc7a5 235 if (!$canload) {
485ec594
AG
236 return false;
237 }
9c5bc7a5 238 } else if ($filearea === 'template') { //now we check files in templates
abd2899c 239 if (!$template = $DB->get_record('feedback_template', array('id'=>$templateid))) {
485ec594
AG
240 return false;
241 }
22ca3e49 242
485ec594 243 //if the file is not public so the capability edititems has to be there
9c5bc7a5
AG
244 if (!$template->ispublic) {
245 if (!has_capability('mod/feedback:edititems', $context)) {
485ec594
AG
246 return false;
247 }
9c5bc7a5
AG
248 } else { //on public templates, at least the user has to be logged in
249 if (!isloggedin()) {
485ec594
AG
250 return false;
251 }
252 }
9c5bc7a5 253 } else {
32d2ddab
AG
254 return false;
255 }
a59ff6b0 256
32d2ddab 257 if ($context->contextlevel == CONTEXT_MODULE) {
abd2899c 258 if ($filearea !== 'item' and $filearea !== 'page_after_submit') {
a59ff6b0
AG
259 return false;
260 }
32d2ddab 261 }
a59ff6b0 262
f35c4500 263 if ($context->contextlevel == CONTEXT_COURSE || $context->contextlevel == CONTEXT_SYSTEM) {
32d2ddab 264 if ($filearea !== 'template') {
a59ff6b0
AG
265 return false;
266 }
32d2ddab 267 }
0f21a964 268
32d2ddab 269 $relativepath = implode('/', $args);
abd2899c
AG
270 if ($filearea === 'page_after_submit') {
271 $fullpath = "/{$context->id}/mod_feedback/$filearea/$relativepath";
272 } else {
273 $fullpath = "/{$context->id}/mod_feedback/$filearea/{$item->id}/$relativepath";
274 }
a59ff6b0 275
32d2ddab 276 $fs = get_file_storage();
abd2899c 277
32d2ddab
AG
278 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
279 return false;
a59ff6b0
AG
280 }
281
32d2ddab 282 // finally send the file
261cbbac 283 send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security!
32d2ddab 284
a59ff6b0
AG
285 return false;
286}
287
c70ad9f7 288/**
32be99dc 289 * this will delete a given instance.
290 * all referenced data also will be deleted
291 *
292 * @global object
293 * @param int $id the instanceid of feedback
294 * @return boolean
295 */
c70ad9f7 296function feedback_delete_instance($id) {
c18269c7 297 global $DB;
298
c70ad9f7 299 //get all referenced items
c18269c7 300 $feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$id));
37b86a39 301
c70ad9f7 302 //deleting all referenced items and values
9c5bc7a5
AG
303 if (is_array($feedbackitems)) {
304 foreach ($feedbackitems as $feedbackitem) {
c18269c7 305 $DB->delete_records("feedback_value", array("item"=>$feedbackitem->id));
306 $DB->delete_records("feedback_valuetmp", array("item"=>$feedbackitem->id));
c70ad9f7 307 }
9c5bc7a5
AG
308 if ($delitems = $DB->get_records("feedback_item", array("feedback"=>$id))) {
309 foreach ($delitems as $delitem) {
14832313
AG
310 feedback_delete_item($delitem->id, false);
311 }
312 }
c70ad9f7 313 }
37b86a39 314
c70ad9f7 315 //deleting the completeds
c18269c7 316 $DB->delete_records("feedback_completed", array("feedback"=>$id));
37b86a39 317
c70ad9f7 318 //deleting the unfinished completeds
c18269c7 319 $DB->delete_records("feedback_completedtmp", array("feedback"=>$id));
37b86a39 320
c70ad9f7 321 //deleting old events
c18269c7 322 $DB->delete_records('event', array('modulename'=>'feedback', 'instance'=>$id));
323 return $DB->delete_records("feedback", array("id"=>$id));
c70ad9f7 324}
325
c70ad9f7 326/**
32be99dc 327 * Return a small object with summary information about what a
328 * user has done with a given particular instance of this module
329 * Used for user activity reports.
330 * $return->time = the time they did it
331 * $return->info = a short text description
332 *
fa8e0af1
MG
333 * @param stdClass $course
334 * @param stdClass $user
335 * @param cm_info|stdClass $mod
336 * @param stdClass $feedback
337 * @return stdClass
32be99dc 338 */
c70ad9f7 339function feedback_user_outline($course, $user, $mod, $feedback) {
fa8e0af1
MG
340 global $DB;
341 $outline = (object)['info' => '', 'time' => 0];
342 if ($feedback->anonymous != FEEDBACK_ANONYMOUS_NO) {
343 // Do not disclose any user info if feedback is anonymous.
344 return $outline;
345 }
346 $params = array('userid' => $user->id, 'feedback' => $feedback->id,
347 'anonymous_response' => FEEDBACK_ANONYMOUS_NO);
348 $status = null;
349 $context = context_module::instance($mod->id);
350 if ($completed = $DB->get_record('feedback_completed', $params)) {
351 // User has completed feedback.
352 $outline->info = get_string('completed', 'feedback');
353 $outline->time = $completed->timemodified;
354 } else if ($completedtmp = $DB->get_record('feedback_completedtmp', $params)) {
355 // User has started but not completed feedback.
356 $outline->info = get_string('started', 'feedback');
357 $outline->time = $completedtmp->timemodified;
358 } else if (has_capability('mod/feedback:complete', $context, $user)) {
359 // User has not started feedback but has capability to do so.
360 $outline->info = get_string('not_started', 'feedback');
361 }
362
363 return $outline;
c70ad9f7 364}
365
6133bd16 366/**
367 * Returns all users who has completed a specified feedback since a given time
368 * many thanks to Manolescu Dorel, who contributed these two functions
32be99dc 369 *
370 * @global object
371 * @global object
372 * @global object
373 * @global object
374 * @uses CONTEXT_MODULE
375 * @param array $activities Passed by reference
376 * @param int $index Passed by reference
377 * @param int $timemodified Timestamp
378 * @param int $courseid
379 * @param int $cmid
380 * @param int $userid
381 * @param int $groupid
382 * @return void
6133bd16 383 */
9c5bc7a5
AG
384function feedback_get_recent_mod_activity(&$activities, &$index,
385 $timemodified, $courseid,
386 $cmid, $userid="", $groupid="") {
387
6133bd16 388 global $CFG, $COURSE, $USER, $DB;
389
390 if ($COURSE->id == $courseid) {
391 $course = $COURSE;
392 } else {
76b7b592 393 $course = $DB->get_record('course', array('id'=>$courseid));
6133bd16 394 }
395
f20edd52 396 $modinfo = get_fast_modinfo($course);
6133bd16 397
398 $cm = $modinfo->cms[$cmid];
6938191b 399
69f6d0b4 400 $sqlargs = array();
6133bd16 401
e19c086c 402 $userfields = user_picture::fields('u', null, 'useridagain');
e68fc3c6
AG
403 $sql = " SELECT fk . * , fc . * , $userfields
404 FROM {feedback_completed} fc
405 JOIN {feedback} fk ON fk.id = fc.feedback
406 JOIN {user} u ON u.id = fc.userid ";
6133bd16 407
408 if ($groupid) {
69f6d0b4 409 $sql .= " JOIN {groups_members} gm ON gm.userid=u.id ";
6133bd16 410 }
6938191b 411
bd338a45
JB
412 $sql .= " WHERE fc.timemodified > ?
413 AND fk.id = ?
414 AND fc.anonymous_response = ?";
69f6d0b4 415 $sqlargs[] = $timemodified;
fdd375e1 416 $sqlargs[] = $cm->instance;
bd338a45 417 $sqlargs[] = FEEDBACK_ANONYMOUS_NO;
d4b1d58c 418
69f6d0b4
AG
419 if ($userid) {
420 $sql .= " AND u.id = ? ";
421 $sqlargs[] = $userid;
6133bd16 422 }
423
69f6d0b4
AG
424 if ($groupid) {
425 $sql .= " AND gm.groupid = ? ";
426 $sqlargs[] = $groupid;
427 }
6938191b 428
69f6d0b4
AG
429 if (!$feedbackitems = $DB->get_records_sql($sql, $sqlargs)) {
430 return;
431 }
6133bd16 432
09c2ec43
AG
433 $cm_context = context_module::instance($cm->id);
434
435 if (!has_capability('mod/feedback:view', $cm_context)) {
436 return;
437 }
438
6133bd16 439 $accessallgroups = has_capability('moodle/site:accessallgroups', $cm_context);
440 $viewfullnames = has_capability('moodle/site:viewfullnames', $cm_context);
441 $groupmode = groups_get_activity_groupmode($cm, $course);
442
9c5bc7a5 443 $aname = format_string($cm->name, true);
6133bd16 444 foreach ($feedbackitems as $feedbackitem) {
445 if ($feedbackitem->userid != $USER->id) {
d4b1d58c 446
447 if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
9c5bc7a5
AG
448 $usersgroups = groups_get_all_groups($course->id,
449 $feedbackitem->userid,
450 $cm->groupingid);
6133bd16 451 if (!is_array($usersgroups)) {
452 continue;
453 }
454 $usersgroups = array_keys($usersgroups);
e19c086c 455 $intersect = array_intersect($usersgroups, $modinfo->get_groups($cm->groupingid));
6133bd16 456 if (empty($intersect)) {
457 continue;
458 }
459 }
9c5bc7a5 460 }
6133bd16 461
39790bd8 462 $tmpactivity = new stdClass();
6133bd16 463
464 $tmpactivity->type = 'feedback';
465 $tmpactivity->cmid = $cm->id;
466 $tmpactivity->name = $aname;
467 $tmpactivity->sectionnum= $cm->sectionnum;
468 $tmpactivity->timestamp = $feedbackitem->timemodified;
d4b1d58c 469
82e4dc33 470 $tmpactivity->content = new stdClass();
6133bd16 471 $tmpactivity->content->feedbackid = $feedbackitem->id;
472 $tmpactivity->content->feedbackuserid = $feedbackitem->userid;
d4b1d58c 473
e19c086c 474 $tmpactivity->user = user_picture::unalias($feedbackitem, null, 'useridagain');
6133bd16 475 $tmpactivity->user->fullname = fullname($feedbackitem, $viewfullnames);
d4b1d58c 476
6133bd16 477 $activities[$index++] = $tmpactivity;
478 }
479
9c5bc7a5 480 return;
6133bd16 481}
482
483/**
484 * Prints all users who has completed a specified feedback since a given time
485 * many thanks to Manolescu Dorel, who contributed these two functions
32be99dc 486 *
487 * @global object
488 * @param object $activity
489 * @param int $courseid
490 * @param string $detail
491 * @param array $modnames
492 * @return void Output is echo'd
6133bd16 493 */
494function feedback_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
e63f88c9 495 global $CFG, $OUTPUT;
6133bd16 496
497 echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">';
498
499 echo "<tr><td class=\"userpicture\" valign=\"top\">";
812dbaf7 500 echo $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid));
6133bd16 501 echo "</td><td>";
502
503 if ($detail) {
504 $modname = $modnames[$activity->type];
505 echo '<div class="title">';
3e6adcd6 506 echo $OUTPUT->image_icon('icon', $modname, $activity->type);
6133bd16 507 echo "<a href=\"$CFG->wwwroot/mod/feedback/view.php?id={$activity->cmid}\">{$activity->name}</a>";
508 echo '</div>';
509 }
d4b1d58c 510
78946b9b 511 echo '<div class="title">';
6133bd16 512 echo '</div>';
d4b1d58c 513
6133bd16 514 echo '<div class="user">';
82e4dc33 515 echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->id}&amp;course=$courseid\">"
6133bd16 516 ."{$activity->user->fullname}</a> - ".userdate($activity->timestamp);
517 echo '</div>';
518
519 echo "</td></tr></table>";
520
521 return;
522}
523
8a41eb4f
AG
524/**
525 * Obtains the automatic completion state for this feedback based on the condition
526 * in feedback settings.
527 *
528 * @param object $course Course
529 * @param object $cm Course-module
530 * @param int $userid User ID
531 * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
532 * @return bool True if completed, false if not, $type if conditions not set.
533 */
534function feedback_get_completion_state($course, $cm, $userid, $type) {
535 global $CFG, $DB;
536
537 // Get feedback details
538 $feedback = $DB->get_record('feedback', array('id'=>$cm->instance), '*', MUST_EXIST);
539
516c5eca 540 // If completion option is enabled, evaluate it and return true/false
9c5bc7a5
AG
541 if ($feedback->completionsubmit) {
542 $params = array('userid'=>$userid, 'feedback'=>$feedback->id);
97d71cbc 543 return $DB->record_exists('feedback_completed', $params);
8a41eb4f
AG
544 } else {
545 // Completion option is not enabled so just return $type
546 return $type;
547 }
548}
549
c70ad9f7 550/**
32be99dc 551 * Print a detailed representation of what a user has done with
552 * a given particular instance of this module, for user activity reports.
d4b1d58c 553 *
fa8e0af1
MG
554 * @param stdClass $course
555 * @param stdClass $user
556 * @param cm_info|stdClass $mod
557 * @param stdClass $feedback
32be99dc 558 */
c70ad9f7 559function feedback_user_complete($course, $user, $mod, $feedback) {
fa8e0af1
MG
560 global $DB;
561 if ($feedback->anonymous != FEEDBACK_ANONYMOUS_NO) {
562 // Do not disclose any user info if feedback is anonymous.
563 return;
564 }
565 $params = array('userid' => $user->id, 'feedback' => $feedback->id,
566 'anonymous_response' => FEEDBACK_ANONYMOUS_NO);
567 $url = $status = null;
568 $context = context_module::instance($mod->id);
569 if ($completed = $DB->get_record('feedback_completed', $params)) {
570 // User has completed feedback.
571 if (has_capability('mod/feedback:viewreports', $context)) {
572 $url = new moodle_url('/mod/feedback/show_entries.php',
573 ['id' => $mod->id, 'userid' => $user->id,
574 'showcompleted' => $completed->id]);
575 }
576 $status = get_string('completedon', 'feedback', userdate($completed->timemodified));
577 } else if ($completedtmp = $DB->get_record('feedback_completedtmp', $params)) {
578 // User has started but not completed feedback.
579 $status = get_string('startedon', 'feedback', userdate($completedtmp->timemodified));
580 } else if (has_capability('mod/feedback:complete', $context, $user)) {
581 // User has not started feedback but has capability to do so.
582 $status = get_string('not_started', 'feedback');
583 }
584
585 if ($url && $status) {
586 echo html_writer::link($url, $status);
587 } else if ($status) {
588 echo html_writer::div($status);
589 }
c70ad9f7 590}
591
32be99dc 592/**
593 * @return bool true
594 */
c70ad9f7 595function feedback_cron () {
596 return true;
597}
598
32be99dc 599/**
600 * @return bool false
601 */
9c5bc7a5 602function feedback_scale_used ($feedbackid, $scaleid) {
0085fff8 603 return false;
c70ad9f7 604}
605
c27bb7ca
AG
606/**
607 * Checks if scale is being used by any instance of feedback
608 *
609 * This is used to find out if scale used anywhere
610 * @param $scaleid int
611 * @return boolean True if the scale is used by any assignment
612 */
613function feedback_scale_used_anywhere($scaleid) {
614 return false;
615}
616
da457a94 617/**
b2b4ec30
RT
618 * List the actions that correspond to a view of this module.
619 * This is used by the participation report.
620 *
621 * Note: This is not used by new logging system. Event with
622 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will
623 * be considered as view action.
624 *
da457a94
AG
625 * @return array
626 */
627function feedback_get_view_actions() {
cf051cc4 628 return array('view', 'view all');
da457a94
AG
629}
630
631/**
b2b4ec30
RT
632 * List the actions that correspond to a post of this module.
633 * This is used by the participation report.
634 *
635 * Note: This is not used by new logging system. Event with
636 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING
637 * will be considered as post action.
638 *
da457a94
AG
639 * @return array
640 */
641function feedback_get_post_actions() {
642 return array('submit');
643}
644
1c57ce25 645/**
646 * This function is used by the reset_course_userdata function in moodlelib.
647 * This function will remove all responses from the specified feedback
648 * and clean up any related data.
32be99dc 649 *
650 * @global object
651 * @global object
652 * @uses FEEDBACK_RESETFORM_RESET
653 * @uses FEEDBACK_RESETFORM_DROP
654 * @param object $data the data submitted from the reset course.
1c57ce25 655 * @return array status array
c70ad9f7 656 */
1c57ce25 657function feedback_reset_userdata($data) {
0085fff8 658 global $CFG, $DB;
37b86a39 659
c70ad9f7 660 $resetfeedbacks = array();
661 $dropfeedbacks = array();
1c57ce25 662 $status = array();
663 $componentstr = get_string('modulenameplural', 'feedback');
37b86a39 664
c70ad9f7 665 //get the relevant entries from $data
9c5bc7a5 666 foreach ($data as $key => $value) {
c70ad9f7 667 switch(true) {
668 case substr($key, 0, strlen(FEEDBACK_RESETFORM_RESET)) == FEEDBACK_RESETFORM_RESET:
9c5bc7a5 669 if ($value == 1) {
c70ad9f7 670 $templist = explode('_', $key);
9c5bc7a5
AG
671 if (isset($templist[3])) {
672 $resetfeedbacks[] = intval($templist[3]);
673 }
c70ad9f7 674 }
675 break;
676 case substr($key, 0, strlen(FEEDBACK_RESETFORM_DROP)) == FEEDBACK_RESETFORM_DROP:
9c5bc7a5 677 if ($value == 1) {
c70ad9f7 678 $templist = explode('_', $key);
9c5bc7a5
AG
679 if (isset($templist[3])) {
680 $dropfeedbacks[] = intval($templist[3]);
681 }
c70ad9f7 682 }
683 break;
684 }
685 }
37b86a39 686
c70ad9f7 687 //reset the selected feedbacks
9c5bc7a5 688 foreach ($resetfeedbacks as $id) {
0085fff8 689 $feedback = $DB->get_record('feedback', array('id'=>$id));
97d71cbc 690 feedback_delete_all_completeds($feedback);
9c5bc7a5
AG
691 $status[] = array('component'=>$componentstr.':'.$feedback->name,
692 'item'=>get_string('resetting_data', 'feedback'),
693 'error'=>false);
c70ad9f7 694 }
37b86a39 695
b28753f5
SB
696 // Updating dates - shift may be negative too.
697 if ($data->timeshift) {
0d14fcbc
AA
698 // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
699 // See MDL-9367.
b28753f5
SB
700 $shifterror = !shift_course_mod_dates('feedback', array('timeopen', 'timeclose'), $data->timeshift, $data->courseid);
701 $status[] = array('component' => $componentstr, 'item' => get_string('datechanged'), 'error' => $shifterror);
702 }
703
1c57ce25 704 return $status;
705}
706
707/**
708 * Called by course/reset.php
32be99dc 709 *
710 * @global object
711 * @uses FEEDBACK_RESETFORM_RESET
712 * @param object $mform form passed by reference
1c57ce25 713 */
714function feedback_reset_course_form_definition(&$mform) {
0085fff8 715 global $COURSE, $DB;
716
1c57ce25 717 $mform->addElement('header', 'feedbackheader', get_string('modulenameplural', 'feedback'));
37b86a39 718
9c5bc7a5 719 if (!$feedbacks = $DB->get_records('feedback', array('course'=>$COURSE->id), 'name')) {
1c57ce25 720 return;
c70ad9f7 721 }
1c57ce25 722
9c5bc7a5
AG
723 $mform->addElement('static', 'hint', get_string('resetting_data', 'feedback'));
724 foreach ($feedbacks as $feedback) {
1c57ce25 725 $mform->addElement('checkbox', FEEDBACK_RESETFORM_RESET.$feedback->id, $feedback->name);
1c57ce25 726 }
727}
728
729/**
730 * Course reset form defaults.
32be99dc 731 *
732 * @global object
733 * @uses FEEDBACK_RESETFORM_RESET
734 * @param object $course
1c57ce25 735 */
736function feedback_reset_course_form_defaults($course) {
0085fff8 737 global $DB;
738
1c57ce25 739 $return = array();
9c5bc7a5 740 if (!$feedbacks = $DB->get_records('feedback', array('course'=>$course->id), 'name')) {
1c57ce25 741 return;
742 }
9c5bc7a5 743 foreach ($feedbacks as $feedback) {
1c57ce25 744 $return[FEEDBACK_RESETFORM_RESET.$feedback->id] = true;
1c57ce25 745 }
746 return $return;
c70ad9f7 747}
748
37b86a39 749/**
c70ad9f7 750 * Called by course/reset.php and shows the formdata by coursereset.
751 * it prints checkboxes for each feedback available at the given course
9c5bc7a5
AG
752 * there are two checkboxes:
753 * 1) delete userdata and keep the feedback
754 * 2) delete userdata and drop the feedback
32be99dc 755 *
756 * @global object
757 * @uses FEEDBACK_RESETFORM_RESET
758 * @uses FEEDBACK_RESETFORM_DROP
c70ad9f7 759 * @param object $course
760 * @return void
761 */
762function feedback_reset_course_form($course) {
be96a472 763 global $DB, $OUTPUT;
0085fff8 764
c70ad9f7 765 echo get_string('resetting_feedbacks', 'feedback'); echo ':<br />';
0085fff8 766 if (!$feedbacks = $DB->get_records('feedback', array('course'=>$course->id), 'name')) {
767 return;
768 }
37b86a39 769
9c5bc7a5 770 foreach ($feedbacks as $feedback) {
c70ad9f7 771 echo '<p>';
9c5bc7a5
AG
772 echo get_string('name', 'feedback').': '.$feedback->name.'<br />';
773 echo html_writer::checkbox(FEEDBACK_RESETFORM_RESET.$feedback->id,
774 1, true,
775 get_string('resetting_data', 'feedback'));
be96a472 776 echo '<br />';
9c5bc7a5
AG
777 echo html_writer::checkbox(FEEDBACK_RESETFORM_DROP.$feedback->id,
778 1, false,
779 get_string('drop_feedback', 'feedback'));
c70ad9f7 780 echo '</p>';
781 }
782}
783
abd2899c
AG
784/**
785 * This gets an array with default options for the editor
786 *
787 * @return array the options
788 */
789function feedback_get_editor_options() {
790 return array('maxfiles' => EDITOR_UNLIMITED_FILES,
791 'trusttext'=>true);
792}
793
37b86a39 794/**
32be99dc 795 * This creates new events given as timeopen and closeopen by $feedback.
796 *
797 * @global object
798 * @param object $feedback
799 * @return void
c70ad9f7 800 */
801function feedback_set_events($feedback) {
b28753f5 802 global $DB, $CFG;
c18269c7 803
b28753f5
SB
804 // Include calendar/lib.php.
805 require_once($CFG->dirroot.'/calendar/lib.php');
c70ad9f7 806
b28753f5 807 // Get CMID if not sent as part of $feedback.
50aa3fcc 808 if (!isset($feedback->coursemodule)) {
b28753f5 809 $cm = get_coursemodule_from_instance('feedback', $feedback->id, $feedback->course);
50aa3fcc 810 $feedback->coursemodule = $cm->id;
811 }
812
b28753f5
SB
813 // Feedback start calendar events.
814 $eventid = $DB->get_field('event', 'id',
90e8330f 815 array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => FEEDBACK_EVENT_TYPE_OPEN));
b28753f5
SB
816
817 if (isset($feedback->timeopen) && $feedback->timeopen > 0) {
b85b25eb 818 $event = new stdClass();
90e8330f
SL
819 $event->eventtype = FEEDBACK_EVENT_TYPE_OPEN;
820 $event->type = empty($feedback->timeclose) ? CALENDAR_EVENT_TYPE_ACTION : CALENDAR_EVENT_TYPE_STANDARD;
b28753f5 821 $event->name = get_string('calendarstart', 'feedback', $feedback->name);
50aa3fcc 822 $event->description = format_module_intro('feedback', $feedback, $feedback->coursemodule);
c70ad9f7 823 $event->timestart = $feedback->timeopen;
90e8330f 824 $event->timesort = $feedback->timeopen;
c70ad9f7 825 $event->visible = instance_is_visible('feedback', $feedback);
b28753f5
SB
826 $event->timeduration = 0;
827 if ($eventid) {
828 // Calendar event exists so update it.
829 $event->id = $eventid;
830 $calendarevent = calendar_event::load($event->id);
831 $calendarevent->update($event);
c70ad9f7 832 } else {
b28753f5
SB
833 // Event doesn't exist so create one.
834 $event->courseid = $feedback->course;
835 $event->groupid = 0;
836 $event->userid = 0;
837 $event->modulename = 'feedback';
838 $event->instance = $feedback->id;
90e8330f 839 $event->eventtype = FEEDBACK_EVENT_TYPE_OPEN;
b28753f5 840 calendar_event::create($event);
c70ad9f7 841 }
b28753f5
SB
842 } else if ($eventid) {
843 // Calendar event is on longer needed.
844 $calendarevent = calendar_event::load($eventid);
845 $calendarevent->delete();
c70ad9f7 846 }
847
b28753f5
SB
848 // Feedback close calendar events.
849 $eventid = $DB->get_field('event', 'id',
90e8330f 850 array('modulename' => 'feedback', 'instance' => $feedback->id, 'eventtype' => FEEDBACK_EVENT_TYPE_CLOSE));
b28753f5
SB
851
852 if (isset($feedback->timeclose) && $feedback->timeclose > 0) {
b85b25eb 853 $event = new stdClass();
90e8330f
SL
854 $event->type = CALENDAR_EVENT_TYPE_ACTION;
855 $event->eventtype = FEEDBACK_EVENT_TYPE_CLOSE;
b28753f5 856 $event->name = get_string('calendarend', 'feedback', $feedback->name);
50aa3fcc 857 $event->description = format_module_intro('feedback', $feedback, $feedback->coursemodule);
c70ad9f7 858 $event->timestart = $feedback->timeclose;
90e8330f 859 $event->timesort = $feedback->timeclose;
c70ad9f7 860 $event->visible = instance_is_visible('feedback', $feedback);
861 $event->timeduration = 0;
b28753f5
SB
862 if ($eventid) {
863 // Calendar event exists so update it.
864 $event->id = $eventid;
865 $calendarevent = calendar_event::load($event->id);
866 $calendarevent->update($event);
867 } else {
868 // Event doesn't exist so create one.
869 $event->courseid = $feedback->course;
870 $event->groupid = 0;
871 $event->userid = 0;
872 $event->modulename = 'feedback';
873 $event->instance = $feedback->id;
b28753f5
SB
874 calendar_event::create($event);
875 }
876 } else if ($eventid) {
877 // Calendar event is on longer needed.
878 $calendarevent = calendar_event::load($eventid);
879 $calendarevent->delete();
880 }
881}
882
883/**
884 * This standard function will check all instances of this module
885 * and make sure there are up-to-date events created for each of them.
886 * If courseid = 0, then every feedback event in the site is checked, else
887 * only feedback events belonging to the course specified are checked.
888 * This function is used, in its new format, by restore_refresh_events()
889 *
890 * @param int $courseid
9ffbdafc
AG
891 * @param int|stdClass $instance Feedback module instance or ID.
892 * @param int|stdClass $cm Course module object or ID (not used in this module).
b28753f5
SB
893 * @return bool
894 */
9ffbdafc 895function feedback_refresh_events($courseid = 0, $instance = null, $cm = null) {
b28753f5 896 global $DB;
c70ad9f7 897
9ffbdafc
AG
898 // If we have instance information then we can just update the one event instead of updating all events.
899 if (isset($instance)) {
900 if (!is_object($instance)) {
901 $instance = $DB->get_record('feedback', array('id' => $instance), '*', MUST_EXIST);
902 }
903 feedback_set_events($instance);
904 return true;
905 }
906
b28753f5
SB
907 if ($courseid) {
908 if (! $feedbacks = $DB->get_records("feedback", array("course" => $courseid))) {
909 return true;
910 }
911 } else {
912 if (! $feedbacks = $DB->get_records("feedback")) {
913 return true;
914 }
915 }
916
917 foreach ($feedbacks as $feedback) {
918 feedback_set_events($feedback);
c70ad9f7 919 }
b28753f5 920 return true;
c70ad9f7 921}
922
37b86a39 923/**
9c5bc7a5
AG
924 * this function is called by {@link feedback_delete_userdata()}
925 * it drops the feedback-instance from the course_module table
32be99dc 926 *
927 * @global object
9c5bc7a5
AG
928 * @param int $id the id from the coursemodule
929 * @return boolean
c70ad9f7 930 */
931function feedback_delete_course_module($id) {
0085fff8 932 global $DB;
933
934 if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
c70ad9f7 935 return true;
936 }
0085fff8 937 return $DB->delete_records('course_modules', array('id'=>$cm->id));
c70ad9f7 938}
939
940
941
942////////////////////////////////////////////////
943//functions to handle capabilities
944////////////////////////////////////////////////
945
37b86a39 946/**
97d71cbc 947 * @deprecated since 3.1
c70ad9f7 948 */
98f9db0d
MG
949function feedback_get_context() {
950 throw new coding_exception('feedback_get_context() can not be used anymore.');
c70ad9f7 951}
952
37b86a39 953/**
c70ad9f7 954 * returns true if the current role is faked by switching role feature
32be99dc 955 *
956 * @global object
9c5bc7a5 957 * @return boolean
c70ad9f7 958 */
9c5bc7a5 959function feedback_check_is_switchrole() {
c70ad9f7 960 global $USER;
9c5bc7a5
AG
961 if (isset($USER->switchrole) AND
962 is_array($USER->switchrole) AND
963 count($USER->switchrole) > 0) {
964
c70ad9f7 965 return true;
966 }
967 return false;
968}
969
8d7b8286
AG
970/**
971 * count users which have not completed the feedback
972 *
973 * @global object
974 * @uses CONTEXT_MODULE
78f753ac 975 * @param cm_info $cm Course-module object
8d7b8286 976 * @param int $group single groupid
3a787259
AG
977 * @param string $sort
978 * @param int $startpage
979 * @param int $pagecount
682aea9b
JL
980 * @param bool $includestatus to return if the user started or not the feedback among the complete user record
981 * @return array array of user ids or user objects when $includestatus set to true
8d7b8286 982 */
78f753ac 983function feedback_get_incomplete_users(cm_info $cm,
9c5bc7a5
AG
984 $group = false,
985 $sort = '',
986 $startpage = false,
682aea9b
JL
987 $pagecount = false,
988 $includestatus = false) {
9c5bc7a5 989
8d7b8286 990 global $DB;
6938191b 991
2c979976 992 $context = context_module::instance($cm->id);
6938191b 993
8d7b8286
AG
994 //first get all user who can complete this feedback
995 $cap = 'mod/feedback:complete';
682aea9b
JL
996 $allnames = get_all_user_name_fields(true, 'u');
997 $fields = 'u.id, ' . $allnames . ', u.picture, u.email, u.imagealt';
9c5bc7a5
AG
998 if (!$allusers = get_users_by_capability($context,
999 $cap,
1000 $fields,
1001 $sort,
1002 '',
1003 '',
1004 $group,
1005 '',
1006 true)) {
8d7b8286
AG
1007 return false;
1008 }
829d4e3d 1009 // Filter users that are not in the correct group/grouping.
78f753ac 1010 $info = new \core_availability\info_module($cm);
682aea9b 1011 $allusersrecords = $info->filter_user_list($allusers);
829d4e3d 1012
682aea9b 1013 $allusers = array_keys($allusersrecords);
8d7b8286
AG
1014
1015 //now get all completeds
9c5bc7a5 1016 $params = array('feedback'=>$cm->instance);
682aea9b
JL
1017 if ($completedusers = $DB->get_records_menu('feedback_completed', $params, '', 'id, userid')) {
1018 // Now strike all completedusers from allusers.
1019 $allusers = array_diff($allusers, $completedusers);
8d7b8286 1020 }
6938191b 1021
8d7b8286 1022 //for paging I use array_slice()
9c5bc7a5 1023 if ($startpage !== false AND $pagecount !== false) {
8d7b8286
AG
1024 $allusers = array_slice($allusers, $startpage, $pagecount);
1025 }
1026
682aea9b
JL
1027 // Check if we should return the full users objects.
1028 if ($includestatus) {
1029 $userrecords = [];
1030 $startedusers = $DB->get_records_menu('feedback_completedtmp', ['feedback' => $cm->instance], '', 'id, userid');
1031 $startedusers = array_flip($startedusers);
1032 foreach ($allusers as $userid) {
1033 $allusersrecords[$userid]->feedbackstarted = isset($startedusers[$userid]);
1034 $userrecords[] = $allusersrecords[$userid];
1035 }
1036 return $userrecords;
1037 } else { // Return just user ids.
1038 return $allusers;
1039 }
8d7b8286
AG
1040}
1041
1042/**
1043 * count users which have not completed the feedback
1044 *
1045 * @global object
8d7b8286
AG
1046 * @param object $cm
1047 * @param int $group single groupid
3a787259 1048 * @return int count of userrecords
8d7b8286
AG
1049 */
1050function feedback_count_incomplete_users($cm, $group = false) {
9c5bc7a5 1051 if ($allusers = feedback_get_incomplete_users($cm, $group)) {
8d7b8286
AG
1052 return count($allusers);
1053 }
1054 return 0;
1055}
1056
d4b1d58c 1057/**
3a787259 1058 * count users which have completed a feedback
32be99dc 1059 *
1060 * @global object
3a787259 1061 * @uses FEEDBACK_ANONYMOUS_NO
32be99dc 1062 * @param object $cm
1063 * @param int $group single groupid
3a787259 1064 * @return int count of userrecords
c70ad9f7 1065 */
248bafd6
AG
1066function feedback_count_complete_users($cm, $group = false) {
1067 global $DB;
1068
1069 $params = array(FEEDBACK_ANONYMOUS_NO, $cm->instance);
1070
1071 $fromgroup = '';
1072 $wheregroup = '';
9c5bc7a5 1073 if ($group) {
248bafd6
AG
1074 $fromgroup = ', {groups_members} g';
1075 $wheregroup = ' AND g.groupid = ? AND g.userid = c.userid';
1076 $params[] = $group;
1077 }
6938191b 1078
248bafd6
AG
1079 $sql = 'SELECT COUNT(u.id) FROM {user} u, {feedback_completed} c'.$fromgroup.'
1080 WHERE anonymous_response = ? AND u.id = c.userid AND c.feedback = ?
1081 '.$wheregroup;
6938191b 1082
248bafd6
AG
1083 return $DB->count_records_sql($sql, $params);
1084
1085}
1086
1087/**
3a787259 1088 * get users which have completed a feedback
248bafd6
AG
1089 *
1090 * @global object
1091 * @uses CONTEXT_MODULE
3a787259 1092 * @uses FEEDBACK_ANONYMOUS_NO
248bafd6
AG
1093 * @param object $cm
1094 * @param int $group single groupid
0f21a964
PS
1095 * @param string $where a sql where condition (must end with " AND ")
1096 * @param array parameters used in $where
248bafd6 1097 * @param string $sort a table field
3a787259
AG
1098 * @param int $startpage
1099 * @param int $pagecount
248bafd6
AG
1100 * @return object the userrecords
1101 */
9c5bc7a5
AG
1102function feedback_get_complete_users($cm,
1103 $group = false,
1104 $where = '',
1105 array $params = null,
1106 $sort = '',
1107 $startpage = false,
1108 $pagecount = false) {
1109
a600ad35 1110 global $DB;
d4b1d58c 1111
2c979976 1112 $context = context_module::instance($cm->id);
37b86a39 1113
0f21a964
PS
1114 $params = (array)$params;
1115
1116 $params['anon'] = FEEDBACK_ANONYMOUS_NO;
1117 $params['instance'] = $cm->instance;
a600ad35 1118
1119 $fromgroup = '';
1120 $wheregroup = '';
0f21a964 1121 if ($group) {
a600ad35 1122 $fromgroup = ', {groups_members} g';
0f21a964
PS
1123 $wheregroup = ' AND g.groupid = :group AND g.userid = c.userid';
1124 $params['group'] = $group;
a600ad35 1125 }
6938191b 1126
0f21a964 1127 if ($sort) {
248bafd6 1128 $sortsql = ' ORDER BY '.$sort;
0f21a964 1129 } else {
248bafd6
AG
1130 $sortsql = '';
1131 }
6938191b 1132
eae1948c 1133 $ufields = user_picture::fields('u');
a5448b3f 1134 $sql = 'SELECT DISTINCT '.$ufields.', c.timemodified as completed_timemodified
9c5bc7a5
AG
1135 FROM {user} u, {feedback_completed} c '.$fromgroup.'
1136 WHERE '.$where.' anonymous_response = :anon
1137 AND u.id = c.userid
1138 AND c.feedback = :instance
248bafd6 1139 '.$wheregroup.$sortsql;
6938191b 1140
0f21a964 1141 if ($startpage === false OR $pagecount === false) {
248bafd6
AG
1142 $startpage = false;
1143 $pagecount = false;
1144 }
1145 return $DB->get_records_sql($sql, $params, $startpage, $pagecount);
c70ad9f7 1146}
1147
37b86a39 1148/**
32be99dc 1149 * get users which have the viewreports-capability
1150 *
1151 * @uses CONTEXT_MODULE
1152 * @param int $cmid
1153 * @param mixed $groups single groupid or array of groupids - group(s) user is in
1154 * @return object the userrecords
c70ad9f7 1155 */
1156function feedback_get_viewreports_users($cmid, $groups = false) {
1157
2c979976 1158 $context = context_module::instance($cmid);
37b86a39 1159
9c5bc7a5
AG
1160 //description of the call below:
1161 //get_users_by_capability($context, $capability, $fields='', $sort='', $limitfrom='',
1162 // $limitnum='', $groups='', $exceptions='', $doanything=true)
1163 return get_users_by_capability($context,
1164 'mod/feedback:viewreports',
1165 '',
1166 'lastname',
1167 '',
1168 '',
1169 $groups,
1170 '',
1171 false);
c70ad9f7 1172}
1173
37b86a39 1174/**
32be99dc 1175 * get users which have the receivemail-capability
1176 *
1177 * @uses CONTEXT_MODULE
1178 * @param int $cmid
1179 * @param mixed $groups single groupid or array of groupids - group(s) user is in
1180 * @return object the userrecords
c184660d 1181 */
1182function feedback_get_receivemail_users($cmid, $groups = false) {
1183
2c979976 1184 $context = context_module::instance($cmid);
37b86a39 1185
9c5bc7a5
AG
1186 //description of the call below:
1187 //get_users_by_capability($context, $capability, $fields='', $sort='', $limitfrom='',
1188 // $limitnum='', $groups='', $exceptions='', $doanything=true)
1189 return get_users_by_capability($context,
1190 'mod/feedback:receivemail',
1191 '',
1192 'lastname',
1193 '',
1194 '',
1195 $groups,
1196 '',
1197 false);
c184660d 1198}
1199
c70ad9f7 1200////////////////////////////////////////////////
1201//functions to handle the templates
1202////////////////////////////////////////////////
1203////////////////////////////////////////////////
1204
37b86a39 1205/**
32be99dc 1206 * creates a new template-record.
1207 *
1208 * @global object
1209 * @param int $courseid
1210 * @param string $name the name of template shown in the templatelist
1211 * @param int $ispublic 0:privat 1:public
1212 * @return int the new templateid
c70ad9f7 1213 */
1214function feedback_create_template($courseid, $name, $ispublic = 0) {
0085fff8 1215 global $DB;
1216
39790bd8 1217 $templ = new stdClass();
7b1126fd 1218 $templ->course = ($ispublic ? 0 : $courseid);
0085fff8 1219 $templ->name = $name;
c70ad9f7 1220 $templ->ispublic = $ispublic;
0085fff8 1221
14832313
AG
1222 $templid = $DB->insert_record('feedback_template', $templ);
1223 return $DB->get_record('feedback_template', array('id'=>$templid));
c70ad9f7 1224}
1225
37b86a39 1226/**
32be99dc 1227 * creates new template items.
1228 * all items will be copied and the attribute feedback will be set to 0
1229 * and the attribute template will be set to the new templateid
1230 *
1231 * @global object
3a787259
AG
1232 * @uses CONTEXT_MODULE
1233 * @uses CONTEXT_COURSE
32be99dc 1234 * @param object $feedback
1235 * @param string $name the name of template shown in the templatelist
1236 * @param int $ispublic 0:privat 1:public
1237 * @return boolean
c70ad9f7 1238 */
1239function feedback_save_as_template($feedback, $name, $ispublic = 0) {
0085fff8 1240 global $DB;
14832313 1241 $fs = get_file_storage();
0085fff8 1242
1243 if (!$feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$feedback->id))) {
c70ad9f7 1244 return false;
1245 }
37b86a39 1246
0085fff8 1247 if (!$newtempl = feedback_create_template($feedback->course, $name, $ispublic)) {
c70ad9f7 1248 return false;
1249 }
6938191b 1250
7b1126fd
AG
1251 //files in the template_item are in the context of the current course or
1252 //if the template is public the files are in the system context
14832313 1253 //files in the feedback_item are in the feedback_context of the feedback
9c5bc7a5 1254 if ($ispublic) {
0601e0ee 1255 $s_context = context_system::instance();
9c5bc7a5 1256 } else {
2c979976 1257 $s_context = context_course::instance($newtempl->course);
7b1126fd 1258 }
14832313 1259 $cm = get_coursemodule_from_instance('feedback', $feedback->id);
2c979976 1260 $f_context = context_module::instance($cm->id);
6938191b 1261
c70ad9f7 1262 //create items of this new template
73043833
AG
1263 //depend items we are storing temporary in an mapping list array(new id => dependitem)
1264 //we also store a mapping of all items array(oldid => newid)
1265 $dependitemsmap = array();
1266 $itembackup = array();
9c5bc7a5 1267 foreach ($feedbackitems as $item) {
6938191b 1268
14832313 1269 $t_item = clone($item);
6938191b 1270
14832313
AG
1271 unset($t_item->id);
1272 $t_item->feedback = 0;
1273 $t_item->template = $newtempl->id;
1274 $t_item->id = $DB->insert_record('feedback_item', $t_item);
1275 //copy all included files to the feedback_template filearea
9c5bc7a5
AG
1276 $itemfiles = $fs->get_area_files($f_context->id,
1277 'mod_feedback',
1278 'item',
1279 $item->id,
1280 "id",
1281 false);
1282 if ($itemfiles) {
1283 foreach ($itemfiles as $ifile) {
39790bd8 1284 $file_record = new stdClass();
7b1126fd 1285 $file_record->contextid = $s_context->id;
64f93798
PS
1286 $file_record->component = 'mod_feedback';
1287 $file_record->filearea = 'template';
14832313
AG
1288 $file_record->itemid = $t_item->id;
1289 $fs->create_file_from_storedfile($file_record, $ifile);
1290 }
1291 }
6938191b 1292
73043833 1293 $itembackup[$item->id] = $t_item->id;
9c5bc7a5 1294 if ($t_item->dependitem) {
73043833
AG
1295 $dependitemsmap[$t_item->id] = $t_item->dependitem;
1296 }
6938191b 1297
c70ad9f7 1298 }
6938191b 1299
73043833 1300 //remapping the dependency
9c5bc7a5 1301 foreach ($dependitemsmap as $key => $dependitem) {
73043833
AG
1302 $newitem = $DB->get_record('feedback_item', array('id'=>$key));
1303 $newitem->dependitem = $itembackup[$newitem->dependitem];
1304 $DB->update_record('feedback_item', $newitem);
1305 }
6938191b 1306
c70ad9f7 1307 return true;
1308}
1309
37b86a39 1310/**
32be99dc 1311 * deletes all feedback_items related to the given template id
1312 *
1313 * @global object
3a787259 1314 * @uses CONTEXT_COURSE
7b1126fd 1315 * @param object $template the template
32be99dc 1316 * @return void
c70ad9f7 1317 */
7b1126fd 1318function feedback_delete_template($template) {
0085fff8 1319 global $DB;
1320
7b1126fd 1321 //deleting the files from the item is done by feedback_delete_item
9c5bc7a5
AG
1322 if ($t_items = $DB->get_records("feedback_item", array("template"=>$template->id))) {
1323 foreach ($t_items as $t_item) {
7b1126fd 1324 feedback_delete_item($t_item->id, false, $template);
14832313
AG
1325 }
1326 }
7b1126fd 1327 $DB->delete_records("feedback_template", array("id"=>$template->id));
c70ad9f7 1328}
1329
37b86a39 1330/**
32be99dc 1331 * creates new feedback_item-records from template.
1332 * if $deleteold is set true so the existing items of the given feedback will be deleted
1333 * if $deleteold is set false so the new items will be appanded to the old items
1334 *
1335 * @global object
3a787259
AG
1336 * @uses CONTEXT_COURSE
1337 * @uses CONTEXT_MODULE
32be99dc 1338 * @param object $feedback
1339 * @param int $templateid
1340 * @param boolean $deleteold
c70ad9f7 1341 */
1342function feedback_items_from_template($feedback, $templateid, $deleteold = false) {
516c5eca
PS
1343 global $DB, $CFG;
1344
1345 require_once($CFG->libdir.'/completionlib.php');
1346
14832313 1347 $fs = get_file_storage();
0085fff8 1348
9c5bc7a5 1349 if (!$template = $DB->get_record('feedback_template', array('id'=>$templateid))) {
485ec594
AG
1350 return false;
1351 }
c70ad9f7 1352 //get all templateitems
9c5bc7a5 1353 if (!$templitems = $DB->get_records('feedback_item', array('template'=>$templateid))) {
c70ad9f7 1354 return false;
1355 }
37b86a39 1356
14832313
AG
1357 //files in the template_item are in the context of the current course
1358 //files in the feedback_item are in the feedback_context of the feedback
9c5bc7a5 1359 if ($template->ispublic) {
0601e0ee 1360 $s_context = context_system::instance();
9c5bc7a5 1361 } else {
2c979976 1362 $s_context = context_course::instance($feedback->course);
485ec594 1363 }
8a41eb4f 1364 $course = $DB->get_record('course', array('id'=>$feedback->course));
14832313 1365 $cm = get_coursemodule_from_instance('feedback', $feedback->id);
2c979976 1366 $f_context = context_module::instance($cm->id);
14832313 1367
c70ad9f7 1368 //if deleteold then delete all old items before
1369 //get all items
9c5bc7a5
AG
1370 if ($deleteold) {
1371 if ($feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$feedback->id))) {
c70ad9f7 1372 //delete all items of this feedback
9c5bc7a5 1373 foreach ($feedbackitems as $item) {
c70ad9f7 1374 feedback_delete_item($item->id, false);
1375 }
516c5eca 1376
9c5bc7a5
AG
1377 $params = array('feedback'=>$feedback->id);
1378 if ($completeds = $DB->get_records('feedback_completed', $params)) {
8a41eb4f 1379 $completion = new completion_info($course);
9c5bc7a5 1380 foreach ($completeds as $completed) {
c3c97e2e 1381 $DB->delete_records('feedback_completed', array('id' => $completed->id));
8a41eb4f 1382 // Update completion state
c3c97e2e
MG
1383 if ($completion->is_enabled($cm) && $cm->completion == COMPLETION_TRACKING_AUTOMATIC &&
1384 $feedback->completionsubmit) {
8a41eb4f
AG
1385 $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
1386 }
8a41eb4f
AG
1387 }
1388 }
0085fff8 1389 $DB->delete_records('feedback_completedtmp', array('feedback'=>$feedback->id));
c70ad9f7 1390 }
194dd279 1391 $positionoffset = 0;
0085fff8 1392 } else {
c70ad9f7 1393 //if the old items are kept the new items will be appended
1394 //therefor the new position has an offset
0085fff8 1395 $positionoffset = $DB->count_records('feedback_item', array('feedback'=>$feedback->id));
c70ad9f7 1396 }
37b86a39 1397
73043833
AG
1398 //create items of this new template
1399 //depend items we are storing temporary in an mapping list array(new id => dependitem)
1400 //we also store a mapping of all items array(oldid => newid)
1401 $dependitemsmap = array();
1402 $itembackup = array();
9c5bc7a5 1403 foreach ($templitems as $t_item) {
14832313
AG
1404 $item = clone($t_item);
1405 unset($item->id);
1406 $item->feedback = $feedback->id;
1407 $item->template = 0;
1408 $item->position = $item->position + $positionoffset;
1409
1410 $item->id = $DB->insert_record('feedback_item', $item);
22ca3e49 1411
9c5bc7a5
AG
1412 //moving the files to the new item
1413 $templatefiles = $fs->get_area_files($s_context->id,
1414 'mod_feedback',
1415 'template',
1416 $t_item->id,
1417 "id",
1418 false);
1419 if ($templatefiles) {
1420 foreach ($templatefiles as $tfile) {
39790bd8 1421 $file_record = new stdClass();
14832313 1422 $file_record->contextid = $f_context->id;
64f93798
PS
1423 $file_record->component = 'mod_feedback';
1424 $file_record->filearea = 'item';
14832313
AG
1425 $file_record->itemid = $item->id;
1426 $fs->create_file_from_storedfile($file_record, $tfile);
1427 }
1428 }
6938191b 1429
73043833 1430 $itembackup[$t_item->id] = $item->id;
9c5bc7a5 1431 if ($item->dependitem) {
73043833
AG
1432 $dependitemsmap[$item->id] = $item->dependitem;
1433 }
c70ad9f7 1434 }
6938191b 1435
73043833 1436 //remapping the dependency
9c5bc7a5 1437 foreach ($dependitemsmap as $key => $dependitem) {
73043833
AG
1438 $newitem = $DB->get_record('feedback_item', array('id'=>$key));
1439 $newitem->dependitem = $itembackup[$newitem->dependitem];
1440 $DB->update_record('feedback_item', $newitem);
6938191b 1441 }
c70ad9f7 1442}
1443
37b86a39 1444/**
32be99dc 1445 * get the list of available templates.
1446 * if the $onlyown param is set true so only templates from own course will be served
1447 * this is important for droping templates
1448 *
1449 * @global object
1450 * @param object $course
485ec594 1451 * @param string $onlyownorpublic
32be99dc 1452 * @return array the template recordsets
c70ad9f7 1453 */
485ec594
AG
1454function feedback_get_template_list($course, $onlyownorpublic = '') {
1455 global $DB, $CFG;
0085fff8 1456
485ec594
AG
1457 switch($onlyownorpublic) {
1458 case '':
9c5bc7a5
AG
1459 $templates = $DB->get_records_select('feedback_template',
1460 'course = ? OR ispublic = 1',
1461 array($course->id),
1462 'name');
485ec594
AG
1463 break;
1464 case 'own':
9c5bc7a5
AG
1465 $templates = $DB->get_records('feedback_template',
1466 array('course'=>$course->id),
1467 'name');
485ec594
AG
1468 break;
1469 case 'public':
7b1126fd 1470 $templates = $DB->get_records('feedback_template', array('ispublic'=>1), 'name');
485ec594 1471 break;
c70ad9f7 1472 }
1473 return $templates;
1474}
1475
1476////////////////////////////////////////////////
1477//Handling der Items
1478////////////////////////////////////////////////
1479////////////////////////////////////////////////
1480
6cc1599e
AG
1481/**
1482 * load the lib.php from item-plugin-dir and returns the instance of the itemclass
1483 *
97d71cbc
MG
1484 * @param string $typ
1485 * @return feedback_item_base the instance of itemclass
6cc1599e
AG
1486 */
1487function feedback_get_item_class($typ) {
1488 global $CFG;
6938191b 1489
6cc1599e
AG
1490 //get the class of item-typ
1491 $itemclass = 'feedback_item_'.$typ;
1492 //get the instance of item-class
1493 if (!class_exists($itemclass)) {
1494 require_once($CFG->dirroot.'/mod/feedback/item/'.$typ.'/lib.php');
1495 }
1496 return new $itemclass();
1497}
1498
37b86a39 1499/**
32be99dc 1500 * load the available item plugins from given subdirectory of $CFG->dirroot
1501 * the default is "mod/feedback/item"
1502 *
1503 * @global object
1504 * @param string $dir the subdir
1505 * @return array pluginnames as string
c70ad9f7 1506 */
1507function feedback_load_feedback_items($dir = 'mod/feedback/item') {
1508 global $CFG;
3a787259 1509 $names = get_list_of_plugins($dir);
c70ad9f7 1510 $ret_names = array();
1511
9c5bc7a5 1512 foreach ($names as $name) {
c70ad9f7 1513 require_once($CFG->dirroot.'/'.$dir.'/'.$name.'/lib.php');
9c5bc7a5
AG
1514 if (class_exists('feedback_item_'.$name)) {
1515 $ret_names[] = $name;
c70ad9f7 1516 }
1517 }
1518 return $ret_names;
1519}
1520
37b86a39 1521/**
32be99dc 1522 * load the available item plugins to use as dropdown-options
1523 *
1524 * @global object
1525 * @return array pluginnames as string
c70ad9f7 1526 */
1527function feedback_load_feedback_items_options() {
1528 global $CFG;
37b86a39 1529
c70ad9f7 1530 $feedback_options = array("pagebreak" => get_string('add_pagebreak', 'feedback'));
37b86a39 1531
c70ad9f7 1532 if (!$feedback_names = feedback_load_feedback_items('mod/feedback/item')) {
1533 return array();
1534 }
37b86a39 1535
9c5bc7a5
AG
1536 foreach ($feedback_names as $fn) {
1537 $feedback_options[$fn] = get_string($fn, 'feedback');
c70ad9f7 1538 }
1539 asort($feedback_options);
c70ad9f7 1540 return $feedback_options;
1541}
1542
3a787259
AG
1543/**
1544 * load the available items for the depend item dropdown list shown in the edit_item form
1545 *
1546 * @global object
1547 * @param object $feedback
1548 * @param object $item the item of the edit_item form
1549 * @return array all items except the item $item, labels and pagebreaks
1550 */
73043833
AG
1551function feedback_get_depend_candidates_for_item($feedback, $item) {
1552 global $DB;
1553 //all items for dependitem
1554 $where = "feedback = ? AND typ != 'pagebreak' AND hasvalue = 1";
1555 $params = array($feedback->id);
9c5bc7a5 1556 if (isset($item->id) AND $item->id) {
73043833
AG
1557 $where .= ' AND id != ?';
1558 $params[] = $item->id;
1559 }
1560 $dependitems = array(0 => get_string('choose'));
9c5bc7a5
AG
1561 $feedbackitems = $DB->get_records_select_menu('feedback_item',
1562 $where,
1563 $params,
1564 'position',
1565 'id, label');
1566
1567 if (!$feedbackitems) {
73043833
AG
1568 return $dependitems;
1569 }
1570 //adding the choose-option
9c5bc7a5 1571 foreach ($feedbackitems as $key => $val) {
2aad1094
MG
1572 if (trim(strval($val)) !== '') {
1573 $dependitems[$key] = format_string($val);
1574 }
73043833
AG
1575 }
1576 return $dependitems;
1577}
1578
37b86a39 1579/**
97d71cbc 1580 * @deprecated since 3.1
c70ad9f7 1581 */
98f9db0d
MG
1582function feedback_create_item() {
1583 throw new coding_exception('feedback_create_item() can not be used anymore.');
c70ad9f7 1584}
1585
37b86a39 1586/**
32be99dc 1587 * save the changes of a given item.
1588 *
1589 * @global object
1590 * @param object $item
32be99dc 1591 * @return boolean
c70ad9f7 1592 */
9c5bc7a5 1593function feedback_update_item($item) {
0085fff8 1594 global $DB;
0085fff8 1595 return $DB->update_record("feedback_item", $item);
c70ad9f7 1596}
1597
37b86a39 1598/**
3a787259 1599 * deletes an item and also deletes all related values
32be99dc 1600 *
1601 * @global object
3a787259 1602 * @uses CONTEXT_MODULE
32be99dc 1603 * @param int $itemid
1604 * @param boolean $renumber should the kept items renumbered Yes/No
7b1126fd 1605 * @param object $template if the template is given so the items are bound to it
32be99dc 1606 * @return void
c70ad9f7 1607 */
9c5bc7a5 1608function feedback_delete_item($itemid, $renumber = true, $template = false) {
0085fff8 1609 global $DB;
6938191b 1610
0085fff8 1611 $item = $DB->get_record('feedback_item', array('id'=>$itemid));
6938191b 1612
14832313
AG
1613 //deleting the files from the item
1614 $fs = get_file_storage();
22ca3e49 1615
9c5bc7a5
AG
1616 if ($template) {
1617 if ($template->ispublic) {
0601e0ee 1618 $context = context_system::instance();
9c5bc7a5 1619 } else {
2c979976 1620 $context = context_course::instance($template->course);
7b1126fd 1621 }
9c5bc7a5
AG
1622 $templatefiles = $fs->get_area_files($context->id,
1623 'mod_feedback',
1624 'template',
1625 $item->id,
1626 "id",
1627 false);
1628
1629 if ($templatefiles) {
7b1126fd
AG
1630 $fs->delete_area_files($context->id, 'mod_feedback', 'template', $item->id);
1631 }
9c5bc7a5 1632 } else {
7b1126fd
AG
1633 if (!$cm = get_coursemodule_from_instance('feedback', $item->feedback)) {
1634 return false;
1635 }
2c979976 1636 $context = context_module::instance($cm->id);
6938191b 1637
9c5bc7a5
AG
1638 $itemfiles = $fs->get_area_files($context->id,
1639 'mod_feedback',
1640 'item',
1641 $item->id,
1642 "id", false);
1643
1644 if ($itemfiles) {
7b1126fd
AG
1645 $fs->delete_area_files($context->id, 'mod_feedback', 'item', $item->id);
1646 }
14832313 1647 }
6938191b 1648
0085fff8 1649 $DB->delete_records("feedback_value", array("item"=>$itemid));
1650 $DB->delete_records("feedback_valuetmp", array("item"=>$itemid));
6938191b 1651
73043833
AG
1652 //remove all depends
1653 $DB->set_field('feedback_item', 'dependvalue', '', array('dependitem'=>$itemid));
1654 $DB->set_field('feedback_item', 'dependitem', 0, array('dependitem'=>$itemid));
6938191b 1655
0085fff8 1656 $DB->delete_records("feedback_item", array("id"=>$itemid));
9c5bc7a5 1657 if ($renumber) {
c70ad9f7 1658 feedback_renumber_items($item->feedback);
1659 }
1660}
1661
37b86a39 1662/**
32be99dc 1663 * deletes all items of the given feedbackid
1664 *
1665 * @global object
1666 * @param int $feedbackid
1667 * @return void
c70ad9f7 1668 */
9c5bc7a5 1669function feedback_delete_all_items($feedbackid) {
516c5eca
PS
1670 global $DB, $CFG;
1671 require_once($CFG->libdir.'/completionlib.php');
0085fff8 1672
9c5bc7a5 1673 if (!$feedback = $DB->get_record('feedback', array('id'=>$feedbackid))) {
8a41eb4f
AG
1674 return false;
1675 }
516c5eca 1676
8a41eb4f
AG
1677 if (!$cm = get_coursemodule_from_instance('feedback', $feedback->id)) {
1678 return false;
1679 }
516c5eca 1680
9c5bc7a5 1681 if (!$course = $DB->get_record('course', array('id'=>$feedback->course))) {
8a41eb4f
AG
1682 return false;
1683 }
516c5eca 1684
9c5bc7a5 1685 if (!$items = $DB->get_records('feedback_item', array('feedback'=>$feedbackid))) {
c70ad9f7 1686 return;
1687 }
9c5bc7a5 1688 foreach ($items as $item) {
c70ad9f7 1689 feedback_delete_item($item->id, false);
1690 }
9c5bc7a5 1691 if ($completeds = $DB->get_records('feedback_completed', array('feedback'=>$feedback->id))) {
8a41eb4f 1692 $completion = new completion_info($course);
9c5bc7a5 1693 foreach ($completeds as $completed) {
c3c97e2e 1694 $DB->delete_records('feedback_completed', array('id' => $completed->id));
8a41eb4f 1695 // Update completion state
c3c97e2e
MG
1696 if ($completion->is_enabled($cm) && $cm->completion == COMPLETION_TRACKING_AUTOMATIC &&
1697 $feedback->completionsubmit) {
8a41eb4f
AG
1698 $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
1699 }
8a41eb4f
AG
1700 }
1701 }
516c5eca 1702
0085fff8 1703 $DB->delete_records('feedback_completedtmp', array('feedback'=>$feedbackid));
516c5eca 1704
c70ad9f7 1705}
1706
37b86a39 1707/**
32be99dc 1708 * this function toggled the item-attribute required (yes/no)
1709 *
1710 * @global object
1711 * @param object $item
1712 * @return boolean
c70ad9f7 1713 */
1714function feedback_switch_item_required($item) {
6cc1599e
AG
1715 global $DB, $CFG;
1716
1717 $itemobj = feedback_get_item_class($item->typ);
0085fff8 1718
9c5bc7a5
AG
1719 if ($itemobj->can_switch_require()) {
1720 $new_require_val = (int)!(bool)$item->required;
1721 $params = array('id'=>$item->id);
1722 $DB->set_field('feedback_item', 'required', $new_require_val, $params);
6cc1599e
AG
1723 }
1724 return true;
c70ad9f7 1725}
1726
37b86a39 1727/**
32be99dc 1728 * renumbers all items of the given feedbackid
1729 *
1730 * @global object
1731 * @param int $feedbackid
1732 * @return void
c70ad9f7 1733 */
9c5bc7a5 1734function feedback_renumber_items($feedbackid) {
0085fff8 1735 global $DB;
1736
1737 $items = $DB->get_records('feedback_item', array('feedback'=>$feedbackid), 'position');
c70ad9f7 1738 $pos = 1;
9c5bc7a5
AG
1739 if ($items) {
1740 foreach ($items as $item) {
3a787259 1741 $DB->set_field('feedback_item', 'position', $pos, array('id'=>$item->id));
c70ad9f7 1742 $pos++;
c70ad9f7 1743 }
1744 }
1745}
1746
37b86a39 1747/**
32be99dc 1748 * this decreases the position of the given item
1749 *
1750 * @global object
1751 * @param object $item
3a787259 1752 * @return bool
c70ad9f7 1753 */
9c5bc7a5 1754function feedback_moveup_item($item) {
0085fff8 1755 global $DB;
1756
9c5bc7a5 1757 if ($item->position == 1) {
a59ff6b0
AG
1758 return true;
1759 }
6938191b 1760
9c5bc7a5
AG
1761 $params = array('feedback'=>$item->feedback);
1762 if (!$items = $DB->get_records('feedback_item', $params, 'position')) {
a59ff6b0
AG
1763 return false;
1764 }
6938191b 1765
a59ff6b0 1766 $itembefore = null;
9c5bc7a5
AG
1767 foreach ($items as $i) {
1768 if ($i->id == $item->id) {
1769 if (is_null($itembefore)) {
a59ff6b0
AG
1770 return true;
1771 }
1772 $itembefore->position = $item->position;
1773 $item->position--;
1774 feedback_update_item($itembefore);
1775 feedback_update_item($item);
1776 feedback_renumber_items($item->feedback);
1777 return true;
1778 }
1779 $itembefore = $i;
1780 }
1781 return false;
c70ad9f7 1782}
1783
37b86a39 1784/**
32be99dc 1785 * this increased the position of the given item
1786 *
1787 * @global object
1788 * @param object $item
3a787259 1789 * @return bool
c70ad9f7 1790 */
9c5bc7a5 1791function feedback_movedown_item($item) {
0085fff8 1792 global $DB;
1793
9c5bc7a5
AG
1794 $params = array('feedback'=>$item->feedback);
1795 if (!$items = $DB->get_records('feedback_item', $params, 'position')) {
a59ff6b0 1796 return false;
c70ad9f7 1797 }
6938191b 1798
a59ff6b0 1799 $movedownitem = null;
9c5bc7a5
AG
1800 foreach ($items as $i) {
1801 if (!is_null($movedownitem) AND $movedownitem->id == $item->id) {
a59ff6b0
AG
1802 $movedownitem->position = $i->position;
1803 $i->position--;
1804 feedback_update_item($movedownitem);
1805 feedback_update_item($i);
1806 feedback_renumber_items($item->feedback);
1807 return true;
1808 }
1809 $movedownitem = $i;
1810 }
1811 return false;
c70ad9f7 1812}
1813
37b86a39 1814/**
32be99dc 1815 * here the position of the given item will be set to the value in $pos
1816 *
1817 * @global object
1818 * @param object $moveitem
1819 * @param int $pos
1820 * @return boolean
c70ad9f7 1821 */
9c5bc7a5 1822function feedback_move_item($moveitem, $pos) {
0085fff8 1823 global $DB;
1824
9c5bc7a5
AG
1825 $params = array('feedback'=>$moveitem->feedback);
1826 if (!$allitems = $DB->get_records('feedback_item', $params, 'position')) {
c70ad9f7 1827 return false;
1828 }
0085fff8 1829 if (is_array($allitems)) {
c70ad9f7 1830 $index = 1;
9c5bc7a5
AG
1831 foreach ($allitems as $item) {
1832 if ($index == $pos) {
c70ad9f7 1833 $index++;
1834 }
9c5bc7a5 1835 if ($item->id == $moveitem->id) {
a59ff6b0
AG
1836 $moveitem->position = $pos;
1837 feedback_update_item($moveitem);
1838 continue;
1839 }
c70ad9f7 1840 $item->position = $index;
1841 feedback_update_item($item);
1842 $index++;
1843 }
c70ad9f7 1844 return true;
1845 }
1846 return false;
1847}
1848
9d5fbd65 1849/**
97d71cbc 1850 * @deprecated since Moodle 3.1
9d5fbd65 1851 */
98f9db0d
MG
1852function feedback_print_item_preview() {
1853 throw new coding_exception('feedback_print_item_preview() can not be used anymore. '
1854 . 'Items must implement complete_form_element().');
9d5fbd65
AG
1855}
1856
37b86a39 1857/**
97d71cbc 1858 * @deprecated since Moodle 3.1
c70ad9f7 1859 */
98f9db0d
MG
1860function feedback_print_item_complete() {
1861 throw new coding_exception('feedback_print_item_complete() can not be used anymore. '
1862 . 'Items must implement complete_form_element().');
9d5fbd65
AG
1863}
1864
1865/**
97d71cbc 1866 * @deprecated since Moodle 3.1
9d5fbd65 1867 */
98f9db0d
MG
1868function feedback_print_item_show_value() {
1869 throw new coding_exception('feedback_print_item_show_value() can not be used anymore. '
1870 . 'Items must implement complete_form_element().');
c70ad9f7 1871}
1872
37b86a39 1873/**
32be99dc 1874 * if the user completes a feedback and there is a pagebreak so the values are saved temporary.
3a787259 1875 * the values are not saved permanently until the user click on save button
32be99dc 1876 *
1877 * @global object
1878 * @param object $feedbackcompleted
1879 * @return object temporary saved completed-record
c70ad9f7 1880 */
0085fff8 1881function feedback_set_tmp_values($feedbackcompleted) {
1882 global $DB;
1883
c70ad9f7 1884 //first we create a completedtmp
39790bd8 1885 $tmpcpl = new stdClass();
9c5bc7a5 1886 foreach ($feedbackcompleted as $key => $value) {
c70ad9f7 1887 $tmpcpl->{$key} = $value;
1888 }
0085fff8 1889 unset($tmpcpl->id);
c70ad9f7 1890 $tmpcpl->timemodified = time();
7826abc7 1891 $tmpcpl->id = $DB->insert_record('feedback_completedtmp', $tmpcpl);
c70ad9f7 1892 //get all values of original-completed
9c5bc7a5 1893 if (!$values = $DB->get_records('feedback_value', array('completed'=>$feedbackcompleted->id))) {
c70ad9f7 1894 return;
1895 }
9c5bc7a5 1896 foreach ($values as $value) {
0085fff8 1897 unset($value->id);
c70ad9f7 1898 $value->completed = $tmpcpl->id;
0085fff8 1899 $DB->insert_record('feedback_valuetmp', $value);
c70ad9f7 1900 }
1901 return $tmpcpl;
1902}
1903
37b86a39 1904/**
32be99dc 1905 * this saves the temporary saved values permanently
1906 *
1907 * @global object
1908 * @param object $feedbackcompletedtmp the temporary completed
1909 * @param object $feedbackcompleted the target completed
32be99dc 1910 * @return int the id of the completed
c70ad9f7 1911 */
97d71cbc 1912function feedback_save_tmp_values($feedbackcompletedtmp, $feedbackcompleted) {
0085fff8 1913 global $DB;
1914
c70ad9f7 1915 $tmpcplid = $feedbackcompletedtmp->id;
9c5bc7a5 1916 if ($feedbackcompleted) {
c70ad9f7 1917 //first drop all existing values
0085fff8 1918 $DB->delete_records('feedback_value', array('completed'=>$feedbackcompleted->id));
c70ad9f7 1919 //update the current completed
1920 $feedbackcompleted->timemodified = time();
0085fff8 1921 $DB->update_record('feedback_completed', $feedbackcompleted);
9c5bc7a5 1922 } else {
73043833
AG
1923 $feedbackcompleted = clone($feedbackcompletedtmp);
1924 $feedbackcompleted->id = '';
73043833
AG
1925 $feedbackcompleted->timemodified = time();
1926 $feedbackcompleted->id = $DB->insert_record('feedback_completed', $feedbackcompleted);
1927 }
1928
97d71cbc
MG
1929 $allitems = $DB->get_records('feedback_item', array('feedback' => $feedbackcompleted->feedback));
1930
73043833
AG
1931 //save all the new values from feedback_valuetmp
1932 //get all values of tmp-completed
9c5bc7a5 1933 $params = array('completed'=>$feedbackcompletedtmp->id);
97d71cbc 1934 $values = $DB->get_records('feedback_valuetmp', $params);
9c5bc7a5 1935 foreach ($values as $value) {
73043833
AG
1936 //check if there are depend items
1937 $item = $DB->get_record('feedback_item', array('id'=>$value->item));
97d71cbc 1938 if ($item->dependitem > 0 && isset($allitems[$item->dependitem])) {
9c5bc7a5 1939 $check = feedback_compare_item_value($tmpcplid,
97d71cbc 1940 $allitems[$item->dependitem],
9c5bc7a5
AG
1941 $item->dependvalue,
1942 true);
1943 } else {
73043833 1944 $check = true;
c70ad9f7 1945 }
9c5bc7a5 1946 if ($check) {
0085fff8 1947 unset($value->id);
c70ad9f7 1948 $value->completed = $feedbackcompleted->id;
0085fff8 1949 $DB->insert_record('feedback_value', $value);
c70ad9f7 1950 }
c70ad9f7 1951 }
73043833
AG
1952 //drop all the tmpvalues
1953 $DB->delete_records('feedback_valuetmp', array('completed'=>$tmpcplid));
1954 $DB->delete_records('feedback_completedtmp', array('id'=>$tmpcplid));
3f6fd941
AA
1955
1956 // Trigger event for the delete action we performed.
1957 $cm = get_coursemodule_from_instance('feedback', $feedbackcompleted->feedback);
97d71cbc 1958 $event = \mod_feedback\event\response_submitted::create_from_record($feedbackcompleted, $cm);
3f6fd941 1959 $event->trigger();
73043833
AG
1960 return $feedbackcompleted->id;
1961
c70ad9f7 1962}
1963
37b86a39 1964/**
97d71cbc 1965 * @deprecated since Moodle 3.1
c70ad9f7 1966 */
98f9db0d
MG
1967function feedback_delete_completedtmp() {
1968 throw new coding_exception('feedback_delete_completedtmp() can not be used anymore.');
97d71cbc 1969
c70ad9f7 1970}
1971
1972////////////////////////////////////////////////
1973////////////////////////////////////////////////
1974////////////////////////////////////////////////
1975//functions to handle the pagebreaks
1976////////////////////////////////////////////////
1977
37b86a39 1978/**
32be99dc 1979 * this creates a pagebreak.
1980 * a pagebreak is a special kind of item
1981 *
1982 * @global object
1983 * @param int $feedbackid
1984 * @return mixed false if there already is a pagebreak on last position or the id of the pagebreak-item
c70ad9f7 1985 */
1986function feedback_create_pagebreak($feedbackid) {
0085fff8 1987 global $DB;
1988
c70ad9f7 1989 //check if there already is a pagebreak on the last position
0085fff8 1990 $lastposition = $DB->count_records('feedback_item', array('feedback'=>$feedbackid));
9c5bc7a5 1991 if ($lastposition == feedback_get_last_break_position($feedbackid)) {
c70ad9f7 1992 return false;
1993 }
37b86a39 1994
39790bd8 1995 $item = new stdClass();
c70ad9f7 1996 $item->feedback = $feedbackid;
1997
1998 $item->template=0;
1999
2000 $item->name = '';
37b86a39 2001
c70ad9f7 2002 $item->presentation = '';
2003 $item->hasvalue = 0;
37b86a39 2004
c70ad9f7 2005 $item->typ = 'pagebreak';
2006 $item->position = $lastposition + 1;
2007
2008 $item->required=0;
2009
0085fff8 2010 return $DB->insert_record('feedback_item', $item);
c70ad9f7 2011}
2012
37b86a39 2013/**
32be99dc 2014 * get all positions of pagebreaks in the given feedback
2015 *
2016 * @global object
2017 * @param int $feedbackid
2018 * @return array all ordered pagebreak positions
c70ad9f7 2019 */
2020function feedback_get_all_break_positions($feedbackid) {
534792cd 2021 global $DB;
2022
9c5bc7a5
AG
2023 $params = array('typ'=>'pagebreak', 'feedback'=>$feedbackid);
2024 $allbreaks = $DB->get_records_menu('feedback_item', $params, 'position', 'id, position');
2025 if (!$allbreaks) {
2026 return false;
2027 }
c70ad9f7 2028 return array_values($allbreaks);
2029}
2030
37b86a39 2031/**
32be99dc 2032 * get the position of the last pagebreak
d4b1d58c 2033 *
32be99dc 2034 * @param int $feedbackid
2035 * @return int the position of the last pagebreak
c70ad9f7 2036 */
2037function feedback_get_last_break_position($feedbackid) {
9c5bc7a5 2038 if (!$allbreaks = feedback_get_all_break_positions($feedbackid)) {
73043833
AG
2039 return false;
2040 }
c70ad9f7 2041 return $allbreaks[count($allbreaks) - 1];
2042}
2043
37b86a39 2044/**
97d71cbc 2045 * @deprecated since Moodle 3.1
c70ad9f7 2046 */
98f9db0d
MG
2047function feedback_get_page_to_continue() {
2048 throw new coding_exception('feedback_get_page_to_continue() can not be used anymore.');
c70ad9f7 2049}
2050
2051////////////////////////////////////////////////
2052////////////////////////////////////////////////
2053////////////////////////////////////////////////
2054//functions to handle the values
2055////////////////////////////////////////////////
2056
5f17311f 2057/**
97d71cbc 2058 * @deprecated since Moodle 3.1
e7fc2ff4 2059 */
98f9db0d
MG
2060function feedback_clean_input_value() {
2061 throw new coding_exception('feedback_clean_input_value() can not be used anymore. '
2062 . 'Items must implement complete_form_element().');
2063
e7fc2ff4
AG
2064}
2065
37b86a39 2066/**
97d71cbc 2067 * @deprecated since Moodle 3.1
c70ad9f7 2068 */
98f9db0d
MG
2069function feedback_save_values() {
2070 throw new coding_exception('feedback_save_values() can not be used anymore.');
c70ad9f7 2071}
2072
37b86a39 2073/**
97d71cbc 2074 * @deprecated since Moodle 3.1
c70ad9f7 2075 */
98f9db0d
MG
2076function feedback_save_guest_values() {
2077 throw new coding_exception('feedback_save_guest_values() can not be used anymore.');
c70ad9f7 2078}
2079
37b86a39 2080/**
32be99dc 2081 * get the value from the given item related to the given completed.
2082 * the value can come as temporary or as permanently value. the deciding is done by $tmp
2083 *
2084 * @global object
2085 * @param int $completeid
2086 * @param int $itemid
2087 * @param boolean $tmp
2088 * @return mixed the value, the type depends on plugin-definition
c70ad9f7 2089 */
2090function feedback_get_item_value($completedid, $itemid, $tmp = false) {
0085fff8 2091 global $DB;
2092
c70ad9f7 2093 $tmpstr = $tmp ? 'tmp' : '';
9c5bc7a5
AG
2094 $params = array('completed'=>$completedid, 'item'=>$itemid);
2095 return $DB->get_field('feedback_value'.$tmpstr, 'value', $params);
c70ad9f7 2096}
2097
3a787259
AG
2098/**
2099 * compares the value of the itemid related to the completedid with the dependvalue.
2100 * this is used if a depend item is set.
2101 * the value can come as temporary or as permanently value. the deciding is done by $tmp.
2102 *
97d71cbc
MG
2103 * @param int $completedid
2104 * @param stdClass|int $item
3a787259 2105 * @param mixed $dependvalue
97d71cbc 2106 * @param bool $tmp
3a787259
AG
2107 * @return bool
2108 */
97d71cbc
MG
2109function feedback_compare_item_value($completedid, $item, $dependvalue, $tmp = false) {
2110 global $DB;
6938191b 2111
97d71cbc
MG
2112 if (is_int($item)) {
2113 $item = $DB->get_record('feedback_item', array('id' => $item));
2114 }
6938191b 2115
97d71cbc 2116 $dbvalue = feedback_get_item_value($completedid, $item->id, $tmp);
6cc1599e 2117
6cc1599e 2118 $itemobj = feedback_get_item_class($item->typ);
73043833
AG
2119 return $itemobj->compare_value($item, $dbvalue, $dependvalue); //true or false
2120}
2121
37b86a39 2122/**
98f9db0d 2123 * @deprecated since Moodle 3.1
c70ad9f7 2124 */
98f9db0d
MG
2125function feedback_check_values() {
2126 throw new coding_exception('feedback_check_values() can not be used anymore. '
2127 . 'Items must implement complete_form_element().');
c70ad9f7 2128}
2129
37b86a39 2130/**
97d71cbc 2131 * @deprecated since Moodle 3.1
c70ad9f7 2132 */
98f9db0d
MG
2133function feedback_create_values() {
2134 throw new coding_exception('feedback_create_values() can not be used anymore.');
c70ad9f7 2135}
2136
37b86a39 2137/**
98f9db0d 2138 * @deprecated since Moodle 3.1
c70ad9f7 2139 */
98f9db0d
MG
2140function feedback_update_values() {
2141 throw new coding_exception('feedback_update_values() can not be used anymore.');
c70ad9f7 2142}
2143
37b86a39 2144/**
32be99dc 2145 * get the values of an item depending on the given groupid.
2146 * if the feedback is anonymous so the values are shuffled
2147 *
2148 * @global object
2149 * @global object
2150 * @param object $item
2151 * @param int $groupid
2152 * @param int $courseid
3a787259 2153 * @param bool $ignore_empty if this is set true so empty values are not delivered
32be99dc 2154 * @return array the value-records
c70ad9f7 2155 */
9c5bc7a5
AG
2156function feedback_get_group_values($item,
2157 $groupid = false,
2158 $courseid = false,
2159 $ignore_empty = false) {
2160
0085fff8 2161 global $CFG, $DB;
c70ad9f7 2162
2163 //if the groupid is given?
0085fff8 2164 if (intval($groupid) > 0) {
d2097020 2165 $params = array();
9c5bc7a5 2166 if ($ignore_empty) {
d2097020
MG
2167 $value = $DB->sql_compare_text('fbv.value');
2168 $ignore_empty_select = "AND $value != :emptyvalue AND $value != :zerovalue";
2169 $params += array('emptyvalue' => '', 'zerovalue' => '0');
9c5bc7a5 2170 } else {
79473294
AG
2171 $ignore_empty_select = "";
2172 }
6938191b 2173
c70ad9f7 2174 $query = 'SELECT fbv . *
abf1c50f 2175 FROM {feedback_value} fbv, {feedback_completed} fbc, {groups_members} gm
d2097020 2176 WHERE fbv.item = :itemid
37b86a39 2177 AND fbv.completed = fbc.id
2178 AND fbc.userid = gm.userid
79473294 2179 '.$ignore_empty_select.'
d2097020 2180 AND gm.groupid = :groupid
0085fff8 2181 ORDER BY fbc.timemodified';
d2097020
MG
2182 $params += array('itemid' => $item->id, 'groupid' => $groupid);
2183 $values = $DB->get_records_sql($query, $params);
0085fff8 2184
2185 } else {
d2097020 2186 $params = array();
9c5bc7a5 2187 if ($ignore_empty) {
d2097020
MG
2188 $value = $DB->sql_compare_text('value');
2189 $ignore_empty_select = "AND $value != :emptyvalue AND $value != :zerovalue";
2190 $params += array('emptyvalue' => '', 'zerovalue' => '0');
9c5bc7a5 2191 } else {
79473294
AG
2192 $ignore_empty_select = "";
2193 }
6938191b 2194
c70ad9f7 2195 if ($courseid) {
d2097020
MG
2196 $select = "item = :itemid AND course_id = :courseid ".$ignore_empty_select;
2197 $params += array('itemid' => $item->id, 'courseid' => $courseid);
9c5bc7a5 2198 $values = $DB->get_records_select('feedback_value', $select, $params);
c70ad9f7 2199 } else {
d2097020
MG
2200 $select = "item = :itemid ".$ignore_empty_select;
2201 $params += array('itemid' => $item->id);
9c5bc7a5 2202 $values = $DB->get_records_select('feedback_value', $select, $params);
c70ad9f7 2203 }
37b86a39 2204 }
9c5bc7a5
AG
2205 $params = array('id'=>$item->feedback);
2206 if ($DB->get_field('feedback', 'anonymous', $params) == FEEDBACK_ANONYMOUS_YES) {
2207 if (is_array($values)) {
c70ad9f7 2208 shuffle($values);
9c5bc7a5 2209 }
c70ad9f7 2210 }
2211 return $values;
2212}
2213
37b86a39 2214/**
32be99dc 2215 * check for multiple_submit = false.
2216 * if the feedback is global so the courseid must be given
2217 *
2218 * @global object
2219 * @global object
2220 * @param int $feedbackid
2221 * @param int $courseid
2222 * @return boolean true if the feedback already is submitted otherwise false
c70ad9f7 2223 */
2224function feedback_is_already_submitted($feedbackid, $courseid = false) {
534792cd 2225 global $USER, $DB;
37b86a39 2226
97d71cbc 2227 if (!isloggedin() || isguestuser()) {
c70ad9f7 2228 return false;
2229 }
2230
97d71cbc 2231 $params = array('userid' => $USER->id, 'feedback' => $feedbackid);
9c5bc7a5 2232 if ($courseid) {
97d71cbc 2233 $params['courseid'] = $courseid;
c70ad9f7 2234 }
97d71cbc 2235 return $DB->record_exists('feedback_completed', $params);
c70ad9f7 2236}
2237
37b86a39 2238/**
98f9db0d 2239 * @deprecated since Moodle 3.1. Use feedback_get_current_completed_tmp() or feedback_get_last_completed.
c70ad9f7 2240 */
98f9db0d
MG
2241function feedback_get_current_completed() {
2242 throw new coding_exception('feedback_get_current_completed() can not be used anymore. Please ' .
2243 'use either feedback_get_current_completed_tmp() or feedback_get_last_completed()');
c70ad9f7 2244}
2245
37b86a39 2246/**
32be99dc 2247 * get the completeds depending on the given groupid.
2248 *
2249 * @global object
2250 * @global object
2251 * @param object $feedback
2252 * @param int $groupid
3a787259 2253 * @param int $courseid
32be99dc 2254 * @return mixed array of found completeds otherwise false
c70ad9f7 2255 */
59f89d80 2256function feedback_get_completeds_group($feedback, $groupid = false, $courseid = false) {
0085fff8 2257 global $CFG, $DB;
2258
9c5bc7a5 2259 if (intval($groupid) > 0) {
0085fff8 2260 $query = "SELECT fbc.*
abf1c50f 2261 FROM {feedback_completed} fbc, {groups_members} gm
0085fff8 2262 WHERE fbc.feedback = ?
2263 AND gm.groupid = ?
2264 AND fbc.userid = gm.userid";
2265 if ($values = $DB->get_records_sql($query, array($feedback->id, $groupid))) {
c70ad9f7 2266 return $values;
0085fff8 2267 } else {
2268 return false;
2269 }
2270 } else {
9c5bc7a5 2271 if ($courseid) {
59f89d80 2272 $query = "SELECT DISTINCT fbc.*
abf1c50f 2273 FROM {feedback_completed} fbc, {feedback_value} fbv
59f89d80 2274 WHERE fbc.id = fbv.completed
2275 AND fbc.feedback = ?
2276 AND fbv.course_id = ?
2277 ORDER BY random_response";
2278 if ($values = $DB->get_records_sql($query, array($feedback->id, $courseid))) {
2279 return $values;
2280 } else {
2281 return false;
2282 }
9c5bc7a5 2283 } else {
59f89d80 2284 if ($values = $DB->get_records('feedback_completed', array('feedback'=>$feedback->id))) {
2285 return $values;
2286 } else {
2287 return false;
2288 }
0085fff8 2289 }
c70ad9f7 2290 }
2291}
2292
37b86a39 2293/**
32be99dc 2294 * get the count of completeds depending on the given groupid.
2295 *
2296 * @global object
2297 * @global object
2298 * @param object $feedback
2299 * @param int $groupid
2300 * @param int $courseid
2301 * @return mixed count of completeds or false
c70ad9f7 2302 */
2303function feedback_get_completeds_group_count($feedback, $groupid = false, $courseid = false) {
0085fff8 2304 global $CFG, $DB;
2305
2306 if ($courseid > 0 AND !$groupid <= 0) {
2307 $sql = "SELECT id, COUNT(item) AS ci
37b86a39 2308 FROM {feedback_value}
0085fff8 2309 WHERE course_id = ?
2310 GROUP BY item ORDER BY ci DESC";
2311 if ($foundrecs = $DB->get_records_sql($sql, array($courseid))) {
c70ad9f7 2312 $foundrecs = array_values($foundrecs);
2313 return $foundrecs[0]->ci;
2314 }
2315 return false;
2316 }
9c5bc7a5
AG
2317 if ($values = feedback_get_completeds_group($feedback, $groupid)) {
2318 return count($values);
2319 } else {
c70ad9f7 2320 return false;
2321 }
2322}
2323
37b86a39 2324/**
32be99dc 2325 * deletes all completed-recordsets from a feedback.
2326 * all related data such as values also will be deleted
2327 *
97d71cbc
MG
2328 * @param stdClass|int $feedback
2329 * @param stdClass|cm_info $cm
2330 * @param stdClass $course
32be99dc 2331 * @return void
c70ad9f7 2332 */
97d71cbc 2333function feedback_delete_all_completeds($feedback, $cm = null, $course = null) {
0085fff8 2334 global $DB;
2335
97d71cbc
MG
2336 if (is_int($feedback)) {
2337 $feedback = $DB->get_record('feedback', array('id' => $feedback));
2338 }
2339
2340 if (!$completeds = $DB->get_records('feedback_completed', array('feedback' => $feedback->id))) {
0085fff8 2341 return;
2342 }
97d71cbc
MG
2343
2344 if (!$course && !($course = $DB->get_record('course', array('id' => $feedback->course)))) {
2345 return false;
2346 }
2347
2348 if (!$cm && !($cm = get_coursemodule_from_instance('feedback', $feedback->id))) {
2349 return false;
2350 }
2351
9c5bc7a5 2352 foreach ($completeds as $completed) {
97d71cbc 2353 feedback_delete_completed($completed, $feedback, $cm, $course);
c70ad9f7 2354 }
2355}
2356
37b86a39 2357/**
32be99dc 2358 * deletes a completed given by completedid.
2359 * all related data such values or tracking data also will be deleted
d4b1d58c 2360 *
97d71cbc
MG
2361 * @param int|stdClass $completed
2362 * @param stdClass $feedback
2363 * @param stdClass|cm_info $cm
2364 * @param stdClass $course
32be99dc 2365 * @return boolean
c70ad9f7 2366 */
97d71cbc 2367function feedback_delete_completed($completed, $feedback = null, $cm = null, $course = null) {
516c5eca
PS
2368 global $DB, $CFG;
2369 require_once($CFG->libdir.'/completionlib.php');
0085fff8 2370
97d71cbc
MG
2371 if (!isset($completed->id)) {
2372 if (!$completed = $DB->get_record('feedback_completed', array('id' => $completed))) {
2373 return false;
2374 }
c70ad9f7 2375 }
516c5eca 2376
97d71cbc 2377 if (!$feedback && !($feedback = $DB->get_record('feedback', array('id' => $completed->feedback)))) {
8a41eb4f
AG
2378 return false;
2379 }
516c5eca 2380
97d71cbc 2381 if (!$course && !($course = $DB->get_record('course', array('id' => $feedback->course)))) {
8a41eb4f
AG
2382 return false;
2383 }
516c5eca 2384
97d71cbc 2385 if (!$cm && !($cm = get_coursemodule_from_instance('feedback', $feedback->id))) {
8a41eb4f
AG
2386 return false;
2387 }
2388
c70ad9f7 2389 //first we delete all related values
97d71cbc 2390 $DB->delete_records('feedback_value', array('completed' => $completed->id));
37b86a39 2391
c3c97e2e
MG
2392 // Delete the completed record.
2393 $return = $DB->delete_records('feedback_completed', array('id' => $completed->id));
2394
8a41eb4f
AG
2395 // Update completion state
2396 $completion = new completion_info($course);
c3c97e2e 2397 if ($completion->is_enabled($cm) && $cm->completion == COMPLETION_TRACKING_AUTOMATIC && $feedback->completionsubmit) {
8a41eb4f
AG
2398 $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
2399 }
3f6fd941 2400 // Trigger event for the delete action we performed.
97d71cbc 2401 $event = \mod_feedback\event\response_deleted::create_from_record($completed, $cm, $feedback);
3f6fd941
AA
2402 $event->trigger();
2403
2404 return $return;
c70ad9f7 2405}
2406
2407////////////////////////////////////////////////
2408////////////////////////////////////////////////
2409////////////////////////////////////////////////
2410//functions to handle sitecourse mapping
2411////////////////////////////////////////////////
2412
37b86a39 2413/**
97d71cbc 2414 * @deprecated since 3.1
c70ad9f7 2415 */
98f9db0d
MG
2416function feedback_is_course_in_sitecourse_map() {
2417 throw new coding_exception('feedback_is_course_in_sitecourse_map() can not be used anymore.');
c70ad9f7 2418}
2419
37b86a39 2420/**
97d71cbc 2421 * @deprecated since 3.1
c70ad9f7 2422 */
98f9db0d
MG
2423function feedback_is_feedback_in_sitecourse_map() {
2424 throw new coding_exception('feedback_is_feedback_in_sitecourse_map() can not be used anymore.');
c70ad9f7 2425}
2426
37b86a39 2427/**
32be99dc 2428 * gets the feedbacks from table feedback_sitecourse_map.
2429 * this is used to show the global feedbacks on the feedback block
2430 * all feedbacks with the following criteria will be selected:<br />
2431 *
2432 * 1) all feedbacks which id are listed together with the courseid in sitecoursemap and<br />
2433 * 2) all feedbacks which not are listed in sitecoursemap
2434 *
2435 * @global object
2436 * @param int $courseid
2437 * @return array the feedback-records
c70ad9f7 2438 */
2439function feedback_get_feedbacks_from_sitecourse_map($courseid) {
0085fff8 2440 global $DB;
37b86a39 2441
c70ad9f7 2442 //first get all feedbacks listed in sitecourse_map with named courseid
9c5bc7a5
AG
2443 $sql = "SELECT f.id AS id,
2444 cm.id AS cmid,
2445 f.name AS name,
2446 f.timeopen AS timeopen,
2447 f.timeclose AS timeclose
2448 FROM {feedback} f, {course_modules} cm, {feedback_sitecourse_map} sm, {modules} m
2449 WHERE f.id = cm.instance
0085fff8 2450 AND f.course = '".SITEID."'
37b86a39 2451 AND m.id = cm.module
0085fff8 2452 AND m.name = 'feedback'
37b86a39 2453 AND sm.courseid = ?
0085fff8 2454 AND sm.feedbackid = f.id";
37b86a39 2455
0085fff8 2456 if (!$feedbacks1 = $DB->get_records_sql($sql, array($courseid))) {
c70ad9f7 2457 $feedbacks1 = array();
2458 }
37b86a39 2459
c70ad9f7 2460 //second get all feedbacks not listed in sitecourse_map
2461 $feedbacks2 = array();
9c5bc7a5
AG
2462 $sql = "SELECT f.id AS id,
2463 cm.id AS cmid,
2464 f.name AS name,
2465 f.timeopen AS timeopen,
2466 f.timeclose AS timeclose
2467 FROM {feedback} f, {course_modules} cm, {modules} m
2468 WHERE f.id = cm.instance
0085fff8 2469 AND f.course = '".SITEID."'
2470 AND m.id = cm.module
2471 AND m.name = 'feedback'";
2472 if (!$allfeedbacks = $DB->get_records_sql($sql)) {
c70ad9f7 2473 $allfeedbacks = array();
2474 }
9c5bc7a5
AG
2475 foreach ($allfeedbacks as $a) {
2476 if (!$DB->record_exists('feedback_sitecourse_map', array('feedbackid'=>$a->id))) {
c70ad9f7 2477 $feedbacks2[] = $a;
2478 }
2479 }
37b86a39 2480
2a94a2f6
MG
2481 $feedbacks = array_merge($feedbacks1, $feedbacks2);
2482 $modinfo = get_fast_modinfo(SITEID);
2483 return array_filter($feedbacks, function($f) use ($modinfo) {
2484 return ($cm = $modinfo->get_cm($f->cmid)) && $cm->uservisible;
2485 });
37b86a39 2486
c70ad9f7 2487}
2488
37b86a39 2489/**
ed724aac 2490 * Gets the courses from table feedback_sitecourse_map
32be99dc 2491 *
32be99dc 2492 * @param int $feedbackid
2493 * @return array the course-records
c70ad9f7 2494 */
2495function feedback_get_courses_from_sitecourse_map($feedbackid) {
0085fff8 2496 global $DB;
37b86a39 2497
ed724aac 2498 $sql = "SELECT c.id, c.fullname, c.shortname
0085fff8 2499 FROM {feedback_sitecourse_map} f, {course} c
2500 WHERE c.id = f.courseid
2501 AND f.feedbackid = ?
2502 ORDER BY c.fullname";
37b86a39 2503
0085fff8 2504 return $DB->get_records_sql($sql, array($feedbackid));
37b86a39 2505
c70ad9f7 2506}
2507
ed724aac
MG
2508/**
2509 * Updates the course mapping for the feedback
2510 *
2511 * @param stdClass $feedback
2512 * @param array $courses array of course ids
2513 */
2514function feedback_update_sitecourse_map($feedback, $courses) {
2515 global $DB;
2516 if (empty($courses)) {
2517 $courses = array();
2518 }
2519 $currentmapping = $DB->get_fieldset_select('feedback_sitecourse_map', 'courseid', 'feedbackid=?', array($feedback->id));
2520 foreach (array_diff($courses, $currentmapping) as $courseid) {
2521 $DB->insert_record('feedback_sitecourse_map', array('feedbackid' => $feedback->id, 'courseid' => $courseid));
2522 }
2523 foreach (array_diff($currentmapping, $courses) as $courseid) {
2524 $DB->delete_records('feedback_sitecourse_map', array('feedbackid' => $feedback->id, 'courseid' => $courseid));
2525 }
2526 // TODO MDL-53574 add events.
2527}
2528
37b86a39 2529/**
97d71cbc 2530 * @deprecated since 3.1
c70ad9f7 2531 */
2532function feedback_clean_up_sitecourse_map() {
98f9db0d 2533 throw new coding_exception('feedback_clean_up_sitecourse_map() can not be used anymore.');
c70ad9f7 2534}
2535
2536////////////////////////////////////////////////
2537////////////////////////////////////////////////
2538////////////////////////////////////////////////
2539//not relatable functions
2540////////////////////////////////////////////////
2541
37b86a39 2542/**
97d71cbc 2543 * @deprecated since 3.1
c70ad9f7 2544 */
98f9db0d
MG
2545function feedback_print_numeric_option_list() {
2546 throw new coding_exception('feedback_print_numeric_option_list() can not be used anymore.');
c70ad9f7 2547}
2548
37b86a39 2549/**
32be99dc 2550 * sends an email to the teachers of the course where the given feedback is placed.
2551 *
2552 * @global object
2553 * @global object
2554 * @uses FEEDBACK_ANONYMOUS_NO
2555 * @uses FORMAT_PLAIN
2556 * @param object $cm the coursemodule-record
2557 * @param object $feedback
2558 * @param object $course
97d71cbc 2559 * @param stdClass|int $user
d0b4c8a7 2560 * @param stdClass $completed record from feedback_completed if known
32be99dc 2561 * @return void
c70ad9f7 2562 */
d0b4c8a7 2563function feedback_send_email($cm, $feedback, $course, $user, $completed = null) {
c6307ef2 2564 global $CFG, $DB;
37b86a39 2565
c70ad9f7 2566 if ($feedback->email_notification == 0) { // No need to do anything
2567 return;
2568 }
37b86a39 2569
97d71cbc
MG
2570 if (is_int($user)) {
2571 $user = $DB->get_record('user', array('id' => $user));
2572 }
37b86a39 2573
84973212 2574 if (isset($cm->groupmode) && empty($course->groupmodeforce)) {
2575 $groupmode = $cm->groupmode;
2576 } else {
2577 $groupmode = $course->groupmode;
2578 }
2579
2580 if ($groupmode == SEPARATEGROUPS) {
c6307ef2 2581 $groups = $DB->get_records_sql_menu("SELECT g.name, g.id
2582 FROM {groups} g, {groups_members} m
2583 WHERE g.courseid = ?
2584 AND g.id = m.groupid
2585 AND m.userid = ?
97d71cbc 2586 ORDER BY name ASC", array($course->id, $user->id));
c70ad9f7 2587 $groups = array_values($groups);
37b86a39 2588
c184660d 2589 $teachers = feedback_get_receivemail_users($cm->id, $groups);
c70ad9f7 2590 } else {
c184660d 2591 $teachers = feedback_get_receivemail_users($cm->id);
c70ad9f7 2592 }
37b86a39 2593
c70ad9f7 2594 if ($teachers) {
2595
2596 $strfeedbacks = get_string('modulenameplural', 'feedback');
2597 $strfeedback = get_string('modulename', 'feedback');
9c5bc7a5
AG
2598
2599 if ($feedback->anonymous == FEEDBACK_ANONYMOUS_NO) {
2600 $printusername = fullname($user);
2601 } else {
2602 $printusername = get_string('anonymous_user', 'feedback');
2603 }
37b86a39 2604
c70ad9f7 2605 foreach ($teachers as $teacher) {
39790bd8 2606 $info = new stdClass();
c70ad9f7 2607 $info->username = $printusername;
9c5bc7a5
AG
2608 $info->feedback = format_string($feedback->name, true);
2609 $info->url = $CFG->wwwroot.'/mod/feedback/show_entries.php?'.
2610 'id='.$cm->id.'&'.
97d71cbc 2611 'userid=' . $user->id;
d0b4c8a7
MG
2612 if ($completed) {
2613 $info->url .= '&showcompleted=' . $completed->id;
2614 if ($feedback->course == SITEID) {
2615 // Course where feedback was completed (for site feedbacks only).
2616 $info->url .= '&courseid=' . $completed->courseid;
2617 }
2618 }
c70ad9f7 2619
3da723d5 2620 $a = array('username' => $info->username, 'feedbackname' => $feedback->name);
2621
2622 $postsubject = get_string('feedbackcompleted', 'feedback', $a);
c184660d 2623 $posttext = feedback_send_email_text($info, $course);
37b86a39 2624
9c5bc7a5
AG
2625 if ($teacher->mailformat == 1) {
2626 $posthtml = feedback_send_email_html($info, $course, $cm);
2627 } else {
2628 $posthtml = '';
2629 }
2630
2631 if ($feedback->anonymous == FEEDBACK_ANONYMOUS_NO) {
cc350fd9
AD
2632 $eventdata = new \core\message\message();
2633 $eventdata->courseid = $course->id;
51c49cc0
AG
2634 $eventdata->name = 'submission';
2635 $eventdata->component = 'mod_feedback';
3b120e46 2636 $eventdata->userfrom = $user;
2637 $eventdata->userto = $teacher;
2638 $eventdata->subject = $postsubject;
2639 $eventdata->fullmessage = $posttext;
2640 $eventdata->fullmessageformat = FORMAT_PLAIN;
2641 $eventdata->fullmessagehtml = $posthtml;
9d0e8a4f 2642 $eventdata->smallmessage = '';
cc350fd9
AD
2643 $eventdata->courseid = $course->id;
2644 $eventdata->contexturl = $info->url;
2645 $eventdata->contexturlname = $info->feedback;
7c7d3afa 2646 message_send($eventdata);
9c5bc7a5 2647 } else {
cc350fd9
AD
2648 $eventdata = new \core\message\message();
2649 $eventdata->courseid = $course->id;
51c49cc0
AG
2650 $eventdata->name = 'submission';
2651 $eventdata->component = 'mod_feedback';
3b120e46 2652 $eventdata->userfrom = $teacher;
2653 $eventdata->userto = $teacher;
2654 $eventdata->subject = $postsubject;
2655 $eventdata->fullmessage = $posttext;
2656 $eventdata->fullmessageformat = FORMAT_PLAIN;
2657 $eventdata->fullmessagehtml = $posthtml;
9d0e8a4f 2658 $eventdata->smallmessage = '';
cc350fd9
AD
2659 $eventdata->courseid = $course->id;
2660 $eventdata->contexturl = $info->url;
2661 $eventdata->contexturlname = $info->feedback;
7c7d3afa 2662 message_send($eventdata);
c70ad9f7 2663 }
2664 }
2665 }
2666}
2667
37b86a39 2668/**
32be99dc 2669 * sends an email to the teachers of the course where the given feedback is placed.
2670 *
2671 * @global object
2672 * @uses FORMAT_PLAIN
2673 * @param object $cm the coursemodule-record
2674 * @param object $feedback
2675 * @param object $course
2676 * @return void
c70ad9f7 2677 */
c184660d 2678function feedback_send_email_anonym($cm, $feedback, $course) {
c70ad9f7 2679 global $CFG;
37b86a39 2680
9c5bc7a5 2681 if ($feedback->email_notification == 0) { // No need to do anything
c70ad9f7 2682 return;
2683 }
37b86a39 2684
c184660d 2685 $teachers = feedback_get_receivemail_users($cm->id);
c70ad9f7 2686
2687 if ($teachers) {
2688
2689 $strfeedbacks = get_string('modulenameplural', 'feedback');
2690 $strfeedback = get_string('modulename', 'feedback');
c70ad9f7 2691 $printusername = get_string('anonymous_user', 'feedback');
37b86a39 2692
c70ad9f7 2693 foreach ($teachers as $teacher) {
39790bd8 2694 $info = new stdClass();
c70ad9f7 2695 $info->username = $printusername;
9c5bc7a5 2696 $info->feedback = format_string($feedback->name, true);
9bb87baf 2697 $info->url = $CFG->wwwroot.'/mod/feedback/show_entries.php?id=' . $cm->id;
c70ad9f7 2698
3da723d5 2699 $a = array('username' => $info->username, 'feedbackname' => $feedback->name);
2700
2701 $postsubject = get_string('feedbackcompleted', 'feedback', $a);
c184660d 2702 $posttext = feedback_send_email_text($info, $course);
9c5bc7a5
AG
2703
2704 if ($teacher->mailformat == 1) {
2705 $posthtml = feedback_send_email_html($info, $course, $cm);
2706 } else {
2707 $posthtml = '';
2708 }
37b86a39 2709
cc350fd9
AD
2710 $eventdata = new \core\message\message();
2711 $eventdata->courseid = $course->id;
51c49cc0
AG
2712 $eventdata->name = 'submission';
2713 $eventdata->component = 'mod_feedback';
3b120e46 2714 $eventdata->userfrom = $teacher;
2715 $eventdata->userto = $teacher;
2716 $eventdata->subject = $postsubject;
2717 $eventdata->fullmessage = $posttext;
2718 $eventdata->fullmessageformat = FORMAT_PLAIN;
2719 $eventdata->fullmessagehtml = $posthtml;
9d0e8a4f 2720 $eventdata->smallmessage = '';
cc350fd9
AD
2721 $eventdata->courseid = $course->id;
2722 $eventdata->contexturl = $info->url;
2723 $eventdata->contexturlname = $info->feedback;
7c7d3afa 2724 message_send($eventdata);
c70ad9f7 2725 }
2726 }
2727}
2728
37b86a39 2729/**
32be99dc 2730 * send the text-part of the email
2731 *
2732 * @param object $info includes some infos about the feedback you want to send
2733 * @param object $course
2734 * @return string the text you want to post
c70ad9f7 2735 */
c184660d 2736function feedback_send_email_text($info, $course) {
2c979976 2737 $coursecontext = context_course::instance($course->id);
8ebbb06a
SH
2738 $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
2739 $posttext = $courseshortname.' -> '.get_string('modulenameplural', 'feedback').' -> '.
c70ad9f7 2740 $info->feedback."\n";
2741 $posttext .= '---------------------------------------------------------------------'."\n";
2742 $posttext .= get_string("emailteachermail", "feedback", $info)."\n";
2743 $posttext .= '---------------------------------------------------------------------'."\n";
2744 return $posttext;
2745}
2746
2747
37b86a39 2748/**
32be99dc 2749 * send the html-part of the email
2750 *
2751 * @global object
2752 * @param object $info includes some infos about the feedback you want to send
2753 * @param object $course
2754 * @return string the text you want to post
c70ad9f7 2755 */
c184660d 2756function feedback_send_email_html($info, $course, $cm) {
c70ad9f7 2757 global $CFG;
2c979976 2758 $coursecontext = context_course::instance($course->id);
8ebbb06a 2759 $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
9c5bc7a5
AG
2760 $course_url = $CFG->wwwroot.'/course/view.php?id='.$course->id;
2761 $feedback_all_url = $CFG->wwwroot.'/mod/feedback/index.php?id='.$course->id;
2762 $feedback_url = $CFG->wwwroot.'/mod/feedback/view.php?id='.$cm->id;
2763
2764 $posthtml = '<p><font face="sans-serif">'.
2765 '<a href="'.$course_url.'">'.$courseshortname.'</a> ->'.
2766 '<a href="'.$feedback_all_url.'">'.get_string('modulenameplural', 'feedback').'</a> ->'.
2767 '<a href="'.$feedback_url.'">'.$info->feedback.'</a></font></p>';
c70ad9f7 2768 $posthtml .= '<hr /><font face="sans-serif">';
2769 $posthtml .= '<p>'.get_string('emailteachermailhtml', 'feedback', $info).'</p>';
2770 $posthtml .= '</font><hr />';
2771 return $posthtml;
2772}
2773
32be99dc 2774/**
2775 * @param string $url
2776 * @return string
2777 */
c184660d 2778function feedback_encode_target_url($url) {
2779 if (strpos($url, '?')) {
2780 list($part1, $part2) = explode('?', $url, 2); //maximal 2 parts
2781 return $part1 . '?' . htmlentities($part2);
2782 } else {
2783 return $url;
2784 }
2785}
ab5bd34d 2786
0b29477b
SH
2787/**
2788 * Adds module specific settings to the settings block
2789 *
2790 * @param settings_navigation $settings The settings navigation object
2791 * @param navigation_node $feedbacknode The node to add module settings to
2792 */
9c5bc7a5
AG
2793function feedback_extend_settings_navigation(settings_navigation $settings,
2794 navigation_node $feedbacknode) {
2795
ed724aac 2796 global $PAGE;
ab5bd34d 2797
2c979976 2798 if (!$context = context_module::instance($PAGE->cm->id, IGNORE_MISSING)) {
3406acde 2799 print_error('badcontext');
c987dbad
AG
2800 }
2801
3406acde
SH
2802 if (has_capability('mod/feedback:edititems', $context)) {
2803 $questionnode = $feedbacknode->add(get_string('questions', 'feedback'));
9c5bc7a5
AG
2804
2805 $questionnode->add(get_string('edit_items', 'feedback'),
2806 new moodle_url('/mod/feedback/edit.php',
2807 array('id' => $PAGE->cm->id,
2808 'do_show' => 'edit')));
2809
2810 $questionnode->add(get_string('export_questions', 'feedback'),
2811 new moodle_url('/mod/feedback/export.php',
2812 array('id' => $PAGE->cm->id,
2813 'action' => 'exportfile')));
2814
2815 $questionnode->add(get_string('import_questions', 'feedback'),
2816 new moodle_url('/mod/feedback/import.php',
2817 array('id' => $PAGE->cm->id)));
2818
2819 $questionnode->add(get_string('templates', 'feedback'),
2820 new moodle_url('/mod/feedback/edit.php',
2821 array('id' => $PAGE->cm->id,
2822 'do_show' => 'templates')));
ab5bd34d 2823 }
2824
ed724aac
MG
2825 if (has_capability('mod/feedback:mapcourse', $context) && $PAGE->course->id == SITEID) {
2826 $feedbacknode->add(get_string('mappedcourses', 'feedback'),
2827 new moodle_url('/mod/feedback/mapcourse.php',
2828 array('id' => $PAGE->cm->id)));
2829 }
2830
3406acde 2831 if (has_capability('mod/feedback:viewreports', $context)) {
ed724aac 2832 $feedback = $PAGE->activityrecord;
9c5bc7a5
AG
2833 if ($feedback->course == SITEID) {
2834 $feedbacknode->add(get_string('analysis', 'feedback'),
2835 new moodle_url('/mod/feedback/analysis_course.php',
ed724aac 2836 array('id' => $PAGE->cm->id)));
9c5bc7a5
AG
2837 } else {
2838 $feedbacknode->add(get_string('analysis', 'feedback'),
2839 new moodle_url('/mod/feedback/analysis.php',
ed724aac 2840 array('id' => $PAGE->cm->id)));
ab5bd34d 2841 }
ab5bd34d 2842
9c5bc7a5
AG
2843 $feedbacknode->add(get_string('show_entries', 'feedback'),
2844 new moodle_url('/mod/feedback/show_entries.php',
9bb87baf 2845 array('id' => $PAGE->cm->id)));
5e9b2eda
MG
2846
2847 if ($feedback->anonymous == FEEDBACK_ANONYMOUS_NO AND $feedback->course != SITEID) {
2848 $feedbacknode->add(get_string('show_nonrespondents', 'feedback'),
2849 new moodle_url('/mod/feedback/show_nonrespondents.php',
2850 array('id' => $PAGE->cm->id)));
2851 }
ab5bd34d 2852 }
c987dbad 2853}
f1b9e2df 2854
2855function feedback_init_feedback_session() {
2856 //initialize the feedback-Session - not nice at all!!
2857 global $SESSION;
2858 if (!empty($SESSION)) {
2859 if (!isset($SESSION->feedback) OR !is_object($SESSION->feedback)) {
39790bd8 2860 $SESSION->feedback = new stdClass();
f1b9e2df 2861 }
2862 }
05edae9f 2863}
b1627a92
DC
2864
2865/**
2866 * Return a list of page types
2867 * @param string $pagetype current page type
2868 * @param stdClass $parentcontext Block's parent context
2869 * @param stdClass $currentcontext Current context of block
2870 */
b38e2e28 2871function feedback_page_type_list($pagetype, $parentcontext, $currentcontext) {
b1627a92
DC
2872 $module_pagetype = array('mod-feedback-*'=>get_string('page-mod-feedback-x', 'feedback'));
2873 return $module_pagetype;
2874}
3b4afad0
AG
2875
2876/**
2877 * Move save the items of the given $feedback in the order of $itemlist.
2878 * @param string $itemlist a comma separated list with item ids
2879 * @param stdClass $feedback
2880 * @return bool true if success
2881 */
2882function feedback_ajax_saveitemorder($itemlist, $feedback) {
2883 global $DB;
2884
2885 $result = true;
2886 $position = 0;
2887 foreach ($itemlist as $itemid) {
2888 $position++;
2889 $result = $result && $DB->set_field('feedback_item',
2890 'position',
2891 $position,
2892 array('id'=>$itemid, 'feedback'=>$feedback->id));
2893 }
2894 return $result;
2895}
4832a0b3
MG
2896
2897/**
2898 * Checks if current user is able to view feedback on this course.
2899 *
2900 * @param stdClass $feedback
2901 * @param context_module $context
2902 * @param int $courseid
2903 * @return bool
2904 */
2905function feedback_can_view_analysis($feedback, $context, $courseid = false) {
2906 if (has_capability('mod/feedback:viewreports', $context)) {
2907 return true;
2908 }
2909
2910 if (intval($feedback->publish_stats) != 1 ||
2911 !has_capability('mod/feedback:viewanalysepage', $context)) {
2912 return false;
2913 }
2914
2915 if (!isloggedin() || isguestuser()) {
2916 // There is no tracking for the guests, assume that they can view analysis if condition above is satisfied.
2917 return $feedback->course == SITEID;
2918 }
2919
2920 return feedback_is_already_submitted($feedback->id, $courseid);
2921}
2b931458
DW
2922
2923/**
2924 * Get icon mapping for font-awesome.
2925 */
2926function mod_feedback_get_fontawesome_icon_map() {
2927 return [
2928 'mod_feedback:required' => 'fa-exclamation-circle',
ad056aa2 2929 'mod_feedback:notrequired' => 'fa-question-circle-o',
2b931458
DW
2930 ];
2931}
10948ae1
JL
2932
2933/**
2934 * Check if the module has any update that affects the current user since a given time.
2935 *
2936 * @param cm_info $cm course module data
2937 * @param int $from the time to check updates from
2938 * @param array $filter if we need to check only specific updates
2939 * @return stdClass an object with the different type of areas indicating if they were updated or not
2940 * @since Moodle 3.3
2941 */
2942function feedback_check_updates_since(cm_info $cm, $from, $filter = array()) {
2943 global $DB, $USER, $CFG;
2944
2945 $updates = course_check_module_updates_since($cm, $from, array(), $filter);
2946
2947 // Check for new attempts.
2948 $updates->attemptsfinished = (object) array('updated' => false);
2949 $updates->attemptsunfinished = (object) array('updated' => false);
2950 $select = 'feedback = ? AND userid = ? AND timemodified > ?';
2951 $params = array($cm->instance, $USER->id, $from);
2952
2953 $attemptsfinished = $DB->get_records_select('feedback_completed', $select, $params, '', 'id');
2954 if (!empty($attemptsfinished)) {
2955 $updates->attemptsfinished->updated = true;
2956 $updates->attemptsfinished->itemids = array_keys($attemptsfinished);
2957 }
2958 $attemptsunfinished = $DB->get_records_select('feedback_completedtmp', $select, $params, '', 'id');
2959 if (!empty($attemptsunfinished)) {
2960 $updates->attemptsunfinished->updated = true;
2961 $updates->attemptsunfinished->itemids = array_keys($attemptsunfinished);
2962 }
2963
0a348387
JL
2964 // Now, teachers should see other students updates.
2965 if (has_capability('mod/feedback:viewreports', $cm->context)) {
2966 $select = 'feedback = ? AND timemodified > ?';
2967 $params = array($cm->instance, $from);
2968
2969 if (groups_get_activity_groupmode($cm) == SEPARATEGROUPS) {
2970 $groupusers = array_keys(groups_get_activity_shared_group_members($cm));
2971 if (empty($groupusers)) {
2972 return $updates;
2973 }
2974 list($insql, $inparams) = $DB->get_in_or_equal($groupusers);
2975 $select .= ' AND userid ' . $insql;
2976 $params = array_merge($params, $inparams);
2977 }
2978
2979 $updates->userattemptsfinished = (object) array('updated' => false);
2980 $attemptsfinished = $DB->get_records_select('feedback_completed', $select, $params, '', 'id');
2981 if (!empty($attemptsfinished)) {
2982 $updates->userattemptsfinished->updated = true;
2983 $updates->userattemptsfinished->itemids = array_keys($attemptsfinished);
2984 }
2985
2986 $updates->userattemptsunfinished = (object) array('updated' => false);
2987 $attemptsunfinished = $DB->get_records_select('feedback_completedtmp', $select, $params, '', 'id');
2988 if (!empty($attemptsunfinished)) {
2989 $updates->userattemptsunfinished->updated = true;
2990 $updates->userattemptsunfinished->itemids = array_keys($attemptsunfinished);
2991 }
2992 }
2993
10948ae1
JL
2994 return $updates;
2995}
90e8330f 2996
90e8330f 2997/**
59391e80
MN
2998 * This function receives a calendar event and returns the action associated with it, or null if there is none.
2999 *
3000 * This is used by block_myoverview in order to display the event appropriately. If null is returned then the event
3001 * is not displayed on the block.
90e8330f 3002 *
e1cd93ce 3003 * @param calendar_event $event
90e8330f 3004 * @param \core_calendar\action_factory $factory
01f96180 3005 * @return \core_calendar\local\event\entities\action_interface|null
90e8330f 3006 */
e1cd93ce 3007function mod_feedback_core_calendar_provide_event_action(calendar_event $event,
90e8330f 3008 \core_calendar\action_factory $factory) {
90e8330f
SL
3009
3010 $cm = get_fast_modinfo($event->courseid)->instances['feedback'][$event->instance];
aa708b55 3011 $feedbackcompletion = new mod_feedback_completion(null, $cm, 0);
55e55a77 3012
aa708b55
MG
3013 if (!empty($cm->customdata['timeclose']) && $cm->customdata['timeclose'] < time()) {
3014 // Feedback is already closed, do not display it even if it was never submitted.
55e55a77
RW
3015 return null;
3016 }
90e8330f 3017
f2c3818b
RW
3018 if (!$feedbackcompletion->can_complete()) {
3019 // The user can't complete the feedback so there is no action for them.
3020 return null;
3021 }
3022
aa708b55
MG
3023 // The feedback is actionable if it does not have timeopen or timeopen is in the past.
3024 $actionable = $feedbackcompletion->is_open();
3025
3026 if ($actionable && $feedbackcompletion->is_already_submitted()) {
3027 // There is no need to display anything if the user has already submitted the feedback.
3028 return null;
90e8330f
SL
3029 }
3030
3031 return $factory->create_instance(
3032 get_string('answerquestions', 'feedback'),
3033 new \moodle_url('/mod/feedback/view.php', ['id' => $cm->id]),
3034 1,
3035 $actionable
3036 );
3037}
3038
b54bcddd
JD
3039/**
3040 * Add a get_coursemodule_info function in case any feedback type wants to add 'extra' information
3041 * for the course (see resource).
3042 *
3043 * Given a course_module object, this function returns any "extra" information that may be needed
3044 * when printing this activity in a course listing. See get_array_of_activities() in course/lib.php.
3045 *
3046 * @param stdClass $coursemodule The coursemodule object (record).
3047 * @return cached_cm_info An object on information that the courses
3048 * will know about (most noticeably, an icon).
3049 */
3050function feedback_get_coursemodule_info($coursemodule) {
3051 global $DB;
3052
3053 $dbparams = ['id' => $coursemodule->instance];
aa708b55 3054 $fields = 'id, name, intro, introformat, completionsubmit, timeopen, timeclose, anonymous';
b54bcddd
JD
3055 if (!$feedback = $DB->get_record('feedback', $dbparams, $fields)) {
3056 return false;
3057 }
3058
3059 $result = new cached_cm_info();
23e5104a 3060 $result->name = $feedback->name;
b54bcddd 3061
438c01d0
MG
3062 if ($coursemodule->showdescription) {
3063 // Convert intro to html. Do not filter cached version, filters run at display time.
3064 $result->content = format_module_intro('feedback', $feedback, $coursemodule->id, false);
3065 }
3066
b54bcddd
JD
3067 // Populate the custom completion rules as key => value pairs, but only if the completion mode is 'automatic'.
3068 if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) {
3069 $result->customdata['customcompletionrules']['completionsubmit'] = $feedback->completionsubmit;
3070 }
aa708b55
MG
3071 // Populate some other values that can be used in calendar or on dashboard.
3072 if ($feedback->timeopen) {
3073 $result->customdata['timeopen'] = $feedback->timeopen;
3074 }
3075 if ($feedback->timeclose) {
3076 $result->customdata['timeclose'] = $feedback->timeclose;
3077 }
3078 if ($feedback->anonymous) {
3079 $result->customdata['anonymous'] = $feedback->anonymous;
3080 }
b54bcddd
JD
3081
3082 return $result;
3083}
3084
3085/**
3086 * Callback which returns human-readable strings describing the active completion custom rules for the module instance.
3087 *
7f53e8aa 3088 * @param cm_info|stdClass $cm object with fields ->completion and ->customdata['customcompletionrules']
b54bcddd
JD
3089 * @return array $descriptions the array of descriptions for the custom rules.
3090 */
3091function mod_feedback_get_completion_active_rule_descriptions($cm) {
3092 // Values will be present in cm_info, and we assume these are up to date.
7f53e8aa 3093 if (empty($cm->customdata['customcompletionrules'])
b54bcddd
JD
3094 || $cm->completion != COMPLETION_TRACKING_AUTOMATIC) {
3095 return [];
3096 }
3097
3098 $descriptions = [];
3099 foreach ($cm->customdata['customcompletionrules'] as $key => $val) {
3100 switch ($key) {
3101 case 'completionsubmit':
3102 if (empty($val)) {
3103 continue;
3104 }
3105 $descriptions[] = get_string('completionsubmit', 'feedback');
3106 break;
3107 default:
3108 break;
3109 }
3110 }
3111 return $descriptions;
3112}
83d4635d
RW
3113
3114/**
3115 * This function calculates the minimum and maximum cutoff values for the timestart of
3116 * the given event.
3117 *
3118 * It will return an array with two values, the first being the minimum cutoff value and
3119 * the second being the maximum cutoff value. Either or both values can be null, which
3120 * indicates there is no minimum or maximum, respectively.
3121 *
3122 * If a cutoff is required then the function must return an array containing the cutoff
3123 * timestamp and error string to display to the user if the cutoff value is violated.
3124 *
3125 * A minimum and maximum cutoff return value will look like:
3126 * [
3127 * [1505704373, 'The due date must be after the sbumission start date'],
3128 * [1506741172, 'The due date must be before the cutoff date']
3129 * ]
3130 *
3131 * @param calendar_event $event The calendar event to get the time range for
478b1d19 3132 * @param stdClass $instance The module instance to get the range from
83d4635d
RW
3133 * @return array
3134 */
478b1d19 3135function mod_feedback_core_calendar_get_valid_event_timestart_range(\calendar_event $event, \stdClass $instance) {
83d4635d
RW
3136 $mindate = null;
3137 $maxdate = null;