AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** Library of functions and constants for module questionnaire.* @package mod_questionnaire* @copyright 2016 Mike Churchward (mike.churchward@poetopensource.org)* @author Mike Churchward* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*//** This may no longer be needed. */define('QUESTIONNAIRE_RESETFORM_RESET', 'questionnaire_reset_data_');/** This may no longer be needed. */define('QUESTIONNAIRE_RESETFORM_DROP', 'questionnaire_drop_questionnaire_');/*** Library supports implementation.* @param string $feature* @return bool|null*/function questionnaire_supports($feature) {switch($feature) {case FEATURE_BACKUP_MOODLE2:return true;case FEATURE_COMPLETION_TRACKS_VIEWS:return false;case FEATURE_COMPLETION_HAS_RULES:return true;case FEATURE_GRADE_HAS_GRADE:return false;case FEATURE_GRADE_OUTCOMES:return false;case FEATURE_GROUPINGS:return true;case FEATURE_GROUPS:return true;case FEATURE_MOD_INTRO:return true;case FEATURE_SHOW_DESCRIPTION:return true;case FEATURE_MOD_PURPOSE:return MOD_PURPOSE_COMMUNICATION;default:return null;}}/*** Return any extra capabilities.* @return array all other caps used in module*/function questionnaire_get_extra_capabilities() {return array('moodle/site:accessallgroups');}/*** Implementation of get_instance.* @param int $questionnaireid* @return false|mixed|stdClass*/function questionnaire_get_instance($questionnaireid) {global $DB;return $DB->get_record('questionnaire', array('id' => $questionnaireid));}/*** Implementation of add_instance.* @param stdClass $questionnaire* @return bool|int*/function questionnaire_add_instance($questionnaire) {// Given an object containing all the necessary data,// (defined by the form in mod.html) this function// will create a new instance and return the id number// of the new instance.global $DB, $CFG;require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php');require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');$copyfiles = false;// Check the realm and set it to the survey if it's set.if (empty($questionnaire->sid)) {// Create a new survey.$course = get_course($questionnaire->course);$cm = new stdClass();$qobject = new questionnaire($course, $cm, 0, $questionnaire);if ($questionnaire->create == 'new-0') {$sdata = new stdClass();$sdata->name = $questionnaire->name;$sdata->realm = 'private';$sdata->title = $questionnaire->name;$sdata->subtitle = '';$sdata->info = '';$sdata->theme = ''; // Theme is deprecated.$sdata->thanks_page = '';$sdata->thank_head = '';$sdata->thank_body = '';$sdata->email = '';$sdata->feedbacknotes = '';$sdata->courseid = $course->id;if (!($sid = $qobject->survey_update($sdata))) {throw new \moodle_exception('couldnotcreatenewsurvey', 'mod_questionnaire');}} else {$copyid = explode('-', $questionnaire->create);$copyrealm = $copyid[0];$copyid = $copyid[1];if (empty($qobject->survey)) {$qobject->add_survey($copyid);$qobject->add_questions($copyid);}// New questionnaires created as "use public" should not create a new survey instance.if ($copyrealm == 'public') {$sid = $copyid;} else {$sid = $qobject->sid = $qobject->survey_copy($course->id);// All new questionnaires should be created as "private".// Even if they are *copies* of public or template questionnaires.$DB->set_field('questionnaire_survey', 'realm', 'private', array('id' => $sid));// Need to copy any files from the old questionnaire instance to the new one.$questionnaire->copyid = $copyid;}// If the survey has dependency data, need to set the questionnaire to allow dependencies.if ($DB->count_records('questionnaire_dependency', ['surveyid' => $sid]) > 0) {$questionnaire->navigate = 1;}}$questionnaire->sid = $sid;}$questionnaire->timemodified = time();if ($questionnaire->resume == '1') {$questionnaire->resume = 1;} else {$questionnaire->resume = 0;}if (!$questionnaire->id = $DB->insert_record("questionnaire", $questionnaire)) {return false;}questionnaire_set_events($questionnaire);$completiontimeexpected = !empty($questionnaire->completionexpected) ? $questionnaire->completionexpected : null;\core_completion\api::update_completion_date_event($questionnaire->coursemodule, 'questionnaire',$questionnaire->id, $completiontimeexpected);return $questionnaire->id;}/*** Given an object containing all the necessary data, (defined by the form in mod.html) this function will update an existing* instance with new data.* @param stdClass $questionnaire* @return bool*/function questionnaire_update_instance($questionnaire) {global $DB, $CFG;require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');// Check the realm and set it to the survey if its set.if (!empty($questionnaire->sid) && !empty($questionnaire->realm)) {$DB->set_field('questionnaire_survey', 'realm', $questionnaire->realm, array('id' => $questionnaire->sid));}$questionnaire->timemodified = time();$questionnaire->id = $questionnaire->instance;if ($questionnaire->resume == '1') {$questionnaire->resume = 1;} else {$questionnaire->resume = 0;}// Get existing grade item.questionnaire_grade_item_update($questionnaire);questionnaire_set_events($questionnaire);$completiontimeexpected = !empty($questionnaire->completionexpected) ? $questionnaire->completionexpected : null;\core_completion\api::update_completion_date_event($questionnaire->coursemodule, 'questionnaire',$questionnaire->id, $completiontimeexpected);return $DB->update_record("questionnaire", $questionnaire);}/*** Given an ID of an instance of this module, this function will permanently delete the instance and any data that depends on it.* @param int $id* @return bool*/function questionnaire_delete_instance($id) {global $DB, $CFG;require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');if (! $questionnaire = $DB->get_record('questionnaire', array('id' => $id))) {return false;}$result = true;if ($events = $DB->get_records('event', array("modulename" => 'questionnaire', "instance" => $questionnaire->id))) {foreach ($events as $event) {$event = calendar_event::load($event);$event->delete();}}if (! $DB->delete_records('questionnaire', array('id' => $questionnaire->id))) {$result = false;}if ($survey = $DB->get_record('questionnaire_survey', array('id' => $questionnaire->sid))) {// If this survey is owned by this course, delete all of the survey records and responses.if ($survey->courseid == $questionnaire->course) {$result = $result && questionnaire_delete_survey($questionnaire->sid, $questionnaire->id);}}return $result;}/*** Add a get_coursemodule_info function in case any questionnaire type wants to add 'extra' information* for the course (see resource).** Given a course_module object, this function returns any "extra" information that may be needed* when printing this activity in a course listing. See get_array_of_activities() in course/lib.php.** @param stdClass $coursemodule The coursemodule object (record).* @return cached_cm_info An object on information that the courses* will know about (most noticeably, an icon).*/function questionnaire_get_coursemodule_info($coursemodule) {global $DB;$questionnaire = $DB->get_record('questionnaire',array('id' => $coursemodule->instance), 'id, name, intro, introformat,completionsubmit');if (!$questionnaire) {return null;}$info = new cached_cm_info();$info->customdata = (object)[];// Populate the custom completion rules as key => value pairs, but only if the completion mode is 'automatic'.if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) {$info->customdata->customcompletionrules['completionsubmit'] = $questionnaire->completionsubmit;}return $info;}/*** Return a small object with summary information about what a user has done with a given particular instance of this module.* Used for user activity reports.* $return->time = the time they did it* $return->info = a short text description.* $course and $mod are unused, but API requires them. Suppress PHPMD warning.* @param stdClass $course* @param stdClass $user* @param stdClass $mod* @param stdClass $questionnaire* @return stdClass*/function questionnaire_user_outline($course, $user, $mod, $questionnaire) {global $CFG;require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');$result = new stdClass();if ($responses = questionnaire_get_user_responses($questionnaire->id, $user->id, true)) {$n = count($responses);if ($n == 1) {$result->info = $n.' '.get_string("response", "questionnaire");} else {$result->info = $n.' '.get_string("responses", "questionnaire");}$lastresponse = array_pop($responses);$result->time = $lastresponse->submitted;} else {$result->info = get_string("noresponses", "questionnaire");}return $result;}/*** Print a detailed representation of what a user has done with a given particular instance of this module, for user* activity reports.* $course and $mod are unused, but API requires them. Suppress PHPMD warning.* @param stdClass $course* @param stdClass $user* @param stdClass $mod* @param stdClass $questionnaire* @return bool*/function questionnaire_user_complete($course, $user, $mod, $questionnaire) {global $CFG;require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');if ($responses = questionnaire_get_user_responses($questionnaire->id, $user->id, false)) {foreach ($responses as $response) {if ($response->complete == 'y') {echo get_string('submitted', 'questionnaire').' '.userdate($response->submitted).'<br />';} else {echo get_string('attemptstillinprogress', 'questionnaire').' '.userdate($response->submitted).'<br />';}}} else {print_string('noresponses', 'questionnaire');}return true;}/*** Given a course and a time, this module should find recent activity that has occurred in questionnaire activities and print it* out.* Return true if there was output, or false is there was none.* $course, $isteacher and $timestart are unused, but API requires them. Suppress PHPMD warning.* @param stdClass $course* @param bool $isteacher* @param int $timestart* @return false*/function questionnaire_print_recent_activity($course, $isteacher, $timestart) {return false; // True if anything was printed, otherwise false.}/*** Must return an array of grades for a given instance of this module, indexed by user. It also returns a maximum allowed grade.* $questionnaireid is unused, but API requires it. Suppress PHPMD warning.* @param int $questionnaireid* @return null*/function questionnaire_grades($questionnaireid) {return null;}/*** Return grade for given user or all users.** @param stdClass $questionnaire* @param int $userid optional user id, 0 means all users* @return array array of grades, false if none*/function questionnaire_get_user_grades($questionnaire, $userid=0) {global $DB;$params = array();$usersql = '';if (!empty($userid)) {$usersql = "AND u.id = ?";$params[] = $userid;}$sql = "SELECT r.id, u.id AS userid, r.grade AS rawgrade, r.submitted AS dategraded, r.submitted AS datesubmittedFROM {user} u, {questionnaire_response} rWHERE u.id = r.userid AND r.questionnaireid = $questionnaire->id AND r.complete = 'y' $usersql";return $DB->get_records_sql($sql, $params) ?? [];}/*** Update grades by firing grade_updated event.* $nullifnone is unused, but API requires it. Suppress PHPMD warning.* @param stdClass $questionnaire* @param int $userid* @param bool $nullifnone*/function questionnaire_update_grades($questionnaire=null, $userid=0, $nullifnone=true) {global $CFG, $DB;if (!function_exists('grade_update')) { // Workaround for buggy PHP versions.require_once($CFG->libdir.'/gradelib.php');}if ($questionnaire != null) {if ($graderecs = questionnaire_get_user_grades($questionnaire, $userid)) {$grades = array();foreach ($graderecs as $v) {if (!isset($grades[$v->userid])) {$grades[$v->userid] = new stdClass();if ($v->rawgrade == -1) {$grades[$v->userid]->rawgrade = null;} else {$grades[$v->userid]->rawgrade = $v->rawgrade;}$grades[$v->userid]->userid = $v->userid;} else if (isset($grades[$v->userid]) && ($v->rawgrade > $grades[$v->userid]->rawgrade)) {$grades[$v->userid]->rawgrade = $v->rawgrade;}}questionnaire_grade_item_update($questionnaire, $grades);} else {questionnaire_grade_item_update($questionnaire);}} else {$sql = "SELECT q.*, cm.idnumber as cmidnumber, q.course as courseidFROM {questionnaire} q, {course_modules} cm, {modules} mWHERE m.name='questionnaire' AND m.id=cm.module AND cm.instance=q.id";if ($rs = $DB->get_recordset_sql($sql)) {foreach ($rs as $questionnaire) {if ($questionnaire->grade != 0) {questionnaire_update_grades($questionnaire);} else {questionnaire_grade_item_update($questionnaire);}}$rs->close();}}}/*** Create grade item for given questionnaire** @param stdClass $questionnaire object with extra cmidnumber* @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook* @return int 0 if ok, error code otherwise*/function questionnaire_grade_item_update($questionnaire, $grades = null) {global $CFG;if (!function_exists('grade_update')) { // Workaround for buggy PHP versions.require_once($CFG->libdir.'/gradelib.php');}if (!isset($questionnaire->courseid)) {$questionnaire->courseid = $questionnaire->course;}if ($questionnaire->cmidnumber != '') {$params = array('itemname' => $questionnaire->name, 'idnumber' => $questionnaire->cmidnumber);} else {$params = array('itemname' => $questionnaire->name);}if ($questionnaire->grade > 0) {$params['gradetype'] = GRADE_TYPE_VALUE;$params['grademax'] = $questionnaire->grade;$params['grademin'] = 0;} else if ($questionnaire->grade < 0) {$params['gradetype'] = GRADE_TYPE_SCALE;$params['scaleid'] = -$questionnaire->grade;} else if ($questionnaire->grade == 0) { // No Grade..be sure to delete the grade item if it exists.$grades = null;$params = array('deleted' => 1);} else {$params = null; // Allow text comments only.}if ($grades === 'reset') {$params['reset'] = true;$grades = null;}return grade_update('mod/questionnaire', $questionnaire->courseid, 'mod', 'questionnaire',$questionnaire->id, 0, $grades, $params);}/*** This function returns if a scale is being used by one questionnaire* it it has support for grading and scales. Commented code should be* modified if necessary. See forum, glossary or journal modules* as reference.* @param int $questionnaireid* @param int $scaleid* @return boolean True if the scale is used by any questionnaire** Function parameters are unused, but API requires them. Suppress PHPMD warning.*/function questionnaire_scale_used ($questionnaireid, $scaleid) {return false;}/*** Checks if scale is being used by any instance of questionnaire** This is used to find out if scale used anywhere* @param int $scaleid* @return boolean True if the scale is used by any questionnaire** Function parameters are unused, but API requires them. Suppress PHPMD warning.*/function questionnaire_scale_used_anywhere($scaleid) {return false;}/*** Serves the questionnaire attachments. Implements needed access control ;-)** @param stdClass $course* @param stdClass $cm* @param stdClass $context* @param string $filearea* @param array $args* @param bool $forcedownload* @return bool false if file not found, does not return if found - justsend the file** $forcedownload is unused, but API requires it. Suppress PHPMD warning.*/function questionnaire_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {global $DB;if ($context->contextlevel != CONTEXT_MODULE) {return false;}require_course_login($course, true, $cm);$fileareas = ['intro', 'info', 'thankbody', 'question', 'feedbacknotes', 'sectionheading', 'feedback'];if (!in_array($filearea, $fileareas)) {return false;}$componentid = (int)array_shift($args);if ($filearea == 'question') {if (!$DB->record_exists('questionnaire_question', ['id' => $componentid])) {return false;}} else if ($filearea == 'sectionheading') {if (!$DB->record_exists('questionnaire_fb_sections', ['id' => $componentid])) {return false;}} else if ($filearea == 'feedback') {if (!$DB->record_exists('questionnaire_feedback', ['id' => $componentid])) {return false;}} else {if (!$DB->record_exists('questionnaire_survey', ['id' => $componentid])) {return false;}}if (!$DB->record_exists('questionnaire', ['id' => $cm->instance])) {return false;}$fs = get_file_storage();$relativepath = implode('/', $args);$fullpath = "/$context->id/mod_questionnaire/$filearea/$componentid/$relativepath";if (!($file = $fs->get_file_by_hash(sha1($fullpath))) || $file->is_directory()) {return false;}// Finally send the file.send_stored_file($file, 0, 0, true); // Download MUST be forced - security!}/*** Adds module specific settings to the settings block** @param settings_navigation $settings The settings navigation object* @param navigation_node $questionnairenode The node to add module settings to** $settings is unused, but API requires it. Suppress PHPMD warning.*/function questionnaire_extend_settings_navigation(settings_navigation $settings, navigation_node $questionnairenode) {global $DB, $USER, $CFG;$individualresponse = optional_param('individualresponse', false, PARAM_INT);$rid = optional_param('rid', false, PARAM_INT); // Response id.$currentgroupid = optional_param('group', 0, PARAM_INT); // Group id.require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php');$cm = $settings->get_page()->cm;$context = $cm->context;$cmid = $cm->id;$course = $settings->get_page()->course;if (! $questionnaire = $DB->get_record("questionnaire", array("id" => $cm->instance))) {throw new \moodle_exception('invalidcoursemodule', 'mod_questionnaire');}$courseid = $course->id;$questionnaire = new questionnaire($course, $cm, 0, $questionnaire);if ($owner = $DB->get_field('questionnaire_survey', 'courseid', ['id' => $questionnaire->sid])) {$owner = (trim($owner) == trim($courseid));} else {$owner = true;}// On view page, currentgroupid is not yet sent as an optional_param, so get it.$groupmode = groups_get_activity_groupmode($cm, $course);if ($groupmode > 0 && $currentgroupid == 0) {$currentgroupid = groups_get_activity_group($questionnaire->cm);if (!groups_is_member($currentgroupid, $USER->id)) {$currentgroupid = 0;}}// We want to add these new nodes after the Edit settings node, and before the// Locally assigned roles node. Of course, both of those are controlled by capabilities.$keys = $questionnairenode->get_children_key_list();$beforekey = null;$i = array_search('modedit', $keys);if (($i === false) && array_key_exists(0, $keys)) {$beforekey = $keys[0];} else if (array_key_exists($i + 1, $keys)) {$beforekey = $keys[$i + 1];}if (has_capability('mod/questionnaire:manage', $context) && $owner) {$url = '/mod/questionnaire/qsettings.php';$node = navigation_node::create(get_string('advancedsettings'),new moodle_url($url, array('id' => $cmid)),navigation_node::TYPE_SETTING, null, 'advancedsettings',new pix_icon('t/edit', ''));$questionnairenode->add_node($node, $beforekey);}if (has_capability('mod/questionnaire:editquestions', $context) && $owner) {$url = '/mod/questionnaire/questions.php';$node = navigation_node::create(get_string('questions', 'questionnaire'),new moodle_url($url, array('id' => $cmid)),navigation_node::TYPE_SETTING, null, 'questions',new pix_icon('t/edit', ''));$questionnairenode->add_node($node, $beforekey);}if (has_capability('mod/questionnaire:editquestions', $context) && $owner) {$url = '/mod/questionnaire/feedback.php';$node = navigation_node::create(get_string('feedback', 'questionnaire'),new moodle_url($url, array('id' => $cmid)),navigation_node::TYPE_SETTING, null, 'feedback',new pix_icon('t/edit', ''));$questionnairenode->add_node($node, $beforekey);}if (has_capability('mod/questionnaire:preview', $context)) {$url = '/mod/questionnaire/preview.php';$node = navigation_node::create(get_string('preview_label', 'questionnaire'),new moodle_url($url, array('id' => $cmid)),navigation_node::TYPE_SETTING, null, 'preview',new pix_icon('t/preview', ''));$questionnairenode->add_node($node, $beforekey);}if ($questionnaire->user_can_take($USER->id)) {$url = '/mod/questionnaire/complete.php';if ($questionnaire->user_has_saved_response($USER->id)) {$args = ['id' => $cmid, 'resume' => 1];$text = get_string('resumesurvey', 'questionnaire');} else {$args = ['id' => $cmid];$text = get_string('answerquestions', 'questionnaire');}$node = navigation_node::create($text, new moodle_url($url, $args),navigation_node::TYPE_SETTING, null, '', new pix_icon('i/info', 'answerquestions'));$questionnairenode->add_node($node, $beforekey);}$usernumresp = $questionnaire->count_submissions($USER->id);if ($questionnaire->capabilities->readownresponses && ($usernumresp > 0)) {$url = '/mod/questionnaire/myreport.php';if ($usernumresp > 1) {$urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,'byresponse' => 0, 'action' => 'summary', 'group' => $currentgroupid);$node = navigation_node::create(get_string('yourresponses', 'questionnaire'),new moodle_url($url, $urlargs), navigation_node::TYPE_SETTING, null, 'yourresponses');$myreportnode = $questionnairenode->add_node($node, $beforekey);$urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,'byresponse' => 0, 'action' => 'summary', 'group' => $currentgroupid);$myreportnode->add(get_string('summary', 'questionnaire'), new moodle_url($url, $urlargs));$urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,'byresponse' => 1, 'action' => 'vresp', 'group' => $currentgroupid);$byresponsenode = $myreportnode->add(get_string('viewindividualresponse', 'questionnaire'),new moodle_url($url, $urlargs));$urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,'byresponse' => 0, 'action' => 'vall', 'group' => $currentgroupid);$myreportnode->add(get_string('myresponses', 'questionnaire'), new moodle_url($url, $urlargs));if ($questionnaire->capabilities->downloadresponses) {$urlargs = array('instance' => $questionnaire->id, 'user' => $USER->id,'action' => 'dwnpg', 'group' => $currentgroupid);$myreportnode->add(get_string('downloadtextformat', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php', $urlargs));}} else {$urlargs = array('instance' => $questionnaire->id, 'userid' => $USER->id,'byresponse' => 1, 'action' => 'vresp', 'group' => $currentgroupid);$node = navigation_node::create(get_string('yourresponse', 'questionnaire'),new moodle_url($url, $urlargs), navigation_node::TYPE_SETTING, null, 'yourresponse');$myreportnode = $questionnairenode->add_node($node, $beforekey);}}// If questionnaire is set to separate groups, prevent user who is not member of any group// and is not a non-editing teacher to view All responses.if ($questionnaire->can_view_all_responses($usernumresp)) {$url = '/mod/questionnaire/report.php';$node = navigation_node::create(get_string('viewallresponses', 'questionnaire'),new moodle_url($url, array('instance' => $questionnaire->id, 'action' => 'vall')),navigation_node::TYPE_SETTING, null, 'vall');$reportnode = $questionnairenode->add_node($node, $beforekey);if ($questionnaire->capabilities->viewsingleresponse) {$summarynode = $reportnode->add(get_string('summary', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'vall')));} else {$summarynode = $reportnode;}$summarynode->add(get_string('order_default', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'vall', 'group' => $currentgroupid)));$summarynode->add(get_string('order_ascending', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'vallasort', 'group' => $currentgroupid)));$summarynode->add(get_string('order_descending', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'vallarsort', 'group' => $currentgroupid)));if ($questionnaire->capabilities->deleteresponses) {$summarynode->add(get_string('deleteallresponses', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'delallresp', 'group' => $currentgroupid)));}if ($questionnaire->capabilities->downloadresponses) {$summarynode->add(get_string('downloadtextformat', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'dwnpg', 'group' => $currentgroupid)));}if ($questionnaire->capabilities->viewsingleresponse) {$byresponsenode = $reportnode->add(get_string('viewbyresponse', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'vresp', 'byresponse' => 1, 'group' => $currentgroupid)));$byresponsenode->add(get_string('view', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'vresp', 'byresponse' => 1, 'group' => $currentgroupid)));if ($individualresponse) {$byresponsenode->add(get_string('deleteresp', 'questionnaire'),new moodle_url('/mod/questionnaire/report.php',array('instance' => $questionnaire->id, 'action' => 'dresp', 'byresponse' => 1,'rid' => $rid, 'group' => $currentgroupid, 'individualresponse' => 1)));}}}$canviewgroups = true;$groupmode = groups_get_activity_groupmode($cm, $course);if ($groupmode == 1) {$canviewgroups = groups_has_membership($cm, $USER->id);}$canviewallgroups = has_capability('moodle/site:accessallgroups', $context);if ($questionnaire->capabilities->viewsingleresponse && ($canviewallgroups || $canviewgroups)) {$url = '/mod/questionnaire/show_nonrespondents.php';$node = navigation_node::create(get_string('show_nonrespondents', 'questionnaire'),new moodle_url($url, array('id' => $cmid)),navigation_node::TYPE_SETTING, null, 'nonrespondents');$questionnairenode->add_node($node, $beforekey);}}// Any other questionnaire functions go here. Each of them must have a name that// starts with questionnaire_./*** Return the view actions.* @return string[]*/function questionnaire_get_view_actions() {return array('view', 'view all');}/*** Return the post actions.* @return string[]*/function questionnaire_get_post_actions() {return array('submit', 'update');}/*** Return the recent activity.* @param array $activities* @param int $index* @param int $timestart* @param int $courseid* @param int $cmid* @param int $userid* @param int $groupid* @return mixed|void*/function questionnaire_get_recent_mod_activity(&$activities, &$index, $timestart,$courseid, $cmid, $userid = 0, $groupid = 0) {global $CFG, $COURSE, $USER, $DB;require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php');if ($COURSE->id == $courseid) {$course = $COURSE;} else {$course = $DB->get_record('course', ['id' => $courseid]);}$modinfo = get_fast_modinfo($course);$cm = $modinfo->cms[$cmid];$questionnaire = $DB->get_record('questionnaire', ['id' => $cm->instance]);$questionnaire = new questionnaire($course, $cm, 0, $questionnaire);$context = context_module::instance($cm->id);$grader = has_capability('mod/questionnaire:viewsingleresponse', $context);// If this is a copy of a public questionnaire whose original is located in another course,// current user (teacher) cannot view responses.if ($grader) {// For a public questionnaire, look for the original public questionnaire that it is based on.if (!$questionnaire->survey_is_public_master()) {// For a public questionnaire, look for the original public questionnaire that it is based on.$originalquestionnaire = $DB->get_record('questionnaire',['sid' => $questionnaire->survey->id, 'course' => $questionnaire->survey->courseid]);$cmoriginal = get_coursemodule_from_instance("questionnaire", $originalquestionnaire->id,$questionnaire->survey->courseid);$contextoriginal = context_course::instance($questionnaire->survey->courseid, MUST_EXIST);if (!has_capability('mod/questionnaire:viewsingleresponse', $contextoriginal)) {$tmpactivity = new stdClass();$tmpactivity->type = 'questionnaire';$tmpactivity->cmid = $cm->id;$tmpactivity->cannotview = true;$tmpactivity->anonymous = false;$activities[$index++] = $tmpactivity;return $activities;}}}if ($userid) {$userselect = "AND u.id = :userid";$params['userid'] = $userid;} else {$userselect = '';}if ($groupid) {$groupselect = 'AND gm.groupid = :groupid';$groupjoin = 'JOIN {groups_members} gm ON gm.userid=u.id';$params['groupid'] = $groupid;} else {$groupselect = '';$groupjoin = '';}$params['timestart'] = $timestart;$params['questionnaireid'] = $questionnaire->id;$ufields = user_picture::fields('u', null, 'useridagain');if (!$attempts = $DB->get_records_sql("SELECT qr.*,{$ufields}FROM {questionnaire_response} qrJOIN {user} u ON u.id = qr.userid$groupjoinWHERE qr.submitted > :timestartAND qr.questionnaireid = :questionnaireid$userselect$groupselectORDER BY qr.submitted ASC", $params)) {return;}$accessallgroups = has_capability('moodle/site:accessallgroups', $context);$viewfullnames = has_capability('moodle/site:viewfullnames', $context);$groupmode = groups_get_activity_groupmode($cm, $course);$usersgroups = null;$aname = format_string($cm->name, true);$userattempts = array();foreach ($attempts as $attempt) {if ($questionnaire->respondenttype != 'anonymous') {if (!isset($userattempts[$attempt->lastname])) {$userattempts[$attempt->lastname] = 1;} else {$userattempts[$attempt->lastname]++;}}if ($attempt->userid != $USER->id) {if (!$grader) {// View complete individual responses permission required.continue;}if (($groupmode == SEPARATEGROUPS) && !$accessallgroups) {if ($usersgroups === null) {$usersgroups = groups_get_all_groups($course->id,$attempt->userid, $cm->groupingid);if (is_array($usersgroups)) {$usersgroups = array_keys($usersgroups);} else {$usersgroups = array();}}if (!array_intersect($usersgroups, $modinfo->groups[$cm->id])) {continue;}}}$tmpactivity = new stdClass();$tmpactivity->type = 'questionnaire';$tmpactivity->cmid = $cm->id;$tmpactivity->cminstance = $cm->instance;// Current user is admin - or teacher enrolled in original public course.if (isset($cmoriginal)) {$tmpactivity->cminstance = $cmoriginal->instance;}$tmpactivity->cannotview = false;$tmpactivity->anonymous = false;$tmpactivity->name = $aname;$tmpactivity->sectionnum = $cm->sectionnum;$tmpactivity->timestamp = $attempt->submitted;$tmpactivity->groupid = $groupid;if (isset($userattempts[$attempt->lastname])) {$tmpactivity->nbattempts = $userattempts[$attempt->lastname];}$tmpactivity->content = new stdClass();$tmpactivity->content->attemptid = $attempt->id;$userfields = explode(',', user_picture::fields());$tmpactivity->user = new stdClass();foreach ($userfields as $userfield) {if ($userfield == 'id') {$tmpactivity->user->{$userfield} = $attempt->userid;} else {if (!empty($attempt->{$userfield})) {$tmpactivity->user->{$userfield} = $attempt->{$userfield};} else {$tmpactivity->user->{$userfield} = null;}}}if ($questionnaire->respondenttype != 'anonymous') {$tmpactivity->user->fullname = fullname($attempt, $viewfullnames);} else {$tmpactivity->user = '';unset ($tmpactivity->user);$tmpactivity->anonymous = true;}$activities[$index++] = $tmpactivity;}}/*** Prints all users who have completed a specified questionnaire since a given time** @param stdClass $activity* @param int $courseid* @param string $detail not used but needed for compability* @param array $modnames* @return void Output is echo'd** $details and $modenames are unused, but API requires them. Suppress PHPMD warning.*/function questionnaire_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {global $OUTPUT;// If the questionnaire is "anonymous", then $activity->user won't have been set, so do not display respondent info.if ($activity->anonymous) {$stranonymous = ' ('.get_string('anonymous', 'questionnaire').')';$activity->nbattempts = '';} else {$stranonymous = '';}// Current user cannot view responses to public questionnaire.if ($activity->cannotview) {$strcannotview = get_string('cannotviewpublicresponses', 'questionnaire');}echo html_writer::start_tag('div');echo html_writer::start_tag('span', array('class' => 'clearfix','style' => 'margin-top:0px; background-color: white; display: inline-block;'));if (!$activity->anonymous && !$activity->cannotview) {echo html_writer::tag('div', $OUTPUT->user_picture($activity->user, array('courseid' => $courseid)),array('style' => 'float: left; padding-right: 10px;'));}if (!$activity->cannotview) {echo html_writer::start_tag('div');echo html_writer::start_tag('div');$urlparams = array('action' => 'vresp', 'instance' => $activity->cminstance,'group' => $activity->groupid, 'rid' => $activity->content->attemptid, 'individualresponse' => 1);$context = context_module::instance($activity->cmid);if (has_capability('mod/questionnaire:viewsingleresponse', $context)) {$report = 'report.php';} else {$report = 'myreport.php';}echo html_writer::tag('a', get_string('response', 'questionnaire').' '.$activity->nbattempts.$stranonymous,array('href' => new moodle_url('/mod/questionnaire/'.$report, $urlparams)));echo html_writer::end_tag('div');} else {echo html_writer::start_tag('div');echo html_writer::start_tag('div');echo html_writer::tag('div', $strcannotview);echo html_writer::end_tag('div');}if (!$activity->anonymous && !$activity->cannotview) {$url = new moodle_url('/user/view.php', array('course' => $courseid, 'id' => $activity->user->id));$name = $activity->user->fullname;$link = html_writer::link($url, $name);echo html_writer::start_tag('div', array('class' => 'user'));echo $link .' - '. userdate($activity->timestamp);echo html_writer::end_tag('div');}echo html_writer::end_tag('div');echo html_writer::end_tag('span');echo html_writer::end_tag('div');return;}/*** Prints questionnaire summaries on 'My home' page** Prints questionnaire name, due date and attempt information on* questionnaires that have a deadline that has not already passed* and it is available for taking.** @param array $courses An array of course objects to get questionnaire instances from* @param array $htmlarray Store overview output array( course ID => 'questionnaire' => HTML output )* @return void*/function questionnaire_print_overview($courses, &$htmlarray) {global $USER, $CFG, $DB, $OUTPUT;require_once($CFG->dirroot . '/mod/questionnaire/locallib.php');if (!$questionnaires = get_all_instances_in_courses('questionnaire', $courses)) {return;}// Get Necessary Strings.$strquestionnaire = get_string('modulename', 'questionnaire');$strnotattempted = get_string('noattempts', 'questionnaire');$strattempted = get_string('attempted', 'questionnaire');$strsavedbutnotsubmitted = get_string('savedbutnotsubmitted', 'questionnaire');$now = time();foreach ($questionnaires as $questionnaire) {// The questionnaire has a deadline.if (($questionnaire->closedate != 0)// And it is before the deadline has been met.&& ($questionnaire->closedate >= $now)// And the questionnaire is available.&& (($questionnaire->opendate == 0) || ($questionnaire->opendate <= $now))) {if (!$questionnaire->visible) {$class = ' class="dimmed"';} else {$class = '';}$str = $OUTPUT->box("$strquestionnaire:<a$class href=\"$CFG->wwwroot/mod/questionnaire/view.php?id=$questionnaire->coursemodule\">".format_string($questionnaire->name).'</a>', 'name');// Deadline.$str .= $OUTPUT->box(get_string('closeson', 'questionnaire', userdate($questionnaire->closedate)), 'info');$attempts = $DB->get_records('questionnaire_response',['questionnaireid' => $questionnaire->id, 'userid' => $USER->id, 'complete' => 'y']) ?? [];$nbattempts = count($attempts);// Do not display a questionnaire as due if it can only be sumbitted once and it has already been submitted!if ($nbattempts != 0 && $questionnaire->qtype == QUESTIONNAIREONCE) {continue;}// Attempt information.if (has_capability('mod/questionnaire:manage', context_module::instance($questionnaire->coursemodule))) {// Number of user attempts.$attempts = $DB->count_records('questionnaire_response',['questionnaireid' => $questionnaire->id, 'complete' => 'y']);$str .= $OUTPUT->box(get_string('numattemptsmade', 'questionnaire', $attempts), 'info');} else {if ($responses = questionnaire_get_user_responses($questionnaire->id, $USER->id, false)) {foreach ($responses as $response) {if ($response->complete == 'y') {$str .= $OUTPUT->box($strattempted, 'info');break;} else {$str .= $OUTPUT->box($strsavedbutnotsubmitted, 'info');}}} else {$str .= $OUTPUT->box($strnotattempted, 'info');}}$str = $OUTPUT->box($str, 'questionnaire overview');if (empty($htmlarray[$questionnaire->course]['questionnaire'])) {$htmlarray[$questionnaire->course]['questionnaire'] = $str;} else {$htmlarray[$questionnaire->course]['questionnaire'] .= $str;}}}}/*** Implementation of the function for printing the form elements that control* whether the course reset functionality affects the questionnaire.** @param stdClass $mform the course reset form that is being built.*/function questionnaire_reset_course_form_definition($mform) {$mform->addElement('header', 'questionnaireheader', get_string('modulenameplural', 'questionnaire'));$mform->addElement('advcheckbox', 'reset_questionnaire',get_string('removeallquestionnaireattempts', 'questionnaire'));}/*** Course reset form defaults.* @param stdClass $course* @return array the defaults.** Function parameters are unused, but API requires them. Suppress PHPMD warning.*/function questionnaire_reset_course_form_defaults($course) {return array('reset_questionnaire' => 1);}/*** Actual implementation of the reset course functionality, delete all the* questionnaire responses for course $data->courseid.** @param stdClass $data the data submitted from the reset course.* @return array status array*/function questionnaire_reset_userdata($data) {global $CFG, $DB;require_once($CFG->libdir . '/questionlib.php');require_once($CFG->dirroot.'/mod/questionnaire/locallib.php');$componentstr = get_string('modulenameplural', 'questionnaire');$status = array();if (!empty($data->reset_questionnaire)) {$surveys = questionnaire_get_survey_list($data->courseid, '');// Delete responses.foreach ($surveys as $survey) {// Get all responses for this questionnaire.$sql = "SELECT qr.id, qr.questionnaireid, qr.submitted, qr.userid, q.sidFROM {questionnaire} qINNER JOIN {questionnaire_response} qr ON q.id = qr.questionnaireidWHERE q.sid = ?ORDER BY qr.id";$resps = $DB->get_records_sql($sql, [$survey->id]);if (!empty($resps)) {$questionnaire = $DB->get_record("questionnaire", ["sid" => $survey->id, "course" => $survey->courseid]);$questionnaire->course = $DB->get_record("course", array("id" => $questionnaire->course));foreach ($resps as $response) {questionnaire_delete_response($response, $questionnaire);}}// Remove this questionnaire's grades (and feedback) from gradebook (if any).$select = "itemmodule = 'questionnaire' AND iteminstance = ".$survey->qid;$fields = 'id';if ($itemid = $DB->get_record_select('grade_items', $select, null, $fields)) {$itemid = $itemid->id;$DB->delete_records_select('grade_grades', 'itemid = '.$itemid);}}$status[] = array('component' => $componentstr,'item' => get_string('deletedallresp', 'questionnaire'),'error' => false);$status[] = array('component' => $componentstr,'item' => get_string('gradesdeleted', 'questionnaire'),'error' => false);}return $status;}/*** Obtains the automatic completion state for this questionnaire based on the condition* in questionnaire settings.** @param object $cm Course-module* @param int $userid User ID* @param bool $type Type of comparison (or/and; can be used as return value if no conditions)* @return bool True if completed, false if not, $type if conditions not set.**/function questionnaire_get_completion_state($cm, $userid, $type) {global $DB;// Get questionnaire details.$questionnaire = $DB->get_record('questionnaire', array('id' => $cm->instance), '*', MUST_EXIST);// If completion option is enabled, evaluate it and return true/false.if ($questionnaire->completionsubmit) {$params = ['userid' => $userid, 'questionnaireid' => $questionnaire->id, 'complete' => 'y'];return $DB->record_exists('questionnaire_response', $params);} else {// Completion option is not enabled so just return $type.return $type;}}/*** This function receives a calendar event and returns the action associated with it, or null if there is none.** This is used by block_myoverview in order to display the event appropriately. If null is returned then the event* is not displayed on the block.** @param calendar_event $event* @param \core_calendar\action_factory $factory* @return \core_calendar\local\event\entities\action_interface|null*/function mod_questionnaire_core_calendar_provide_event_action(calendar_event $event,\core_calendar\action_factory $factory) {$cm = get_fast_modinfo($event->courseid)->instances['questionnaire'][$event->instance];$completion = new \completion_info($cm->get_course());$completiondata = $completion->get_data($cm, false);if ($completiondata->completionstate != COMPLETION_INCOMPLETE) {return null;}return $factory->create_instance(get_string('view'),new \moodle_url('/mod/questionnaire/view.php', ['id' => $cm->id]),1,true);}/*** Called after the activity and module have been created. Use this to copy any images if the questionnaire was created from another* questionnaire survey.** @param stdClass $data* @param stdClass $course* @throws coding_exception*/function mod_questionnaire_coursemodule_edit_post_actions($data, $course) {global $DB;if (!empty($data->copyid)) {$cm = (object)['id' => $data->coursemodule];$questionnaire = new questionnaire($course, $cm, 0, $data);$oldquestionnaireid = $DB->get_field('questionnaire', 'id', ['sid' => $data->copyid]);$oldcm = get_coursemodule_from_instance('questionnaire', $oldquestionnaireid);$oldquestionnaire = new questionnaire($course, $oldcm, $oldquestionnaireid, null);$oldcontext = context_module::instance($oldcm->id);$newcontext = context_module::instance($data->coursemodule);$areas = $questionnaire->get_all_file_areas();$oldareas = $oldquestionnaire->get_all_file_areas();$fs = new \mod_questionnaire\file_storage();foreach ($areas as $area => $ids) {if (is_array($ids)) {$oldid = current($oldareas[$area]);foreach ($ids as $id) {$fs->copy_area_files_to_new_context($oldcontext->id, $newcontext->id, 'mod_questionnaire', $area, $oldid, $id);$oldid = next($oldareas[$area]);}} else {$fs->copy_area_files_to_new_context($oldcontext->id, $newcontext->id, 'mod_questionnaire', $area,$oldareas[$area], $ids);}}}return $data;}