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