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