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/>./*** Updates the contents of the survey with the provided data. If no data is provided, it checks for posted data.** This library replaces the phpESP application with Moodle specific code. It will eventually* replace all of the phpESP application, removing the dependency on that.** @package mod_questionnaire* @copyright 2016 Mike Churchward (mike.churchward@poetgroup.org)* @author Mike Churchward* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later**/defined('MOODLE_INTERNAL') || die();require_once($CFG->dirroot.'/calendar/lib.php');// Constants.define ('QUESTIONNAIREUNLIMITED', 0);define ('QUESTIONNAIREONCE', 1);define ('QUESTIONNAIREDAILY', 2);define ('QUESTIONNAIREWEEKLY', 3);define ('QUESTIONNAIREMONTHLY', 4);define ('QUESTIONNAIRE_STUDENTVIEWRESPONSES_NEVER', 0);define ('QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENANSWERED', 1);define ('QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENCLOSED', 2);define ('QUESTIONNAIRE_STUDENTVIEWRESPONSES_ALWAYS', 3);define('QUESTIONNAIRE_MAX_EVENT_LENGTH', 5 * 24 * 60 * 60); // 5 days maximum.define('QUESTIONNAIRE_DEFAULT_PAGE_COUNT', 20);global $questionnairetypes;$questionnairetypes = array (QUESTIONNAIREUNLIMITED => get_string('qtypeunlimited', 'questionnaire'),QUESTIONNAIREONCE => get_string('qtypeonce', 'questionnaire'),QUESTIONNAIREDAILY => get_string('qtypedaily', 'questionnaire'),QUESTIONNAIREWEEKLY => get_string('qtypeweekly', 'questionnaire'),QUESTIONNAIREMONTHLY => get_string('qtypemonthly', 'questionnaire'));global $questionnairerespondents;$questionnairerespondents = array ('fullname' => get_string('respondenttypefullname', 'questionnaire'),'anonymous' => get_string('respondenttypeanonymous', 'questionnaire'));global $questionnairerealms;$questionnairerealms = array ('private' => get_string('private', 'questionnaire'),'public' => get_string('public', 'questionnaire'),'template' => get_string('template', 'questionnaire'));global $questionnaireresponseviewers;$questionnaireresponseviewers = array (QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENANSWERED => get_string('responseviewstudentswhenanswered', 'questionnaire'),QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENCLOSED => get_string('responseviewstudentswhenclosed', 'questionnaire'),QUESTIONNAIRE_STUDENTVIEWRESPONSES_ALWAYS => get_string('responseviewstudentsalways', 'questionnaire'),QUESTIONNAIRE_STUDENTVIEWRESPONSES_NEVER => get_string('responseviewstudentsnever', 'questionnaire'));global $autonumbering;$autonumbering = array (0 => get_string('autonumberno', 'questionnaire'),1 => get_string('autonumberquestions', 'questionnaire'),2 => get_string('autonumberpages', 'questionnaire'),3 => get_string('autonumberpagesandquestions', 'questionnaire'));/*** Return the choice values for the content.* @param string $content* @return stdClass*/function questionnaire_choice_values($content) {// If we run the content through format_text first, any filters we want to use (e.g. multilanguage) should work.// examines the content of a possible answer from radio button, check boxes or rate question// returns ->text to be displayed, ->image if present, ->modname name of modality, image ->title.$contents = new stdClass();$contents->text = '';$contents->image = '';$contents->modname = '';$contents->title = '';// Has image.if (preg_match('/(<img)\s .*(src="(.[^"]{1,})")/isxmU', $content, $matches)) {$contents->image = $matches[0];$imageurl = $matches[3];// Image has a title or alt text: use one of them.if (preg_match('/(title=.)([^"]{1,})/', $content, $matches)|| preg_match('/(alt=.)([^"]{1,})/', $content, $matches) ) {$contents->title = $matches[2];} else {// Image has no title nor alt text: use its filename (without the extension).preg_match("/.*\/(.*)\..*$/", $imageurl, $matches);$contents->title = $matches[1];}// Content has text or named modality plus an image.if (preg_match('/(.*)(<img.*)/', $content, $matches)) {$content = $matches[1];} else {// Just an image.return $contents;}}// Check for score value first (used e.g. by personality test feature).$r = preg_match_all("/^(\d{1,2}=)(.*)$/", $content, $matches);if ($r) {$content = $matches[2][0];}// Look for named modalities.$contents->text = $content;// DEV JR from version 2.5, a double colon :: must be used here instead of the equal sign.if ($pos = strpos($content, '::')) {$contents->text = substr($content, $pos + 2);$contents->modname = substr($content, 0, $pos);}return $contents;}/*** Get the information about the standard questionnaire JavaScript module.* @return array a standard jsmodule structure.*/function questionnaire_get_js_module() {return array('name' => 'mod_questionnaire','fullpath' => '/mod/questionnaire/module.js','requires' => array('base', 'dom', 'event-delegate', 'event-key','core_question_engine', 'moodle-core-formchangechecker'),'strings' => array(array('cancel', 'moodle'),array('flagged', 'question'),array('functiondisabledbysecuremode', 'quiz'),array('startattempt', 'quiz'),array('timesup', 'quiz'),array('changesmadereallygoaway', 'moodle'),),);}/*** Get all the questionnaire responses for a user.* @param int $questionnaireid* @param int $userid* @param bool $complete* @return array*/function questionnaire_get_user_responses($questionnaireid, $userid, $complete=true) {global $DB;$andcomplete = '';if ($complete) {$andcomplete = " AND complete = 'y' ";}return $DB->get_records_sql ("SELECT *FROM {questionnaire_response}WHERE questionnaireid = ?AND userid = ?".$andcomplete."ORDER BY submitted ASC ", array($questionnaireid, $userid)) ?? [];}/*** get the capabilities for the questionnaire* @param int $cmid* @return object the available capabilities from current user*/function questionnaire_load_capabilities($cmid) {static $cb;if (isset($cb)) {return $cb;}$context = questionnaire_get_context($cmid);$cb = new stdClass();$cb->view = has_capability('mod/questionnaire:view', $context);$cb->submit = has_capability('mod/questionnaire:submit', $context);$cb->viewsingleresponse = has_capability('mod/questionnaire:viewsingleresponse', $context);$cb->submissionnotification = has_capability('mod/questionnaire:submissionnotification', $context);$cb->downloadresponses = has_capability('mod/questionnaire:downloadresponses', $context);$cb->deleteresponses = has_capability('mod/questionnaire:deleteresponses', $context);$cb->manage = has_capability('mod/questionnaire:manage', $context);$cb->editquestions = has_capability('mod/questionnaire:editquestions', $context);$cb->createtemplates = has_capability('mod/questionnaire:createtemplates', $context);$cb->createpublic = has_capability('mod/questionnaire:createpublic', $context);$cb->readownresponses = has_capability('mod/questionnaire:readownresponses', $context);$cb->readallresponses = has_capability('mod/questionnaire:readallresponses', $context);$cb->readallresponseanytime = has_capability('mod/questionnaire:readallresponseanytime', $context);$cb->printblank = has_capability('mod/questionnaire:printblank', $context);$cb->preview = has_capability('mod/questionnaire:preview', $context);$cb->viewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $context, null, false);return $cb;}/*** returns the context-id related to the given coursemodule-id* @param int $cmid the coursemodule-id* @return object $context*/function questionnaire_get_context($cmid) {static $context;if (isset($context)) {return $context;}if (!$context = context_module::instance($cmid)) {throw new \moodle_exception('badcontext', 'mod_questionnaire');}return $context;}/*** This function *really* shouldn't be needed, but since sometimes we can end up with* orphaned surveys, this will clean them up.* @return bool* @throws dml_exception*/function questionnaire_cleanup() {global $DB;// Find surveys that don't have questionnaires associated with them.$sql = 'SELECT qs.* FROM {questionnaire_survey} qs '.'LEFT JOIN {questionnaire} q ON q.sid = qs.id '.'WHERE q.sid IS NULL';if ($surveys = $DB->get_records_sql($sql)) {foreach ($surveys as $survey) {questionnaire_delete_survey($survey->id, 0);}}// Find deleted questions and remove them from database (with their associated choices, etc.).return true;}/*** Delete the survey.* @param int $sid* @param int $questionnaireid* @return bool*/function questionnaire_delete_survey($sid, $questionnaireid) {global $DB;$status = true;// Delete all survey attempts and responses.if ($responses = $DB->get_records('questionnaire_response', ['questionnaireid' => $questionnaireid], 'id')) {foreach ($responses as $response) {$status = $status && questionnaire_delete_response($response);}}// There really shouldn't be any more, but just to make sure...$DB->delete_records('questionnaire_response', ['questionnaireid' => $questionnaireid]);// Delete all question data for the survey.if ($questions = $DB->get_records('questionnaire_question', ['surveyid' => $sid], 'id')) {foreach ($questions as $question) {$DB->delete_records('questionnaire_quest_choice', ['question_id' => $question->id]);questionnaire_delete_dependencies($question->id);}$status = $status && $DB->delete_records('questionnaire_question', ['surveyid' => $sid]);// Just to make sure.$status = $status && $DB->delete_records('questionnaire_dependency', ['surveyid' => $sid]);}// Delete all feedback sections and feedback messages for the survey.if ($fbsections = $DB->get_records('questionnaire_fb_sections', ['surveyid' => $sid], 'id')) {foreach ($fbsections as $fbsection) {$DB->delete_records('questionnaire_feedback', ['sectionid' => $fbsection->id]);}$status = $status && $DB->delete_records('questionnaire_fb_sections', ['surveyid' => $sid]);}$status = $status && $DB->delete_records('questionnaire_survey', ['id' => $sid]);return $status;}/*** Delete the response.* @param stdClass $response* @param string $questionnaire* @return bool*/function questionnaire_delete_response($response, $questionnaire='') {global $DB;$status = true;$cm = '';$rid = $response->id;// The questionnaire_delete_survey function does not send the questionnaire array.if ($questionnaire != '') {$cm = get_coursemodule_from_instance("questionnaire", $questionnaire->id, $questionnaire->course->id);}// Delete all of the response data for a response.$DB->delete_records('questionnaire_response_bool', array('response_id' => $rid));$DB->delete_records('questionnaire_response_date', array('response_id' => $rid));$DB->delete_records('questionnaire_resp_multiple', array('response_id' => $rid));$DB->delete_records('questionnaire_response_other', array('response_id' => $rid));$DB->delete_records('questionnaire_response_rank', array('response_id' => $rid));$DB->delete_records('questionnaire_resp_single', array('response_id' => $rid));$DB->delete_records('questionnaire_response_text', array('response_id' => $rid));$status = $status && $DB->delete_records('questionnaire_response', array('id' => $rid));if ($status && $cm) {// Update completion state if necessary.$completion = new completion_info($questionnaire->course);if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC && $questionnaire->completionsubmit) {$completion->update_state($cm, COMPLETION_INCOMPLETE, $response->userid);}}return $status;}/*** Delete all responses for the questionnaire.* @param int $qid* @return bool*/function questionnaire_delete_responses($qid) {global $DB;// Delete all of the response data for a question.$DB->delete_records('questionnaire_response_bool', ['question_id' => $qid]);$DB->delete_records('questionnaire_response_date', ['question_id' => $qid]);$DB->delete_records('questionnaire_resp_multiple', ['question_id' => $qid]);$DB->delete_records('questionnaire_response_other', ['question_id' => $qid]);$DB->delete_records('questionnaire_response_rank', ['question_id' => $qid]);$DB->delete_records('questionnaire_resp_single', ['question_id' => $qid]);$DB->delete_records('questionnaire_response_text', ['question_id' => $qid]);return true;}/*** Delete all dependencies for the questionnaire.* @param int $qid* @return bool*/function questionnaire_delete_dependencies($qid) {global $DB;// Delete all dependencies for this question.$DB->delete_records('questionnaire_dependency', ['questionid' => $qid]);$DB->delete_records('questionnaire_dependency', ['dependquestionid' => $qid]);return true;}/*** Get a survey selection records.* @param int $courseid* @param string $type* @return array|false*/function questionnaire_get_survey_list($courseid=0, $type='') {global $DB;if ($courseid == 0) {if (isadmin()) {$sql = "SELECT id,name,courseid,realm,status " ."{questionnaire_survey} " ."ORDER BY realm,name ";$params = null;} else {return false;}} else {if ($type == 'public') {$sql = "SELECT s.id,s.name,s.courseid,s.realm,s.status,s.title,q.id as qid,q.name as qname " ."FROM {questionnaire} q " ."INNER JOIN {questionnaire_survey} s ON s.id = q.sid AND s.courseid = q.course " ."WHERE realm = ? " ."ORDER BY realm,name ";$params = [$type];} else if ($type == 'template') {$sql = "SELECT s.id,s.name,s.courseid,s.realm,s.status,s.title,q.id as qid,q.name as qname " ."FROM {questionnaire} q " ."INNER JOIN {questionnaire_survey} s ON s.id = q.sid AND s.courseid = q.course " ."WHERE (realm = ?) " ."ORDER BY realm,name ";$params = [$type];} else if ($type == 'private') {$sql = "SELECT s.id,s.name,s.courseid,s.realm,s.status,q.id as qid,q.name as qname " ."FROM {questionnaire} q " ."INNER JOIN {questionnaire_survey} s ON s.id = q.sid " ."WHERE s.courseid = ? and realm = ? " ."ORDER BY realm,name ";$params = [$courseid, $type];} else {// Current get_survey_list is called from function questionnaire_reset_userdata so we need to get a// complete list of all questionnaires in current course to reset them.$sql = "SELECT s.id,s.name,s.courseid,s.realm,s.status,q.id as qid,q.name as qname " ."FROM {questionnaire} q " ."INNER JOIN {questionnaire_survey} s ON s.id = q.sid AND s.courseid = q.course " ."WHERE s.courseid = ? " ."ORDER BY realm,name ";$params = [$courseid];}}return $DB->get_records_sql($sql, $params) ?? [];}/*** Get survey selection list.* @param int $courseid* @param string $type* @return array*/function questionnaire_get_survey_select($courseid=0, $type='') {global $OUTPUT, $DB;$surveylist = array();if ($surveys = questionnaire_get_survey_list($courseid, $type)) {$strpreview = get_string('preview_questionnaire', 'questionnaire');foreach ($surveys as $survey) {$originalcourse = $DB->get_record('course', ['id' => $survey->courseid]);if (!$originalcourse) {// This should not happen, but we found a case where a public survey// still existed in a course that had been deleted, and so this// code lead to a notice, and a broken link. Since that is useless// we just skip surveys like this.continue;}// Prevent creating a copy of a public questionnaire IN THE SAME COURSE as the original.if (($type == 'public') && ($survey->courseid == $courseid)) {continue;} else {$args = "sid={$survey->id}&popup=1";if (!empty($survey->qid)) {$args .= "&qid={$survey->qid}";}$link = new moodle_url("/mod/questionnaire/preview.php?{$args}");$action = new popup_action('click', $link);$label = $OUTPUT->action_link($link, $survey->qname.' ['.$originalcourse->fullname.']',$action, array('title' => $strpreview));$surveylist[$type.'-'.$survey->id] = $label;}}}return $surveylist;}/*** Return the language string for the specified question type.* @param int $id* @return lang_string|mixed|string* @throws coding_exception*/function questionnaire_get_type ($id) {switch ($id) {case 1:return get_string('yesno', 'questionnaire');case 2:return get_string('textbox', 'questionnaire');case 3:return get_string('essaybox', 'questionnaire');case 4:return get_string('radiobuttons', 'questionnaire');case 5:return get_string('checkboxes', 'questionnaire');case 6:return get_string('dropdown', 'questionnaire');case 8:return get_string('ratescale', 'questionnaire');case 9:return get_string('date', 'questionnaire');case 10:return get_string('numeric', 'questionnaire');case 11:return get_string('slider', 'questionnaire');case 100:return get_string('sectiontext', 'questionnaire');case 99:return get_string('sectionbreak', 'questionnaire');default:return $id;}}/*** This creates new events given as opendate and closedate by $questionnaire.* @param object $questionnaire* @return void*/function questionnaire_set_events($questionnaire) {// Adding the questionnaire to the eventtable.global $DB;if ($events = $DB->get_records('event', array('modulename' => 'questionnaire', 'instance' => $questionnaire->id))) {foreach ($events as $event) {$event = calendar_event::load($event);$event->delete();}}// The open-event.$event = new stdClass;$event->description = $questionnaire->name;$event->courseid = $questionnaire->course;$event->groupid = 0;$event->userid = 0;$event->modulename = 'questionnaire';$event->instance = $questionnaire->id;$event->eventtype = 'open';$event->type = CALENDAR_EVENT_TYPE_ACTION;$event->timestart = $questionnaire->opendate;$event->visible = instance_is_visible('questionnaire', $questionnaire);$event->timeduration = ($questionnaire->closedate - $questionnaire->opendate);if ($questionnaire->closedate && $questionnaire->opendate && ($event->timeduration <= QUESTIONNAIRE_MAX_EVENT_LENGTH)) {// Single event for the whole questionnaire.$event->name = $questionnaire->name;$event->timesort = $questionnaire->opendate;calendar_event::create($event);} else {// Separate start and end events.$event->timeduration = 0;if ($questionnaire->opendate) {$event->name = $questionnaire->name.' ('.get_string('questionnaireopens', 'questionnaire').')';$event->timesort = $questionnaire->opendate;calendar_event::create($event);unset($event->id); // So we can use the same object for the close event.}if ($questionnaire->closedate) {$event->name = $questionnaire->name.' ('.get_string('questionnairecloses', 'questionnaire').')';$event->timestart = $questionnaire->closedate;$event->timesort = $questionnaire->closedate;$event->eventtype = 'close';calendar_event::create($event);}}}/*** Get users who have not completed the questionnaire** @param object $cm* @param int $sid* @param bool $group single groupid* @param string $sort* @param bool $startpage* @param bool $pagecount* @return object the userrecords* @throws coding_exception* @throws dml_exception*/function questionnaire_get_incomplete_users($cm, $sid,$group = false,$sort = '',$startpage = false,$pagecount = false) {global $DB;$context = context_module::instance($cm->id);// First get all users who can complete this questionnaire.$cap = 'mod/questionnaire:submit';$fields = 'u.id, u.username';if (!$allusers = get_enrolled_users($context, $cap, $group, $fields, $sort)) {return false;}$allusers = array_keys($allusers);// Nnow get all completed questionnaires.$params = array('questionnaireid' => $cm->instance, 'complete' => 'y');$sql = "SELECT userid FROM {questionnaire_response} " ."WHERE questionnaireid = :questionnaireid AND complete = :complete " ."GROUP BY userid ";if (!$completedusers = $DB->get_records_sql($sql, $params)) {return $allusers;}$completedusers = array_keys($completedusers);// Now strike all completedusers from allusers.$allusers = array_diff($allusers, $completedusers);// For paging I use array_slice().if (($startpage !== false) && ($pagecount !== false)) {$allusers = array_slice($allusers, $startpage, $pagecount);}return $allusers;}/*** Called by HTML editor in showrespondents and Essay question. Based on question/essay/renderer.* Pending general solution to using the HTML editor outside of moodleforms in Moodle pages.* @param int $context* @return array*/function questionnaire_get_editor_options($context) {return array('subdirs' => 0,'maxbytes' => 0,'maxfiles' => -1,'context' => $context,'noclean' => 0,'trusttext' => 0);}/*** Get the parent of a child question.* @param stdClass $question* @return array*/function questionnaire_get_parent ($question) {global $DB;$qid = $question->id;$parent = array();$dependquestion = $DB->get_record('questionnaire_question', ['id' => $question->dependquestionid],'id, position, name, type_id');if (is_object($dependquestion)) {$qdependchoice = '';switch ($dependquestion->type_id) {case QUESRADIO:case QUESDROP:case QUESCHECK:$dependchoice = $DB->get_record('questionnaire_quest_choice', ['id' => $question->dependchoiceid], 'id,content');$qdependchoice = $dependchoice->id;$dependchoice = $dependchoice->content;$contents = questionnaire_choice_values($dependchoice);if ($contents->modname) {$dependchoice = $contents->modname;}break;case QUESYESNO:switch ($question->dependchoiceid) {case 0:$dependchoice = get_string('yes');$qdependchoice = 'y';break;case 1:$dependchoice = get_string('no');$qdependchoice = 'n';break;}break;}// Qdependquestion, parenttype and qdependchoice fields to be used in preview mode.$parent[$qid]['qdependquestion'] = 'q'.$dependquestion->id;$parent[$qid]['qdependchoice'] = $qdependchoice;$parent[$qid]['parenttype'] = $dependquestion->type_id;// Other fields to be used in Questions edit mode.$parent[$qid]['position'] = $question->position;$parent[$qid]['name'] = $question->name;$parent[$qid]['content'] = $question->content;$parent[$qid]['parentposition'] = $dependquestion->position;$parent[$qid]['parent'] = format_string($dependquestion->name) . '->' . format_string ($dependchoice);}return $parent;}/*** Get parent position of all child questions in current questionnaire.* Use the parent with the largest position value.** @param array $questions* @return array An array with Child-ID->Parentposition.*/function questionnaire_get_parent_positions ($questions) {$parentpositions = array();foreach ($questions as $question) {foreach ($question->dependencies as $dependency) {$dependquestion = $dependency->dependquestionid;if (isset($dependquestion) && $dependquestion != 0) {$childid = $question->id;$parentpos = $questions[$dependquestion]->position;if (!isset($parentpositions[$childid])) {$parentpositions[$childid] = $parentpos;}if (isset ($parentpositions[$childid]) && $parentpos > $parentpositions[$childid]) {$parentpositions[$childid] = $parentpos;}}}}return $parentpositions;}/*** Get child position of all parent questions in current questionnaire.* Use the child with the smallest position value.** @param array $questions* @return array An array with Parent-ID->Childposition.*/function questionnaire_get_child_positions ($questions) {$childpositions = array();foreach ($questions as $question) {foreach ($question->dependencies as $dependency) {$dependquestion = $dependency->dependquestionid;if (isset($dependquestion) && $dependquestion != 0) {$parentid = $questions[$dependquestion]->id; // Equals $dependquestion?.$childpos = $question->position;if (!isset($childpositions[$parentid])) {$childpositions[$parentid] = $childpos;}if (isset ($childpositions[$parentid]) && $childpos < $childpositions[$parentid]) {$childpositions[$parentid] = $childpos;}}}}return $childpositions;}/*** Check that the needed page breaks are present to separate child questions.* @param stdClass $questionnaire* @return false|lang_string|string*/function questionnaire_check_page_breaks($questionnaire) {global $DB;$msg = '';// Store the new page breaks ids.$newpbids = array();$delpb = 0;$sid = $questionnaire->survey->id;$positions = array();if ($questions = $DB->get_records('questionnaire_question', ['surveyid' => $sid, 'deleted' => 'n'], 'position')) {foreach ($questions as $key => $qu) {$newqu = new stdClass();$newqu->question_id = $key;$newqu->type_id = $qu->type_id;$newqu->qname = $qu->name;$newqu->qpos = $qu->position;$dependencies = $DB->get_records('questionnaire_dependency', ['questionid' => $key, 'surveyid' => $sid],'id ASC', 'id, dependquestionid, dependchoiceid, dependlogic');$newqu->dependencies = $dependencies ?? [];$positions[] = (array)$newqu;}}$count = count($positions);for ($i = $count - 1; $i >= 0; $i--) {$qu = $positions[$i];$questionnb = $i;$prevqu = null;$prevtypeid = null;if ($i > 0) {$prevqu = $positions[$i - 1];$prevtypeid = $prevqu['type_id'];}if ($qu['type_id'] == QUESPAGEBREAK) {$questionnb--;// If more than one consecutive page breaks, remove extra one(s).// Remove that extra page break in 1st position.if ($prevtypeid == QUESPAGEBREAK || $i == $count - 1 || $qu['qpos'] == 1) {$qid = $qu['question_id'];$delpb ++;$msg .= get_string("checkbreaksremoved", "questionnaire", $delpb).'<br />';// Need to reload questions.if ($questions = $DB->get_records('questionnaire_question', ['surveyid' => $sid, 'deleted' => 'n'], 'id')) {$DB->set_field('questionnaire_question', 'deleted', 'y', ['id' => $qid, 'surveyid' => $sid]);$select = 'surveyid = ' . $sid . ' AND deleted = \'n\' AND position > ' .$questions[$qid]->position;if ($records = $DB->get_records_select('questionnaire_question', $select, null, 'position ASC')) {foreach ($records as $record) {$DB->set_field('questionnaire_question', 'position', $record->position - 1, ['id' => $record->id]);}}}}}// Add pagebreak between question child and not dependent question that follows.if ($qu['type_id'] != QUESPAGEBREAK) {if ($prevqu) {$prevdependencies = $prevqu['dependencies'];$outerdependencies = count($qu['dependencies']) >= count($prevdependencies) ?$qu['dependencies'] : $prevdependencies;$innerdependencies = count($qu['dependencies']) < count($prevdependencies) ?$qu['dependencies'] : $prevdependencies;$okeys = [];$ikeys = [];foreach ($outerdependencies as $okey => $outerdependency) {foreach ($innerdependencies as $ikey => $innerdependency) {if ($outerdependency->dependquestionid === $innerdependency->dependquestionid &&$outerdependency->dependchoiceid === $innerdependency->dependchoiceid &&$outerdependency->dependlogic === $innerdependency->dependlogic) {$okeys[] = $okey;$ikeys[] = $ikey;}}}foreach ($okeys as $key) {if (key_exists($key, $outerdependencies)) {unset($outerdependencies[$key]);}}foreach ($ikeys as $key) {if (key_exists($key, $innerdependencies)) {unset($innerdependencies[$key]);}}$diffdependencies = count($outerdependencies) + count($innerdependencies);if (($prevtypeid != QUESPAGEBREAK && $diffdependencies != 0)|| (!isset($qu['dependencies']) && isset($prevdependencies))) {$sql = 'SELECT MAX(position) as maxpos FROM {questionnaire_question} ' .'WHERE surveyid = ' . $questionnaire->survey->id . ' AND deleted = \'n\'';if ($record = $DB->get_record_sql($sql)) {$pos = $record->maxpos + 1;} else {$pos = 1;}$question = new stdClass();$question->surveyid = $questionnaire->survey->id;$question->type_id = QUESPAGEBREAK;$question->position = $pos;$question->content = 'break';if (!($newqid = $DB->insert_record('questionnaire_question', $question))) {return (false);}$newpbids[] = $newqid;$questionnaire = new questionnaire($course, $cm, $questionnaire->id, null);$questionnaire->move_question($newqid, $qu['qpos']);}}}}if (empty($newpbids) && !$msg) {$msg = get_string('checkbreaksok', 'questionnaire');} else if ($newpbids) {$msg .= get_string('checkbreaksadded', 'questionnaire').' ';$newpbids = array_reverse ($newpbids);$questionnaire = new questionnaire($course, $cm, $questionnaire->id, null);foreach ($newpbids as $newpbid) {$msg .= $questionnaire->questions[$newpbid]->position.' ';}}return($msg);}/*** Code snippet used to set up the questionform.* @param stdClass $questionnaire* @param int $qid* @param int $qtype* @return mixed|\mod_questionnaire\question\question*/function questionnaire_prep_for_questionform($questionnaire, $qid, $qtype) {$context = context_module::instance($questionnaire->cm->id);if ($qid != 0) {$question = clone($questionnaire->questions[$qid]);$question->qid = $question->id;$question->sid = $questionnaire->survey->id;$question->id = $questionnaire->cm->id;$draftideditor = file_get_submitted_draft_itemid('question');$content = file_prepare_draft_area($draftideditor, $context->id, 'mod_questionnaire', 'question',$qid, array('subdirs' => true), $question->content);$question->content = array('text' => $content, 'format' => FORMAT_HTML, 'itemid' => $draftideditor);if (isset($question->dependencies)) {foreach ($question->dependencies as $dependencies) {if ($dependencies->dependandor === "and") {$question->dependquestions_and[] = $dependencies->dependquestionid.','.$dependencies->dependchoiceid;$question->dependlogic_and[] = $dependencies->dependlogic;} else if ($dependencies->dependandor === "or") {$question->dependquestions_or[] = $dependencies->dependquestionid.','.$dependencies->dependchoiceid;$question->dependlogic_or[] = $dependencies->dependlogic;}}}} else {$question = \mod_questionnaire\question\question::question_builder($qtype);$question->sid = $questionnaire->survey->id;$question->id = $questionnaire->cm->id;$question->type_id = $qtype;$question->type = '';$draftideditor = file_get_submitted_draft_itemid('question');$content = file_prepare_draft_area($draftideditor, $context->id, 'mod_questionnaire', 'question',null, array('subdirs' => true), '');$question->content = array('text' => $content, 'format' => FORMAT_HTML, 'itemid' => $draftideditor);}return $question;}/*** Get the standard page contructs and check for validity.* @param int $id The coursemodule id.* @param int $a The module instance id.* @return array An array with the $cm, $course, and $questionnaire records in that order.*/function questionnaire_get_standard_page_items($id = null, $a = null) {global $DB;if ($id) {if (! $cm = get_coursemodule_from_id('questionnaire', $id)) {throw new \moodle_exception('invalidcoursemodule', 'mod_questionnaire');}if (! $course = $DB->get_record("course", array("id" => $cm->course))) {throw new \moodle_exception('coursemisconf', 'mod_questionnaire');}if (! $questionnaire = $DB->get_record("questionnaire", array("id" => $cm->instance))) {throw new \moodle_exception('invalidcoursemodule', 'mod_questionnaire');}} else {if (! $questionnaire = $DB->get_record("questionnaire", array("id" => $a))) {throw new \moodle_exception('invalidcoursemodule', 'mod_questionnaire');}if (! $course = $DB->get_record("course", array("id" => $questionnaire->course))) {throw new \moodle_exception('coursemisconf', 'mod_questionnaire');}if (! $cm = get_coursemodule_from_instance("questionnaire", $questionnaire->id, $course->id)) {throw new \moodle_exception('invalidcoursemodule', 'mod_questionnaire');}}return (array($cm, $course, $questionnaire));}