"Fossies" - the Fresh Open Source Software Archive

Member "moodle/mod/feedback/lib.php" (6 Sep 2019, 110914 Bytes) of package /linux/www/moodle-3.6.6.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "lib.php" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.6.5_vs_3.6.6.

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