MDL-40047 events: Add unit tests for feedback events
[moodle.git] / mod / feedback / lib.php
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/>.
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  */
26 /** Include eventslib.php */
27 require_once($CFG->libdir.'/eventslib.php');
28 /** Include calendar/lib.php */
29 require_once($CFG->dirroot.'/calendar/lib.php');
30 // Include forms lib.
31 require_once($CFG->libdir.'/formslib.php');
33 define('FEEDBACK_ANONYMOUS_YES', 1);
34 define('FEEDBACK_ANONYMOUS_NO', 2);
35 define('FEEDBACK_MIN_ANONYMOUS_COUNT_IN_GROUP', 2);
36 define('FEEDBACK_DECIMAL', '.');
37 define('FEEDBACK_THOUSAND', ',');
38 define('FEEDBACK_RESETFORM_RESET', 'feedback_reset_data_');
39 define('FEEDBACK_RESETFORM_DROP', 'feedback_drop_feedback_');
40 define('FEEDBACK_MAX_PIX_LENGTH', '400'); //max. Breite des grafischen Balkens in der Auswertung
41 define('FEEDBACK_DEFAULT_PAGE_COUNT', 20);
43 /**
44  * Returns all other caps used in module.
45  *
46  * @return array
47  */
48 function feedback_get_extra_capabilities() {
49     return array('moodle/site:accessallgroups');
50 }
52 /**
53  * @uses FEATURE_GROUPS
54  * @uses FEATURE_GROUPINGS
55  * @uses FEATURE_GROUPMEMBERSONLY
56  * @uses FEATURE_MOD_INTRO
57  * @uses FEATURE_COMPLETION_TRACKS_VIEWS
58  * @uses FEATURE_GRADE_HAS_GRADE
59  * @uses FEATURE_GRADE_OUTCOMES
60  * @param string $feature FEATURE_xx constant for requested feature
61  * @return mixed True if module supports feature, null if doesn't know
62  */
63 function feedback_supports($feature) {
64     switch($feature) {
65         case FEATURE_GROUPS:                  return true;
66         case FEATURE_GROUPINGS:               return true;
67         case FEATURE_GROUPMEMBERSONLY:        return true;
68         case FEATURE_MOD_INTRO:               return true;
69         case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
70         case FEATURE_COMPLETION_HAS_RULES:    return true;
71         case FEATURE_GRADE_HAS_GRADE:         return false;
72         case FEATURE_GRADE_OUTCOMES:          return false;
73         case FEATURE_BACKUP_MOODLE2:          return true;
74         case FEATURE_SHOW_DESCRIPTION:        return true;
76         default: return null;
77     }
78 }
80 /**
81  * this will create a new instance and return the id number
82  * of the new instance.
83  *
84  * @global object
85  * @param object $feedback the object given by mod_feedback_mod_form
86  * @return int
87  */
88 function feedback_add_instance($feedback) {
89     global $DB;
91     $feedback->timemodified = time();
92     $feedback->id = '';
94     if (empty($feedback->site_after_submit)) {
95         $feedback->site_after_submit = '';
96     }
98     //saving the feedback in db
99     $feedbackid = $DB->insert_record("feedback", $feedback);
101     $feedback->id = $feedbackid;
103     feedback_set_events($feedback);
105     if (!isset($feedback->coursemodule)) {
106         $cm = get_coursemodule_from_id('feedback', $feedback->id);
107         $feedback->coursemodule = $cm->id;
108     }
109     $context = context_module::instance($feedback->coursemodule);
111     $editoroptions = feedback_get_editor_options();
113     // process the custom wysiwyg editor in page_after_submit
114     if ($draftitemid = $feedback->page_after_submit_editor['itemid']) {
115         $feedback->page_after_submit = file_save_draft_area_files($draftitemid, $context->id,
116                                                     'mod_feedback', 'page_after_submit',
117                                                     0, $editoroptions,
118                                                     $feedback->page_after_submit_editor['text']);
120         $feedback->page_after_submitformat = $feedback->page_after_submit_editor['format'];
121     }
122     $DB->update_record('feedback', $feedback);
124     return $feedbackid;
127 /**
128  * this will update a given instance
129  *
130  * @global object
131  * @param object $feedback the object given by mod_feedback_mod_form
132  * @return boolean
133  */
134 function feedback_update_instance($feedback) {
135     global $DB;
137     $feedback->timemodified = time();
138     $feedback->id = $feedback->instance;
140     if (empty($feedback->site_after_submit)) {
141         $feedback->site_after_submit = '';
142     }
144     //save the feedback into the db
145     $DB->update_record("feedback", $feedback);
147     //create or update the new events
148     feedback_set_events($feedback);
150     $context = context_module::instance($feedback->coursemodule);
152     $editoroptions = feedback_get_editor_options();
154     // process the custom wysiwyg editor in page_after_submit
155     if ($draftitemid = $feedback->page_after_submit_editor['itemid']) {
156         $feedback->page_after_submit = file_save_draft_area_files($draftitemid, $context->id,
157                                                     'mod_feedback', 'page_after_submit',
158                                                     0, $editoroptions,
159                                                     $feedback->page_after_submit_editor['text']);
161         $feedback->page_after_submitformat = $feedback->page_after_submit_editor['format'];
162     }
163     $DB->update_record('feedback', $feedback);
165     return true;
168 /**
169  * Serves the files included in feedback items like label. Implements needed access control ;-)
170  *
171  * There are two situations in general where the files will be sent.
172  * 1) filearea = item, 2) filearea = template
173  *
174  * @package  mod_feedback
175  * @category files
176  * @param stdClass $course course object
177  * @param stdClass $cm course module object
178  * @param stdClass $context context object
179  * @param string $filearea file area
180  * @param array $args extra arguments
181  * @param bool $forcedownload whether or not force download
182  * @param array $options additional options affecting the file serving
183  * @return bool false if file not found, does not return if found - justsend the file
184  */
185 function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
186     global $CFG, $DB;
188     if ($filearea === 'item' or $filearea === 'template') {
189         $itemid = (int)array_shift($args);
190         //get the item what includes the file
191         if (!$item = $DB->get_record('feedback_item', array('id'=>$itemid))) {
192             return false;
193         }
194         $feedbackid = $item->feedback;
195         $templateid = $item->template;
196     }
198     if ($filearea === 'page_after_submit' or $filearea === 'item') {
199         if (! $feedback = $DB->get_record("feedback", array("id"=>$cm->instance))) {
200             return false;
201         }
203         $feedbackid = $feedback->id;
205         //if the filearea is "item" so we check the permissions like view/complete the feedback
206         $canload = false;
207         //first check whether the user has the complete capability
208         if (has_capability('mod/feedback:complete', $context)) {
209             $canload = true;
210         }
212         //now we check whether the user has the view capability
213         if (has_capability('mod/feedback:view', $context)) {
214             $canload = true;
215         }
217         //if the feedback is on frontpage and anonymous and the fullanonymous is allowed
218         //so the file can be loaded too.
219         if (isset($CFG->feedback_allowfullanonymous)
220                     AND $CFG->feedback_allowfullanonymous
221                     AND $course->id == SITEID
222                     AND $feedback->anonymous == FEEDBACK_ANONYMOUS_YES ) {
223             $canload = true;
224         }
226         if (!$canload) {
227             return false;
228         }
229     } else if ($filearea === 'template') { //now we check files in templates
230         if (!$template = $DB->get_record('feedback_template', array('id'=>$templateid))) {
231             return false;
232         }
234         //if the file is not public so the capability edititems has to be there
235         if (!$template->ispublic) {
236             if (!has_capability('mod/feedback:edititems', $context)) {
237                 return false;
238             }
239         } else { //on public templates, at least the user has to be logged in
240             if (!isloggedin()) {
241                 return false;
242             }
243         }
244     } else {
245         return false;
246     }
248     if ($context->contextlevel == CONTEXT_MODULE) {
249         if ($filearea !== 'item' and $filearea !== 'page_after_submit') {
250             return false;
251         }
252     }
254     if ($context->contextlevel == CONTEXT_COURSE || $context->contextlevel == CONTEXT_SYSTEM) {
255         if ($filearea !== 'template') {
256             return false;
257         }
258     }
260     $relativepath = implode('/', $args);
261     if ($filearea === 'page_after_submit') {
262         $fullpath = "/{$context->id}/mod_feedback/$filearea/$relativepath";
263     } else {
264         $fullpath = "/{$context->id}/mod_feedback/$filearea/{$item->id}/$relativepath";
265     }
267     $fs = get_file_storage();
269     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
270         return false;
271     }
273     // finally send the file
274     send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security!
276     return false;
279 /**
280  * this will delete a given instance.
281  * all referenced data also will be deleted
282  *
283  * @global object
284  * @param int $id the instanceid of feedback
285  * @return boolean
286  */
287 function feedback_delete_instance($id) {
288     global $DB;
290     //get all referenced items
291     $feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$id));
293     //deleting all referenced items and values
294     if (is_array($feedbackitems)) {
295         foreach ($feedbackitems as $feedbackitem) {
296             $DB->delete_records("feedback_value", array("item"=>$feedbackitem->id));
297             $DB->delete_records("feedback_valuetmp", array("item"=>$feedbackitem->id));
298         }
299         if ($delitems = $DB->get_records("feedback_item", array("feedback"=>$id))) {
300             foreach ($delitems as $delitem) {
301                 feedback_delete_item($delitem->id, false);
302             }
303         }
304     }
306     //deleting the referenced tracking data
307     $DB->delete_records('feedback_tracking', array('feedback'=>$id));
309     //deleting the completeds
310     $DB->delete_records("feedback_completed", array("feedback"=>$id));
312     //deleting the unfinished completeds
313     $DB->delete_records("feedback_completedtmp", array("feedback"=>$id));
315     //deleting old events
316     $DB->delete_records('event', array('modulename'=>'feedback', 'instance'=>$id));
317     return $DB->delete_records("feedback", array("id"=>$id));
320 /**
321  * this is called after deleting all instances if the course will be deleted.
322  * only templates have to be deleted
323  *
324  * @global object
325  * @param object $course
326  * @return boolean
327  */
328 function feedback_delete_course($course) {
329     global $DB;
331     //delete all templates of given course
332     return $DB->delete_records('feedback_template', array('course'=>$course->id));
335 /**
336  * Return a small object with summary information about what a
337  * user has done with a given particular instance of this module
338  * Used for user activity reports.
339  * $return->time = the time they did it
340  * $return->info = a short text description
341  *
342  * @param object $course
343  * @param object $user
344  * @param object $mod
345  * @param object $feedback
346  * @return object
347  */
348 function feedback_user_outline($course, $user, $mod, $feedback) {
349     return null;
352 /**
353  * Returns all users who has completed a specified feedback since a given time
354  * many thanks to Manolescu Dorel, who contributed these two functions
355  *
356  * @global object
357  * @global object
358  * @global object
359  * @global object
360  * @uses CONTEXT_MODULE
361  * @param array $activities Passed by reference
362  * @param int $index Passed by reference
363  * @param int $timemodified Timestamp
364  * @param int $courseid
365  * @param int $cmid
366  * @param int $userid
367  * @param int $groupid
368  * @return void
369  */
370 function feedback_get_recent_mod_activity(&$activities, &$index,
371                                           $timemodified, $courseid,
372                                           $cmid, $userid="", $groupid="") {
374     global $CFG, $COURSE, $USER, $DB;
376     if ($COURSE->id == $courseid) {
377         $course = $COURSE;
378     } else {
379         $course = $DB->get_record('course', array('id'=>$courseid));
380     }
382     $modinfo = get_fast_modinfo($course);
384     $cm = $modinfo->cms[$cmid];
386     $sqlargs = array();
388     //TODO: user user_picture::fields;
389     $sql = " SELECT fk . * , fc . * , u.firstname, u.lastname, u.email, u.picture, u.email
390                                             FROM {feedback_completed} fc
391                                                 JOIN {feedback} fk ON fk.id = fc.feedback
392                                                 JOIN {user} u ON u.id = fc.userid ";
394     if ($groupid) {
395         $sql .= " JOIN {groups_members} gm ON  gm.userid=u.id ";
396     }
398     $sql .= " WHERE fc.timemodified > ? AND fk.id = ? ";
399     $sqlargs[] = $timemodified;
400     $sqlargs[] = $cm->instance;
402     if ($userid) {
403         $sql .= " AND u.id = ? ";
404         $sqlargs[] = $userid;
405     }
407     if ($groupid) {
408         $sql .= " AND gm.groupid = ? ";
409         $sqlargs[] = $groupid;
410     }
412     if (!$feedbackitems = $DB->get_records_sql($sql, $sqlargs)) {
413         return;
414     }
416     $cm_context = context_module::instance($cm->id);
418     if (!has_capability('mod/feedback:view', $cm_context)) {
419         return;
420     }
422     $accessallgroups = has_capability('moodle/site:accessallgroups', $cm_context);
423     $viewfullnames   = has_capability('moodle/site:viewfullnames', $cm_context);
424     $groupmode       = groups_get_activity_groupmode($cm, $course);
426     if (is_null($modinfo->groups)) {
427         // load all my groups and cache it in modinfo
428         $modinfo->groups = groups_get_user_groups($course->id);
429     }
431     $aname = format_string($cm->name, true);
432     foreach ($feedbackitems as $feedbackitem) {
433         if ($feedbackitem->userid != $USER->id) {
435             if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
436                 $usersgroups = groups_get_all_groups($course->id,
437                                                      $feedbackitem->userid,
438                                                      $cm->groupingid);
439                 if (!is_array($usersgroups)) {
440                     continue;
441                 }
442                 $usersgroups = array_keys($usersgroups);
443                 $intersect = array_intersect($usersgroups, $modinfo->groups[$cm->id]);
444                 if (empty($intersect)) {
445                     continue;
446                 }
447             }
448         }
450         $tmpactivity = new stdClass();
452         $tmpactivity->type      = 'feedback';
453         $tmpactivity->cmid      = $cm->id;
454         $tmpactivity->name      = $aname;
455         $tmpactivity->sectionnum= $cm->sectionnum;
456         $tmpactivity->timestamp = $feedbackitem->timemodified;
458         $tmpactivity->content = new stdClass();
459         $tmpactivity->content->feedbackid = $feedbackitem->id;
460         $tmpactivity->content->feedbackuserid = $feedbackitem->userid;
462         $userfields = explode(',', user_picture::fields());
463         $tmpactivity->user = new stdClass();
464         foreach ($userfields as $userfield) {
465             if ($userfield == 'id') {
466                 $tmpactivity->user->{$userfield} = $feedbackitem->userid; // aliased in SQL above
467             } else {
468                 if (!empty($feedbackitem->{$userfield})) {
469                     $tmpactivity->user->{$userfield} = $feedbackitem->{$userfield};
470                 } else {
471                     $tmpactivity->user->{$userfield} = null;
472                 }
473             }
474         }
475         $tmpactivity->user->fullname = fullname($feedbackitem, $viewfullnames);
477         $activities[$index++] = $tmpactivity;
478     }
480     return;
483 /**
484  * Prints all users who has completed a specified feedback since a given time
485  * many thanks to Manolescu Dorel, who contributed these two functions
486  *
487  * @global object
488  * @param object $activity
489  * @param int $courseid
490  * @param string $detail
491  * @param array $modnames
492  * @return void Output is echo'd
493  */
494 function feedback_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
495     global $CFG, $OUTPUT;
497     echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">';
499     echo "<tr><td class=\"userpicture\" valign=\"top\">";
500     echo $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid));
501     echo "</td><td>";
503     if ($detail) {
504         $modname = $modnames[$activity->type];
505         echo '<div class="title">';
506         echo "<img src=\"" . $OUTPUT->pix_url('icon', $activity->type) . "\" ".
507              "class=\"icon\" alt=\"$modname\" />";
508         echo "<a href=\"$CFG->wwwroot/mod/feedback/view.php?id={$activity->cmid}\">{$activity->name}</a>";
509         echo '</div>';
510     }
512     echo '<div class="title">';
513     echo '</div>';
515     echo '<div class="user">';
516     echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->id}&amp;course=$courseid\">"
517          ."{$activity->user->fullname}</a> - ".userdate($activity->timestamp);
518     echo '</div>';
520     echo "</td></tr></table>";
522     return;
525 /**
526  * Obtains the automatic completion state for this feedback based on the condition
527  * in feedback settings.
528  *
529  * @param object $course Course
530  * @param object $cm Course-module
531  * @param int $userid User ID
532  * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
533  * @return bool True if completed, false if not, $type if conditions not set.
534  */
535 function feedback_get_completion_state($course, $cm, $userid, $type) {
536     global $CFG, $DB;
538     // Get feedback details
539     $feedback = $DB->get_record('feedback', array('id'=>$cm->instance), '*', MUST_EXIST);
541     // If completion option is enabled, evaluate it and return true/false
542     if ($feedback->completionsubmit) {
543         $params = array('userid'=>$userid, 'feedback'=>$feedback->id);
544         return $DB->record_exists('feedback_tracking', $params);
545     } else {
546         // Completion option is not enabled so just return $type
547         return $type;
548     }
552 /**
553  * Print a detailed representation of what a  user has done with
554  * a given particular instance of this module, for user activity reports.
555  *
556  * @param object $course
557  * @param object $user
558  * @param object $mod
559  * @param object $feedback
560  * @return bool
561  */
562 function feedback_user_complete($course, $user, $mod, $feedback) {
563     return true;
566 /**
567  * @return bool true
568  */
569 function feedback_cron () {
570     return true;
573 /**
574  * @return bool false
575  */
576 function feedback_scale_used ($feedbackid, $scaleid) {
577     return false;
580 /**
581  * Checks if scale is being used by any instance of feedback
582  *
583  * This is used to find out if scale used anywhere
584  * @param $scaleid int
585  * @return boolean True if the scale is used by any assignment
586  */
587 function feedback_scale_used_anywhere($scaleid) {
588     return false;
591 /**
592  * @return array
593  */
594 function feedback_get_view_actions() {
595     return array('view', 'view all');
598 /**
599  * @return array
600  */
601 function feedback_get_post_actions() {
602     return array('submit');
605 /**
606  * This function is used by the reset_course_userdata function in moodlelib.
607  * This function will remove all responses from the specified feedback
608  * and clean up any related data.
609  *
610  * @global object
611  * @global object
612  * @uses FEEDBACK_RESETFORM_RESET
613  * @uses FEEDBACK_RESETFORM_DROP
614  * @param object $data the data submitted from the reset course.
615  * @return array status array
616  */
617 function feedback_reset_userdata($data) {
618     global $CFG, $DB;
620     $resetfeedbacks = array();
621     $dropfeedbacks = array();
622     $status = array();
623     $componentstr = get_string('modulenameplural', 'feedback');
625     //get the relevant entries from $data
626     foreach ($data as $key => $value) {
627         switch(true) {
628             case substr($key, 0, strlen(FEEDBACK_RESETFORM_RESET)) == FEEDBACK_RESETFORM_RESET:
629                 if ($value == 1) {
630                     $templist = explode('_', $key);
631                     if (isset($templist[3])) {
632                         $resetfeedbacks[] = intval($templist[3]);
633                     }
634                 }
635             break;
636             case substr($key, 0, strlen(FEEDBACK_RESETFORM_DROP)) == FEEDBACK_RESETFORM_DROP:
637                 if ($value == 1) {
638                     $templist = explode('_', $key);
639                     if (isset($templist[3])) {
640                         $dropfeedbacks[] = intval($templist[3]);
641                     }
642                 }
643             break;
644         }
645     }
647     //reset the selected feedbacks
648     foreach ($resetfeedbacks as $id) {
649         $feedback = $DB->get_record('feedback', array('id'=>$id));
650         feedback_delete_all_completeds($id);
651         $status[] = array('component'=>$componentstr.':'.$feedback->name,
652                         'item'=>get_string('resetting_data', 'feedback'),
653                         'error'=>false);
654     }
656     return $status;
659 /**
660  * Called by course/reset.php
661  *
662  * @global object
663  * @uses FEEDBACK_RESETFORM_RESET
664  * @param object $mform form passed by reference
665  */
666 function feedback_reset_course_form_definition(&$mform) {
667     global $COURSE, $DB;
669     $mform->addElement('header', 'feedbackheader', get_string('modulenameplural', 'feedback'));
671     if (!$feedbacks = $DB->get_records('feedback', array('course'=>$COURSE->id), 'name')) {
672         return;
673     }
675     $mform->addElement('static', 'hint', get_string('resetting_data', 'feedback'));
676     foreach ($feedbacks as $feedback) {
677         $mform->addElement('checkbox', FEEDBACK_RESETFORM_RESET.$feedback->id, $feedback->name);
678     }
681 /**
682  * Course reset form defaults.
683  *
684  * @global object
685  * @uses FEEDBACK_RESETFORM_RESET
686  * @param object $course
687  */
688 function feedback_reset_course_form_defaults($course) {
689     global $DB;
691     $return = array();
692     if (!$feedbacks = $DB->get_records('feedback', array('course'=>$course->id), 'name')) {
693         return;
694     }
695     foreach ($feedbacks as $feedback) {
696         $return[FEEDBACK_RESETFORM_RESET.$feedback->id] = true;
697     }
698     return $return;
701 /**
702  * Called by course/reset.php and shows the formdata by coursereset.
703  * it prints checkboxes for each feedback available at the given course
704  * there are two checkboxes:
705  * 1) delete userdata and keep the feedback
706  * 2) delete userdata and drop the feedback
707  *
708  * @global object
709  * @uses FEEDBACK_RESETFORM_RESET
710  * @uses FEEDBACK_RESETFORM_DROP
711  * @param object $course
712  * @return void
713  */
714 function feedback_reset_course_form($course) {
715     global $DB, $OUTPUT;
717     echo get_string('resetting_feedbacks', 'feedback'); echo ':<br />';
718     if (!$feedbacks = $DB->get_records('feedback', array('course'=>$course->id), 'name')) {
719         return;
720     }
722     foreach ($feedbacks as $feedback) {
723         echo '<p>';
724         echo get_string('name', 'feedback').': '.$feedback->name.'<br />';
725         echo html_writer::checkbox(FEEDBACK_RESETFORM_RESET.$feedback->id,
726                                 1, true,
727                                 get_string('resetting_data', 'feedback'));
728         echo '<br />';
729         echo html_writer::checkbox(FEEDBACK_RESETFORM_DROP.$feedback->id,
730                                 1, false,
731                                 get_string('drop_feedback', 'feedback'));
732         echo '</p>';
733     }
736 /**
737  * This gets an array with default options for the editor
738  *
739  * @return array the options
740  */
741 function feedback_get_editor_options() {
742     return array('maxfiles' => EDITOR_UNLIMITED_FILES,
743                 'trusttext'=>true);
746 /**
747  * This creates new events given as timeopen and closeopen by $feedback.
748  *
749  * @global object
750  * @param object $feedback
751  * @return void
752  */
753 function feedback_set_events($feedback) {
754     global $DB;
756     // adding the feedback to the eventtable (I have seen this at quiz-module)
757     $DB->delete_records('event', array('modulename'=>'feedback', 'instance'=>$feedback->id));
759     if (!isset($feedback->coursemodule)) {
760         $cm = get_coursemodule_from_id('feedback', $feedback->id);
761         $feedback->coursemodule = $cm->id;
762     }
764     // the open-event
765     if ($feedback->timeopen > 0) {
766         $event = new stdClass();
767         $event->name         = get_string('start', 'feedback').' '.$feedback->name;
768         $event->description  = format_module_intro('feedback', $feedback, $feedback->coursemodule);
769         $event->courseid     = $feedback->course;
770         $event->groupid      = 0;
771         $event->userid       = 0;
772         $event->modulename   = 'feedback';
773         $event->instance     = $feedback->id;
774         $event->eventtype    = 'open';
775         $event->timestart    = $feedback->timeopen;
776         $event->visible      = instance_is_visible('feedback', $feedback);
777         if ($feedback->timeclose > 0) {
778             $event->timeduration = ($feedback->timeclose - $feedback->timeopen);
779         } else {
780             $event->timeduration = 0;
781         }
783         calendar_event::create($event);
784     }
786     // the close-event
787     if ($feedback->timeclose > 0) {
788         $event = new stdClass();
789         $event->name         = get_string('stop', 'feedback').' '.$feedback->name;
790         $event->description  = format_module_intro('feedback', $feedback, $feedback->coursemodule);
791         $event->courseid     = $feedback->course;
792         $event->groupid      = 0;
793         $event->userid       = 0;
794         $event->modulename   = 'feedback';
795         $event->instance     = $feedback->id;
796         $event->eventtype    = 'close';
797         $event->timestart    = $feedback->timeclose;
798         $event->visible      = instance_is_visible('feedback', $feedback);
799         $event->timeduration = 0;
801         calendar_event::create($event);
802     }
805 /**
806  * this function is called by {@link feedback_delete_userdata()}
807  * it drops the feedback-instance from the course_module table
808  *
809  * @global object
810  * @param int $id the id from the coursemodule
811  * @return boolean
812  */
813 function feedback_delete_course_module($id) {
814     global $DB;
816     if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
817         return true;
818     }
819     return $DB->delete_records('course_modules', array('id'=>$cm->id));
824 ////////////////////////////////////////////////
825 //functions to handle capabilities
826 ////////////////////////////////////////////////
828 /**
829  * returns the context-id related to the given coursemodule-id
830  *
831  * @staticvar object $context
832  * @param int $cmid the coursemodule-id
833  * @return object $context
834  */
835 function feedback_get_context($cmid) {
836     static $context;
838     if (isset($context)) {
839         return $context;
840     }
842     $context = context_module::instance($cmid);
843     return $context;
846 /**
847  *  returns true if the current role is faked by switching role feature
848  *
849  * @global object
850  * @return boolean
851  */
852 function feedback_check_is_switchrole() {
853     global $USER;
854     if (isset($USER->switchrole) AND
855             is_array($USER->switchrole) AND
856             count($USER->switchrole) > 0) {
858         return true;
859     }
860     return false;
863 /**
864  * count users which have not completed the feedback
865  *
866  * @global object
867  * @uses CONTEXT_MODULE
868  * @param object $cm
869  * @param int $group single groupid
870  * @param string $sort
871  * @param int $startpage
872  * @param int $pagecount
873  * @return object the userrecords
874  */
875 function feedback_get_incomplete_users($cm,
876                                        $group = false,
877                                        $sort = '',
878                                        $startpage = false,
879                                        $pagecount = false) {
881     global $DB;
883     $context = context_module::instance($cm->id);
885     //first get all user who can complete this feedback
886     $cap = 'mod/feedback:complete';
887     $fields = 'u.id, u.username';
888     if (!$allusers = get_users_by_capability($context,
889                                             $cap,
890                                             $fields,
891                                             $sort,
892                                             '',
893                                             '',
894                                             $group,
895                                             '',
896                                             true)) {
897         return false;
898     }
899     $allusers = array_keys($allusers);
901     //now get all completeds
902     $params = array('feedback'=>$cm->instance);
903     if (!$completedusers = $DB->get_records_menu('feedback_completed', $params, '', 'userid,id')) {
904         return $allusers;
905     }
906     $completedusers = array_keys($completedusers);
908     //now strike all completedusers from allusers
909     $allusers = array_diff($allusers, $completedusers);
911     //for paging I use array_slice()
912     if ($startpage !== false AND $pagecount !== false) {
913         $allusers = array_slice($allusers, $startpage, $pagecount);
914     }
916     return $allusers;
919 /**
920  * count users which have not completed the feedback
921  *
922  * @global object
923  * @param object $cm
924  * @param int $group single groupid
925  * @return int count of userrecords
926  */
927 function feedback_count_incomplete_users($cm, $group = false) {
928     if ($allusers = feedback_get_incomplete_users($cm, $group)) {
929         return count($allusers);
930     }
931     return 0;
934 /**
935  * count users which have completed a feedback
936  *
937  * @global object
938  * @uses FEEDBACK_ANONYMOUS_NO
939  * @param object $cm
940  * @param int $group single groupid
941  * @return int count of userrecords
942  */
943 function feedback_count_complete_users($cm, $group = false) {
944     global $DB;
946     $params = array(FEEDBACK_ANONYMOUS_NO, $cm->instance);
948     $fromgroup = '';
949     $wheregroup = '';
950     if ($group) {
951         $fromgroup = ', {groups_members} g';
952         $wheregroup = ' AND g.groupid = ? AND g.userid = c.userid';
953         $params[] = $group;
954     }
956     $sql = 'SELECT COUNT(u.id) FROM {user} u, {feedback_completed} c'.$fromgroup.'
957               WHERE anonymous_response = ? AND u.id = c.userid AND c.feedback = ?
958               '.$wheregroup;
960     return $DB->count_records_sql($sql, $params);
964 /**
965  * get users which have completed a feedback
966  *
967  * @global object
968  * @uses CONTEXT_MODULE
969  * @uses FEEDBACK_ANONYMOUS_NO
970  * @param object $cm
971  * @param int $group single groupid
972  * @param string $where a sql where condition (must end with " AND ")
973  * @param array parameters used in $where
974  * @param string $sort a table field
975  * @param int $startpage
976  * @param int $pagecount
977  * @return object the userrecords
978  */
979 function feedback_get_complete_users($cm,
980                                      $group = false,
981                                      $where = '',
982                                      array $params = null,
983                                      $sort = '',
984                                      $startpage = false,
985                                      $pagecount = false) {
987     global $DB;
989     $context = context_module::instance($cm->id);
991     $params = (array)$params;
993     $params['anon'] = FEEDBACK_ANONYMOUS_NO;
994     $params['instance'] = $cm->instance;
996     $fromgroup = '';
997     $wheregroup = '';
998     if ($group) {
999         $fromgroup = ', {groups_members} g';
1000         $wheregroup = ' AND g.groupid = :group AND g.userid = c.userid';
1001         $params['group'] = $group;
1002     }
1004     if ($sort) {
1005         $sortsql = ' ORDER BY '.$sort;
1006     } else {
1007         $sortsql = '';
1008     }
1010     $ufields = user_picture::fields('u');
1011     $sql = 'SELECT DISTINCT '.$ufields.', c.timemodified as completed_timemodified
1012             FROM {user} u, {feedback_completed} c '.$fromgroup.'
1013             WHERE '.$where.' anonymous_response = :anon
1014                 AND u.id = c.userid
1015                 AND c.feedback = :instance
1016               '.$wheregroup.$sortsql;
1018     if ($startpage === false OR $pagecount === false) {
1019         $startpage = false;
1020         $pagecount = false;
1021     }
1022     return $DB->get_records_sql($sql, $params, $startpage, $pagecount);
1025 /**
1026  * get users which have the viewreports-capability
1027  *
1028  * @uses CONTEXT_MODULE
1029  * @param int $cmid
1030  * @param mixed $groups single groupid or array of groupids - group(s) user is in
1031  * @return object the userrecords
1032  */
1033 function feedback_get_viewreports_users($cmid, $groups = false) {
1035     $context = context_module::instance($cmid);
1037     //description of the call below:
1038     //get_users_by_capability($context, $capability, $fields='', $sort='', $limitfrom='',
1039     //                          $limitnum='', $groups='', $exceptions='', $doanything=true)
1040     return get_users_by_capability($context,
1041                             'mod/feedback:viewreports',
1042                             '',
1043                             'lastname',
1044                             '',
1045                             '',
1046                             $groups,
1047                             '',
1048                             false);
1051 /**
1052  * get users which have the receivemail-capability
1053  *
1054  * @uses CONTEXT_MODULE
1055  * @param int $cmid
1056  * @param mixed $groups single groupid or array of groupids - group(s) user is in
1057  * @return object the userrecords
1058  */
1059 function feedback_get_receivemail_users($cmid, $groups = false) {
1061     $context = context_module::instance($cmid);
1063     //description of the call below:
1064     //get_users_by_capability($context, $capability, $fields='', $sort='', $limitfrom='',
1065     //                          $limitnum='', $groups='', $exceptions='', $doanything=true)
1066     return get_users_by_capability($context,
1067                             'mod/feedback:receivemail',
1068                             '',
1069                             'lastname',
1070                             '',
1071                             '',
1072                             $groups,
1073                             '',
1074                             false);
1077 ////////////////////////////////////////////////
1078 //functions to handle the templates
1079 ////////////////////////////////////////////////
1080 ////////////////////////////////////////////////
1082 /**
1083  * creates a new template-record.
1084  *
1085  * @global object
1086  * @param int $courseid
1087  * @param string $name the name of template shown in the templatelist
1088  * @param int $ispublic 0:privat 1:public
1089  * @return int the new templateid
1090  */
1091 function feedback_create_template($courseid, $name, $ispublic = 0) {
1092     global $DB;
1094     $templ = new stdClass();
1095     $templ->course   = ($ispublic ? 0 : $courseid);
1096     $templ->name     = $name;
1097     $templ->ispublic = $ispublic;
1099     $templid = $DB->insert_record('feedback_template', $templ);
1100     return $DB->get_record('feedback_template', array('id'=>$templid));
1103 /**
1104  * creates new template items.
1105  * all items will be copied and the attribute feedback will be set to 0
1106  * and the attribute template will be set to the new templateid
1107  *
1108  * @global object
1109  * @uses CONTEXT_MODULE
1110  * @uses CONTEXT_COURSE
1111  * @param object $feedback
1112  * @param string $name the name of template shown in the templatelist
1113  * @param int $ispublic 0:privat 1:public
1114  * @return boolean
1115  */
1116 function feedback_save_as_template($feedback, $name, $ispublic = 0) {
1117     global $DB;
1118     $fs = get_file_storage();
1120     if (!$feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$feedback->id))) {
1121         return false;
1122     }
1124     if (!$newtempl = feedback_create_template($feedback->course, $name, $ispublic)) {
1125         return false;
1126     }
1128     //files in the template_item are in the context of the current course or
1129     //if the template is public the files are in the system context
1130     //files in the feedback_item are in the feedback_context of the feedback
1131     if ($ispublic) {
1132         $s_context = context_system::instance();
1133     } else {
1134         $s_context = context_course::instance($newtempl->course);
1135     }
1136     $cm = get_coursemodule_from_instance('feedback', $feedback->id);
1137     $f_context = context_module::instance($cm->id);
1139     //create items of this new template
1140     //depend items we are storing temporary in an mapping list array(new id => dependitem)
1141     //we also store a mapping of all items array(oldid => newid)
1142     $dependitemsmap = array();
1143     $itembackup = array();
1144     foreach ($feedbackitems as $item) {
1146         $t_item = clone($item);
1148         unset($t_item->id);
1149         $t_item->feedback = 0;
1150         $t_item->template     = $newtempl->id;
1151         $t_item->id = $DB->insert_record('feedback_item', $t_item);
1152         //copy all included files to the feedback_template filearea
1153         $itemfiles = $fs->get_area_files($f_context->id,
1154                                     'mod_feedback',
1155                                     'item',
1156                                     $item->id,
1157                                     "id",
1158                                     false);
1159         if ($itemfiles) {
1160             foreach ($itemfiles as $ifile) {
1161                 $file_record = new stdClass();
1162                 $file_record->contextid = $s_context->id;
1163                 $file_record->component = 'mod_feedback';
1164                 $file_record->filearea = 'template';
1165                 $file_record->itemid = $t_item->id;
1166                 $fs->create_file_from_storedfile($file_record, $ifile);
1167             }
1168         }
1170         $itembackup[$item->id] = $t_item->id;
1171         if ($t_item->dependitem) {
1172             $dependitemsmap[$t_item->id] = $t_item->dependitem;
1173         }
1175     }
1177     //remapping the dependency
1178     foreach ($dependitemsmap as $key => $dependitem) {
1179         $newitem = $DB->get_record('feedback_item', array('id'=>$key));
1180         $newitem->dependitem = $itembackup[$newitem->dependitem];
1181         $DB->update_record('feedback_item', $newitem);
1182     }
1184     return true;
1187 /**
1188  * deletes all feedback_items related to the given template id
1189  *
1190  * @global object
1191  * @uses CONTEXT_COURSE
1192  * @param object $template the template
1193  * @return void
1194  */
1195 function feedback_delete_template($template) {
1196     global $DB;
1198     //deleting the files from the item is done by feedback_delete_item
1199     if ($t_items = $DB->get_records("feedback_item", array("template"=>$template->id))) {
1200         foreach ($t_items as $t_item) {
1201             feedback_delete_item($t_item->id, false, $template);
1202         }
1203     }
1204     $DB->delete_records("feedback_template", array("id"=>$template->id));
1207 /**
1208  * creates new feedback_item-records from template.
1209  * if $deleteold is set true so the existing items of the given feedback will be deleted
1210  * if $deleteold is set false so the new items will be appanded to the old items
1211  *
1212  * @global object
1213  * @uses CONTEXT_COURSE
1214  * @uses CONTEXT_MODULE
1215  * @param object $feedback
1216  * @param int $templateid
1217  * @param boolean $deleteold
1218  */
1219 function feedback_items_from_template($feedback, $templateid, $deleteold = false) {
1220     global $DB, $CFG;
1222     require_once($CFG->libdir.'/completionlib.php');
1224     $fs = get_file_storage();
1226     if (!$template = $DB->get_record('feedback_template', array('id'=>$templateid))) {
1227         return false;
1228     }
1229     //get all templateitems
1230     if (!$templitems = $DB->get_records('feedback_item', array('template'=>$templateid))) {
1231         return false;
1232     }
1234     //files in the template_item are in the context of the current course
1235     //files in the feedback_item are in the feedback_context of the feedback
1236     if ($template->ispublic) {
1237         $s_context = context_system::instance();
1238     } else {
1239         $s_context = context_course::instance($feedback->course);
1240     }
1241     $course = $DB->get_record('course', array('id'=>$feedback->course));
1242     $cm = get_coursemodule_from_instance('feedback', $feedback->id);
1243     $f_context = context_module::instance($cm->id);
1245     //if deleteold then delete all old items before
1246     //get all items
1247     if ($deleteold) {
1248         if ($feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$feedback->id))) {
1249             //delete all items of this feedback
1250             foreach ($feedbackitems as $item) {
1251                 feedback_delete_item($item->id, false);
1252             }
1253             //delete tracking-data
1254             $DB->delete_records('feedback_tracking', array('feedback'=>$feedback->id));
1256             $params = array('feedback'=>$feedback->id);
1257             if ($completeds = $DB->get_records('feedback_completed', $params)) {
1258                 $completion = new completion_info($course);
1259                 foreach ($completeds as $completed) {
1260                     // Update completion state
1261                     if ($completion->is_enabled($cm) && $feedback->completionsubmit) {
1262                         $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
1263                     }
1264                     $DB->delete_records('feedback_completed', array('id'=>$completed->id));
1265                 }
1266             }
1267             $DB->delete_records('feedback_completedtmp', array('feedback'=>$feedback->id));
1268         }
1269         $positionoffset = 0;
1270     } else {
1271         //if the old items are kept the new items will be appended
1272         //therefor the new position has an offset
1273         $positionoffset = $DB->count_records('feedback_item', array('feedback'=>$feedback->id));
1274     }
1276     //create items of this new template
1277     //depend items we are storing temporary in an mapping list array(new id => dependitem)
1278     //we also store a mapping of all items array(oldid => newid)
1279     $dependitemsmap = array();
1280     $itembackup = array();
1281     foreach ($templitems as $t_item) {
1282         $item = clone($t_item);
1283         unset($item->id);
1284         $item->feedback = $feedback->id;
1285         $item->template = 0;
1286         $item->position = $item->position + $positionoffset;
1288         $item->id = $DB->insert_record('feedback_item', $item);
1290         //moving the files to the new item
1291         $templatefiles = $fs->get_area_files($s_context->id,
1292                                         'mod_feedback',
1293                                         'template',
1294                                         $t_item->id,
1295                                         "id",
1296                                         false);
1297         if ($templatefiles) {
1298             foreach ($templatefiles as $tfile) {
1299                 $file_record = new stdClass();
1300                 $file_record->contextid = $f_context->id;
1301                 $file_record->component = 'mod_feedback';
1302                 $file_record->filearea = 'item';
1303                 $file_record->itemid = $item->id;
1304                 $fs->create_file_from_storedfile($file_record, $tfile);
1305             }
1306         }
1308         $itembackup[$t_item->id] = $item->id;
1309         if ($item->dependitem) {
1310             $dependitemsmap[$item->id] = $item->dependitem;
1311         }
1312     }
1314     //remapping the dependency
1315     foreach ($dependitemsmap as $key => $dependitem) {
1316         $newitem = $DB->get_record('feedback_item', array('id'=>$key));
1317         $newitem->dependitem = $itembackup[$newitem->dependitem];
1318         $DB->update_record('feedback_item', $newitem);
1319     }
1322 /**
1323  * get the list of available templates.
1324  * if the $onlyown param is set true so only templates from own course will be served
1325  * this is important for droping templates
1326  *
1327  * @global object
1328  * @param object $course
1329  * @param string $onlyownorpublic
1330  * @return array the template recordsets
1331  */
1332 function feedback_get_template_list($course, $onlyownorpublic = '') {
1333     global $DB, $CFG;
1335     switch($onlyownorpublic) {
1336         case '':
1337             $templates = $DB->get_records_select('feedback_template',
1338                                                  'course = ? OR ispublic = 1',
1339                                                  array($course->id),
1340                                                  'name');
1341             break;
1342         case 'own':
1343             $templates = $DB->get_records('feedback_template',
1344                                           array('course'=>$course->id),
1345                                           'name');
1346             break;
1347         case 'public':
1348             $templates = $DB->get_records('feedback_template', array('ispublic'=>1), 'name');
1349             break;
1350     }
1351     return $templates;
1354 ////////////////////////////////////////////////
1355 //Handling der Items
1356 ////////////////////////////////////////////////
1357 ////////////////////////////////////////////////
1359 /**
1360  * load the lib.php from item-plugin-dir and returns the instance of the itemclass
1361  *
1362  * @global object
1363  * @param object $item
1364  * @return object the instanz of itemclass
1365  */
1366 function feedback_get_item_class($typ) {
1367     global $CFG;
1369     //get the class of item-typ
1370     $itemclass = 'feedback_item_'.$typ;
1371     //get the instance of item-class
1372     if (!class_exists($itemclass)) {
1373         require_once($CFG->dirroot.'/mod/feedback/item/'.$typ.'/lib.php');
1374     }
1375     return new $itemclass();
1378 /**
1379  * load the available item plugins from given subdirectory of $CFG->dirroot
1380  * the default is "mod/feedback/item"
1381  *
1382  * @global object
1383  * @param string $dir the subdir
1384  * @return array pluginnames as string
1385  */
1386 function feedback_load_feedback_items($dir = 'mod/feedback/item') {
1387     global $CFG;
1388     $names = get_list_of_plugins($dir);
1389     $ret_names = array();
1391     foreach ($names as $name) {
1392         require_once($CFG->dirroot.'/'.$dir.'/'.$name.'/lib.php');
1393         if (class_exists('feedback_item_'.$name)) {
1394             $ret_names[] = $name;
1395         }
1396     }
1397     return $ret_names;
1400 /**
1401  * load the available item plugins to use as dropdown-options
1402  *
1403  * @global object
1404  * @return array pluginnames as string
1405  */
1406 function feedback_load_feedback_items_options() {
1407     global $CFG;
1409     $feedback_options = array("pagebreak" => get_string('add_pagebreak', 'feedback'));
1411     if (!$feedback_names = feedback_load_feedback_items('mod/feedback/item')) {
1412         return array();
1413     }
1415     foreach ($feedback_names as $fn) {
1416         $feedback_options[$fn] = get_string($fn, 'feedback');
1417     }
1418     asort($feedback_options);
1419     $feedback_options = array_merge( array(' ' => get_string('select')), $feedback_options );
1420     return $feedback_options;
1423 /**
1424  * load the available items for the depend item dropdown list shown in the edit_item form
1425  *
1426  * @global object
1427  * @param object $feedback
1428  * @param object $item the item of the edit_item form
1429  * @return array all items except the item $item, labels and pagebreaks
1430  */
1431 function feedback_get_depend_candidates_for_item($feedback, $item) {
1432     global $DB;
1433     //all items for dependitem
1434     $where = "feedback = ? AND typ != 'pagebreak' AND hasvalue = 1";
1435     $params = array($feedback->id);
1436     if (isset($item->id) AND $item->id) {
1437         $where .= ' AND id != ?';
1438         $params[] = $item->id;
1439     }
1440     $dependitems = array(0 => get_string('choose'));
1441     $feedbackitems = $DB->get_records_select_menu('feedback_item',
1442                                                   $where,
1443                                                   $params,
1444                                                   'position',
1445                                                   'id, label');
1447     if (!$feedbackitems) {
1448         return $dependitems;
1449     }
1450     //adding the choose-option
1451     foreach ($feedbackitems as $key => $val) {
1452         $dependitems[$key] = $val;
1453     }
1454     return $dependitems;
1457 /**
1458  * creates a new item-record
1459  *
1460  * @global object
1461  * @param object $data the data from edit_item_form
1462  * @return int the new itemid
1463  */
1464 function feedback_create_item($data) {
1465     global $DB;
1467     $item = new stdClass();
1468     $item->feedback = $data->feedbackid;
1470     $item->template=0;
1471     if (isset($data->templateid)) {
1472             $item->template = intval($data->templateid);
1473     }
1475     $itemname = trim($data->itemname);
1476     $item->name = ($itemname ? $data->itemname : get_string('no_itemname', 'feedback'));
1478     if (!empty($data->itemlabel)) {
1479         $item->label = trim($data->itemlabel);
1480     } else {
1481         $item->label = get_string('no_itemlabel', 'feedback');
1482     }
1484     $itemobj = feedback_get_item_class($data->typ);
1485     $item->presentation = ''; //the date comes from postupdate() of the itemobj
1487     $item->hasvalue = $itemobj->get_hasvalue();
1489     $item->typ = $data->typ;
1490     $item->position = $data->position;
1492     $item->required=0;
1493     if (!empty($data->required)) {
1494         $item->required = $data->required;
1495     }
1497     $item->id = $DB->insert_record('feedback_item', $item);
1499     //move all itemdata to the data
1500     $data->id = $item->id;
1501     $data->feedback = $item->feedback;
1502     $data->name = $item->name;
1503     $data->label = $item->label;
1504     $data->required = $item->required;
1505     return $itemobj->postupdate($data);
1508 /**
1509  * save the changes of a given item.
1510  *
1511  * @global object
1512  * @param object $item
1513  * @return boolean
1514  */
1515 function feedback_update_item($item) {
1516     global $DB;
1517     return $DB->update_record("feedback_item", $item);
1520 /**
1521  * deletes an item and also deletes all related values
1522  *
1523  * @global object
1524  * @uses CONTEXT_MODULE
1525  * @param int $itemid
1526  * @param boolean $renumber should the kept items renumbered Yes/No
1527  * @param object $template if the template is given so the items are bound to it
1528  * @return void
1529  */
1530 function feedback_delete_item($itemid, $renumber = true, $template = false) {
1531     global $DB;
1533     $item = $DB->get_record('feedback_item', array('id'=>$itemid));
1535     //deleting the files from the item
1536     $fs = get_file_storage();
1538     if ($template) {
1539         if ($template->ispublic) {
1540             $context = context_system::instance();
1541         } else {
1542             $context = context_course::instance($template->course);
1543         }
1544         $templatefiles = $fs->get_area_files($context->id,
1545                                     'mod_feedback',
1546                                     'template',
1547                                     $item->id,
1548                                     "id",
1549                                     false);
1551         if ($templatefiles) {
1552             $fs->delete_area_files($context->id, 'mod_feedback', 'template', $item->id);
1553         }
1554     } else {
1555         if (!$cm = get_coursemodule_from_instance('feedback', $item->feedback)) {
1556             return false;
1557         }
1558         $context = context_module::instance($cm->id);
1560         $itemfiles = $fs->get_area_files($context->id,
1561                                     'mod_feedback',
1562                                     'item',
1563                                     $item->id,
1564                                     "id", false);
1566         if ($itemfiles) {
1567             $fs->delete_area_files($context->id, 'mod_feedback', 'item', $item->id);
1568         }
1569     }
1571     $DB->delete_records("feedback_value", array("item"=>$itemid));
1572     $DB->delete_records("feedback_valuetmp", array("item"=>$itemid));
1574     //remove all depends
1575     $DB->set_field('feedback_item', 'dependvalue', '', array('dependitem'=>$itemid));
1576     $DB->set_field('feedback_item', 'dependitem', 0, array('dependitem'=>$itemid));
1578     $DB->delete_records("feedback_item", array("id"=>$itemid));
1579     if ($renumber) {
1580         feedback_renumber_items($item->feedback);
1581     }
1584 /**
1585  * deletes all items of the given feedbackid
1586  *
1587  * @global object
1588  * @param int $feedbackid
1589  * @return void
1590  */
1591 function feedback_delete_all_items($feedbackid) {
1592     global $DB, $CFG;
1593     require_once($CFG->libdir.'/completionlib.php');
1595     if (!$feedback = $DB->get_record('feedback', array('id'=>$feedbackid))) {
1596         return false;
1597     }
1599     if (!$cm = get_coursemodule_from_instance('feedback', $feedback->id)) {
1600         return false;
1601     }
1603     if (!$course = $DB->get_record('course', array('id'=>$feedback->course))) {
1604         return false;
1605     }
1607     if (!$items = $DB->get_records('feedback_item', array('feedback'=>$feedbackid))) {
1608         return;
1609     }
1610     foreach ($items as $item) {
1611         feedback_delete_item($item->id, false);
1612     }
1613     if ($completeds = $DB->get_records('feedback_completed', array('feedback'=>$feedback->id))) {
1614         $completion = new completion_info($course);
1615         foreach ($completeds as $completed) {
1616             // Update completion state
1617             if ($completion->is_enabled($cm) && $feedback->completionsubmit) {
1618                 $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
1619             }
1620             $DB->delete_records('feedback_completed', array('id'=>$completed->id));
1621         }
1622     }
1624     $DB->delete_records('feedback_completedtmp', array('feedback'=>$feedbackid));
1628 /**
1629  * this function toggled the item-attribute required (yes/no)
1630  *
1631  * @global object
1632  * @param object $item
1633  * @return boolean
1634  */
1635 function feedback_switch_item_required($item) {
1636     global $DB, $CFG;
1638     $itemobj = feedback_get_item_class($item->typ);
1640     if ($itemobj->can_switch_require()) {
1641         $new_require_val = (int)!(bool)$item->required;
1642         $params = array('id'=>$item->id);
1643         $DB->set_field('feedback_item', 'required', $new_require_val, $params);
1644     }
1645     return true;
1648 /**
1649  * renumbers all items of the given feedbackid
1650  *
1651  * @global object
1652  * @param int $feedbackid
1653  * @return void
1654  */
1655 function feedback_renumber_items($feedbackid) {
1656     global $DB;
1658     $items = $DB->get_records('feedback_item', array('feedback'=>$feedbackid), 'position');
1659     $pos = 1;
1660     if ($items) {
1661         foreach ($items as $item) {
1662             $DB->set_field('feedback_item', 'position', $pos, array('id'=>$item->id));
1663             $pos++;
1664         }
1665     }
1668 /**
1669  * this decreases the position of the given item
1670  *
1671  * @global object
1672  * @param object $item
1673  * @return bool
1674  */
1675 function feedback_moveup_item($item) {
1676     global $DB;
1678     if ($item->position == 1) {
1679         return true;
1680     }
1682     $params = array('feedback'=>$item->feedback);
1683     if (!$items = $DB->get_records('feedback_item', $params, 'position')) {
1684         return false;
1685     }
1687     $itembefore = null;
1688     foreach ($items as $i) {
1689         if ($i->id == $item->id) {
1690             if (is_null($itembefore)) {
1691                 return true;
1692             }
1693             $itembefore->position = $item->position;
1694             $item->position--;
1695             feedback_update_item($itembefore);
1696             feedback_update_item($item);
1697             feedback_renumber_items($item->feedback);
1698             return true;
1699         }
1700         $itembefore = $i;
1701     }
1702     return false;
1705 /**
1706  * this increased the position of the given item
1707  *
1708  * @global object
1709  * @param object $item
1710  * @return bool
1711  */
1712 function feedback_movedown_item($item) {
1713     global $DB;
1715     $params = array('feedback'=>$item->feedback);
1716     if (!$items = $DB->get_records('feedback_item', $params, 'position')) {
1717         return false;
1718     }
1720     $movedownitem = null;
1721     foreach ($items as $i) {
1722         if (!is_null($movedownitem) AND $movedownitem->id == $item->id) {
1723             $movedownitem->position = $i->position;
1724             $i->position--;
1725             feedback_update_item($movedownitem);
1726             feedback_update_item($i);
1727             feedback_renumber_items($item->feedback);
1728             return true;
1729         }
1730         $movedownitem = $i;
1731     }
1732     return false;
1735 /**
1736  * here the position of the given item will be set to the value in $pos
1737  *
1738  * @global object
1739  * @param object $moveitem
1740  * @param int $pos
1741  * @return boolean
1742  */
1743 function feedback_move_item($moveitem, $pos) {
1744     global $DB;
1746     $params = array('feedback'=>$moveitem->feedback);
1747     if (!$allitems = $DB->get_records('feedback_item', $params, 'position')) {
1748         return false;
1749     }
1750     if (is_array($allitems)) {
1751         $index = 1;
1752         foreach ($allitems as $item) {
1753             if ($index == $pos) {
1754                 $index++;
1755             }
1756             if ($item->id == $moveitem->id) {
1757                 $moveitem->position = $pos;
1758                 feedback_update_item($moveitem);
1759                 continue;
1760             }
1761             $item->position = $index;
1762             feedback_update_item($item);
1763             $index++;
1764         }
1765         return true;
1766     }
1767     return false;
1770 /**
1771  * prints the given item as a preview.
1772  * each item-class has an own print_item_preview function implemented.
1773  *
1774  * @global object
1775  * @param object $item the item what we want to print out
1776  * @return void
1777  */
1778 function feedback_print_item_preview($item) {
1779     global $CFG;
1780     if ($item->typ == 'pagebreak') {
1781         return;
1782     }
1783     //get the instance of the item-class
1784     $itemobj = feedback_get_item_class($item->typ);
1785     $itemobj->print_item_preview($item);
1788 /**
1789  * prints the given item in the completion form.
1790  * each item-class has an own print_item_complete function implemented.
1791  *
1792  * @param object $item the item what we want to print out
1793  * @param mixed $value the value
1794  * @param boolean $highlightrequire if this set true and the value are false on completing so the item will be highlighted
1795  * @return void
1796  */
1797 function feedback_print_item_complete($item, $value = false, $highlightrequire = false) {
1798     global $CFG;
1799     if ($item->typ == 'pagebreak') {
1800         return;
1801     }
1803     //get the instance of the item-class
1804     $itemobj = feedback_get_item_class($item->typ);
1805     $itemobj->print_item_complete($item, $value, $highlightrequire);
1808 /**
1809  * prints the given item in the show entries page.
1810  * each item-class has an own print_item_show_value function implemented.
1811  *
1812  * @param object $item the item what we want to print out
1813  * @param mixed $value
1814  * @return void
1815  */
1816 function feedback_print_item_show_value($item, $value = false) {
1817     global $CFG;
1818     if ($item->typ == 'pagebreak') {
1819         return;
1820     }
1822     //get the instance of the item-class
1823     $itemobj = feedback_get_item_class($item->typ);
1824     $itemobj->print_item_show_value($item, $value);
1827 /**
1828  * if the user completes a feedback and there is a pagebreak so the values are saved temporary.
1829  * the values are not saved permanently until the user click on save button
1830  *
1831  * @global object
1832  * @param object $feedbackcompleted
1833  * @return object temporary saved completed-record
1834  */
1835 function feedback_set_tmp_values($feedbackcompleted) {
1836     global $DB;
1838     //first we create a completedtmp
1839     $tmpcpl = new stdClass();
1840     foreach ($feedbackcompleted as $key => $value) {
1841         $tmpcpl->{$key} = $value;
1842     }
1843     unset($tmpcpl->id);
1844     $tmpcpl->timemodified = time();
1845     $tmpcpl->id = $DB->insert_record('feedback_completedtmp', $tmpcpl);
1846     //get all values of original-completed
1847     if (!$values = $DB->get_records('feedback_value', array('completed'=>$feedbackcompleted->id))) {
1848         return;
1849     }
1850     foreach ($values as $value) {
1851         unset($value->id);
1852         $value->completed = $tmpcpl->id;
1853         $DB->insert_record('feedback_valuetmp', $value);
1854     }
1855     return $tmpcpl;
1858 /**
1859  * this saves the temporary saved values permanently
1860  *
1861  * @global object
1862  * @param object $feedbackcompletedtmp the temporary completed
1863  * @param object $feedbackcompleted the target completed
1864  * @param int $userid
1865  * @return int the id of the completed
1866  */
1867 function feedback_save_tmp_values($feedbackcompletedtmp, $feedbackcompleted, $userid) {
1868     global $DB;
1870     $tmpcplid = $feedbackcompletedtmp->id;
1871     if ($feedbackcompleted) {
1872         //first drop all existing values
1873         $DB->delete_records('feedback_value', array('completed'=>$feedbackcompleted->id));
1874         //update the current completed
1875         $feedbackcompleted->timemodified = time();
1876         $DB->update_record('feedback_completed', $feedbackcompleted);
1877     } else {
1878         $feedbackcompleted = clone($feedbackcompletedtmp);
1879         $feedbackcompleted->id = '';
1880         $feedbackcompleted->userid = $userid;
1881         $feedbackcompleted->timemodified = time();
1882         $feedbackcompleted->id = $DB->insert_record('feedback_completed', $feedbackcompleted);
1883     }
1885     //save all the new values from feedback_valuetmp
1886     //get all values of tmp-completed
1887     $params = array('completed'=>$feedbackcompletedtmp->id);
1888     if (!$values = $DB->get_records('feedback_valuetmp', $params)) {
1889         return false;
1890     }
1891     foreach ($values as $value) {
1892         //check if there are depend items
1893         $item = $DB->get_record('feedback_item', array('id'=>$value->item));
1894         if ($item->dependitem > 0) {
1895             $check = feedback_compare_item_value($tmpcplid,
1896                                         $item->dependitem,
1897                                         $item->dependvalue,
1898                                         true);
1899         } else {
1900             $check = true;
1901         }
1902         if ($check) {
1903             unset($value->id);
1904             $value->completed = $feedbackcompleted->id;
1905             $DB->insert_record('feedback_value', $value);
1906         }
1907     }
1908     //drop all the tmpvalues
1909     $DB->delete_records('feedback_valuetmp', array('completed'=>$tmpcplid));
1910     $DB->delete_records('feedback_completedtmp', array('id'=>$tmpcplid));
1911     return $feedbackcompleted->id;
1915 /**
1916  * deletes the given temporary completed and all related temporary values
1917  *
1918  * @global object
1919  * @param int $tmpcplid
1920  * @return void
1921  */
1922 function feedback_delete_completedtmp($tmpcplid) {
1923     global $DB;
1925     $DB->delete_records('feedback_valuetmp', array('completed'=>$tmpcplid));
1926     $DB->delete_records('feedback_completedtmp', array('id'=>$tmpcplid));
1929 ////////////////////////////////////////////////
1930 ////////////////////////////////////////////////
1931 ////////////////////////////////////////////////
1932 //functions to handle the pagebreaks
1933 ////////////////////////////////////////////////
1935 /**
1936  * this creates a pagebreak.
1937  * a pagebreak is a special kind of item
1938  *
1939  * @global object
1940  * @param int $feedbackid
1941  * @return mixed false if there already is a pagebreak on last position or the id of the pagebreak-item
1942  */
1943 function feedback_create_pagebreak($feedbackid) {
1944     global $DB;
1946     //check if there already is a pagebreak on the last position
1947     $lastposition = $DB->count_records('feedback_item', array('feedback'=>$feedbackid));
1948     if ($lastposition == feedback_get_last_break_position($feedbackid)) {
1949         return false;
1950     }
1952     $item = new stdClass();
1953     $item->feedback = $feedbackid;
1955     $item->template=0;
1957     $item->name = '';
1959     $item->presentation = '';
1960     $item->hasvalue = 0;
1962     $item->typ = 'pagebreak';
1963     $item->position = $lastposition + 1;
1965     $item->required=0;
1967     return $DB->insert_record('feedback_item', $item);
1970 /**
1971  * get all positions of pagebreaks in the given feedback
1972  *
1973  * @global object
1974  * @param int $feedbackid
1975  * @return array all ordered pagebreak positions
1976  */
1977 function feedback_get_all_break_positions($feedbackid) {
1978     global $DB;
1980     $params = array('typ'=>'pagebreak', 'feedback'=>$feedbackid);
1981     $allbreaks = $DB->get_records_menu('feedback_item', $params, 'position', 'id, position');
1982     if (!$allbreaks) {
1983         return false;
1984     }
1985     return array_values($allbreaks);
1988 /**
1989  * get the position of the last pagebreak
1990  *
1991  * @param int $feedbackid
1992  * @return int the position of the last pagebreak
1993  */
1994 function feedback_get_last_break_position($feedbackid) {
1995     if (!$allbreaks = feedback_get_all_break_positions($feedbackid)) {
1996         return false;
1997     }
1998     return $allbreaks[count($allbreaks) - 1];
2001 /**
2002  * this returns the position where the user can continue the completing.
2003  *
2004  * @global object
2005  * @global object
2006  * @global object
2007  * @param int $feedbackid
2008  * @param int $courseid
2009  * @param string $guestid this id will be saved temporary and is unique
2010  * @return int the position to continue
2011  */
2012 function feedback_get_page_to_continue($feedbackid, $courseid = false, $guestid = false) {
2013     global $CFG, $USER, $DB;
2015     //is there any break?
2017     if (!$allbreaks = feedback_get_all_break_positions($feedbackid)) {
2018         return false;
2019     }
2021     $params = array();
2022     if ($courseid) {
2023         $courseselect = "AND fv.course_id = :courseid";
2024         $params['courseid'] = $courseid;
2025     } else {
2026         $courseselect = '';
2027     }
2029     if ($guestid) {
2030         $userselect = "AND fc.guestid = :guestid";
2031         $usergroup = "GROUP BY fc.guestid";
2032         $params['guestid'] = $guestid;
2033     } else {
2034         $userselect = "AND fc.userid = :userid";
2035         $usergroup = "GROUP BY fc.userid";
2036         $params['userid'] = $USER->id;
2037     }
2039     $sql =  "SELECT MAX(fi.position)
2040                FROM {feedback_completedtmp} fc, {feedback_valuetmp} fv, {feedback_item} fi
2041               WHERE fc.id = fv.completed
2042                     $userselect
2043                     AND fc.feedback = :feedbackid
2044                     $courseselect
2045                     AND fi.id = fv.item
2046          $usergroup";
2047     $params['feedbackid'] = $feedbackid;
2049     $lastpos = $DB->get_field_sql($sql, $params);
2051     //the index of found pagebreak is the searched pagenumber
2052     foreach ($allbreaks as $pagenr => $br) {
2053         if ($lastpos < $br) {
2054             return $pagenr;
2055         }
2056     }
2057     return count($allbreaks);
2060 ////////////////////////////////////////////////
2061 ////////////////////////////////////////////////
2062 ////////////////////////////////////////////////
2063 //functions to handle the values
2064 ////////////////////////////////////////////////
2066 /**
2067  * cleans the userinput while submitting the form.
2068  *
2069  * @param mixed $value
2070  * @return mixed
2071  */
2072 function feedback_clean_input_value($item, $value) {
2073     $itemobj = feedback_get_item_class($item->typ);
2074     return $itemobj->clean_input_value($value);
2077 /**
2078  * this saves the values of an completed.
2079  * if the param $tmp is set true so the values are saved temporary in table feedback_valuetmp.
2080  * if there is already a completed and the userid is set so the values are updated.
2081  * on all other things new value records will be created.
2082  *
2083  * @global object
2084  * @param int $userid
2085  * @param boolean $tmp
2086  * @return mixed false on error or the completeid
2087  */
2088 function feedback_save_values($usrid, $tmp = false) {
2089     global $DB;
2091     $completedid = optional_param('completedid', 0, PARAM_INT);
2093     $tmpstr = $tmp ? 'tmp' : '';
2094     $time = time();
2095     $timemodified = mktime(0, 0, 0, date('m', $time), date('d', $time), date('Y', $time));
2097     if ($usrid == 0) {
2098         return feedback_create_values($usrid, $timemodified, $tmp);
2099     }
2100     $completed = $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$completedid));
2101     if (!$completed) {
2102         return feedback_create_values($usrid, $timemodified, $tmp);
2103     } else {
2104         $completed->timemodified = $timemodified;
2105         return feedback_update_values($completed, $tmp);
2106     }
2109 /**
2110  * this saves the values from anonymous user such as guest on the main-site
2111  *
2112  * @global object
2113  * @param string $guestid the unique guestidentifier
2114  * @return mixed false on error or the completeid
2115  */
2116 function feedback_save_guest_values($guestid) {
2117     global $DB;
2119     $completedid = optional_param('completedid', false, PARAM_INT);
2121     $timemodified = time();
2122     if (!$completed = $DB->get_record('feedback_completedtmp', array('id'=>$completedid))) {
2123         return feedback_create_values(0, $timemodified, true, $guestid);
2124     } else {
2125         $completed->timemodified = $timemodified;
2126         return feedback_update_values($completed, true);
2127     }
2130 /**
2131  * get the value from the given item related to the given completed.
2132  * the value can come as temporary or as permanently value. the deciding is done by $tmp
2133  *
2134  * @global object
2135  * @param int $completeid
2136  * @param int $itemid
2137  * @param boolean $tmp
2138  * @return mixed the value, the type depends on plugin-definition
2139  */
2140 function feedback_get_item_value($completedid, $itemid, $tmp = false) {
2141     global $DB;
2143     $tmpstr = $tmp ? 'tmp' : '';
2144     $params = array('completed'=>$completedid, 'item'=>$itemid);
2145     return $DB->get_field('feedback_value'.$tmpstr, 'value', $params);
2148 /**
2149  * compares the value of the itemid related to the completedid with the dependvalue.
2150  * this is used if a depend item is set.
2151  * the value can come as temporary or as permanently value. the deciding is done by $tmp.
2152  *
2153  * @global object
2154  * @global object
2155  * @param int $completeid
2156  * @param int $itemid
2157  * @param mixed $dependvalue
2158  * @param boolean $tmp
2159  * @return bool
2160  */
2161 function feedback_compare_item_value($completedid, $itemid, $dependvalue, $tmp = false) {
2162     global $DB, $CFG;
2164     $dbvalue = feedback_get_item_value($completedid, $itemid, $tmp);
2166     //get the class of the given item-typ
2167     $item = $DB->get_record('feedback_item', array('id'=>$itemid));
2169     //get the instance of the item-class
2170     $itemobj = feedback_get_item_class($item->typ);
2171     return $itemobj->compare_value($item, $dbvalue, $dependvalue); //true or false
2174 /**
2175  * this function checks the correctness of values.
2176  * the rules for this are implemented in the class of each item.
2177  * it can be the required attribute or the value self e.g. numeric.
2178  * the params first/lastitem are given to determine the visible range between pagebreaks.
2179  *
2180  * @global object
2181  * @param int $firstitem the position of firstitem for checking
2182  * @param int $lastitem the position of lastitem for checking
2183  * @return boolean
2184  */
2185 function feedback_check_values($firstitem, $lastitem) {
2186     global $DB, $CFG;
2188     $feedbackid = optional_param('feedbackid', 0, PARAM_INT);
2190     //get all items between the first- and lastitem
2191     $select = "feedback = ?
2192                     AND position >= ?
2193                     AND position <= ?
2194                     AND hasvalue = 1";
2195     $params = array($feedbackid, $firstitem, $lastitem);
2196     if (!$feedbackitems = $DB->get_records_select('feedback_item', $select, $params)) {
2197         //if no values are given so no values can be wrong ;-)
2198         return true;
2199     }
2201     foreach ($feedbackitems as $item) {
2202         //get the instance of the item-class
2203         $itemobj = feedback_get_item_class($item->typ);
2205         //the name of the input field of the completeform is given in a special form:
2206         //<item-typ>_<item-id> eg. numeric_234
2207         //this is the key to get the value for the correct item
2208         $formvalname = $item->typ . '_' . $item->id;
2210         if ($itemobj->value_is_array()) {
2211             //get the raw value here. It is cleaned after that by the object itself
2212             $value = optional_param_array($formvalname, null, PARAM_RAW);
2213         } else {
2214             //get the raw value here. It is cleaned after that by the object itself
2215             $value = optional_param($formvalname, null, PARAM_RAW);
2216         }
2217         $value = $itemobj->clean_input_value($value);
2219         //check if the value is set
2220         if (is_null($value) AND $item->required == 1) {
2221             return false;
2222         }
2224         //now we let check the value by the item-class
2225         if (!$itemobj->check_value($value, $item)) {
2226             return false;
2227         }
2228     }
2229     //if no wrong values so we can return true
2230     return true;
2233 /**
2234  * this function create a complete-record and the related value-records.
2235  * depending on the $tmp (true/false) the values are saved temporary or permanently
2236  *
2237  * @global object
2238  * @param int $userid
2239  * @param int $timemodified
2240  * @param boolean $tmp
2241  * @param string $guestid a unique identifier to save temporary data
2242  * @return mixed false on error or the completedid
2243  */
2244 function feedback_create_values($usrid, $timemodified, $tmp = false, $guestid = false) {
2245     global $DB;
2247     $feedbackid = optional_param('feedbackid', false, PARAM_INT);
2248     $anonymous_response = optional_param('anonymous_response', false, PARAM_INT);
2249     $courseid = optional_param('courseid', false, PARAM_INT);
2251     $tmpstr = $tmp ? 'tmp' : '';
2252     //first we create a new completed record
2253     $completed = new stdClass();
2254     $completed->feedback           = $feedbackid;
2255     $completed->userid             = $usrid;
2256     $completed->guestid            = $guestid;
2257     $completed->timemodified       = $timemodified;
2258     $completed->anonymous_response = $anonymous_response;
2260     $completedid = $DB->insert_record('feedback_completed'.$tmpstr, $completed);
2262     $completed = $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$completedid));
2264     //the keys are in the form like abc_xxx
2265     //with explode we make an array with(abc, xxx) and (abc=typ und xxx=itemnr)
2267     //get the items of the feedback
2268     if (!$allitems = $DB->get_records('feedback_item', array('feedback'=>$completed->feedback))) {
2269         return false;
2270     }
2271     foreach ($allitems as $item) {
2272         if (!$item->hasvalue) {
2273             continue;
2274         }
2275         //get the class of item-typ
2276         $itemobj = feedback_get_item_class($item->typ);
2278         $keyname = $item->typ.'_'.$item->id;
2280         if ($itemobj->value_is_array()) {
2281             $itemvalue = optional_param_array($keyname, null, $itemobj->value_type());
2282         } else {
2283             $itemvalue = optional_param($keyname, null, $itemobj->value_type());
2284         }
2286         if (is_null($itemvalue)) {
2287             continue;
2288         }
2290         $value = new stdClass();
2291         $value->item = $item->id;
2292         $value->completed = $completed->id;
2293         $value->course_id = $courseid;
2295         //the kind of values can be absolutely different
2296         //so we run create_value directly by the item-class
2297         $value->value = $itemobj->create_value($itemvalue);
2298         $DB->insert_record('feedback_value'.$tmpstr, $value);
2299     }
2300     return $completed->id;
2303 /**
2304  * this function updates a complete-record and the related value-records.
2305  * depending on the $tmp (true/false) the values are saved temporary or permanently
2306  *
2307  * @global object
2308  * @param object $completed
2309  * @param boolean $tmp
2310  * @return int the completedid
2311  */
2312 function feedback_update_values($completed, $tmp = false) {
2313     global $DB;
2315     $courseid = optional_param('courseid', false, PARAM_INT);
2316     $tmpstr = $tmp ? 'tmp' : '';
2318     $DB->update_record('feedback_completed'.$tmpstr, $completed);
2319     //get the values of this completed
2320     $values = $DB->get_records('feedback_value'.$tmpstr, array('completed'=>$completed->id));
2322     //get the items of the feedback
2323     if (!$allitems = $DB->get_records('feedback_item', array('feedback'=>$completed->feedback))) {
2324         return false;
2325     }
2326     foreach ($allitems as $item) {
2327         if (!$item->hasvalue) {
2328             continue;
2329         }
2330         //get the class of item-typ
2331         $itemobj = feedback_get_item_class($item->typ);
2333         $keyname = $item->typ.'_'.$item->id;
2335         if ($itemobj->value_is_array()) {
2336             $itemvalue = optional_param_array($keyname, null, $itemobj->value_type());
2337         } else {
2338             $itemvalue = optional_param($keyname, null, $itemobj->value_type());
2339         }
2341         //is the itemvalue set (could be a subset of items because pagebreak)?
2342         if (is_null($itemvalue)) {
2343             continue;
2344         }
2346         $newvalue = new stdClass();
2347         $newvalue->item = $item->id;
2348         $newvalue->completed = $completed->id;
2349         $newvalue->course_id = $courseid;
2351         //the kind of values can be absolutely different
2352         //so we run create_value directly by the item-class
2353         $newvalue->value = $itemobj->create_value($itemvalue);
2355         //check, if we have to create or update the value
2356         $exist = false;
2357         foreach ($values as $value) {
2358             if ($value->item == $newvalue->item) {
2359                 $newvalue->id = $value->id;
2360                 $exist = true;
2361                 break;
2362             }
2363         }
2364         if ($exist) {
2365             $DB->update_record('feedback_value'.$tmpstr, $newvalue);
2366         } else {
2367             $DB->insert_record('feedback_value'.$tmpstr, $newvalue);
2368         }
2369     }
2371     return $completed->id;
2374 /**
2375  * get the values of an item depending on the given groupid.
2376  * if the feedback is anonymous so the values are shuffled
2377  *
2378  * @global object
2379  * @global object
2380  * @param object $item
2381  * @param int $groupid
2382  * @param int $courseid
2383  * @param bool $ignore_empty if this is set true so empty values are not delivered
2384  * @return array the value-records
2385  */
2386 function feedback_get_group_values($item,
2387                                    $groupid = false,
2388                                    $courseid = false,
2389                                    $ignore_empty = false) {
2391     global $CFG, $DB;
2393     //if the groupid is given?
2394     if (intval($groupid) > 0) {
2395         if ($ignore_empty) {
2396             $ignore_empty_select = "AND fbv.value != '' AND fbv.value != '0'";
2397         } else {
2398             $ignore_empty_select = "";
2399         }
2401         $query = 'SELECT fbv .  *
2402                     FROM {feedback_value} fbv, {feedback_completed} fbc, {groups_members} gm
2403                    WHERE fbv.item = ?
2404                          AND fbv.completed = fbc.id
2405                          AND fbc.userid = gm.userid
2406                          '.$ignore_empty_select.'
2407                          AND gm.groupid = ?
2408                 ORDER BY fbc.timemodified';
2409         $values = $DB->get_records_sql($query, array($item->id, $groupid));
2411     } else {
2412         if ($ignore_empty) {
2413             $ignore_empty_select = "AND value != '' AND value != '0'";
2414         } else {
2415             $ignore_empty_select = "";
2416         }
2418         if ($courseid) {
2419             $select = "item = ? AND course_id = ? ".$ignore_empty_select;
2420             $params = array($item->id, $courseid);
2421             $values = $DB->get_records_select('feedback_value', $select, $params);
2422         } else {
2423             $select = "item = ? ".$ignore_empty_select;
2424             $params = array($item->id);
2425             $values = $DB->get_records_select('feedback_value', $select, $params);
2426         }
2427     }
2428     $params = array('id'=>$item->feedback);
2429     if ($DB->get_field('feedback', 'anonymous', $params) == FEEDBACK_ANONYMOUS_YES) {
2430         if (is_array($values)) {
2431             shuffle($values);
2432         }
2433     }
2434     return $values;
2437 /**
2438  * check for multiple_submit = false.
2439  * if the feedback is global so the courseid must be given
2440  *
2441  * @global object
2442  * @global object
2443  * @param int $feedbackid
2444  * @param int $courseid
2445  * @return boolean true if the feedback already is submitted otherwise false
2446  */
2447 function feedback_is_already_submitted($feedbackid, $courseid = false) {
2448     global $USER, $DB;
2450     $params = array('userid'=>$USER->id, 'feedback'=>$feedbackid);
2451     if (!$trackings = $DB->get_records_menu('feedback_tracking', $params, '', 'id, completed')) {
2452         return false;
2453     }
2455     if ($courseid) {
2456         $select = 'completed IN ('.implode(',', $trackings).') AND course_id = ?';
2457         if (!$values = $DB->get_records_select('feedback_value', $select, array($courseid))) {
2458             return false;
2459         }
2460     }
2462     return true;
2465 /**
2466  * if the completion of a feedback will be continued eg.
2467  * by pagebreak or by multiple submit so the complete must be found.
2468  * if the param $tmp is set true so all things are related to temporary completeds
2469  *
2470  * @global object
2471  * @global object
2472  * @global object
2473  * @param int $feedbackid
2474  * @param boolean $tmp
2475  * @param int $courseid
2476  * @param string $guestid
2477  * @return int the id of the found completed
2478  */
2479 function feedback_get_current_completed($feedbackid,
2480                                         $tmp = false,
2481                                         $courseid = false,
2482                                         $guestid = false) {
2484     global $USER, $CFG, $DB;
2486     $tmpstr = $tmp ? 'tmp' : '';
2488     if (!$courseid) {
2489         if ($guestid) {
2490             $params = array('feedback'=>$feedbackid, 'guestid'=>$guestid);
2491             return $DB->get_record('feedback_completed'.$tmpstr, $params);
2492         } else {
2493             $params = array('feedback'=>$feedbackid, 'userid'=>$USER->id);
2494             return $DB->get_record('feedback_completed'.$tmpstr, $params);
2495         }
2496     }
2498     $params = array();
2500     if ($guestid) {
2501         $userselect = "AND fc.guestid = :guestid";
2502         $params['guestid'] = $guestid;
2503     } else {
2504         $userselect = "AND fc.userid = :userid";
2505         $params['userid'] = $USER->id;
2506     }
2507     //if courseid is set the feedback is global.
2508     //there can be more than one completed on one feedback
2509     $sql =  "SELECT DISTINCT fc.*
2510                FROM {feedback_value{$tmpstr}} fv, {feedback_completed{$tmpstr}} fc
2511               WHERE fv.course_id = :courseid
2512                     AND fv.completed = fc.id
2513                     $userselect
2514                     AND fc.feedback = :feedbackid";
2515     $params['courseid']   = intval($courseid);
2516     $params['feedbackid'] = $feedbackid;
2518     if (!$sqlresult = $DB->get_records_sql($sql, $params)) {
2519         return false;
2520     }
2521     foreach ($sqlresult as $r) {
2522         return $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$r->id));
2523     }
2526 /**
2527  * get the completeds depending on the given groupid.
2528  *
2529  * @global object
2530  * @global object
2531  * @param object $feedback
2532  * @param int $groupid
2533  * @param int $courseid
2534  * @return mixed array of found completeds otherwise false
2535  */
2536 function feedback_get_completeds_group($feedback, $groupid = false, $courseid = false) {
2537     global $CFG, $DB;
2539     if (intval($groupid) > 0) {
2540         $query = "SELECT fbc.*
2541                     FROM {feedback_completed} fbc, {groups_members} gm
2542                    WHERE fbc.feedback = ?
2543                          AND gm.groupid = ?
2544                          AND fbc.userid = gm.userid";
2545         if ($values = $DB->get_records_sql($query, array($feedback->id, $groupid))) {
2546             return $values;
2547         } else {
2548             return false;
2549         }
2550     } else {
2551         if ($courseid) {
2552             $query = "SELECT DISTINCT fbc.*
2553                         FROM {feedback_completed} fbc, {feedback_value} fbv
2554                         WHERE fbc.id = fbv.completed
2555                             AND fbc.feedback = ?
2556                             AND fbv.course_id = ?
2557                         ORDER BY random_response";
2558             if ($values = $DB->get_records_sql($query, array($feedback->id, $courseid))) {
2559                 return $values;
2560             } else {
2561                 return false;
2562             }
2563         } else {
2564             if ($values = $DB->get_records('feedback_completed', array('feedback'=>$feedback->id))) {
2565                 return $values;
2566             } else {
2567                 return false;
2568             }
2569         }
2570     }
2573 /**
2574  * get the count of completeds depending on the given groupid.
2575  *
2576  * @global object
2577  * @global object
2578  * @param object $feedback
2579  * @param int $groupid
2580  * @param int $courseid
2581  * @return mixed count of completeds or false
2582  */
2583 function feedback_get_completeds_group_count($feedback, $groupid = false, $courseid = false) {
2584     global $CFG, $DB;
2586     if ($courseid > 0 AND !$groupid <= 0) {
2587         $sql = "SELECT id, COUNT(item) AS ci
2588                   FROM {feedback_value}
2589                  WHERE course_id  = ?
2590               GROUP BY item ORDER BY ci DESC";
2591         if ($foundrecs = $DB->get_records_sql($sql, array($courseid))) {
2592             $foundrecs = array_values($foundrecs);
2593             return $foundrecs[0]->ci;
2594         }
2595         return false;
2596     }
2597     if ($values = feedback_get_completeds_group($feedback, $groupid)) {
2598         return count($values);
2599     } else {
2600         return false;
2601     }
2604 /**
2605  * deletes all completed-recordsets from a feedback.
2606  * all related data such as values also will be deleted
2607  *
2608  * @global object
2609  * @param int $feedbackid
2610  * @return void
2611  */
2612 function feedback_delete_all_completeds($feedbackid) {
2613     global $DB;
2615     if (!$completeds = $DB->get_records('feedback_completed', array('feedback'=>$feedbackid))) {
2616         return;
2617     }
2618     foreach ($completeds as $completed) {
2619         feedback_delete_completed($completed->id);
2620     }
2623 /**
2624  * deletes a completed given by completedid.
2625  * all related data such values or tracking data also will be deleted
2626  *
2627  * @global object
2628  * @param int $completedid
2629  * @return boolean
2630  */
2631 function feedback_delete_completed($completedid) {
2632     global $DB, $CFG;
2633     require_once($CFG->libdir.'/completionlib.php');
2635     if (!$completed = $DB->get_record('feedback_completed', array('id'=>$completedid))) {
2636         return false;
2637     }
2639     if (!$feedback = $DB->get_record('feedback', array('id'=>$completed->feedback))) {
2640         return false;
2641     }
2643     if (!$course = $DB->get_record('course', array('id'=>$feedback->course))) {
2644         return false;
2645     }
2647     if (!$cm = get_coursemodule_from_instance('feedback', $feedback->id)) {
2648         return false;
2649     }
2651     //first we delete all related values
2652     $DB->delete_records('feedback_value', array('completed'=>$completed->id));
2654     //now we delete all tracking data
2655     $params = array('completed'=>$completed->id, 'feedback'=>$completed->feedback);
2656     if ($tracking = $DB->get_record('feedback_tracking', $params)) {
2657         $DB->delete_records('feedback_tracking', array('completed'=>$completed->id));
2658     }
2660     // Update completion state
2661     $completion = new completion_info($course);
2662     if ($completion->is_enabled($cm) && $feedback->completionsubmit) {
2663         $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
2664     }
2665     //last we delete the completed-record
2666     return $DB->delete_records('feedback_completed', array('id'=>$completed->id));
2669 ////////////////////////////////////////////////
2670 ////////////////////////////////////////////////
2671 ////////////////////////////////////////////////
2672 //functions to handle sitecourse mapping
2673 ////////////////////////////////////////////////
2675 /**
2676  * checks if the course and the feedback is in the table feedback_sitecourse_map.
2677  *
2678  * @global object
2679  * @param int $feedbackid
2680  * @param int $courseid
2681  * @return int the count of records
2682  */
2683 function feedback_is_course_in_sitecourse_map($feedbackid, $courseid) {
2684     global $DB;
2685     $params = array('feedbackid'=>$feedbackid, 'courseid'=>$courseid);
2686     return $DB->count_records('feedback_sitecourse_map', $params);
2689 /**
2690  * checks if the feedback is in the table feedback_sitecourse_map.
2691  *
2692  * @global object
2693  * @param int $feedbackid
2694  * @return boolean
2695  */
2696 function feedback_is_feedback_in_sitecourse_map($feedbackid) {
2697     global $DB;
2698     return $DB->record_exists('feedback_sitecourse_map', array('feedbackid'=>$feedbackid));
2701 /**
2702  * gets the feedbacks from table feedback_sitecourse_map.
2703  * this is used to show the global feedbacks on the feedback block
2704  * all feedbacks with the following criteria will be selected:<br />
2705  *
2706  * 1) all feedbacks which id are listed together with the courseid in sitecoursemap and<br />
2707  * 2) all feedbacks which not are listed in sitecoursemap
2708  *
2709  * @global object
2710  * @param int $courseid
2711  * @return array the feedback-records
2712  */
2713 function feedback_get_feedbacks_from_sitecourse_map($courseid) {
2714     global $DB;
2716     //first get all feedbacks listed in sitecourse_map with named courseid
2717     $sql = "SELECT f.id AS id,
2718                    cm.id AS cmid,
2719                    f.name AS name,
2720                    f.timeopen AS timeopen,
2721                    f.timeclose AS timeclose
2722             FROM {feedback} f, {course_modules} cm, {feedback_sitecourse_map} sm, {modules} m
2723             WHERE f.id = cm.instance
2724                    AND f.course = '".SITEID."'
2725                    AND m.id = cm.module
2726                    AND m.name = 'feedback'
2727                    AND sm.courseid = ?
2728                    AND sm.feedbackid = f.id";
2730     if (!$feedbacks1 = $DB->get_records_sql($sql, array($courseid))) {
2731         $feedbacks1 = array();
2732     }
2734     //second get all feedbacks not listed in sitecourse_map
2735     $feedbacks2 = array();
2736     $sql = "SELECT f.id AS id,
2737                    cm.id AS cmid,
2738                    f.name AS name,
2739                    f.timeopen AS timeopen,
2740                    f.timeclose AS timeclose
2741             FROM {feedback} f, {course_modules} cm, {modules} m
2742             WHERE f.id = cm.instance
2743                    AND f.course = '".SITEID."'
2744                    AND m.id = cm.module
2745                    AND m.name = 'feedback'";
2746     if (!$allfeedbacks = $DB->get_records_sql($sql)) {
2747         $allfeedbacks = array();
2748     }
2749     foreach ($allfeedbacks as $a) {
2750         if (!$DB->record_exists('feedback_sitecourse_map', array('feedbackid'=>$a->id))) {
2751             $feedbacks2[] = $a;
2752         }
2753     }
2755     return array_merge($feedbacks1, $feedbacks2);
2759 /**
2760  * gets the courses from table feedback_sitecourse_map.
2761  *
2762  * @global object
2763  * @param int $feedbackid
2764  * @return array the course-records
2765  */
2766 function feedback_get_courses_from_sitecourse_map($feedbackid) {
2767     global $DB;
2769     $sql = "SELECT f.id, f.courseid, c.fullname, c.shortname
2770               FROM {feedback_sitecourse_map} f, {course} c
2771              WHERE c.id = f.courseid
2772                    AND f.feedbackid = ?
2773           ORDER BY c.fullname";
2775     return $DB->get_records_sql($sql, array($feedbackid));
2779 /**
2780  * removes non existing courses or feedbacks from sitecourse_map.
2781  * it shouldn't be called all too often
2782  * a good place for it could be the mapcourse.php or unmapcourse.php
2783  *
2784  * @global object
2785  * @return void
2786  */
2787 function feedback_clean_up_sitecourse_map() {
2788     global $DB;
2790     $maps = $DB->get_records('feedback_sitecourse_map');
2791     foreach ($maps as $map) {
2792         if (!$DB->get_record('course', array('id'=>$map->courseid))) {
2793             $params = array('courseid'=>$map->courseid, 'feedbackid'=>$map->feedbackid);
2794             $DB->delete_records('feedback_sitecourse_map', $params);
2795             continue;
2796         }
2797         if (!$DB->get_record('feedback', array('id'=>$map->feedbackid))) {
2798             $params = array('courseid'=>$map->courseid, 'feedbackid'=>$map->feedbackid);
2799             $DB->delete_records('feedback_sitecourse_map', $params);
2800             continue;
2801         }
2803     }
2806 ////////////////////////////////////////////////
2807 ////////////////////////////////////////////////
2808 ////////////////////////////////////////////////
2809 //not relatable functions
2810 ////////////////////////////////////////////////
2812 /**
2813  * prints the option items of a selection-input item (dropdownlist).
2814  * @param int $startval the first value of the list
2815  * @param int $endval the last value of the list
2816  * @param int $selectval which item should be selected
2817  * @param int $interval the stepsize from the first to the last value
2818  * @return void
2819  */
2820 function feedback_print_numeric_option_list($startval, $endval, $selectval = '', $interval = 1) {
2821     for ($i = $startval; $i <= $endval; $i += $interval) {
2822         if ($selectval == ($i)) {
2823             $selected = 'selected="selected"';
2824         } else {
2825             $selected = '';
2826         }
2827         echo '<option '.$selected.'>'.$i.'</option>';
2828     }
2831 /**
2832  * sends an email to the teachers of the course where the given feedback is placed.
2833  *
2834  * @global object
2835  * @global object
2836  * @uses FEEDBACK_ANONYMOUS_NO
2837  * @uses FORMAT_PLAIN
2838  * @param object $cm the coursemodule-record
2839  * @param object $feedback
2840  * @param object $course
2841  * @param int $userid
2842  * @return void
2843  */
2844 function feedback_send_email($cm, $feedback, $course, $userid) {
2845     global $CFG, $DB;
2847     if ($feedback->email_notification == 0) {  // No need to do anything
2848         return;
2849     }
2851     $user = $DB->get_record('user', array('id'=>$userid));
2853     if (isset($cm->groupmode) && empty($course->groupmodeforce)) {
2854         $groupmode =  $cm->groupmode;
2855     } else {
2856         $groupmode = $course->groupmode;
2857     }
2859     if ($groupmode == SEPARATEGROUPS) {
2860         $groups = $DB->get_records_sql_menu("SELECT g.name, g.id
2861                                                FROM {groups} g, {groups_members} m
2862                                               WHERE g.courseid = ?
2863                                                     AND g.id = m.groupid
2864                                                     AND m.userid = ?
2865                                            ORDER BY name ASC", array($course->id, $userid));
2866         $groups = array_values($groups);
2868         $teachers = feedback_get_receivemail_users($cm->id, $groups);
2869     } else {
2870         $teachers = feedback_get_receivemail_users($cm->id);
2871     }
2873     if ($teachers) {
2875         $strfeedbacks = get_string('modulenameplural', 'feedback');
2876         $strfeedback  = get_string('modulename', 'feedback');
2877         $strcompleted  = get_string('completed', 'feedback');
2879         if ($feedback->anonymous == FEEDBACK_ANONYMOUS_NO) {
2880             $printusername = fullname($user);
2881         } else {
2882             $printusername = get_string('anonymous_user', 'feedback');
2883         }
2885         foreach ($teachers as $teacher) {
2886             $info = new stdClass();
2887             $info->username = $printusername;
2888             $info->feedback = format_string($feedback->name, true);
2889             $info->url = $CFG->wwwroot.'/mod/feedback/show_entries.php?'.
2890                             'id='.$cm->id.'&'.
2891                             'userid='.$userid.'&'.
2892                             'do_show=showentries';
2894             $postsubject = $strcompleted.': '.$info->username.' -> '.$feedback->name;
2895             $posttext = feedback_send_email_text($info, $course);
2897             if ($teacher->mailformat == 1) {
2898                 $posthtml = feedback_send_email_html($info, $course, $cm);
2899             } else {
2900                 $posthtml = '';
2901             }
2903             if ($feedback->anonymous == FEEDBACK_ANONYMOUS_NO) {
2904                 $eventdata = new stdClass();
2905                 $eventdata->name             = 'submission';
2906                 $eventdata->component        = 'mod_feedback';
2907                 $eventdata->userfrom         = $user;
2908                 $eventdata->userto           = $teacher;
2909                 $eventdata->subject          = $postsubject;
2910                 $eventdata->fullmessage      = $posttext;
2911                 $eventdata->fullmessageformat = FORMAT_PLAIN;
2912                 $eventdata->fullmessagehtml  = $posthtml;
2913                 $eventdata->smallmessage     = '';
2914                 message_send($eventdata);
2915             } else {
2916                 $eventdata = new stdClass();
2917                 $eventdata->name             = 'submission';
2918                 $eventdata->component        = 'mod_feedback';
2919                 $eventdata->userfrom         = $teacher;
2920                 $eventdata->userto           = $teacher;
2921                 $eventdata->subject          = $postsubject;
2922                 $eventdata->fullmessage      = $posttext;
2923                 $eventdata->fullmessageformat = FORMAT_PLAIN;
2924                 $eventdata->fullmessagehtml  = $posthtml;
2925                 $eventdata->smallmessage     = '';
2926                 message_send($eventdata);
2927             }
2928         }
2929     }
2932 /**
2933  * sends an email to the teachers of the course where the given feedback is placed.
2934  *
2935  * @global object
2936  * @uses FORMAT_PLAIN
2937  * @param object $cm the coursemodule-record
2938  * @param object $feedback
2939  * @param object $course
2940  * @return void
2941  */
2942 function feedback_send_email_anonym($cm, $feedback, $course) {
2943     global $CFG;
2945     if ($feedback->email_notification == 0) { // No need to do anything
2946         return;
2947     }
2949     $teachers = feedback_get_receivemail_users($cm->id);
2951     if ($teachers) {
2953         $strfeedbacks = get_string('modulenameplural', 'feedback');
2954         $strfeedback  = get_string('modulename', 'feedback');
2955         $strcompleted  = get_string('completed', 'feedback');
2956         $printusername = get_string('anonymous_user', 'feedback');
2958         foreach ($teachers as $teacher) {
2959             $info = new stdClass();
2960             $info->username = $printusername;
2961             $info->feedback = format_string($feedback->name, true);
2962             $info->url = $CFG->wwwroot.'/mod/feedback/show_entries_anonym.php?id='.$cm->id;
2964             $postsubject = $strcompleted.': '.$info->username.' -> '.$feedback->name;
2965             $posttext = feedback_send_email_text($info, $course);
2967             if ($teacher->mailformat == 1) {
2968                 $posthtml = feedback_send_email_html($info, $course, $cm);
2969             } else {
2970                 $posthtml = '';
2971             }
2973             $eventdata = new stdClass();
2974             $eventdata->name             = 'submission';
2975             $eventdata->component        = 'mod_feedback';
2976             $eventdata->userfrom         = $teacher;
2977             $eventdata->userto           = $teacher;
2978             $eventdata->subject          = $postsubject;
2979             $eventdata->fullmessage      = $posttext;
2980             $eventdata->fullmessageformat = FORMAT_PLAIN;
2981             $eventdata->fullmessagehtml  = $posthtml;
2982             $eventdata->smallmessage     = '';
2983             message_send($eventdata);
2984         }
2985     }
2988 /**
2989  * send the text-part of the email
2990  *
2991  * @param object $info includes some infos about the feedback you want to send
2992  * @param object $course
2993  * @return string the text you want to post
2994  */
2995 function feedback_send_email_text($info, $course) {
2996     $coursecontext = context_course::instance($course->id);
2997     $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
2998     $posttext  = $courseshortname.' -> '.get_string('modulenameplural', 'feedback').' -> '.
2999                     $info->feedback."\n";
3000     $posttext .= '---------------------------------------------------------------------'."\n";
3001     $posttext .= get_string("emailteachermail", "feedback", $info)."\n";
3002     $posttext .= '---------------------------------------------------------------------'."\n";
3003     return $posttext;
3007 /**
3008  * send the html-part of the email
3009  *
3010  * @global object
3011  * @param object $info includes some infos about the feedback you want to send
3012  * @param object $course
3013  * @return string the text you want to post
3014  */
3015 function feedback_send_email_html($info, $course, $cm) {
3016     global $CFG;
3017     $coursecontext = context_course::instance($course->id);
3018     $courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
3019     $course_url = $CFG->wwwroot.'/course/view.php?id='.$course->id;
3020     $feedback_all_url = $CFG->wwwroot.'/mod/feedback/index.php?id='.$course->id;
3021     $feedback_url = $CFG->wwwroot.'/mod/feedback/view.php?id='.$cm->id;
3023     $posthtml = '<p><font face="sans-serif">'.
3024             '<a href="'.$course_url.'">'.$courseshortname.'</a> ->'.
3025             '<a href="'.$feedback_all_url.'">'.get_string('modulenameplural', 'feedback').'</a> ->'.
3026             '<a href="'.$feedback_url.'">'.$info->feedback.'</a></font></p>';
3027     $posthtml .= '<hr /><font face="sans-serif">';
3028     $posthtml .= '<p>'.get_string('emailteachermailhtml', 'feedback', $info).'</p>';
3029     $posthtml .= '</font><hr />';
3030     return $posthtml;
3033 /**
3034  * @param string $url
3035  * @return string
3036  */
3037 function feedback_encode_target_url($url) {
3038     if (strpos($url, '?')) {
3039         list($part1, $part2) = explode('?', $url, 2); //maximal 2 parts
3040         return $part1 . '?' . htmlentities($part2);
3041     } else {
3042         return $url;
3043     }
3046 /**
3047  * Adds module specific settings to the settings block
3048  *
3049  * @param settings_navigation $settings The settings navigation object
3050  * @param navigation_node $feedbacknode The node to add module settings to
3051  */
3052 function feedback_extend_settings_navigation(settings_navigation $settings,
3053                                              navigation_node $feedbacknode) {
3055     global $PAGE, $DB;
3057     if (!$context = context_module::instance($PAGE->cm->id, IGNORE_MISSING)) {
3058         print_error('badcontext');
3059     }
3061     if (has_capability('mod/feedback:edititems', $context)) {
3062         $questionnode = $feedbacknode->add(get_string('questions', 'feedback'));
3064         $questionnode->add(get_string('edit_items', 'feedback'),
3065                     new moodle_url('/mod/feedback/edit.php',
3066                                     array('id' => $PAGE->cm->id,
3067                                           'do_show' => 'edit')));
3069         $questionnode->add(get_string('export_questions', 'feedback'),
3070                     new moodle_url('/mod/feedback/export.php',
3071                                     array('id' => $PAGE->cm->id,
3072                                           'action' => 'exportfile')));
3074         $questionnode->add(get_string('import_questions', 'feedback'),
3075                     new moodle_url('/mod/feedback/import.php',
3076                                     array('id' => $PAGE->cm->id)));
3078         $questionnode->add(get_string('templates', 'feedback'),
3079                     new moodle_url('/mod/feedback/edit.php',
3080                                     array('id' => $PAGE->cm->id,
3081                                           'do_show' => 'templates')));
3082     }
3084     if (has_capability('mod/feedback:viewreports', $context)) {
3085         $feedback = $DB->get_record('feedback', array('id'=>$PAGE->cm->instance));
3086         if ($feedback->course == SITEID) {
3087             $feedbacknode->add(get_string('analysis', 'feedback'),
3088                     new moodle_url('/mod/feedback/analysis_course.php',
3089                                     array('id' => $PAGE->cm->id,
3090                                           'course' => $PAGE->course->id,
3091                                           'do_show' => 'analysis')));
3092         } else {
3093             $feedbacknode->add(get_string('analysis', 'feedback'),
3094                     new moodle_url('/mod/feedback/analysis.php',
3095                                     array('id' => $PAGE->cm->id,
3096                                           'course' => $PAGE->course->id,
3097                                           'do_show' => 'analysis')));
3098         }
3100         $feedbacknode->add(get_string('show_entries', 'feedback'),
3101                     new moodle_url('/mod/feedback/show_entries.php',
3102                                     array('id' => $PAGE->cm->id,
3103                                           'do_show' => 'showentries')));
3104     }
3107 function feedback_init_feedback_session() {
3108     //initialize the feedback-Session - not nice at all!!
3109     global $SESSION;
3110     if (!empty($SESSION)) {
3111         if (!isset($SESSION->feedback) OR !is_object($SESSION->feedback)) {
3112             $SESSION->feedback = new stdClass();
3113         }
3114     }
3117 /**
3118  * Return a list of page types
3119  * @param string $pagetype current page type
3120  * @param stdClass $parentcontext Block's parent context
3121  * @param stdClass $currentcontext Current context of block
3122  */
3123 function feedback_page_type_list($pagetype, $parentcontext, $currentcontext) {
3124     $module_pagetype = array('mod-feedback-*'=>get_string('page-mod-feedback-x', 'feedback'));
3125     return $module_pagetype;
3128 /**
3129  * Move save the items of the given $feedback in the order of $itemlist.
3130  * @param string $itemlist a comma separated list with item ids
3131  * @param stdClass $feedback
3132  * @return bool true if success
3133  */
3134 function feedback_ajax_saveitemorder($itemlist, $feedback) {
3135     global $DB;
3137     $result = true;
3138     $position = 0;
3139     foreach ($itemlist as $itemid) {
3140         $position++;
3141         $result = $result && $DB->set_field('feedback_item',
3142                                             'position',
3143                                             $position,
3144                                             array('id'=>$itemid, 'feedback'=>$feedback->id));
3145     }
3146     return $result;