Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 38 and 311] [Versions 39 and 311]

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