MDL-15587 - Course Participation report for a feedback activity
[moodle.git] / mod / feedback / lib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Library of functions and constants for module feedback
20  * includes the main-part of feedback-functions
21  *
22  * @package mod-feedback
23  * @copyright Andreas Grabs
24  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25  */
27 /** Include eventslib.php */
28 require_once($CFG->libdir.'/eventslib.php');
29 /** Include calendar/lib.php */
30 require_once($CFG->dirroot.'/calendar/lib.php');
32 define('FEEDBACK_ANONYMOUS_YES', 1);
33 define('FEEDBACK_ANONYMOUS_NO', 2);
34 define('FEEDBACK_MIN_ANONYMOUS_COUNT_IN_GROUP', 2);
35 define('FEEDBACK_DECIMAL', '.');
36 define('FEEDBACK_THOUSAND', ',');
37 define('FEEDBACK_RESETFORM_RESET', 'feedback_reset_data_');
38 define('FEEDBACK_RESETFORM_DROP', 'feedback_drop_feedback_');
39 define('FEEDBACK_MAX_PIX_LENGTH', '400'); //max. Breite des grafischen Balkens in der Auswertung
40 define('FEEDBACK_DEFAULT_PAGE_COUNT', 20);
42 /**
43  * @uses FEATURE_GROUPS
44  * @uses FEATURE_GROUPINGS
45  * @uses FEATURE_GROUPMEMBERSONLY
46  * @uses FEATURE_MOD_INTRO
47  * @uses FEATURE_COMPLETION_TRACKS_VIEWS
48  * @uses FEATURE_GRADE_HAS_GRADE
49  * @uses FEATURE_GRADE_OUTCOMES
50  * @param string $feature FEATURE_xx constant for requested feature
51  * @return mixed True if module supports feature, null if doesn't know
52  */
53 function feedback_supports($feature) {
54     switch($feature) {
55         case FEATURE_GROUPS:                  return true;
56         case FEATURE_GROUPINGS:               return true;
57         case FEATURE_GROUPMEMBERSONLY:        return true;
58         case FEATURE_MOD_INTRO:               return true;
59         case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
60         case FEATURE_COMPLETION_HAS_RULES:    return true;
61         case FEATURE_GRADE_HAS_GRADE:         return false;
62         case FEATURE_GRADE_OUTCOMES:          return false;
63         case FEATURE_BACKUP_MOODLE2:          return true;
65         default: return null;
66     }
67 }
69 /**
70  * this will create a new instance and return the id number
71  * of the new instance.
72  *
73  * @global object
74  * @param object $feedback the object given by mod_feedback_mod_form
75  * @return int
76  */
77 function feedback_add_instance($feedback) {
78     global $DB;
80     $feedback->timemodified = time();
81     $feedback->id = '';
83     //check if openenable and/or closeenable is set and set correctly to save in db
84     if(empty($feedback->openenable)) {
85         $feedback->timeopen = 0;
86     }
87     if(empty($feedback->closeenable)) {
88         $feedback->timeclose = 0;
89     }
90     if(empty($feedback->site_after_submit)) {
91         $feedback->site_after_submit = '';
92     }
94     //saving the feedback in db
95     $feedbackid = $DB->insert_record("feedback", $feedback);
97     $feedback->id = $feedbackid;
99     feedback_set_events($feedback);
101     return $feedbackid;
104 /**
105  * this will update a given instance
106  *
107  * @global object
108  * @param object $feedback the object given by mod_feedback_mod_form
109  * @return boolean
110  */
111 function feedback_update_instance($feedback) {
112     global $DB;
114     $feedback->timemodified = time();
115     $feedback->id = $feedback->instance;
117     //check if openenable and/or closeenable is set and set correctly to save in db
118     if(empty($feedback->openenable)) {
119         $feedback->timeopen = 0;
120     }
121     if(empty($feedback->closeenable)) {
122         $feedback->timeclose = 0;
123     }
124     if(empty($feedback->site_after_submit)) {
125         $feedback->site_after_submit = '';
126     }
128     //save the feedback into the db
129     $DB->update_record("feedback", $feedback);
131     //create or update the new events
132     feedback_set_events($feedback);
134     return true;
137 /**
138  * Serves the files included in feedback items like label. Implements needed access control ;-)
139  *
140  * @param object $course
141  * @param object $cm
142  * @param object $context
143  * @param string $filearea
144  * @param array $args
145  * @param bool $forcedownload
146  * @return bool false if file not found, does not return if found - justsend the file
147  */
148 function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
149     global $CFG, $DB;
151     require_login($course, false, $cm);
153     $itemid = (int)array_shift($args);
155     require_course_login($course, true, $cm);
157     if (!$item = $DB->get_record('feedback_item', array('id'=>$itemid))) {
158         return false;
159     }
161     if (!has_capability('mod/feedback:view', $context)) {
162         return false;
163     }
165     if ($context->contextlevel == CONTEXT_MODULE) {
166         if ($filearea !== 'item') {
167             return false;
168         }
170         if ($item->feedback == $cm->instance) {
171             $filecontext = $context;
172         } else {
173             return false;
174         }
175     }
177     if ($context->contextlevel == CONTEXT_COURSE) {
178         if ($filearea !== 'template') {
179             return false;
180         }
181     }
183     $relativepath = implode('/', $args);
184     $fullpath = "/$context->id/mod_feedback/$filearea/$itemid/$relativepath";
186     $fs = get_file_storage();
187     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
188         return false;
189     }
191     // finally send the file
192     send_stored_file($file, 0, 0, true); // download MUST be forced - security!
194     return false;
198 /**
199  * this will delete a given instance.
200  * all referenced data also will be deleted
201  *
202  * @global object
203  * @param int $id the instanceid of feedback
204  * @return boolean
205  */
206 function feedback_delete_instance($id) {
207     global $DB;
209     //get all referenced items
210     $feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$id));
212     //deleting all referenced items and values
213     if (is_array($feedbackitems)){
214         foreach($feedbackitems as $feedbackitem){
215             $DB->delete_records("feedback_value", array("item"=>$feedbackitem->id));
216             $DB->delete_records("feedback_valuetmp", array("item"=>$feedbackitem->id));
217         }
218         if($delitems = $DB->get_records("feedback_item", array("feedback"=>$id))) {
219             foreach($delitems as $delitem) {
220                 feedback_delete_item($delitem->id, false);
221             }
222         }
223     }
225     //deleting the referenced tracking data
226     $DB->delete_records('feedback_tracking', array('feedback'=>$id));
228     //deleting the completeds
229     $DB->delete_records("feedback_completed", array("feedback"=>$id));
231     //deleting the unfinished completeds
232     $DB->delete_records("feedback_completedtmp", array("feedback"=>$id));
234     //deleting old events
235     $DB->delete_records('event', array('modulename'=>'feedback', 'instance'=>$id));
236     return $DB->delete_records("feedback", array("id"=>$id));
239 /**
240  * this is called after deleting all instances if the course will be deleted.
241  * only templates have to be deleted
242  *
243  * @global object
244  * @param object $course
245  * @return boolean
246  */
247 function feedback_delete_course($course) {
248     global $DB;
250     //delete all templates of given course
251     return $DB->delete_records('feedback_template', array('course'=>$course->id));
254 /**
255  * Return a small object with summary information about what a
256  * user has done with a given particular instance of this module
257  * Used for user activity reports.
258  * $return->time = the time they did it
259  * $return->info = a short text description
260  *
261  * @param object $course
262  * @param object $user
263  * @param object $mod
264  * @param object $feedback
265  * @return object
266  */
267 function feedback_user_outline($course, $user, $mod, $feedback) {
268     return null;
271 /**
272  * Returns all users who has completed a specified feedback since a given time
273  * many thanks to Manolescu Dorel, who contributed these two functions
274  *
275  * @global object
276  * @global object
277  * @global object
278  * @global object
279  * @uses CONTEXT_MODULE
280  * @param array $activities Passed by reference
281  * @param int $index Passed by reference
282  * @param int $timemodified Timestamp
283  * @param int $courseid
284  * @param int $cmid
285  * @param int $userid
286  * @param int $groupid
287  * @return void
288  */
289 function feedback_get_recent_mod_activity(&$activities, &$index, $timemodified, $courseid, $cmid, $userid="", $groupid="")  {
290     global $CFG, $COURSE, $USER, $DB;
292     if ($COURSE->id == $courseid) {
293         $course = $COURSE;
294     } else {
295         $course = $DB->get_record('course', array('id'=>$courseid));
296     }
298     $modinfo =& get_fast_modinfo($course);
300     $cm = $modinfo->cms[$cmid];
302     $sqlargs = array();
304     //TODO: user user_picture::fields;
305     $sql = " SELECT fk . * , fc . * , u.firstname, u.lastname, u.email, u.picture, u.email
306                                             FROM {feedback_completed} fc
307                                                 JOIN {feedback} fk ON fk.id = fc.feedback
308                                                 JOIN {user} u ON u.id = fc.userid ";
310     if ($groupid) {
311         $sql .= " JOIN {groups_members} gm ON  gm.userid=u.id ";
312     }
314     $sql .= " WHERE fc.timemodified > ? AND fk.id = ? ";
315     $sqlargs[] = $timemodified;
316     $sqlargs[] = $cm->instace;
318     if ($userid) {
319         $sql .= " AND u.id = ? ";
320         $sqlargs[] = $userid;
321     }
323     if ($groupid) {
324         $sql .= " AND gm.groupid = ? ";
325         $sqlargs[] = $groupid;
326     }
328     if (!$feedbackitems = $DB->get_records_sql($sql, $sqlargs)) {
329         return;
330     }
332     $cm_context      = get_context_instance(CONTEXT_MODULE, $cm->id);
333     $accessallgroups = has_capability('moodle/site:accessallgroups', $cm_context);
334     $viewfullnames   = has_capability('moodle/site:viewfullnames', $cm_context);
335     $groupmode       = groups_get_activity_groupmode($cm, $course);
337     if (is_null($modinfo->groups)) {
338         $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
339     }
341     $aname = format_string($cm->name,true);
342     foreach ($feedbackitems as $feedbackitem) {
343         if ($feedbackitem->userid != $USER->id) {
345             if ($groupmode == SEPARATEGROUPS and !$accessallgroups) {
346                 $usersgroups = groups_get_all_groups($course->id, $feedbackitem->userid, $cm->groupingid);
347                 if (!is_array($usersgroups)) {
348                     continue;
349                 }
350                 $usersgroups = array_keys($usersgroups);
351                 $intersect = array_intersect($usersgroups, $modinfo->groups[$cm->id]);
352                 if (empty($intersect)) {
353                     continue;
354                 }
355             }
356        }
358         $tmpactivity = new stdClass();
360         $tmpactivity->type      = 'feedback';
361         $tmpactivity->cmid      = $cm->id;
362         $tmpactivity->name      = $aname;
363         $tmpactivity->sectionnum= $cm->sectionnum;
364         $tmpactivity->timestamp = $feedbackitem->timemodified;
366         $tmpactivity->content->feedbackid = $feedbackitem->id;
367         $tmpactivity->content->feedbackuserid = $feedbackitem->userid;
369         //TODO: add all necessary user fields, this is not enough for user_picture
370         $tmpactivity->user->userid   = $feedbackitem->userid;
371         $tmpactivity->user->fullname = fullname($feedbackitem, $viewfullnames);
372         $tmpactivity->user->picture  = $feedbackitem->picture;
374         $activities[$index++] = $tmpactivity;
375     }
377   return;
380 /**
381  * Prints all users who has completed a specified feedback since a given time
382  * many thanks to Manolescu Dorel, who contributed these two functions
383  *
384  * @global object
385  * @param object $activity
386  * @param int $courseid
387  * @param string $detail
388  * @param array $modnames
389  * @return void Output is echo'd
390  */
391 function feedback_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
392     global $CFG, $OUTPUT;
394     echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">';
396     echo "<tr><td class=\"userpicture\" valign=\"top\">";
397     echo $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid));
398     echo "</td><td>";
400     if ($detail) {
401         $modname = $modnames[$activity->type];
402         echo '<div class="title">';
403         echo "<img src=\"" . $OUTPUT->pix_url('icon', $activity->type) . "\" ".
404              "class=\"icon\" alt=\"$modname\" />";
405         echo "<a href=\"$CFG->wwwroot/mod/feedback/view.php?id={$activity->cmid}\">{$activity->name}</a>";
406         echo '</div>';
407     }
409     echo '<div class="title">';
410     echo '</div>';
412     echo '<div class="user">';
413     echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->userid}&amp;course=$courseid\">"
414          ."{$activity->user->fullname}</a> - ".userdate($activity->timestamp);
415     echo '</div>';
417     echo "</td></tr></table>";
419     return;
422 /**
423  * Obtains the automatic completion state for this feedback based on the condition
424  * in feedback settings.
425  *
426  * @param object $course Course
427  * @param object $cm Course-module
428  * @param int $userid User ID
429  * @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
430  * @return bool True if completed, false if not, $type if conditions not set.
431  */
432 function feedback_get_completion_state($course, $cm, $userid, $type) {
433     global $CFG, $DB;
435     // Get feedback details
436     $feedback = $DB->get_record('feedback', array('id'=>$cm->instance), '*', MUST_EXIST);
438     // If completion option is enabled, evaluate it and return true/false
439     if($feedback->completionsubmit) {
440         return $DB->record_exists('feedback_tracking', array('userid'=>$userid, 'feedback'=>$feedback->id));
441     } else {
442         // Completion option is not enabled so just return $type
443         return $type;
444     }
448 /**
449  * Print a detailed representation of what a  user has done with
450  * a given particular instance of this module, for user activity reports.
451  *
452  * @param object $course
453  * @param object $user
454  * @param object $mod
455  * @param object $feedback
456  * @return bool
457  */
458 function feedback_user_complete($course, $user, $mod, $feedback) {
459     return true;
462 /**
463  * @return bool true
464  */
465 function feedback_cron () {
466     return true;
469 /**
470  * @todo: deprecated - to be deleted in 2.2
471  * @return bool false
472  */
473 function feedback_get_participants($feedbackid) {
474     return false;
478 /**
479  * @return bool false
480  */
481 function feedback_scale_used ($feedbackid,$scaleid) {
482     return false;
485 /**
486  * Checks if scale is being used by any instance of feedback
487  *
488  * This is used to find out if scale used anywhere
489  * @param $scaleid int
490  * @return boolean True if the scale is used by any assignment
491  */
492 function feedback_scale_used_anywhere($scaleid) {
493     return false;
496 /**
497  * @return array
498  */
499 function feedback_get_view_actions() {
500     return array('view','view all');
503 /**
504  * @return array
505  */
506 function feedback_get_post_actions() {
507     return array('submit');
510 /**
511  * This function is used by the reset_course_userdata function in moodlelib.
512  * This function will remove all responses from the specified feedback
513  * and clean up any related data.
514  *
515  * @global object
516  * @global object
517  * @uses FEEDBACK_RESETFORM_RESET
518  * @uses FEEDBACK_RESETFORM_DROP
519  * @param object $data the data submitted from the reset course.
520  * @return array status array
521  */
522 function feedback_reset_userdata($data) {
523     global $CFG, $DB;
525     $resetfeedbacks = array();
526     $dropfeedbacks = array();
527     $status = array();
528     $componentstr = get_string('modulenameplural', 'feedback');
530     //get the relevant entries from $data
531     foreach($data as $key => $value) {
532         switch(true) {
533             case substr($key, 0, strlen(FEEDBACK_RESETFORM_RESET)) == FEEDBACK_RESETFORM_RESET:
534                 if($value == 1) {
535                     $templist = explode('_', $key);
536                     if(isset($templist[3]))$resetfeedbacks[] = intval($templist[3]);
537                 }
538             break;
539             case substr($key, 0, strlen(FEEDBACK_RESETFORM_DROP)) == FEEDBACK_RESETFORM_DROP:
540                 if($value == 1) {
541                     $templist = explode('_', $key);
542                     if(isset($templist[3]))$dropfeedbacks[] = intval($templist[3]);
543                 }
544             break;
545         }
546     }
548     //reset the selected feedbacks
549     foreach($resetfeedbacks as $id) {
550         $feedback = $DB->get_record('feedback', array('id'=>$id));
551         feedback_delete_all_completeds($id);
552         $status[] = array('component'=>$componentstr.':'.$feedback->name, 'item'=>get_string('resetting_data','feedback'), 'error'=>false);
553     }
555     //drop the selected feedbacks
556     // foreach($dropfeedbacks as $id) {
557         // $cm = get_coursemodule_from_instance('feedback', $id);
558         // feedback_delete_instance($id);
559         // feedback_delete_course_module($cm->id);
560         // $status[] = array('component'=>$componentstr, 'item'=>get_string('drop_feedback','feedback'), 'error'=>false);
561     // }
562     return $status;
565 /**
566  * Called by course/reset.php
567  *
568  * @global object
569  * @uses FEEDBACK_RESETFORM_RESET
570  * @param object $mform form passed by reference
571  */
572 function feedback_reset_course_form_definition(&$mform) {
573     global $COURSE, $DB;
575     $mform->addElement('header', 'feedbackheader', get_string('modulenameplural', 'feedback'));
577     if(!$feedbacks = $DB->get_records('feedback', array('course'=>$COURSE->id), 'name')){
578         return;
579     }
581     $mform->addElement('static', 'hint', get_string('resetting_data','feedback'));
582     foreach($feedbacks as $feedback) {
583         $mform->addElement('checkbox', FEEDBACK_RESETFORM_RESET.$feedback->id, $feedback->name);
584         // $mform->addElement('checkbox', FEEDBACK_RESETFORM_DROP.$feedback->id, get_string('drop_feedback','feedback'));
585     }
588 /**
589  * Course reset form defaults.
590  *
591  * @global object
592  * @uses FEEDBACK_RESETFORM_RESET
593  * @param object $course
594  */
595 function feedback_reset_course_form_defaults($course) {
596     global $DB;
598     $return = array();
599     if(!$feedbacks = $DB->get_records('feedback', array('course'=>$course->id), 'name')){
600         return;
601     }
602     foreach($feedbacks as $feedback) {
603         $return[FEEDBACK_RESETFORM_RESET.$feedback->id] = true;
604         // $return[FEEDBACK_RESETFORM_DROP.$feedback->id] = false;
605     }
606     return $return;
609 /**
610  * Called by course/reset.php and shows the formdata by coursereset.
611  * it prints checkboxes for each feedback available at the given course
612  * there are two checkboxes: 1) delete userdata and keep the feedback 2) delete userdata and drop the feedback
613  *
614  * @global object
615  * @uses FEEDBACK_RESETFORM_RESET
616  * @uses FEEDBACK_RESETFORM_DROP
617  * @param object $course
618  * @return void
619  */
620 function feedback_reset_course_form($course) {
621     global $DB, $OUTPUT;
623     echo get_string('resetting_feedbacks', 'feedback'); echo ':<br />';
624     if (!$feedbacks = $DB->get_records('feedback', array('course'=>$course->id), 'name')) {
625         return;
626     }
628     foreach($feedbacks as $feedback) {
629         echo '<p>';
630         echo get_string('name','feedback').': '.$feedback->name.'<br />';
631         echo html_writer::checkbox(FEEDBACK_RESETFORM_RESET.$feedback->id, 1, true, get_string('resetting_data','feedback'));
632         echo '<br />';
633         echo html_writer::checkbox(FEEDBACK_RESETFORM_DROP.$feedback->id, 1, false, get_string('drop_feedback','feedback'));
634         echo '</p>';
635     }
638 /**
639  * This creates new events given as timeopen and closeopen by $feedback.
640  *
641  * @global object
642  * @param object $feedback
643  * @return void
644  */
645 function feedback_set_events($feedback) {
646     global $DB;
648     // adding the feedback to the eventtable (I have seen this at quiz-module)
649     $DB->delete_records('event', array('modulename'=>'feedback', 'instance'=>$feedback->id));
651     if (!isset($feedback->coursemodule)) {
652         $cm = get_coursemodule_from_id('feedback', $feedback->id);
653         $feedback->coursemodule = $cm->id;
654     }
656     // the open-event
657     if($feedback->timeopen > 0) {
658         $event = NULL;
659         $event->name         = get_string('start', 'feedback').' '.$feedback->name;
660         $event->description  = format_module_intro('feedback', $feedback, $feedback->coursemodule);
661         $event->courseid     = $feedback->course;
662         $event->groupid      = 0;
663         $event->userid       = 0;
664         $event->modulename   = 'feedback';
665         $event->instance     = $feedback->id;
666         $event->eventtype    = 'open';
667         $event->timestart    = $feedback->timeopen;
668         $event->visible      = instance_is_visible('feedback', $feedback);
669         if($feedback->timeclose > 0) {
670             $event->timeduration = ($feedback->timeclose - $feedback->timeopen);
671         } else {
672             $event->timeduration = 0;
673         }
675         calendar_event::create($event);
676     }
678     // the close-event
679     if($feedback->timeclose > 0) {
680         $event = NULL;
681         $event->name         = get_string('stop', 'feedback').' '.$feedback->name;
682         $event->description  = format_module_intro('feedback', $feedback, $feedback->coursemodule);
683         $event->courseid     = $feedback->course;
684         $event->groupid      = 0;
685         $event->userid       = 0;
686         $event->modulename   = 'feedback';
687         $event->instance     = $feedback->id;
688         $event->eventtype    = 'close';
689         $event->timestart    = $feedback->timeclose;
690         $event->visible      = instance_is_visible('feedback', $feedback);
691         $event->timeduration = 0;
693         calendar_event::create($event);
694     }
697 /**
698  *  this function is called by {@link feedback_delete_userdata()}
699  *  it drops the feedback-instance from the course_module table
700  *
701  * @global object
702  *  @param int $id the id from the coursemodule
703  *  @return boolean
704  */
705 function feedback_delete_course_module($id) {
706     global $DB;
708     if (!$cm = $DB->get_record('course_modules', array('id'=>$id))) {
709         return true;
710     }
711     return $DB->delete_records('course_modules', array('id'=>$cm->id));
716 ////////////////////////////////////////////////
717 //functions to handle capabilities
718 ////////////////////////////////////////////////
720 /**
721  *  returns the context-id related to the given coursemodule-id
722  *
723  * @staticvar object $context
724  *  @param int $cmid the coursemodule-id
725  *  @return object $context
726  */
727 function feedback_get_context($cmid) {
728     static $context;
730     if(isset($context)) return $context;
732     if (!$context = get_context_instance(CONTEXT_MODULE, $cmid)) {
733             print_error('badcontext');
734     }
735     return $context;
738 /**
739  *  returns true if the current role is faked by switching role feature
740  *
741  * @global object
742  *  @return boolean
743  */
744 function feedback_check_is_switchrole(){
745     global $USER;
746     if(isset($USER->switchrole) AND is_array($USER->switchrole) AND count($USER->switchrole) > 0) {
747         return true;
748     }
749     return false;
752 /**
753  * count users which have not completed the feedback
754  *
755  * @global object
756  * @uses CONTEXT_MODULE
757  * @param object $cm
758  * @param int $group single groupid
759  * @param string $sort
760  * @param int $startpage
761  * @param int $pagecount
762  * @return object the userrecords
763  */
764 function feedback_get_incomplete_users($cm, $group = false, $sort = '', $startpage = false, $pagecount = false) {
765     global $DB;
767     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
769     //first get all user who can complete this feedback
770     $cap = 'mod/feedback:complete';
771     $fields = 'u.id, u.username';
772     if(!$allusers = get_users_by_capability($context, $cap, $fields, $sort, '', '', $group, '', true)) {
773         return false;
774     }
775     $allusers = array_keys($allusers);
777     //now get all completeds
778     if(!$completedusers = $DB->get_records_menu('feedback_completed', array('feedback'=>$cm->instance), '', 'userid,id')){
779         return $allusers;
780     }
781     $completedusers = array_keys($completedusers);
783     //now strike all completedusers from allusers
784     $allusers = array_diff($allusers, $completedusers);
786     //for paging I use array_slice()
787     if($startpage !== false AND $pagecount !== false) {
788         $allusers = array_slice($allusers, $startpage, $pagecount);
789     }
791     return $allusers;
794 /**
795  * count users which have not completed the feedback
796  *
797  * @global object
798  * @param object $cm
799  * @param int $group single groupid
800  * @return int count of userrecords
801  */
802 function feedback_count_incomplete_users($cm, $group = false) {
803     if($allusers = feedback_get_incomplete_users($cm, $group)) {
804         return count($allusers);
805     }
806     return 0;
809 /**
810  * count users which have completed a feedback
811  *
812  * @global object
813  * @uses FEEDBACK_ANONYMOUS_NO
814  * @param object $cm
815  * @param int $group single groupid
816  * @return int count of userrecords
817  */
818 function feedback_count_complete_users($cm, $group = false) {
819     global $DB;
821     $params = array(FEEDBACK_ANONYMOUS_NO, $cm->instance);
823     $fromgroup = '';
824     $wheregroup = '';
825     if($group) {
826         $fromgroup = ', {groups_members} g';
827         $wheregroup = ' AND g.groupid = ? AND g.userid = c.userid';
828         $params[] = $group;
829     }
831     $sql = 'SELECT COUNT(u.id) FROM {user} u, {feedback_completed} c'.$fromgroup.'
832               WHERE anonymous_response = ? AND u.id = c.userid AND c.feedback = ?
833               '.$wheregroup;
834               ;
836     return $DB->count_records_sql($sql, $params);
840 /**
841  * get users which have completed a feedback
842  *
843  * @global object
844  * @uses CONTEXT_MODULE
845  * @uses FEEDBACK_ANONYMOUS_NO
846  * @param object $cm
847  * @param int $group single groupid
848  * @param string $where a sql where condition (must end with " AND ")
849  * @param array parameters used in $where
850  * @param string $sort a table field
851  * @param int $startpage
852  * @param int $pagecount
853  * @return object the userrecords
854  */
855 function feedback_get_complete_users($cm, $group = false, $where = '', array $params = NULL, $sort = '', $startpage = false, $pagecount = false) {
856     global $DB;
858     if (!$context = get_context_instance(CONTEXT_MODULE, $cm->id)) {
859             print_error('badcontext');
860     }
862     $params = (array)$params;
864     $params['anon'] = FEEDBACK_ANONYMOUS_NO;
865     $params['instance'] = $cm->instance;
867     $fromgroup = '';
868     $wheregroup = '';
869     if ($group) {
870         $fromgroup = ', {groups_members} g';
871         $wheregroup = ' AND g.groupid = :group AND g.userid = c.userid';
872         $params['group'] = $group;
873     }
875     if ($sort) {
876         $sortsql = ' ORDER BY '.$sort;
877     } else {
878         $sortsql = '';
879     }
881     $ufields = user_picture::fields('u');
882     $sql = 'SELECT DISTINCT '.$ufields.' FROM {user} u, {feedback_completed} c '.$fromgroup.'
883               WHERE '.$where.' anonymous_response = :anon AND u.id = c.userid AND c.feedback = :instance
884               '.$wheregroup.$sortsql;
886     if ($startpage === false OR $pagecount === false) {
887         $startpage = false;
888         $pagecount = false;
889     }
890     return $DB->get_records_sql($sql, $params, $startpage, $pagecount);
893 /**
894  * get users which have the viewreports-capability
895  *
896  * @uses CONTEXT_MODULE
897  * @param int $cmid
898  * @param mixed $groups single groupid or array of groupids - group(s) user is in
899  * @return object the userrecords
900  */
901 function feedback_get_viewreports_users($cmid, $groups = false) {
903     if (!$context = get_context_instance(CONTEXT_MODULE, $cmid)) {
904             print_error('badcontext');
905     }
907     //description of the call below: get_users_by_capability($context, $capability, $fields='', $sort='', $limitfrom='', $limitnum='', $groups='', $exceptions='', $doanything=true)
908     return get_users_by_capability($context, 'mod/feedback:viewreports', '', 'lastname', '', '', $groups, '', false);
911 /**
912  * get users which have the receivemail-capability
913  *
914  * @uses CONTEXT_MODULE
915  * @param int $cmid
916  * @param mixed $groups single groupid or array of groupids - group(s) user is in
917  * @return object the userrecords
918  */
919 function feedback_get_receivemail_users($cmid, $groups = false) {
921     if (!$context = get_context_instance(CONTEXT_MODULE, $cmid)) {
922             print_error('badcontext');
923     }
925     //description of the call below: get_users_by_capability($context, $capability, $fields='', $sort='', $limitfrom='', $limitnum='', $groups='', $exceptions='', $doanything=true)
926     return get_users_by_capability($context, 'mod/feedback:receivemail', '', 'lastname', '', '', $groups, '', false);
929 ////////////////////////////////////////////////
930 //functions to handle the templates
931 ////////////////////////////////////////////////
932 ////////////////////////////////////////////////
934 /**
935  * creates a new template-record.
936  *
937  * @global object
938  * @param int $courseid
939  * @param string $name the name of template shown in the templatelist
940  * @param int $ispublic 0:privat 1:public
941  * @return int the new templateid
942  */
943 function feedback_create_template($courseid, $name, $ispublic = 0) {
944     global $DB;
946     $templ = new stdClass();
947     $templ->course   = $courseid;
948     $templ->name     = $name;
949     $templ->ispublic = $ispublic;
951     $templid = $DB->insert_record('feedback_template', $templ);
952     return $DB->get_record('feedback_template', array('id'=>$templid));
955 /**
956  * creates new template items.
957  * all items will be copied and the attribute feedback will be set to 0
958  * and the attribute template will be set to the new templateid
959  *
960  * @global object
961  * @uses CONTEXT_MODULE
962  * @uses CONTEXT_COURSE
963  * @param object $feedback
964  * @param string $name the name of template shown in the templatelist
965  * @param int $ispublic 0:privat 1:public
966  * @return boolean
967  */
968 function feedback_save_as_template($feedback, $name, $ispublic = 0) {
969     global $DB;
970     $fs = get_file_storage();
972     if (!$feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$feedback->id))) {
973         return false;
974     }
976     if (!$newtempl = feedback_create_template($feedback->course, $name, $ispublic)) {
977         return false;
978     }
980     //files in the template_item are in the context of the current course
981     //files in the feedback_item are in the feedback_context of the feedback
982     $c_context = get_context_instance(CONTEXT_COURSE, $newtempl->course);
983     $cm = get_coursemodule_from_instance('feedback', $feedback->id);
984     $f_context = get_context_instance(CONTEXT_MODULE, $cm->id);
986     //create items of this new template
987     //depend items we are storing temporary in an mapping list array(new id => dependitem)
988     //we also store a mapping of all items array(oldid => newid)
989     $dependitemsmap = array();
990     $itembackup = array();
991     foreach($feedbackitems as $item) {
993         $t_item = clone($item);
995         unset($t_item->id);
996         $t_item->feedback = 0;
997         $t_item->template     = $newtempl->id;
998         $t_item->id = $DB->insert_record('feedback_item', $t_item);
999         //copy all included files to the feedback_template filearea
1000         if ($itemfiles = $fs->get_area_files($f_context->id, 'mod_feedback', 'item', $item->id, "id", false)) {
1001             foreach($itemfiles as $ifile) {
1002                 $file_record = new stdClass();
1003                 $file_record->contextid = $c_context->id;
1004                 $file_record->component = 'mod_feedback';
1005                 $file_record->filearea = 'template';
1006                 $file_record->itemid = $t_item->id;
1007                 $fs->create_file_from_storedfile($file_record, $ifile);
1008             }
1009         }
1011         $itembackup[$item->id] = $t_item->id;
1012         if($t_item->dependitem) {
1013             $dependitemsmap[$t_item->id] = $t_item->dependitem;
1014         }
1016     }
1018     //remapping the dependency
1019     foreach($dependitemsmap as $key => $dependitem) {
1020         $newitem = $DB->get_record('feedback_item', array('id'=>$key));
1021         $newitem->dependitem = $itembackup[$newitem->dependitem];
1022         $DB->update_record('feedback_item', $newitem);
1023     }
1025     return true;
1028 /**
1029  * deletes all feedback_items related to the given template id
1030  *
1031  * @global object
1032  * @uses CONTEXT_COURSE
1033  * @param int $id the templateid
1034  * @return void
1035  */
1036 function feedback_delete_template($id) {
1037     global $DB;
1039     $template = $DB->get_record("feedback_template", array("id"=>$id));
1041     //deleting the files from the item
1042     $fs = get_file_storage();
1043     $context = get_context_instance(CONTEXT_COURSE, $template->course);
1046     if($t_items = $DB->get_records("feedback_item", array("template"=>$id))) {
1047         foreach($t_items as $t_item) {
1048             if ($templatefiles = $fs->get_area_files($context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) {
1049                 $fs->delete_area_files($context->id, 'mod_feedback', 'template', $t_item->id);
1050             }
1051         }
1052     }
1053     $DB->delete_records("feedback_template", array("id"=>$id));
1056 /**
1057  * creates new feedback_item-records from template.
1058  * if $deleteold is set true so the existing items of the given feedback will be deleted
1059  * if $deleteold is set false so the new items will be appanded to the old items
1060  *
1061  * @global object
1062  * @uses CONTEXT_COURSE
1063  * @uses CONTEXT_MODULE
1064  * @param object $feedback
1065  * @param int $templateid
1066  * @param boolean $deleteold
1067  */
1068 function feedback_items_from_template($feedback, $templateid, $deleteold = false) {
1069     global $DB, $CFG;
1071     require_once($CFG->libdir.'/completionlib.php');
1073     $fs = get_file_storage();
1075     //get all templateitems
1076     if(!$templitems = $DB->get_records('feedback_item', array('template'=>$templateid))) {
1077         return false;
1078     }
1080     //files in the template_item are in the context of the current course
1081     //files in the feedback_item are in the feedback_context of the feedback
1082     $c_context = get_context_instance(CONTEXT_COURSE, $feedback->course);
1083     $course = $DB->get_record('course', array('id'=>$feedback->course));
1084     $cm = get_coursemodule_from_instance('feedback', $feedback->id);
1085     $f_context = get_context_instance(CONTEXT_MODULE, $cm->id);
1087     //if deleteold then delete all old items before
1088     //get all items
1089     if($deleteold) {
1090         if($feedbackitems = $DB->get_records('feedback_item', array('feedback'=>$feedback->id))) {
1091             //delete all items of this feedback
1092             foreach($feedbackitems as $item) {
1093                 feedback_delete_item($item->id, false);
1094             }
1095             //delete tracking-data
1096             $DB->delete_records('feedback_tracking', array('feedback'=>$feedback->id));
1098             if($completeds = $DB->get_records('feedback_completed', array('feedback'=>$feedback->id))) {
1099                 $completion = new completion_info($course);
1100                 foreach($completeds as $completed) {
1101                     // Update completion state
1102                     if ($completion->is_enabled($cm) && $feedback->completionsubmit) {
1103                         $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
1104                     }
1105                     $DB->delete_records('feedback_completed', array('id'=>$completed->id));
1106                 }
1107             }
1108             $DB->delete_records('feedback_completedtmp', array('feedback'=>$feedback->id));
1109         }
1110         $positionoffset = 0;
1111     } else {
1112         //if the old items are kept the new items will be appended
1113         //therefor the new position has an offset
1114         $positionoffset = $DB->count_records('feedback_item', array('feedback'=>$feedback->id));
1115     }
1117     //create items of this new template
1118     //depend items we are storing temporary in an mapping list array(new id => dependitem)
1119     //we also store a mapping of all items array(oldid => newid)
1120     $dependitemsmap = array();
1121     $itembackup = array();
1122     foreach($templitems as $t_item) {
1123         $item = clone($t_item);
1124         unset($item->id);
1125         $item->feedback = $feedback->id;
1126         $item->template = 0;
1127         $item->position = $item->position + $positionoffset;
1129         $item->id = $DB->insert_record('feedback_item', $item);
1131         //TODO: moving the files to the new items
1132         if ($templatefiles = $fs->get_area_files($c_context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) {
1133             foreach($templatefiles as $tfile) {
1134                 $file_record = new stdClass();
1135                 $file_record->contextid = $f_context->id;
1136                 $file_record->component = 'mod_feedback';
1137                 $file_record->filearea = 'item';
1138                 $file_record->itemid = $item->id;
1139                 $fs->create_file_from_storedfile($file_record, $tfile);
1140             }
1141         }
1143         $itembackup[$t_item->id] = $item->id;
1144         if($item->dependitem) {
1145             $dependitemsmap[$item->id] = $item->dependitem;
1146         }
1147     }
1149     //remapping the dependency
1150     foreach($dependitemsmap as $key => $dependitem) {
1151         $newitem = $DB->get_record('feedback_item', array('id'=>$key));
1152         $newitem->dependitem = $itembackup[$newitem->dependitem];
1153         $DB->update_record('feedback_item', $newitem);
1154     }
1157 /**
1158  * get the list of available templates.
1159  * if the $onlyown param is set true so only templates from own course will be served
1160  * this is important for droping templates
1161  *
1162  * @global object
1163  * @param object $course
1164  * @param boolean $onlyown
1165  * @return array the template recordsets
1166  */
1167 function feedback_get_template_list($course, $onlyown = false) {
1168     global $DB;
1170     if ($onlyown) {
1171         $templates = $DB->get_records('feedback_template', array('course'=>$course->id));
1172     } else {
1173         $templates = $DB->get_records_select('feedback_template', 'course = ? OR ispublic = 1', array($course->id));
1174     }
1175     return $templates;
1178 ////////////////////////////////////////////////
1179 //Handling der Items
1180 ////////////////////////////////////////////////
1181 ////////////////////////////////////////////////
1183 /**
1184  * load the lib.php from item-plugin-dir and returns the instance of the itemclass
1185  *
1186  * @global object
1187  * @param object $item
1188  * @return object the instanz of itemclass
1189  */
1190 function feedback_get_item_class($typ) {
1191     global $CFG;
1193     //get the class of item-typ
1194     $itemclass = 'feedback_item_'.$typ;
1195     //get the instance of item-class
1196     if (!class_exists($itemclass)) {
1197         require_once($CFG->dirroot.'/mod/feedback/item/'.$typ.'/lib.php');
1198     }
1199     return new $itemclass();
1202 /**
1203  * load the available item plugins from given subdirectory of $CFG->dirroot
1204  * the default is "mod/feedback/item"
1205  *
1206  * @global object
1207  * @param string $dir the subdir
1208  * @return array pluginnames as string
1209  */
1210 function feedback_load_feedback_items($dir = 'mod/feedback/item') {
1211     global $CFG;
1212     $names = get_list_of_plugins($dir);
1213     $ret_names = array();
1215     foreach($names as $name) {
1216         require_once($CFG->dirroot.'/'.$dir.'/'.$name.'/lib.php');
1217         if(class_exists('feedback_item_'.$name)) {
1218           $ret_names[] = $name;
1219         }
1220     }
1221     return $ret_names;
1224 /**
1225  * load the available item plugins to use as dropdown-options
1226  *
1227  * @global object
1228  * @return array pluginnames as string
1229  */
1230 function feedback_load_feedback_items_options() {
1231     global $CFG;
1233     $feedback_options = array("pagebreak" => get_string('add_pagebreak', 'feedback'));
1235     if (!$feedback_names = feedback_load_feedback_items('mod/feedback/item')) {
1236         return array();
1237     }
1239     foreach($feedback_names as $fn) {
1240         $feedback_options[$fn] = get_string($fn,'feedback');
1241     }
1242     asort($feedback_options);
1243     $feedback_options = array_merge( array(' ' => get_string('select')), $feedback_options );
1244     return $feedback_options;
1247 /**
1248  * load the available items for the depend item dropdown list shown in the edit_item form
1249  *
1250  * @global object
1251  * @param object $feedback
1252  * @param object $item the item of the edit_item form
1253  * @return array all items except the item $item, labels and pagebreaks
1254  */
1255 function feedback_get_depend_candidates_for_item($feedback, $item) {
1256     global $DB;
1257     //all items for dependitem
1258     $where = "feedback = ? AND typ != 'pagebreak' AND hasvalue = 1";
1259     $params = array($feedback->id);
1260     if(isset($item->id) AND $item->id) {
1261         $where .= ' AND id != ?';
1262         $params[] = $item->id;
1263     }
1264     $dependitems = array(0 => get_string('choose'));
1265     if(!$feedbackitems = $DB->get_records_select_menu('feedback_item', $where, $params, 'position', 'id, label')) {
1266         return $dependitems;
1267     }
1268     //adding the choose-option
1269     foreach($feedbackitems as $key=>$val) {
1270         $dependitems[$key] = $val;
1271     }
1272     return $dependitems;
1275 /**
1276  * creates a new item-record
1277  *
1278  * @global object
1279  * @param object $data the data from edit_item_form
1280  * @return int the new itemid
1281  */
1282 function feedback_create_item($data) {
1283     global $DB;
1285     $item = new stdClass();
1286     $item->feedback = $data->feedbackid;
1288     $item->template=0;
1289     if (isset($data->templateid)) {
1290             $item->template = intval($data->templateid);
1291     }
1293     $itemname = trim($data->itemname);
1294     $item->name = ($itemname ? $data->itemname : get_string('no_itemname', 'feedback'));
1296     if (!empty($data->itemlabel)) {
1297         $item->label = trim($data->itemlabel);
1298     } else {
1299         $item->label = get_string('no_itemlabel', 'feedback');
1300     }
1302     $itemobj = feedback_get_item_class($data->typ);
1303     // $item->presentation = $itemobj->get_presentation($data);
1304     $item->presentation = ''; //the date comes from postupdate() of the itemobj
1306     $item->hasvalue = $itemobj->get_hasvalue();
1308     $item->typ = $data->typ;
1309     $item->position = $data->position;
1311     $item->required=0;
1312     if (!empty($data->required)) {
1313         $item->required = $data->required;
1314     }
1316     $item->id = $DB->insert_record('feedback_item', $item);
1318     //move all itemdata to the data
1319     $data->id = $item->id;
1320     $data->feedback = $item->feedback;
1321     $data->name = $item->name;
1322     $data->label = $item->label;
1323     $data->required = $item->required;
1324     return $itemobj->postupdate($data);
1327 /**
1328  * save the changes of a given item.
1329  *
1330  * @global object
1331  * @param object $item
1332  * @return boolean
1333  */
1334 function feedback_update_item($item){
1335     global $DB;
1336     return $DB->update_record("feedback_item", $item);
1339 /**
1340  * deletes an item and also deletes all related values
1341  *
1342  * @global object
1343  * @uses CONTEXT_MODULE
1344  * @param int $itemid
1345  * @param boolean $renumber should the kept items renumbered Yes/No
1346  * @return void
1347  */
1348 function feedback_delete_item($itemid, $renumber = true){
1349     global $DB;
1352     $item = $DB->get_record('feedback_item', array('id'=>$itemid));
1354     //deleting the files from the item
1355     $fs = get_file_storage();
1356     if (!$cm = get_coursemodule_from_instance('feedback', $item->feedback)) {
1357         return false;
1358     }
1359     $context = get_context_instance(CONTEXT_MODULE, $cm->id);
1361     if ($itemfiles = $fs->get_area_files($context->id, 'mod_feedback', 'item', $item->id, "id", false)) {
1362         $fs->delete_area_files($context->id, 'mod_feedback', 'item', $item->id);
1363     }
1365     $DB->delete_records("feedback_value", array("item"=>$itemid));
1366     $DB->delete_records("feedback_valuetmp", array("item"=>$itemid));
1368     //remove all depends
1369     $DB->set_field('feedback_item', 'dependvalue', '', array('dependitem'=>$itemid));
1370     $DB->set_field('feedback_item', 'dependitem', 0, array('dependitem'=>$itemid));
1372     $DB->delete_records("feedback_item", array("id"=>$itemid));
1373     if($renumber) {
1374         feedback_renumber_items($item->feedback);
1375     }
1378 /**
1379  * deletes all items of the given feedbackid
1380  *
1381  * @global object
1382  * @param int $feedbackid
1383  * @return void
1384  */
1385 function feedback_delete_all_items($feedbackid){
1386     global $DB, $CFG;
1387     require_once($CFG->libdir.'/completionlib.php');
1389     if(!$feedback = $DB->get_record('feedback', array('id'=>$feedbackid))) {
1390         return false;
1391     }
1393     if (!$cm = get_coursemodule_from_instance('feedback', $feedback->id)) {
1394         return false;
1395     }
1397     if(!$course = $DB->get_record('course', array('id'=>$feedback->course))) {
1398         return false;
1399     }
1401     if(!$items = $DB->get_records('feedback_item', array('feedback'=>$feedbackid))) {
1402         return;
1403     }
1404     foreach($items as $item) {
1405         feedback_delete_item($item->id, false);
1406     }
1407     // $DB->delete_records('feedback_completed', array('feedback'=>$feedbackid));
1408     if($completeds = $DB->get_records('feedback_completed', array('feedback'=>$feedback->id))) {
1409         $completion = new completion_info($course);
1410         foreach($completeds as $completed) {
1411             // Update completion state
1412             if ($completion->is_enabled($cm) && $feedback->completionsubmit) {
1413                 $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
1414             }
1415             $DB->delete_records('feedback_completed', array('id'=>$completed->id));
1416         }
1417     }
1419     $DB->delete_records('feedback_completedtmp', array('feedback'=>$feedbackid));
1423 /**
1424  * this function toggled the item-attribute required (yes/no)
1425  *
1426  * @global object
1427  * @param object $item
1428  * @return boolean
1429  */
1430 function feedback_switch_item_required($item) {
1431     global $DB, $CFG;
1433     $itemobj = feedback_get_item_class($item->typ);
1435     if($itemobj->can_switch_require()) {
1436         $DB->set_field('feedback_item', 'required', (int)!(bool)$item->required, array('id'=>$item->id));
1437     }
1438     return true;
1441 /**
1442  * renumbers all items of the given feedbackid
1443  *
1444  * @global object
1445  * @param int $feedbackid
1446  * @return void
1447  */
1448 function feedback_renumber_items($feedbackid){
1449     global $DB;
1451     $items = $DB->get_records('feedback_item', array('feedback'=>$feedbackid), 'position');
1452     $pos = 1;
1453     if($items) {
1454         foreach($items as $item){
1455             $DB->set_field('feedback_item', 'position', $pos, array('id'=>$item->id));
1456             $pos++;
1457         }
1458     }
1461 /**
1462  * this decreases the position of the given item
1463  *
1464  * @global object
1465  * @param object $item
1466  * @return bool
1467  */
1468 function feedback_moveup_item($item){
1469     global $DB;
1471     if($item->position == 1) {
1472         return true;
1473     }
1475     if(!$items = $DB->get_records('feedback_item', array('feedback'=>$item->feedback), 'position')) {
1476         return false;
1477     }
1479     $itembefore = null;
1480     foreach($items as $i) {
1481         if($i->id == $item->id) {
1482             if(is_null($itembefore)) {
1483                 return true;
1484             }
1485             $itembefore->position = $item->position;
1486             $item->position--;
1487             feedback_update_item($itembefore);
1488             feedback_update_item($item);
1489             feedback_renumber_items($item->feedback);
1490             return true;
1491         }
1492         $itembefore = $i;
1493     }
1494     return false;
1497 /**
1498  * this increased the position of the given item
1499  *
1500  * @global object
1501  * @param object $item
1502  * @return bool
1503  */
1504 function feedback_movedown_item($item){
1505     global $DB;
1507     if(!$items = $DB->get_records('feedback_item', array('feedback'=>$item->feedback), 'position')) {
1508         return false;
1509     }
1511     $movedownitem = null;
1512     foreach($items as $i) {
1513         if(!is_null($movedownitem) AND $movedownitem->id == $item->id) {
1514             $movedownitem->position = $i->position;
1515             $i->position--;
1516             feedback_update_item($movedownitem);
1517             feedback_update_item($i);
1518             feedback_renumber_items($item->feedback);
1519             return true;
1520         }
1521         $movedownitem = $i;
1522     }
1523     return false;
1526 /**
1527  * here the position of the given item will be set to the value in $pos
1528  *
1529  * @global object
1530  * @param object $moveitem
1531  * @param int $pos
1532  * @return boolean
1533  */
1534 function feedback_move_item($moveitem, $pos){
1535     global $DB;
1537     if (!$allitems = $DB->get_records('feedback_item', array('feedback'=>$moveitem->feedback), 'position')) {
1538         return false;
1539     }
1540     if (is_array($allitems)) {
1541         $index = 1;
1542         foreach($allitems as $item) {
1543             if($index == $pos) {
1544                 $index++;
1545             }
1546             if($item->id == $moveitem->id) {
1547                 $moveitem->position = $pos;
1548                 feedback_update_item($moveitem);
1549                 continue;
1550             }
1551             $item->position = $index;
1552             feedback_update_item($item);
1553             $index++;
1554         }
1555         return true;
1556     }
1557     return false;
1560 /**
1561  * prints the given item as a preview.
1562  * each item-class has an own print_item_preview function implemented.
1563  *
1564  * @global object
1565  * @param object $item the item what we want to print out
1566  * @return void
1567  */
1568 function feedback_print_item_preview($item){
1569     global $CFG;
1570     if($item->typ == 'pagebreak') {
1571         return;
1572     }
1573     //get the instance of the item-class
1574     $itemobj = feedback_get_item_class($item->typ);
1575     $itemobj->print_item_preview($item);
1578 /**
1579  * prints the given item in the completion form.
1580  * each item-class has an own print_item_complete function implemented.
1581  *
1582  * @param object $item the item what we want to print out
1583  * @param mixed $value the value
1584  * @param boolean $highlightrequire if this set true and the value are false on completing so the item will be highlighted
1585  * @return void
1586  */
1587 function feedback_print_item_complete($item, $value = false, $highlightrequire = false){
1588     global $CFG;
1589     if($item->typ == 'pagebreak') {
1590         return;
1591     }
1593     //get the instance of the item-class
1594     $itemobj = feedback_get_item_class($item->typ);
1595     $itemobj->print_item_complete($item, $value, $highlightrequire);
1598 /**
1599  * prints the given item in the show entries page.
1600  * each item-class has an own print_item_show_value function implemented.
1601  *
1602  * @param object $item the item what we want to print out
1603  * @param mixed $value
1604  * @return void
1605  */
1606 function feedback_print_item_show_value($item, $value = false){
1607     global $CFG;
1608     if($item->typ == 'pagebreak') {
1609         return;
1610     }
1612     //get the instance of the item-class
1613     $itemobj = feedback_get_item_class($item->typ);
1614     $itemobj->print_item_show_value($item, $value);
1617 /**
1618  * if the user completes a feedback and there is a pagebreak so the values are saved temporary.
1619  * the values are not saved permanently until the user click on save button
1620  *
1621  * @global object
1622  * @param object $feedbackcompleted
1623  * @return object temporary saved completed-record
1624  */
1625 function feedback_set_tmp_values($feedbackcompleted) {
1626     global $DB;
1628     //first we create a completedtmp
1629     $tmpcpl = new stdClass();
1630     foreach($feedbackcompleted as $key => $value) {
1631         $tmpcpl->{$key} = $value;
1632     }
1633     // $tmpcpl = $feedbackcompleted;
1634     unset($tmpcpl->id);
1635     $tmpcpl->timemodified = time();
1636     $tmpcpl->id = $DB->insert_record('feedback_completedtmp', $tmpcpl);
1637     //get all values of original-completed
1638     if(!$values = $DB->get_records('feedback_value', array('completed'=>$feedbackcompleted->id))) {
1639         return;
1640     }
1641     foreach($values as $value) {
1642         unset($value->id);
1643         $value->completed = $tmpcpl->id;
1644         $DB->insert_record('feedback_valuetmp', $value);
1645     }
1646     return $tmpcpl;
1649 /**
1650  * this saves the temporary saved values permanently
1651  *
1652  * @global object
1653  * @param object $feedbackcompletedtmp the temporary completed
1654  * @param object $feedbackcompleted the target completed
1655  * @param int $userid
1656  * @return int the id of the completed
1657  */
1658 function feedback_save_tmp_values($feedbackcompletedtmp, $feedbackcompleted, $userid) {
1659     global $DB;
1661     $tmpcplid = $feedbackcompletedtmp->id;
1662     if($feedbackcompleted) {
1663         //first drop all existing values
1664         $DB->delete_records('feedback_value', array('completed'=>$feedbackcompleted->id));
1665         //update the current completed
1666         $feedbackcompleted->timemodified = time();
1667         $DB->update_record('feedback_completed', $feedbackcompleted);
1668     }else {
1669         $feedbackcompleted = clone($feedbackcompletedtmp);
1670         $feedbackcompleted->id = '';
1671         $feedbackcompleted->userid = $userid;
1672         $feedbackcompleted->timemodified = time();
1673         $feedbackcompleted->id = $DB->insert_record('feedback_completed', $feedbackcompleted);
1674     }
1676     //save all the new values from feedback_valuetmp
1677     //get all values of tmp-completed
1678     if(!$values = $DB->get_records('feedback_valuetmp', array('completed'=>$feedbackcompletedtmp->id))) {
1679         return false;
1680     }
1681     foreach($values as $value) {
1682         //check if there are depend items
1683         $item = $DB->get_record('feedback_item', array('id'=>$value->item));
1684         if($item->dependitem > 0) {
1685             $check = feedback_compare_item_value($tmpcplid, $item->dependitem, $item->dependvalue, true);
1686         }else {
1687             $check = true;
1688         }
1689         if($check) {
1690             unset($value->id);
1691             $value->completed = $feedbackcompleted->id;
1692             $DB->insert_record('feedback_value', $value);
1693         }
1694     }
1695     //drop all the tmpvalues
1696     $DB->delete_records('feedback_valuetmp', array('completed'=>$tmpcplid));
1697     $DB->delete_records('feedback_completedtmp', array('id'=>$tmpcplid));
1698     return $feedbackcompleted->id;
1702 /**
1703  * deletes the given temporary completed and all related temporary values
1704  *
1705  * @global object
1706  * @param int $tmpcplid
1707  * @return void
1708  */
1709 function feedback_delete_completedtmp($tmpcplid) {
1710     global $DB;
1712     $DB->delete_records('feedback_valuetmp', array('completed'=>$tmpcplid));
1713     $DB->delete_records('feedback_completedtmp', array('id'=>$tmpcplid));
1716 ////////////////////////////////////////////////
1717 ////////////////////////////////////////////////
1718 ////////////////////////////////////////////////
1719 //functions to handle the pagebreaks
1720 ////////////////////////////////////////////////
1722 /**
1723  * this creates a pagebreak.
1724  * a pagebreak is a special kind of item
1725  *
1726  * @global object
1727  * @param int $feedbackid
1728  * @return mixed false if there already is a pagebreak on last position or the id of the pagebreak-item
1729  */
1730 function feedback_create_pagebreak($feedbackid) {
1731     global $DB;
1733     //check if there already is a pagebreak on the last position
1734     $lastposition = $DB->count_records('feedback_item', array('feedback'=>$feedbackid));
1735     if($lastposition == feedback_get_last_break_position($feedbackid)) {
1736         return false;
1737     }
1739     $item = new stdClass();
1740     $item->feedback = $feedbackid;
1742     $item->template=0;
1744     $item->name = '';
1746     $item->presentation = '';
1747     $item->hasvalue = 0;
1749     $item->typ = 'pagebreak';
1750     $item->position = $lastposition + 1;
1752     $item->required=0;
1754     return $DB->insert_record('feedback_item', $item);
1757 /**
1758  * get all positions of pagebreaks in the given feedback
1759  *
1760  * @global object
1761  * @param int $feedbackid
1762  * @return array all ordered pagebreak positions
1763  */
1764 function feedback_get_all_break_positions($feedbackid) {
1765     global $DB;
1767     if(!$allbreaks = $DB->get_records_menu('feedback_item', array('typ'=>'pagebreak', 'feedback'=>$feedbackid), 'position', 'id, position')) return false;
1768     return array_values($allbreaks);
1771 /**
1772  * get the position of the last pagebreak
1773  *
1774  * @param int $feedbackid
1775  * @return int the position of the last pagebreak
1776  */
1777 function feedback_get_last_break_position($feedbackid) {
1778     if(!$allbreaks = feedback_get_all_break_positions($feedbackid)) {
1779         return false;
1780     }
1781     return $allbreaks[count($allbreaks) - 1];
1784 /**
1785  * this returns the position where the user can continue the completing.
1786  *
1787  * @global object
1788  * @global object
1789  * @global object
1790  * @param int $feedbackid
1791  * @param int $courseid
1792  * @param string $guestid this id will be saved temporary and is unique
1793  * @return int the position to continue
1794  */
1795 function feedback_get_page_to_continue($feedbackid, $courseid = false, $guestid = false) {
1796     global $CFG, $USER, $DB;
1798     //is there any break?
1800     if(!$allbreaks = feedback_get_all_break_positions($feedbackid)) return false;
1802     $params = array();
1803     if($courseid) {
1804         $courseselect = "AND fv.course_id = :courseid";
1805         $params['courseid'] = $courseid;
1806     }else {
1807         $courseselect = '';
1808     }
1810     if($guestid) {
1811         $userselect = "AND fc.guestid = :guestid";
1812         $usergroup = "GROUP BY fc.guestid";
1813         $params['guestid'] = $guestid;
1814     }else {
1815         $userselect = "AND fc.userid = :userid";
1816         $usergroup = "GROUP BY fc.userid";
1817         $params['userid'] = $USER->id;
1818     }
1820     $sql =  "SELECT MAX(fi.position)
1821                FROM {feedback_completedtmp} fc, {feedback_valuetmp} fv, {feedback_item} fi
1822               WHERE fc.id = fv.completed
1823                     $userselect
1824                     AND fc.feedback = :feedbackid
1825                     $courseselect
1826                     AND fi.id = fv.item
1827          $usergroup";
1828     $params['feedbackid'] = $feedbackid;
1830     $lastpos = $DB->get_field_sql($sql, $params);
1832     //the index of found pagebreak is the searched pagenumber
1833     foreach($allbreaks as $pagenr => $br) {
1834         if($lastpos < $br) return $pagenr;
1835     }
1836     return count($allbreaks);
1839 ////////////////////////////////////////////////
1840 ////////////////////////////////////////////////
1841 ////////////////////////////////////////////////
1842 //functions to handle the values
1843 ////////////////////////////////////////////////
1845 /**
1846  * this saves the values of an completed.
1847  * if the param $tmp is set true so the values are saved temporary in table feedback_valuetmp.
1848  * if there is already a completed and the userid is set so the values are updated.
1849  * on all other things new value records will be created.
1850  *
1851  * @global object
1852  * @param int $userid
1853  * @param boolean $tmp
1854  * @return mixed false on error or the completeid
1855  */
1856 function feedback_save_values($usrid, $tmp = false) {
1857     global $DB;
1859     $completedid = optional_param('completedid', false, PARAM_INT);
1861     $tmpstr = $tmp ? 'tmp' : '';
1862     $time = time();
1863     $timemodified = mktime(0, 0, 0, date('m', $time),date('d', $time),date('Y', $time));
1865     if($usrid == 0) {
1866         return feedback_create_values($usrid, $timemodified, $tmp);
1867     }
1868     if(!$completedid or !$completed = $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$completedid))) {
1869         return feedback_create_values($usrid, $timemodified, $tmp);
1870     }else{
1871         $completed->timemodified = $timemodified;
1872         return feedback_update_values($completed, $tmp);
1873     }
1876 /**
1877  * this saves the values from anonymous user such as guest on the main-site
1878  *
1879  * @global object
1880  * @param string $guestid the unique guestidentifier
1881  * @return mixed false on error or the completeid
1882  */
1883 function feedback_save_guest_values($guestid) {
1884     global $DB;
1886     $completedid = optional_param('completedid', false, PARAM_INT);
1888     $timemodified = time();
1889     if(!$completed = $DB->get_record('feedback_completedtmp', array('id'=>$completedid))) {
1890         return feedback_create_values(0, $timemodified, true, $guestid);
1891     }else {
1892         $completed->timemodified = $timemodified;
1893         return feedback_update_values($completed, true);
1894     }
1897 /**
1898  * get the value from the given item related to the given completed.
1899  * the value can come as temporary or as permanently value. the deciding is done by $tmp
1900  *
1901  * @global object
1902  * @param int $completeid
1903  * @param int $itemid
1904  * @param boolean $tmp
1905  * @return mixed the value, the type depends on plugin-definition
1906  */
1907 function feedback_get_item_value($completedid, $itemid, $tmp = false) {
1908     global $DB;
1910     $tmpstr = $tmp ? 'tmp' : '';
1911     return $DB->get_field('feedback_value'.$tmpstr, 'value', array('completed'=>$completedid, 'item'=>$itemid));
1914 /**
1915  * compares the value of the itemid related to the completedid with the dependvalue.
1916  * this is used if a depend item is set.
1917  * the value can come as temporary or as permanently value. the deciding is done by $tmp.
1918  *
1919  * @global object
1920  * @global object
1921  * @param int $completeid
1922  * @param int $itemid
1923  * @param mixed $dependvalue
1924  * @param boolean $tmp
1925  * @return bool
1926  */
1927 function feedback_compare_item_value($completedid, $itemid, $dependvalue, $tmp = false) {
1928     global $DB, $CFG;
1930     $dbvalue = feedback_get_item_value($completedid, $itemid, $tmp);
1932     //get the class of the given item-typ
1933     $item = $DB->get_record('feedback_item', array('id'=>$itemid));
1935     //get the instance of the item-class
1936     $itemobj = feedback_get_item_class($item->typ);
1937     return $itemobj->compare_value($item, $dbvalue, $dependvalue); //true or false
1940 /**
1941  * this function checks the correctness of values.
1942  * the rules for this are implemented in the class of each item.
1943  * it can be the required attribute or the value self e.g. numeric.
1944  * the params first/lastitem are given to determine the visible range between pagebreaks.
1945  *
1946  * @global object
1947  * @param int $firstitem the position of firstitem for checking
1948  * @param int $lastitem the position of lastitem for checking
1949  * @return boolean
1950  */
1951 function feedback_check_values($firstitem, $lastitem) {
1952     global $DB, $CFG;
1954     $feedbackid = optional_param('feedbackid', 0, PARAM_INT);
1956     //get all items between the first- and lastitem
1957     $select = "feedback = ?
1958                     AND position >= ?
1959                     AND position <= ?
1960                     AND hasvalue = 1";
1961     $params = array($feedbackid, $firstitem, $lastitem);
1962     if(!$feedbackitems = $DB->get_records_select('feedback_item', $select, $params)) {
1963         //if no values are given so no values can be wrong ;-)
1964         return true;
1965     }
1967     foreach($feedbackitems as $item) {
1968         //the name of the input field of the completeform is given in a special form:
1969         //<item-typ>_<item-id> eg. numeric_234
1970         //this is the key to get the value for the correct item
1971         $formvalname = $item->typ . '_' . $item->id;
1973         $value = optional_param($formvalname, NULL, PARAM_RAW);
1975         //check if the value is set
1976         if(is_null($value) AND $item->required == 1) {
1977             return false;
1978         }
1980         //get the instance of the item-class
1981         $itemobj = feedback_get_item_class($item->typ);
1983         //now we let check the value by the item-class
1984         if(!$itemobj->check_value($value, $item)) {
1985             return false;
1986         }
1987     }
1988     //if no wrong values so we can return true
1989     return true;
1992 /**
1993  * this function create a complete-record and the related value-records.
1994  * depending on the $tmp (true/false) the values are saved temporary or permanently
1995  *
1996  * @global object
1997  * @param int $userid
1998  * @param int $timemodified
1999  * @param boolean $tmp
2000  * @param string $guestid a unique identifier to save temporary data
2001  * @return mixed false on error or the completedid
2002  */
2003 function feedback_create_values($usrid, $timemodified, $tmp = false, $guestid = false){
2004     global $DB;
2006     $feedbackid = optional_param('feedbackid', false, PARAM_INT);
2007     $anonymous_response = optional_param('anonymous_response', false, PARAM_INT);
2008     $courseid = optional_param('courseid', false, PARAM_INT);
2010     $tmpstr = $tmp ? 'tmp' : '';
2011     //first we create a new completed record
2012     $completed = new stdClass();
2013     $completed->feedback           = $feedbackid;
2014     $completed->userid             = $usrid;
2015     $completed->guestid            = $guestid;
2016     $completed->timemodified       = $timemodified;
2017     $completed->anonymous_response = $anonymous_response;
2019     $completedid = $DB->insert_record('feedback_completed'.$tmpstr, $completed);
2021     $completed = $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$completedid));
2023     //the keys are in the form like abc_xxx
2024     //with explode we make an array with(abc, xxx) and (abc=typ und xxx=itemnr)
2026     //get the items of the feedback
2027     if(!$allitems = $DB->get_records('feedback_item', array('feedback'=>$completed->feedback))) {
2028         return false;
2029     }
2030     foreach($allitems as $item) {
2031         $keyname = $item->typ.'_'.$item->id;
2032         $itemvalue = optional_param($keyname, NULL, PARAM_RAW);
2033         if(is_null($itemvalue)) {
2034             continue;
2035         }
2036         $value = new stdClass();
2037         $value->item = $item->id;
2038         $value->completed = $completed->id;
2039         $value->course_id = $courseid;
2041         //get the class of item-typ
2042         $itemobj = feedback_get_item_class($item->typ);
2044         //the kind of values can be absolutely different so we run create_value directly by the item-class
2045         $value->value = $itemobj->create_value($itemvalue);
2046         $DB->insert_record('feedback_value'.$tmpstr, $value);
2047     }
2048     return $completed->id;
2051 /**
2052  * this function updates a complete-record and the related value-records.
2053  * depending on the $tmp (true/false) the values are saved temporary or permanently
2054  *
2055  * @global object
2056  * @param object $completed
2057  * @param boolean $tmp
2058  * @return int the completedid
2059  */
2060 function feedback_update_values($completed, $tmp = false) {
2061     global $DB;
2062     $courseid = optional_param('courseid', false, PARAM_INT);
2064     $tmpstr = $tmp ? 'tmp' : '';
2066     $DB->update_record('feedback_completed'.$tmpstr, $completed);
2067     //get the values of this completed
2068     $values = $DB->get_records('feedback_value'.$tmpstr, array('completed'=>$completed->id));
2070     //get the items of the feedback
2071     if(!$allitems = $DB->get_records('feedback_item', array('feedback'=>$completed->feedback))) {
2072         return false;
2073     }
2074     foreach($allitems as $item) {
2075         $keyname = $item->typ.'_'.$item->id;
2076         $itemvalue = optional_param($keyname, NULL, PARAM_RAW);
2077         //is the itemvalue set (could be a subset of items because pagebreak)?
2078         if(is_null($itemvalue)) {
2079             continue;
2080         }
2082         $newvalue = new stdClass();
2083         $newvalue->item = $item->id;
2084         $newvalue->completed = $completed->id;
2085         $newvalue->course_id = $courseid;
2087         //get the class of item-typ
2088         $itemobj = feedback_get_item_class($item->typ);
2089         //the kind of values can be absolutely different so we run create_value directly by the item-class
2090         $newvalue->value = $itemobj->create_value($itemvalue);
2092         //check, if we have to create or update the value
2093         $exist = false;
2094         foreach($values as $value){
2095             if($value->item == $newvalue->item){
2096                 $newvalue->id = $value->id;
2097                 $exist = true;
2098                 break;
2099             }
2100         }
2101         if($exist){
2102             $DB->update_record('feedback_value'.$tmpstr, $newvalue);
2103         }else {
2104             $DB->insert_record('feedback_value'.$tmpstr, $newvalue);
2105         }
2106     }
2108     return $completed->id;
2111 /**
2112  * get the values of an item depending on the given groupid.
2113  * if the feedback is anonymous so the values are shuffled
2114  *
2115  * @global object
2116  * @global object
2117  * @param object $item
2118  * @param int $groupid
2119  * @param int $courseid
2120  * @param bool $ignore_empty if this is set true so empty values are not delivered
2121  * @return array the value-records
2122  */
2123 function feedback_get_group_values($item, $groupid = false, $courseid = false, $ignore_empty = false){
2124     global $CFG, $DB;
2126     //if the groupid is given?
2127     if (intval($groupid) > 0) {
2128         if($ignore_empty) {
2129             $ignore_empty_select = "AND fbv.value != ''";
2130         }
2131         else {
2132             $ignore_empty_select = "";
2133         }
2135         $query = 'SELECT fbv .  *
2136                     FROM {feedback_value} fbv, {feedback_completed} fbc, {groups_members} gm
2137                    WHERE fbv.item = ?
2138                          AND fbv.completed = fbc.id
2139                          AND fbc.userid = gm.userid
2140                          '.$ignore_empty_select.'
2141                          AND gm.groupid = ?
2142                 ORDER BY fbc.timemodified';
2143         $values = $DB->get_records_sql($query, array($item->id, $groupid));
2145     } else {
2146         if($ignore_empty) {
2147             $ignore_empty_select = "AND value != ''";
2148         }
2149         else {
2150             $ignore_empty_select = "";
2151         }
2153         if ($courseid) {
2154             $values = $DB->get_records_select('feedback_value', "item = ? AND course_id = ? ".$ignore_empty_select, array($item->id, $courseid));
2155         } else {
2156             $values = $DB->get_records_select('feedback_value', "item = ? ".$ignore_empty_select, array($item->id));
2157         }
2158     }
2159     if ($DB->get_field('feedback', 'anonymous', array('id'=>$item->feedback)) == FEEDBACK_ANONYMOUS_YES) {
2160         if(is_array($values))
2161             shuffle($values);
2162     }
2163     return $values;
2166 /**
2167  * check for multiple_submit = false.
2168  * if the feedback is global so the courseid must be given
2169  *
2170  * @global object
2171  * @global object
2172  * @param int $feedbackid
2173  * @param int $courseid
2174  * @return boolean true if the feedback already is submitted otherwise false
2175  */
2176 function feedback_is_already_submitted($feedbackid, $courseid = false) {
2177     global $USER, $DB;
2179     if (!$trackings = $DB->get_records_menu('feedback_tracking', array('userid'=>$USER->id, 'feedback'=>$feedbackid), '', 'id, completed')) {
2180         return false;
2181     }
2183     if($courseid) {
2184         $select = 'completed IN ('.implode(',',$trackings).') AND course_id = ?';
2185         if(!$values = $DB->get_records_select('feedback_value', $select, array($courseid))) {
2186             return false;
2187         }
2188     }
2190     return true;
2193 /**
2194  * if the completion of a feedback will be continued eg. by pagebreak or by multiple submit so the complete must be found.
2195  * if the param $tmp is set true so all things are related to temporary completeds
2196  *
2197  * @global object
2198  * @global object
2199  * @global object
2200  * @param int $feedbackid
2201  * @param boolean $tmp
2202  * @param int $courseid
2203  * @param string $guestid
2204  * @return int the id of the found completed
2205  */
2206 function feedback_get_current_completed($feedbackid, $tmp = false, $courseid = false, $guestid = false) {
2207     global $USER, $CFG, $DB;
2209     $tmpstr = $tmp ? 'tmp' : '';
2211     if(!$courseid) {
2212         if($guestid) {
2213             return $DB->get_record('feedback_completed'.$tmpstr, array('feedback'=>$feedbackid, 'guestid'=>$guestid));
2214         }else {
2215             return $DB->get_record('feedback_completed'.$tmpstr, array('feedback'=>$feedbackid, 'userid'=>$USER->id));
2216         }
2217     }
2219     $params = array();
2221     if ($guestid) {
2222         $userselect = "AND fc.guestid = :guestid";
2223         $params['guestid'] = $guestid;
2224     }else {
2225         $userselect = "AND fc.userid = :userid";
2226         $params['userid'] = $USER->id;
2227     }
2228     //if courseid is set the feedback is global. there can be more than one completed on one feedback
2229     $sql =  "SELECT DISTINCT fc.*
2230                FROM {feedback_value{$tmpstr}} fv, {feedback_completed{$tmpstr}} fc
2231               WHERE fv.course_id = :courseid
2232                     AND fv.completed = fc.id
2233                     $userselect
2234                     AND fc.feedback = :feedbackid";
2235     $params['courseid']   = intval($courseid);
2236     $params['feedbackid'] = $feedbackid;
2238     if (!$sqlresult = $DB->get_records_sql($sql, $params)) {
2239         return false;
2240     }
2241     foreach($sqlresult as $r) {
2242         return $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$r->id));
2243     }
2246 /**
2247  * get the completeds depending on the given groupid.
2248  *
2249  * @global object
2250  * @global object
2251  * @param object $feedback
2252  * @param int $groupid
2253  * @param int $courseid
2254  * @return mixed array of found completeds otherwise false
2255  */
2256 function feedback_get_completeds_group($feedback, $groupid = false, $courseid = false) {
2257     global $CFG, $DB;
2259     if (intval($groupid) > 0){
2260         $query = "SELECT fbc.*
2261                     FROM {feedback_completed} fbc, {groups_members} gm
2262                    WHERE fbc.feedback = ?
2263                          AND gm.groupid = ?
2264                          AND fbc.userid = gm.userid";
2265         if ($values = $DB->get_records_sql($query, array($feedback->id, $groupid))) {
2266             return $values;
2267         } else {
2268             return false;
2269         }
2270     } else {
2271         if($courseid) {
2272             $query = "SELECT DISTINCT fbc.*
2273                         FROM {feedback_completed} fbc, {feedback_value} fbv
2274                         WHERE fbc.id = fbv.completed
2275                             AND fbc.feedback = ?
2276                             AND fbv.course_id = ?
2277                         ORDER BY random_response";
2278             if ($values = $DB->get_records_sql($query, array($feedback->id, $courseid))) {
2279                 return $values;
2280             } else {
2281                 return false;
2282             }
2283         }else {
2284             if ($values = $DB->get_records('feedback_completed', array('feedback'=>$feedback->id))) {
2285                 return $values;
2286             } else {
2287                 return false;
2288             }
2289         }
2290     }
2293 /**
2294  * get the count of completeds depending on the given groupid.
2295  *
2296  * @global object
2297  * @global object
2298  * @param object $feedback
2299  * @param int $groupid
2300  * @param int $courseid
2301  * @return mixed count of completeds or false
2302  */
2303 function feedback_get_completeds_group_count($feedback, $groupid = false, $courseid = false) {
2304     global $CFG, $DB;
2306     if ($courseid > 0 AND !$groupid <= 0) {
2307         $sql = "SELECT id, COUNT(item) AS ci
2308                   FROM {feedback_value}
2309                  WHERE course_id  = ?
2310               GROUP BY item ORDER BY ci DESC";
2311         if ($foundrecs = $DB->get_records_sql($sql, array($courseid))) {
2312             $foundrecs = array_values($foundrecs);
2313             return $foundrecs[0]->ci;
2314         }
2315         return false;
2316     }
2317     if($values = feedback_get_completeds_group($feedback, $groupid)) {
2318         return sizeof($values);
2319     }else {
2320         return false;
2321     }
2324 /**
2325  * deletes all completed-recordsets from a feedback.
2326  * all related data such as values also will be deleted
2327  *
2328  * @global object
2329  * @param int $feedbackid
2330  * @return void
2331  */
2332 function feedback_delete_all_completeds($feedbackid) {
2333     global $DB;
2335     if (!$completeds = $DB->get_records('feedback_completed', array('feedback'=>$feedbackid))) {
2336         return;
2337     }
2338     foreach($completeds as $completed) {
2339         feedback_delete_completed($completed->id);
2340     }
2343 /**
2344  * deletes a completed given by completedid.
2345  * all related data such values or tracking data also will be deleted
2346  *
2347  * @global object
2348  * @param int $completedid
2349  * @return boolean
2350  */
2351 function feedback_delete_completed($completedid) {
2352     global $DB, $CFG;
2353     require_once($CFG->libdir.'/completionlib.php');
2355     if (!$completed = $DB->get_record('feedback_completed', array('id'=>$completedid))) {
2356         return false;
2357     }
2359     if (!$feedback = $DB->get_record('feedback', array('id'=>$completed->feedback))) {
2360         return false;
2361     }
2363     if (!$course = $DB->get_record('course', array('id'=>$feedback->course))) {
2364         return false;
2365     }
2367     if (!$cm = get_coursemodule_from_instance('feedback', $feedback->id)) {
2368         return false;
2369     }
2371     //first we delete all related values
2372     $DB->delete_records('feedback_value', array('completed'=>$completed->id));
2374     //now we delete all tracking data
2375     if($tracking = $DB->get_record('feedback_tracking', array('completed'=>$completed->id, 'feedback'=>$completed->feedback))) {
2376         $DB->delete_records('feedback_tracking', array('completed'=>$completed->id));
2377     }
2379     // Update completion state
2380     $completion = new completion_info($course);
2381     if ($completion->is_enabled($cm) && $feedback->completionsubmit) {
2382         $completion->update_state($cm, COMPLETION_INCOMPLETE, $completed->userid);
2383     }
2384     //last we delete the completed-record
2385     return $DB->delete_records('feedback_completed', array('id'=>$completed->id));
2388 ////////////////////////////////////////////////
2389 ////////////////////////////////////////////////
2390 ////////////////////////////////////////////////
2391 //functions to handle sitecourse mapping
2392 ////////////////////////////////////////////////
2394 /**
2395  * checks if the course and the feedback is in the table feedback_sitecourse_map.
2396  *
2397  * @global object
2398  * @param int $feedbackid
2399  * @param int $courseid
2400  * @return int the count of records
2401  */
2402 function feedback_is_course_in_sitecourse_map($feedbackid, $courseid) {
2403     global $DB;
2404     return $DB->count_records('feedback_sitecourse_map', array('feedbackid'=>$feedbackid, 'courseid'=>$courseid));
2407 /**
2408  * checks if the feedback is in the table feedback_sitecourse_map.
2409  *
2410  * @global object
2411  * @param int $feedbackid
2412  * @return boolean
2413  */
2414 function feedback_is_feedback_in_sitecourse_map($feedbackid) {
2415     global $DB;
2416     return $DB->record_exists('feedback_sitecourse_map', array('feedbackid'=>$feedbackid));
2419 /**
2420  * gets the feedbacks from table feedback_sitecourse_map.
2421  * this is used to show the global feedbacks on the feedback block
2422  * all feedbacks with the following criteria will be selected:<br />
2423  *
2424  * 1) all feedbacks which id are listed together with the courseid in sitecoursemap and<br />
2425  * 2) all feedbacks which not are listed in sitecoursemap
2426  *
2427  * @global object
2428  * @param int $courseid
2429  * @return array the feedback-records
2430  */
2431 function feedback_get_feedbacks_from_sitecourse_map($courseid) {
2432     global $DB;
2434     //first get all feedbacks listed in sitecourse_map with named courseid
2435     $sql = "SELECT f.id AS id, cm.id AS cmid, f.name AS name, f.timeopen AS timeopen, f.timeclose AS timeclose
2436               FROM {feedback} f, {course_modules} cm, {feedback_sitecourse_map} sm, {modules} m
2437              WHERE f.id = cm.instance
2438                    AND f.course = '".SITEID."'
2439                    AND m.id = cm.module
2440                    AND m.name = 'feedback'
2441                    AND sm.courseid = ?
2442                    AND sm.feedbackid = f.id";
2444     if (!$feedbacks1 = $DB->get_records_sql($sql, array($courseid))) {
2445         $feedbacks1 = array();
2446     }
2448     //second get all feedbacks not listed in sitecourse_map
2449     $feedbacks2 = array();
2450     $sql = "SELECT f.id AS id, cm.id AS cmid, f.name AS name, f.timeopen AS timeopen, f.timeclose AS timeclose
2451               FROM {feedback} f, {course_modules} cm, {modules} m
2452              WHERE f.id = cm.instance
2453                    AND f.course = '".SITEID."'
2454                    AND m.id = cm.module
2455                    AND m.name = 'feedback'";
2456     if (!$allfeedbacks = $DB->get_records_sql($sql)) {
2457         $allfeedbacks = array();
2458     }
2459     foreach($allfeedbacks as $a) {
2460         if(!$DB->record_exists('feedback_sitecourse_map', array('feedbackid'=>$a->id))) {
2461             $feedbacks2[] = $a;
2462         }
2463     }
2465     return array_merge($feedbacks1, $feedbacks2);
2469 /**
2470  * gets the courses from table feedback_sitecourse_map.
2471  *
2472  * @global object
2473  * @param int $feedbackid
2474  * @return array the course-records
2475  */
2476 function feedback_get_courses_from_sitecourse_map($feedbackid) {
2477     global $DB;
2479     $sql = "SELECT f.id, f.courseid, c.fullname, c.shortname
2480               FROM {feedback_sitecourse_map} f, {course} c
2481              WHERE c.id = f.courseid
2482                    AND f.feedbackid = ?
2483           ORDER BY c.fullname";
2485     return $DB->get_records_sql($sql, array($feedbackid));
2489 /**
2490  * removes non existing courses or feedbacks from sitecourse_map.
2491  * it shouldn't be called all too often
2492  * a good place for it could be the mapcourse.php or unmapcourse.php
2493  *
2494  * @global object
2495  * @return void
2496  */
2497 function feedback_clean_up_sitecourse_map() {
2498     global $DB;
2500     $maps = $DB->get_records('feedback_sitecourse_map');
2501     foreach($maps as $map) {
2502         if (!$DB->get_record('course', array('id'=>$map->courseid))) {
2503             $DB->delete_records('feedback_sitecourse_map', array('courseid'=>$map->courseid, 'feedbackid'=>$map->feedbackid));
2504             continue;
2505         }
2506         if (!$DB->get_record('feedback', array('id'=>$map->feedbackid))) {
2507             $DB->delete_records('feedback_sitecourse_map', array('courseid'=>$map->courseid, 'feedbackid'=>$map->feedbackid));
2508             continue;
2509         }
2511     }
2514 ////////////////////////////////////////////////
2515 ////////////////////////////////////////////////
2516 ////////////////////////////////////////////////
2517 //not relatable functions
2518 ////////////////////////////////////////////////
2520 /**
2521  * prints the option items of a selection-input item (dropdownlist).
2522  * @param int $startval the first value of the list
2523  * @param int $endval the last value of the list
2524  * @param int $selectval which item should be selected
2525  * @param int $interval the stepsize from the first to the last value
2526  * @return void
2527  */
2528 function feedback_print_numeric_option_list($startval, $endval, $selectval = '', $interval = 1){
2529     for($i = $startval; $i <= $endval; $i += $interval){
2530         if($selectval == ($i)){
2531             $selected = 'selected="selected"';
2532         }else{
2533             $selected = '';
2534         }
2535         echo '<option '.$selected.'>'.$i.'</option>';
2536     }
2539 /**
2540  * sends an email to the teachers of the course where the given feedback is placed.
2541  *
2542  * @global object
2543  * @global object
2544  * @uses FEEDBACK_ANONYMOUS_NO
2545  * @uses FORMAT_PLAIN
2546  * @param object $cm the coursemodule-record
2547  * @param object $feedback
2548  * @param object $course
2549  * @param int $userid
2550  * @return void
2551  */
2552 function feedback_send_email($cm, $feedback, $course, $userid) {
2553     global $CFG, $DB;
2555     if ($feedback->email_notification == 0) {  // No need to do anything
2556         return;
2557     }
2559     $user = $DB->get_record('user', array('id'=>$userid));
2561     if (isset($cm->groupmode) && empty($course->groupmodeforce)) {
2562         $groupmode =  $cm->groupmode;
2563     } else {
2564         $groupmode = $course->groupmode;
2565     }
2567     if ($groupmode == SEPARATEGROUPS) {
2568         $groups = $DB->get_records_sql_menu("SELECT g.name, g.id
2569                                                FROM {groups} g, {groups_members} m
2570                                               WHERE g.courseid = ?
2571                                                     AND g.id = m.groupid
2572                                                     AND m.userid = ?
2573                                            ORDER BY name ASC", array($course->id, $userid));
2574         $groups = array_values($groups);
2576         $teachers = feedback_get_receivemail_users($cm->id, $groups);
2577     } else {
2578         $teachers = feedback_get_receivemail_users($cm->id);
2579     }
2581     if ($teachers) {
2583         $strfeedbacks = get_string('modulenameplural', 'feedback');
2584         $strfeedback  = get_string('modulename', 'feedback');
2585         $strcompleted  = get_string('completed', 'feedback');
2586         $printusername = $feedback->anonymous == FEEDBACK_ANONYMOUS_NO ? fullname($user) : get_string('anonymous_user', 'feedback');
2588         foreach ($teachers as $teacher) {
2589             $info = new stdClass();
2590             $info->username = $printusername;
2591             $info->feedback = format_string($feedback->name,true);
2592             $info->url = $CFG->wwwroot.'/mod/feedback/show_entries.php?id='.$cm->id.'&userid='.$userid.'&do_show=showentries';
2594             $postsubject = $strcompleted.': '.$info->username.' -> '.$feedback->name;
2595             $posttext = feedback_send_email_text($info, $course);
2596             $posthtml = ($teacher->mailformat == 1) ? feedback_send_email_html($info, $course, $cm) : '';
2598             if($feedback->anonymous == FEEDBACK_ANONYMOUS_NO) {
2599                 $eventdata = new stdClass();
2600                 $eventdata->name             = 'submission';
2601                 $eventdata->component        = 'mod_feedback';
2602                 $eventdata->userfrom         = $user;
2603                 $eventdata->userto           = $teacher;
2604                 $eventdata->subject          = $postsubject;
2605                 $eventdata->fullmessage      = $posttext;
2606                 $eventdata->fullmessageformat = FORMAT_PLAIN;
2607                 $eventdata->fullmessagehtml  = $posthtml;
2608                 $eventdata->smallmessage     = '';
2609                 message_send($eventdata);
2610             }else {
2611                 $eventdata = new stdClass();
2612                 $eventdata->name             = 'submission';
2613                 $eventdata->component        = 'mod_feedback';
2614                 $eventdata->userfrom         = $teacher;
2615                 $eventdata->userto           = $teacher;
2616                 $eventdata->subject          = $postsubject;
2617                 $eventdata->fullmessage      = $posttext;
2618                 $eventdata->fullmessageformat = FORMAT_PLAIN;
2619                 $eventdata->fullmessagehtml  = $posthtml;
2620                 $eventdata->smallmessage     = '';
2621                 message_send($eventdata);
2622             }
2623         }
2624     }
2627 /**
2628  * sends an email to the teachers of the course where the given feedback is placed.
2629  *
2630  * @global object
2631  * @uses FORMAT_PLAIN
2632  * @param object $cm the coursemodule-record
2633  * @param object $feedback
2634  * @param object $course
2635  * @return void
2636  */
2637 function feedback_send_email_anonym($cm, $feedback, $course) {
2638     global $CFG;
2640     if ($feedback->email_notification == 0) {             // No need to do anything
2641         return;
2642     }
2644     $teachers = feedback_get_receivemail_users($cm->id);
2646     if ($teachers) {
2648         $strfeedbacks = get_string('modulenameplural', 'feedback');
2649         $strfeedback  = get_string('modulename', 'feedback');
2650         $strcompleted  = get_string('completed', 'feedback');
2651         $printusername = get_string('anonymous_user', 'feedback');
2653         foreach ($teachers as $teacher) {
2654             $info = new stdClass();
2655             $info->username = $printusername;
2656             $info->feedback = format_string($feedback->name,true);
2657             $info->url = $CFG->wwwroot.'/mod/feedback/show_entries_anonym.php?id='.$cm->id;
2659             $postsubject = $strcompleted.': '.$info->username.' -> '.$feedback->name;
2660             $posttext = feedback_send_email_text($info, $course);
2661             $posthtml = ($teacher->mailformat == 1) ? feedback_send_email_html($info, $course, $cm) : '';
2663             $eventdata = new stdClass();
2664             $eventdata->name             = 'submission';
2665             $eventdata->component        = 'mod_feedback';
2666             $eventdata->userfrom         = $teacher;
2667             $eventdata->userto           = $teacher;
2668             $eventdata->subject          = $postsubject;
2669             $eventdata->fullmessage      = $posttext;
2670             $eventdata->fullmessageformat = FORMAT_PLAIN;
2671             $eventdata->fullmessagehtml  = $posthtml;
2672             $eventdata->smallmessage     = '';
2673             message_send($eventdata);
2674         }
2675     }
2678 /**
2679  * send the text-part of the email
2680  *
2681  * @param object $info includes some infos about the feedback you want to send
2682  * @param object $course
2683  * @return string the text you want to post
2684  */
2685 function feedback_send_email_text($info, $course) {
2686     $posttext  = $course->shortname.' -> '.get_string('modulenameplural', 'feedback').' -> '.
2687                     $info->feedback."\n";
2688     $posttext .= '---------------------------------------------------------------------'."\n";
2689     $posttext .= get_string("emailteachermail", "feedback", $info)."\n";
2690     $posttext .= '---------------------------------------------------------------------'."\n";
2691     return $posttext;
2695 /**
2696  * send the html-part of the email
2697  *
2698  * @global object
2699  * @param object $info includes some infos about the feedback you want to send
2700  * @param object $course
2701  * @return string the text you want to post
2702  */
2703 function feedback_send_email_html($info, $course, $cm) {
2704     global $CFG;
2705     $posthtml  = '<p><font face="sans-serif">'.
2706                 '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> ->'.
2707                 '<a href="'.$CFG->wwwroot.'/mod/feedback/index.php?id='.$course->id.'">'.get_string('modulenameplural', 'feedback').'</a> ->'.
2708                 '<a href="'.$CFG->wwwroot.'/mod/feedback/view.php?id='.$cm->id.'">'.$info->feedback.'</a></font></p>';
2709     $posthtml .= '<hr /><font face="sans-serif">';
2710     $posthtml .= '<p>'.get_string('emailteachermailhtml', 'feedback', $info).'</p>';
2711     $posthtml .= '</font><hr />';
2712     return $posthtml;
2715 /**
2716  * print some errors to inform users about this.
2717  *
2718  * @global object
2719  * @return void
2720  */
2721 function feedback_print_errors() {
2723     global $SESSION, $OUTPUT;
2725     feedback_init_feedback_session();
2727     if(empty($SESSION->feedback->errors)) {
2728         return;
2729     }
2731     echo $OUTPUT->box_start('generalbox errorboxcontent boxaligncenter boxwidthnormal');
2732     echo $OUTPUT->heading(get_string('handling_error', 'feedback'));
2734     echo '<p align="center"><b><font color="black"><pre>';
2735     print_r($SESSION->feedback->errors) . "\n";
2736     echo '</pre></font></b></p>';
2738     echo $OUTPUT->box_end();
2739     echo '<br /><br />';
2740     $SESSION->feedback->errors = array(); //remove errors
2743 /**
2744  * @param string $url
2745  * @return string
2746  */
2747 function feedback_encode_target_url($url) {
2748     if (strpos($url, '?')) {
2749         list($part1, $part2) = explode('?', $url, 2); //maximal 2 parts
2750         return $part1 . '?' . htmlentities($part2);
2751     } else {
2752         return $url;
2753     }
2756 /**
2757  * Adds module specific settings to the settings block
2758  *
2759  * @param settings_navigation $settings The settings navigation object
2760  * @param navigation_node $feedbacknode The node to add module settings to
2761  */
2762 function feedback_extend_settings_navigation(settings_navigation $settings, navigation_node $feedbacknode) {
2763     global $PAGE, $DB;
2765     if (!$context = get_context_instance(CONTEXT_MODULE, $PAGE->cm->id)) {
2766         print_error('badcontext');
2767     }
2769     if (has_capability('mod/feedback:edititems', $context)) {
2770         $questionnode = $feedbacknode->add(get_string('questions', 'feedback'));
2771         $questionnode->add(get_string('edit_items', 'feedback'), new moodle_url('/mod/feedback/edit.php', array('id'=>$PAGE->cm->id, 'do_show'=>'edit')));
2772         $questionnode->add(get_string('export_questions', 'feedback'), new moodle_url('/mod/feedback/export.php', array('id'=>$PAGE->cm->id, 'action'=>'exportfile')));
2773         $questionnode->add(get_string('import_questions', 'feedback'), new moodle_url('/mod/feedback/import.php', array('id'=>$PAGE->cm->id)));
2774         $questionnode->add(get_string('templates', 'feedback'), new moodle_url('/mod/feedback/edit.php', array('id'=>$PAGE->cm->id, 'do_show'=>'templates')));
2775     }
2777     if (has_capability('mod/feedback:viewreports', $context)) {
2778         $feedback = $DB->get_record('feedback', array('id'=>$PAGE->cm->instance));
2779         if($feedback->course == SITEID){
2780             $feedbacknode->add(get_string('analysis', 'feedback'), new moodle_url('/mod/feedback/analysis_course.php', array('id'=>$PAGE->cm->id, 'course'=>$PAGE->course->id,'do_show'=>'analysis')));
2781         }else {
2782             $feedbacknode->add(get_string('analysis', 'feedback'), new moodle_url('/mod/feedback/analysis.php', array('id'=>$PAGE->cm->id, 'course'=>$PAGE->course->id,'do_show'=>'analysis')));
2783         }
2785         $feedbacknode->add(get_string('show_entries', 'feedback'), new moodle_url('/mod/feedback/show_entries.php', array('id'=>$PAGE->cm->id, 'do_show'=>'showentries')));
2786     }
2789 function feedback_init_feedback_session() {
2790     //initialize the feedback-Session - not nice at all!!
2791     global $SESSION;
2792     if (!empty($SESSION)) {
2793         if (!isset($SESSION->feedback) OR !is_object($SESSION->feedback)) {
2794             $SESSION->feedback = new stdClass();
2795         }
2796     }
2799 /**
2800  * Return a list of page types
2801  * @param string $pagetype current page type
2802  * @param stdClass $parentcontext Block's parent context
2803  * @param stdClass $currentcontext Current context of block
2804  */
2805 function feedback_pagetypelist($pagetype, $parentcontext, $currentcontext) {
2806     $module_pagetype = array('mod-feedback-*'=>get_string('page-mod-feedback-x', 'feedback'));
2807     return $module_pagetype;