diff --git a/mod/questionnaire/index.php b/mod/questionnaire/index.php index 3030b31..b346fcb 100644 --- a/mod/questionnaire/index.php +++ b/mod/questionnaire/index.php @@ -18,7 +18,6 @@ require_once("../../config.php"); - require_once("lib.php"); $id = required_param('id', PARAM_INT); diff --git a/mod/questionnaire/lib.php b/mod/questionnaire/lib.php index 18e23d9..ee413ea 100644 --- a/mod/questionnaire/lib.php +++ b/mod/questionnaire/lib.php @@ -17,38 +17,6 @@ /// Library of functions and constants for module questionnaire /// (replace questionnaire with the name of your module and delete this line) -require_once('locallib.php'); -/** - * If start and end date for the questionnaire are more than this many seconds - * apart they will be represented by two separate events in the calendar - */ -require_once($CFG->libdir.'/eventslib.php'); - -/// Constants - -define ('QUESTIONNAIRE_BGALT_COLOR1', '#FFFFFF'); -define ('QUESTIONNAIRE_BGALT_COLOR2', '#EEEEEE'); - -define ('QUESTIONNAIREUNLIMITED', 0); -define ('QUESTIONNAIREONCE', 1); -define ('QUESTIONNAIREDAILY', 2); -define ('QUESTIONNAIREWEEKLY', 3); -define ('QUESTIONNAIREMONTHLY', 4); - -define ('QUESTIONNAIRE_EDITING', 0); -define ('QUESTIONNAIRE_ACTIVE1', 1); -define ('QUESTIONNAIRE_ENDED', 3); -define ('QUESTIONNAIRE_ARCHIVED', 4); -define ('QUESTIONNAIRE_TESTING', 8); -define ('QUESTIONNAIRE_ACTIVE2', 9); - -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 - function questionnaire_supports($feature) { switch($feature) { case FEATURE_BACKUP_MOODLE2: return true; @@ -72,40 +40,14 @@ function questionnaire_get_extra_capabilities() { return array('moodle/site:accessallgroups'); } -global $QUESTIONNAIRE_TYPES; -$QUESTIONNAIRE_TYPES = 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 $QUESTIONNAIRE_RESPONDENTS; -$QUESTIONNAIRE_RESPONDENTS = array ('fullname' => get_string('respondenttypefullname', 'questionnaire'), - 'anonymous' => get_string('respondenttypeanonymous', 'questionnaire')); - -global $QUESTIONNAIRE_ELIGIBLES; -$QUESTIONNAIRE_ELIGIBLES = array ('all' => get_string('respondenteligibleall', 'questionnaire'), - 'students' => get_string('respondenteligiblestudents', 'questionnaire'), - 'teachers' => get_string('respondenteligibleteachers', 'questionnaire')); - -global $QUESTIONNAIRE_REALMS; -$QUESTIONNAIRE_REALMS = array ('private' => get_string('private', 'questionnaire'), - 'public' => get_string('public', 'questionnaire'), - 'template' => get_string('template', 'questionnaire')); - -global $QUESTIONNAIRE_RESPONSEVIEWERS; -$QUESTIONNAIRE_RESPONSEVIEWERS = - array ( QUESTIONNAIRE_STUDENTVIEWRESPONSES_NEVER => get_string('responseviewstudentsnever', 'questionnaire'), - QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENANSWERED => get_string('responseviewstudentswhenanswered', 'questionnaire'), - QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENCLOSED => get_string('responseviewstudentswhenclosed', 'questionnaire'), - QUESTIONNAIRE_STUDENTVIEWRESPONSES_ALWAYS => get_string('responseviewstudentsalways', 'questionnaire')); - 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 $COURSE, $DB; + require_once('questionnaire.class.php'); + require_once('locallib.php'); // Check the realm and set it to the survey if it's set. if (!empty($questionnaire->sid) && !empty($questionnaire->realm)) { @@ -178,13 +120,12 @@ function questionnaire_add_instance($questionnaire) { return $questionnaire->id; } - -function questionnaire_update_instance($questionnaire) { - global $DB; - /// 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. +function questionnaire_update_instance($questionnaire) { + global $DB; + require_once('locallib.php'); // Check the realm and set it to the survey if its set. if (!empty($questionnaire->sid) && !empty($questionnaire->realm)) { @@ -218,13 +159,12 @@ function questionnaire_update_instance($questionnaire) { return $DB->update_record("questionnaire", $questionnaire); } - -function questionnaire_delete_instance($id) { - global $DB; - /// Given an ID of an instance of this module, /// this function will permanently delete the instance /// and any data that depends on it. +function questionnaire_delete_instance($id) { + global $DB; + require_once('locallib.php'); if (! $questionnaire = $DB->get_record('questionnaire', array('id' => $id))) { return false; @@ -252,12 +192,14 @@ function questionnaire_delete_instance($id) { return $result; } -function questionnaire_user_outline($course, $user, $mod, $questionnaire) { /// 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 +function questionnaire_user_outline($course, $user, $mod, $questionnaire) { + require_once('locallib.php'); + $result = new stdClass(); if ($responses = questionnaire_get_user_responses($questionnaire->sid, $user->id, $complete=true)) { $n = count($responses); @@ -274,26 +216,11 @@ function questionnaire_user_outline($course, $user, $mod, $questionnaire) { return $result; } -/** - * Get all the questionnaire responses for a user - */ -function questionnaire_get_user_responses($surveyid, $userid, $complete=true) { - global $DB; - $andcomplete = ''; - if ($complete) { - $andcomplete = " AND complete = 'y' "; - } - return $DB->get_records_sql ("SELECT * - FROM {questionnaire_response} - WHERE survey_id = ? - AND username = ? - ".$andcomplete." - ORDER BY submitted ASC ", array($surveyid, $userid)); -} - -function questionnaire_user_complete($course, $user, $mod, $questionnaire) { /// Print a detailed representation of what a user has done with /// a given particular instance of this module, for user activity reports. +function questionnaire_user_complete($course, $user, $mod, $questionnaire) { + require_once('locallib.php'); + if ($responses = questionnaire_get_user_responses($questionnaire->sid, $user->id, $complete=false)) { foreach ($responses as $response) { if ($response->complete == 'y') { @@ -309,30 +236,28 @@ function questionnaire_user_complete($course, $user, $mod, $questionnaire) { return true; } -function questionnaire_print_recent_activity($course, $isteacher, $timestart) { /// 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. - +function questionnaire_print_recent_activity($course, $isteacher, $timestart) { global $CFG; return false; // True if anything was printed, otherwise false } -function questionnaire_cron () { /// Function to be run periodically according to the moodle cron /// This function searches for things that need to be done, such /// as sending out mail, toggling flags etc ... - +function questionnaire_cron () { + require_once('locallib.php'); global $CFG; return questionnaire_cleanup(); } -function questionnaire_grades($questionnaireid) { /// Must return an array of grades for a given instance of this module, /// indexed by user. It also returns a maximum allowed grade. - +function questionnaire_grades($questionnaireid) { return NULL; } @@ -459,22 +384,6 @@ function questionnaire_grade_item_update($questionnaire, $grades=NULL) { return grade_update('mod/questionnaire', $questionnaire->courseid, 'mod', 'questionnaire', $questionnaire->id, 0, $grades, $params); } -function questionnaire_get_participants($questionnaireid) { -//Must return an array of user records (all data) who are participants -//for a given instance of questionnaire. Must include every user involved -//in the instance, independient of his role (student, teacher, admin...) -//See other modules as example. - global $DB; - - //Get students - $users = $DB->get_records_sql('SELECT DISTINCT u.* '. - 'FROM {user} u, '. - ' {questionnaire_attempts} qa '. - 'WHERE qa.qid = \''.$questionnaireid.'\' AND '. - ' u.id = qa.userid'); - return ($users); -} - /** * This function returns if a scale is being used by one book * it it has support for grading and scales. Commented code should be @@ -500,22 +409,6 @@ function questionnaire_scale_used_anywhere($scaleid) { } /** - * 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 = get_context_instance(CONTEXT_MODULE, $cmid)) { - print_error('badcontext'); - } - return $context; -} - -/** * Serves the questionnaire attachments. Implements needed access control ;-) * * @param object $course @@ -566,263 +459,11 @@ function questionnaire_pluginfile($course, $cm, $context, $filearea, $args, $for // finally send the file send_stored_file($file, 0, 0, true); // download MUST be forced - security! } -/** - * 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 object; - $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->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->copysurveys = has_capability('mod/questionnaire:copysurveys', $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->viewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $context, NULL, false); - - return $cb; -} ////////////////////////////////////////////////////////////////////////////////////// /// Any other questionnaire functions go here. Each of them must have a name that /// starts with questionnaire_ -/// This function *really* shouldn't be needed, but since sometimes we can end up with -/// orphaned surveys, this will clean them up. -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. // TODO - return true; -} - -function questionnaire_record_submission(&$questionnaire, $userid, $rid=0) { - global $DB; - - $attempt['qid'] = $questionnaire->id; - $attempt['userid'] = $userid; - $attempt['rid'] = $rid; - $attempt['timemodified'] = time(); - return $DB->insert_record("questionnaire_attempts", (object)$attempt, false); -} - -function questionnaire_delete_survey($sid, $qid) { - global $DB; -/// Until backup is implemented, just mark the survey as archived. - - $status = true; - - /// Delete all responses for the survey: - if ($responses = $DB->get_records('questionnaire_response', array('survey_id' => $sid), 'id')) { - foreach ($responses as $response) { - $status = $status && questionnaire_delete_response($response->id); - } - } - - /// There really shouldn't be any more, but just to make sure... - $DB->delete_records('questionnaire_response', array('survey_id' => $sid)); - $DB->delete_records('questionnaire_attempts', array('qid' => $qid)); - - /// Delete all question data for the survey: - if ($questions = $DB->get_records('questionnaire_question', array('survey_id' => $sid), 'id')) { - foreach ($questions as $question) { - $DB->delete_records('questionnaire_quest_choice', array('question_id' => $question->id)); - } - $status = $status && $DB->delete_records('questionnaire_question', array('survey_id' => $sid)); - } - - $status = $status && $DB->delete_records('questionnaire_survey', array('id' => $sid)); - - return $status; -} - -function questionnaire_delete_response($rid) { - global $DB; - - $status = true; - - /// Delete all of the survey response data: - $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)); - $status = $status && $DB->delete_records('questionnaire_attempts', array('rid' => $rid)); - - return $status; -} - -function questionnaire_get_active_surveys_menu() { - global $DB; - - $select = "status in (". QUESTIONNAIRE_ACTIVE1 . "," . QUESTIONNAIRE_ACTIVE2 . ")"; - return $DB->get_records_select_menu('questionnaire_survey', $select); -} - -function questionnaire_get_surveys_menu($status=NULL) { - global $DB; - - $field = ($status) ? 'status' : $status; - return $DB->get_records_menu('questionnaire_survey', array($field => $status)); -} - -/// Functions to call directly into phpESP. -/// Make sure a "require_once('phpESP/admin/phpESP.ini.php')" line is included. -/// Don't need to include this for all library functions, so don't. -function questionnaire_get_survey_list($courseid=0, $type='') { - global $DB; - - if ($courseid == 0) { - if (isadmin()) { - $sql = "SELECT id,name,owner,realm,status " . - "{questionnaire_survey} " . - "ORDER BY realm,name "; - $params = null; - } else { - return false; - } - } else if (!empty($type)) { - if ($type == 'public') { - $sql = "SELECT s.id,s.name,s.owner,s.realm,s.status,s.title,q.id as qid " . - "FROM {questionnaire} q " . - "INNER JOIN {questionnaire_survey} s ON s.id = q.sid " . - "WHERE status != ? AND realm = ? " . - "ORDER BY realm,name "; - $params = array(QUESTIONNAIRE_ARCHIVED, $type); - /// Any survey owned by the user or typed as 'template' can be copied. - } else if ($type == 'template') { - $sql = "SELECT s.id,s.name,s.owner,s.realm,s.status,s.title,q.id as qid " . - "FROM {questionnaire} q " . - "INNER JOIN {questionnaire_survey} s ON s.id = q.sid " . - "WHERE status != ? AND (realm = ? OR owner = ?) " . - "ORDER BY realm,name "; - $params = array(QUESTIONNAIRE_ARCHIVED, $type, $courseid); - } - } else { - $sql = "SELECT s.id,s.name,s.owner,s.realm,s.status,q.id as qid " . - "FROM {questionnaire} q " . - "INNER JOIN {questionnaire_survey} s ON s.id = q.sid " . - "WHERE status != ? AND owner = ? " . - "ORDER BY realm,name "; - $params = array(QUESTIONNAIRE_ARCHIVED, $courseid); - } - return $DB->get_records_sql($sql, $params); -} - -function questionnaire_survey_has_questions($sid) { - global $DB; - - return $DB->record_exists('questionnaire_question', array('survey_id' => $sid, 'deleted' => 'n')); -} - -function questionnaire_survey_exists($sid) { - global $DB; - - return $DB->record_exists('questionnaire_survey', array('id' => $sid)); -} - -function questionnaire_get_survey_select($instance, $courseid=0, $sid=0, $type='') { - global $OUTPUT; - - $surveylist = array(); - if ($surveys = questionnaire_get_survey_list($courseid, $type)) { - - $strpreview = get_string('preview'); - $strunknown = get_string('unknown', 'questionnaire'); - $strpublic = get_string('public', 'questionnaire'); - $strprivate = get_string('private', 'questionnaire'); - $strtemplate = get_string('template', 'questionnaire'); - $strviewresp = get_string('viewresponses', 'questionnaire'); - - foreach ($surveys as $survey) { - if (empty($survey->realm)) { - $stat = $strunknown; - } else if ($survey->realm == 'public') { - $stat = $strpublic; - } else if ($survey->realm == 'private') { - $stat = $strprivate; - } else if ($survey->realm == 'template') { - $stat = $strtemplate; - } else { - $stat = $strunknown; - } - // prevent creation of a new questionnaire using a public questionnaire IN THE SAME COURSE! - if ($type == 'public' && $survey->owner == $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->title, $action, array('title'=>$survey->title)); - $surveylist[$type.'-'.$survey->id] = $label; - } - } - } - return $surveylist; -} - -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 100: - return get_string('sectiontext', 'questionnaire'); - case 99: - return get_string('sectionbreak', 'questionnaire'); - default: - return $id; - } -} - function questionnaire_get_view_actions() { return array('view','view all'); } @@ -839,9 +480,9 @@ function questionnaire_get_post_actions() { * -where, which this function adds its new html to. */ function questionnaire_print_overview($courses,&$htmlarray) { - global $USER, $CFG; global $DB; + require_once('locallib.php'); if (empty($courses) || !is_array($courses) || count($courses) == 0) { return array(); @@ -895,6 +536,7 @@ function questionnaire_print_overview($courses,&$htmlarray) { //Go through the list of all questionnaires build previously, and check whether //they have had any activity. + require_once('questionnaire.class.php'); foreach ($questionnaires as $questionnaire) { if (array_key_exists($questionnaire->id, $new) && !empty($new[$questionnaire->id])) { @@ -938,53 +580,4 @@ function questionnaire_print_overview($courses,&$htmlarray) { } } } -} -/** - * This creates new events given as opendate and closedate by $questionnaire. - * @param object $questionnaire - * @return void - */ - /* added by JR 16 march 2009 based on lesson_process_post_save script */ - -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) { - delete_event($event->id); - } - } - - // 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->timestart = $questionnaire->opendate; - $event->visible = instance_is_visible('questionnaire', $questionnaire); - $event->timeduration = ($questionnaire->closedate - $questionnaire->opendate); - - if ($questionnaire->closedate and $questionnaire->opendate and $event->timeduration <= QUESTIONNAIRE_MAX_EVENT_LENGTH) { - // Single event for the whole questionnaire. - $event->name = $questionnaire->name; - add_event($event); - } else { - // Separate start and end events. - $event->timeduration = 0; - if ($questionnaire->opendate) { - $event->name = $questionnaire->name.' ('.get_string('questionnaireopens', 'questionnaire').')'; - add_event($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->eventtype = 'close'; - add_event($event); - } - } } \ No newline at end of file diff --git a/mod/questionnaire/locallib.php b/mod/questionnaire/locallib.php index d698a77..952a8c8 100644 --- a/mod/questionnaire/locallib.php +++ b/mod/questionnaire/locallib.php @@ -33,2846 +33,537 @@ require_once('questiontypes/questiontypes.class.php'); -class questionnaire { - -/// Class Properties - /** - * The survey record. - * @var object $survey - */ - var $survey; - -/// Class Methods - - /** - * The class constructor - * - */ - function __construct($id = 0, $questionnaire = null, &$course, &$cm, $addquestions = true) { - global $DB; - - if ($id) { - $questionnaire = $DB->get_record('questionnaire', array('id' => $id)); - } - - if (is_object($questionnaire)) { - $properties = get_object_vars($questionnaire); - foreach ($properties as $property => $value) { - $this->$property = $value; - } - } - - if (!empty($this->sid)) { - $this->add_survey($this->sid); - } - - $this->course = $course; - $this->cm = $cm; - /// When we are creating a brand new questionnaire, we will not yet have a context. - if (!empty($cm) && !empty($this->id)) { - $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); - } else { - $this->context = null; - } - - if ($addquestions && !empty($this->sid)) { - $this->add_questions($this->sid); - } - - $this->usehtmleditor = can_use_html_editor(); - - /// Load the capabilities for this user and questionnaire, if not creating a new one. - if (!empty($this->cm->id)) { - $this->capabilities = questionnaire_load_capabilities($this->cm->id); - } - } - - /** - * Adding a survey record to the object. - * - */ - function add_survey($sid = 0, $survey = null) { - global $DB; - - if ($sid) { - $this->survey = $DB->get_record('questionnaire_survey', array('id' => $sid)); - } else if (is_object($survey)) { - $this->survey = clone($survey); - } - } - - /** - * Adding questions to the object. - */ - function add_questions($sid = false, $section = false) { - global $DB; - - if ($sid === false) { - $sid = $this->sid; - } - - if (!isset($this->questions)) { - $this->questions = array(); - $this->questionsbysec = array(); - } - - $select = 'survey_id = '.$sid.' AND deleted != \'y\''; - if ($records = $DB->get_records_select('questionnaire_question', $select, null, 'position')) { - $sec = 1; - $isbreak = false; - foreach ($records as $record) { - $this->questions[$record->id] = new questionnaire_question(0, $record, $this->context); - if ($record->type_id != 99) { - $this->questionsbysec[$sec][$record->id] = &$this->questions[$record->id]; - $isbreak = false; - } else { - // sanity check: no section break allowed as first position, no 2 consecutive section breaks - if ($record->position != 1 && $isbreak == false) { - $sec++; - $isbreak = true; - } - } - } - } - } - - function view() { - global $CFG, $USER, $PAGE, $OUTPUT; - - $PAGE->set_title(format_string($this->name)); - $PAGE->set_heading(format_string($this->course->fullname)); - $PAGE->set_button(update_module_button($this->cm->id, $this->course->id, $this->strquestionnaire)); - // Initialise the JavaScript. - $PAGE->requires->js_init_call('M.mod_questionnaire.init_attempt_form', null, false, questionnaire_get_js_module()); - - echo $OUTPUT->header(); - - /// print the tabs - $questionnaire = $this; - include('tabs.php'); - - if (!$this->cm->visible && !$this->capabilities->viewhiddenactivities) { - notice(get_string("activityiscurrentlyhidden")); - } - - if (!$this->capabilities->view) { - echo('
'); - questionnaire_notify(get_string("guestsno", "questionnaire", $this->name)); - echo('
'. - get_string("continue").'
'); - exit; - } - - /// Print the main part of the page - - if (!$this->is_active()) { - echo '
' - .get_string('notavail', 'questionnaire') - .'
'; - } - else if (!$this->is_open()) { - echo '
' - .get_string('notopen', 'questionnaire', userdate($this->opendate)) - .'
'; - } - else if ($this->is_closed()) { - echo '
' - .get_string('closed', 'questionnaire', userdate($this->closedate)) - .'
'; - } - else if (!$this->user_is_eligible($USER->id)) { - echo '
' - .get_string('noteligible', 'questionnaire') - .'
'; - } - else if ($this->user_can_take($USER->id)) { - $sid=$this->sid; - $quser = $USER->id; - - if ($this->survey->realm == 'template') { - print_string('templatenotviewable', 'questionnaire'); - echo $OUTPUT->footer($this->course); - exit(); - } - - if ((!empty($this->questions)) && $this->capabilities->printblank) { - // open print friendly as popup window - $image_url = $CFG->wwwroot.'/mod/questionnaire/images/'; - $linkname = 'Printer-friendly version'; - $title = get_string('printblanktooltip','questionnaire'); - $url = '/mod/questionnaire/print.php?qid='.$this->id.'&rid=0&'.'courseid='.$this->course->id.'&sec=1'; - $options = array('menubar' => true, 'location' => false, 'scrollbars' => true, 'resizable' => true, - 'height' => 600, 'width' => 800, 'title'=>$title); - $name = 'popup'; - $link = new moodle_url($url); - $action = new popup_action('click', $link, $name, $options); - $class = "floatprinticon"; - echo $OUTPUT->action_link($link, $linkname, $action, array('class'=>$class, 'title'=>$title)); - } - $msg = $this->print_survey($USER->id, $quser); - /// If Survey was submitted with all required fields completed ($msg is empty), - /// then record the submittal. - $viewform = data_submitted($CFG->wwwroot."/mod/questionnaire/view.php"); - if (!empty($viewform->rid)) { - $viewform->rid = (int)$viewform->rid; - } - if (!empty($viewform->sec)) { - $viewform->sec = (int)$viewform->sec; - } - if (data_submitted() && confirm_sesskey() && isset($viewform->submit) && isset($viewform->submittype) && - ($viewform->submittype == "Submit Survey") && empty($msg)) { - - $this->response_delete($viewform->rid, $viewform->sec); - $this->rid = $this->response_insert($this->survey->id, $viewform->sec, $viewform->rid, $quser); - $this->response_commit($this->rid); - - /// If it was a previous save, rid is in the form... - if (!empty($viewform->rid) && is_numeric($viewform->rid)) { - $rid = $viewform->rid; - - /// Otherwise its in this object. - } else { - $rid = $this->rid; - } - - questionnaire_record_submission($this, $USER->id, $rid); - - if ($this->grade != 0) { - $questionnaire = new Object(); - $questionnaire->id = $this->id; - $questionnaire->name = $this->name; - $questionnaire->grade = $this->grade; - $questionnaire->cmidnumber = $this->cm->idnumber; - $questionnaire->courseid = $this->course->id; - questionnaire_update_grades($questionnaire, $quser); - } - - add_to_log($this->course->id, "questionnaire", "submit", "view.php?id={$this->cm->id}", "{$this->name}", $this->cm->id, $USER->id); - - $this->response_send_email($this->rid); - $this->response_goto_thankyou(); - } - - } else { - switch ($this->qtype) { - case QUESTIONNAIREDAILY: - $msgstring = ' '.get_string('today', 'questionnaire'); - break; - case QUESTIONNAIREWEEKLY: - $msgstring = ' '.get_string('thisweek', 'questionnaire'); - break; - case QUESTIONNAIREMONTHLY: - $msgstring = ' '.get_string('thismonth', 'questionnaire'); - break; - default: - $msgstring = ''; - break; - } - echo ('
'.get_string("alreadyfilled", "questionnaire", $msgstring).'
'); - } - - /// Finish the page - echo $OUTPUT->footer($this->course); - } - - /** - * Function to view an entire responses data. - * - */ - function view_response($rid, $blankquestionnaire=false) { - global $OUTPUT; - - echo $OUTPUT->box_start(); - $this->print_survey_start('', 1, 1, 0, $rid, $blankquestionnaire); - - $data = new Object(); - $i = 1; - if (!$blankquestionnaire) { - $this->response_import_all($rid, $data); - } - foreach ($this->questions as $question) { - if ($question->type_id < QUESPAGEBREAK) { - $question->response_display($data, $i++); - } - } - - $this->print_survey_end(1, 1); - echo $OUTPUT->box_end(); - } - - /** - * Function to view an entire responses data. - * - */ - function view_all_responses($resps) { - global $QTYPENAMES, $OUTPUT; - echo $OUTPUT->box_start(); - $this->print_survey_start('', 1, 1, 0); - - foreach ($resps as $resp) { - $data[$resp->id] = new Object(); - $this->response_import_all($resp->id, $data[$resp->id]); - } - - $i = 1; - echo '
'; - foreach ($this->questions as $question) { - if ($question->type_id < QUESPAGEBREAK) { - $method = $QTYPENAMES[$question->type_id].'_response_display'; - if (method_exists($question, $method)) { - $question->questionstart_survey_display($i); - $numItems = count($data); - $inneri = 0; - foreach ($data as $respid => $respdata) { - echo '
'.userdate($resps[$respid]->submitted).'
'; - $question->$method($respdata); - $inneri++; - if ($inneri < $numItems) { - echo '
'; - } - } - $question->questionend_survey_display($i); - } else { - print_error('displaymethod', 'questionnaire'); - } - $i++; - } - } - echo '
'; - - $this->print_survey_end(1, 1); - echo $OUTPUT->box_end(); - } - -/// Access Methods - function is_active() { - return (!empty($this->survey)); - } - - function is_open() { - return ($this->opendate > 0) ? ($this->opendate < time()) : true; - } - - function is_closed() { - return ($this->closedate > 0) ? ($this->closedate < time()) : false; - } - - function user_can_take($userid) { - - if (!$this->is_active() || !$this->user_is_eligible($userid)) { - return false; - } - else if ($this->qtype == QUESTIONNAIREUNLIMITED) { - return true; - } - else if ($userid > 0){ - return $this->user_time_for_new_attempt($userid); - } - else { - return false; - } - } - - function user_is_eligible($userid) { - return ($this->capabilities->view && $this->capabilities->submit); - } - - function user_time_for_new_attempt($userid) { - global $DB; - - $select = 'qid = '.$this->id.' AND userid = '.$userid; - if (!($attempts = $DB->get_records_select('questionnaire_attempts', $select, null, 'timemodified DESC'))) { - return true; - } - - $attempt = reset($attempts); - $timenow = time(); - - switch ($this->qtype) { - - case QUESTIONNAIREUNLIMITED: - $cantake = true; - break; - - case QUESTIONNAIREONCE: - $cantake = false; - break; - - case QUESTIONNAIREDAILY: - $attemptyear = date('Y', $attempt->timemodified); - $currentyear = date('Y', $timenow); - $attemptdayofyear = date('z', $attempt->timemodified); - $currentdayofyear = date('z', $timenow); - $cantake = (($attemptyear < $currentyear) || - (($attemptyear == $currentyear) && ($attemptdayofyear < $currentdayofyear))); - break; - - case QUESTIONNAIREWEEKLY: - $attemptyear = date('Y', $attempt->timemodified); - $currentyear = date('Y', $timenow); - $attemptweekofyear = date('W', $attempt->timemodified); - $currentweekofyear = date('W', $timenow); - $cantake = (($attemptyear < $currentyear) || - (($attemptyear == $currentyear) && ($attemptweekofyear < $currentweekofyear))); - break; - - case QUESTIONNAIREMONTHLY: - $attemptyear = date('Y', $attempt->timemodified); - $currentyear = date('Y', $timenow); - $attemptmonthofyear = date('n', $attempt->timemodified); - $currentmonthofyear = date('n', $timenow); - $cantake = (($attemptyear < $currentyear) || - (($attemptyear == $currentyear) && ($attemptmonthofyear < $currentmonthofyear))); - break; - - default: - $cantake = false; - break; - } - - return $cantake; - } - - function is_survey_owner() { - return (!empty($this->survey->owner) && ($this->course->id == $this->survey->owner)); - } - - function can_view_response($rid) { - global $USER, $DB; - - if (!empty($rid)) { - $response = $DB->get_record('questionnaire_response', array('id' => $rid)); - - /// If the response was not found, can't view it. - if (empty($response)) { - return false; - } - - /// If the response belongs to a different survey than this one, can't view it. - if ($response->survey_id != $this->survey->id) { - return false; - } - - /// If you can view all responses always, then you can view it. - if ($this->capabilities->readallresponseanytime) { - return true; - } - - /// If you are allowed to view this response for another user. - if ($this->capabilities->readallresponses && - ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_ALWAYS || - ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENCLOSED && $this->is_closed()) || - ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENANSWERED && !$this->user_can_take($USER->id)))) { - return true; - } - - /// If you can read your own response - if (($response->username == $USER->id) && $this->capabilities->readownresponses && ($this->count_submissions($USER->id) > 0)) { - return true; - } - - } else { - /// If you can view all responses always, then you can view it. - if ($this->capabilities->readallresponseanytime) { - return true; - } - - /// If you are allowed to view this response for another user. - if ($this->capabilities->readallresponses && - ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_ALWAYS || - ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENCLOSED && $this->is_closed()) || - ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENANSWERED && !$this->user_can_take($USER->id)))) { - return true; - } - - /// If you can read your own response - if ($this->capabilities->readownresponses && ($this->count_submissions($USER->id) > 0)) { - return true; - } - - } - } - - function count_submissions($userid=false) { - global $DB; - - if (!$userid) { - // provide for groups setting - return $DB->count_records('questionnaire_response', array('survey_id' => $this->sid, 'complete' => 'y')); - } else { - return $DB->count_records('questionnaire_response', array('survey_id' => $this->sid, 'username' => $userid, - 'complete' => 'y')); - } - } - - function has_required($section = 0) { - if (empty($this->questions)) { - return false; - } else if ($section <= 0) { - foreach ($this->questions as $question) { - if ($question->required == 'y') { - return true; - } - } - } else { - foreach ($this->questionsbysec[$section] as $question) { - if ($question->required == 'y') { - return true; - } - } - } - return false; - } -/// Display Methods - - function print_survey($userid=false, $quser) { - global $CFG; - - $formdata = new stdClass(); - if (data_submitted() && confirm_sesskey()) { - $formdata = data_submitted(); - } - $formdata->rid = $this->get_response($quser); - if (!empty($formdata->rid) && (empty($formdata->sec) || intval($formdata->sec) < 1)) { - $formdata->sec = $this->response_select_max_sec($formdata->rid); - } - if (empty($formdata->sec)) { - $formdata->sec = 1; - } else { - $formdata->sec = (intval($formdata->sec) > 0) ? intval($formdata->sec) : 1; - } - - $num_sections = isset($this->questionsbysec) ? count($this->questionsbysec) : 0; /// indexed by section. - $msg = ''; - $action = $CFG->wwwroot.'/mod/questionnaire/view.php?id='.$this->cm->id; - -/// TODO - Need to rework this. Too much crossover with ->view method. - if(!empty($formdata->submit)) { - $msg = $this->response_check_format($formdata->sec, $formdata); - if(empty($msg)) { - return; - } - } - - if(!empty($formdata->resume) && ($this->resume)) { - $this->response_delete($formdata->rid, $formdata->sec); - $formdata->rid = $this->response_insert($this->survey->id, $formdata->sec, $formdata->rid, $quser, $resume=true); - $this->response_goto_saved($action); - return; - } - // JR save each section 's $formdata somewhere in case user returns to that page when navigating the questionnaire... - if(!empty($formdata->next)) { - $this->response_delete($formdata->rid, $formdata->sec); - $formdata->rid = $this->response_insert($this->survey->id, $formdata->sec, $formdata->rid, $quser); - $msg = $this->response_check_format($formdata->sec, $formdata); - if ( $msg ) { - $formdata->next = ''; - } else { - $formdata->sec++; - } - } - if (!empty($formdata->prev) && ($this->navigate)) { - $this->response_delete($formdata->rid, $formdata->sec); - $formdata->rid = $this->response_insert($this->survey->id, $formdata->sec, $formdata->rid, $quser); - $msg = $this->response_check_format($formdata->sec, $formdata); - if ( $msg ) { - $formdata->prev = ''; - } else { - $formdata->sec--; - } - } - - if (!empty($formdata->rid)) { - $this->response_import_sec($formdata->rid, $formdata->sec, $formdata); - } - echo ' - - '; - - echo '
'; - - ?> -
-
- - - - - - -
- questions) && $num_sections) { // sanity check - $this->survey_render($formdata->sec, $msg, $formdata); - echo '
'; - if (($this->navigate) && ($formdata->sec > 1)) { - echo ''; - } - if ($this->resume) { - echo ''; - } - // Add a 'hidden' variable for the mod's 'view.php', and use a language variable for the submit button. - - if($formdata->sec == $num_sections) { - echo ' -
-
'; - } else { - echo '
'; - } - echo '
'; //divs notice & buttons - echo '
'; - - echo '
'; //div class="generalbox" - - return $msg; - } else { - echo '

'.get_string('noneinuse','questionnaire').'

'; - echo ''; - echo ''; //div class="generalbox" - } - } - - function survey_render($section = 1, $message = '', &$formdata) { - - $this->usehtmleditor = null; - - if(empty($section)) { - $section = 1; - } - - $num_sections = isset($this->questionsbysec) ? count($this->questionsbysec) : 0; /// indexed by section. - if($section > $num_sections) { - return(false); // invalid section - } - - // check to see if there are required questions - $has_required = $this->has_required($section); - - // find out what question number we are on $i New fix for question numbering - $i = 0; - if ($section > 1) { - for($j = 2; $j<=$section; $j++) { - foreach ($this->questionsbysec[$j-1] as $question) { - if ($question->type_id < 99) { - $i++; - } - } - } - } - - $this->print_survey_start($message, $section, $num_sections, $has_required); - foreach ($this->questionsbysec[$section] as $question) { - if ($question->type === 'Essay Box') { - $this->usehtmleditor = can_use_html_editor(); - } - if ($question->type_id != QUESSECTIONTEXT) { - $i++; - } - $question->survey_display($formdata, $i, $this->usehtmleditor); - /// Bug MDL-7292 - Don't count section text as a question number. - // process each question - } - // end of questions - echo ('
'); - $this->print_survey_end($section, $num_sections); - echo '
'; - return; - } - - function print_survey_start($message, $section, $num_sections, $has_required, $rid='', $blankquestionnaire=false) { - global $CFG; - global $DB; - require_once($CFG->libdir.'/filelib.php'); - - $userid = ''; - $resp = ''; - $groupname = ''; - $timesubmitted = ''; - //available group modes (0 = no groups; 1 = separate groups; 2 = visible groups) - - if ($rid) { - $courseid = $this->course->id; - if ($resp = $DB->get_record('questionnaire_response', array('id' => $rid)) ) { - if ($this->respondenttype == 'fullname') { - $userid = $resp->username; - // display name of group(s) that student belongs to... if questionnaire is set to Groups separate or visible - if ($this->cm->groupmode > 0) { - if ($groups = groups_get_all_groups($courseid, $resp->username)) { - if (count($groups) == 1) { - $group = current($groups); - $groupname = ' ('.get_string('group').': '.$group->name.')'; - } else { - $groupname = ' ('.get_string('groups').': '; - foreach ($groups as $group) { - $groupname.= $group->name.', '; - } - $groupname = substr($groupname, 0, strlen($groupname) -2).')'; - } - } else { - $groupname = ' ('.get_string('groupnonmembers').')'; - } - } - } - } - } - $ruser = ''; - if ($resp && !$blankquestionnaire) { - if ($userid) { - if ($user = $DB->get_record('user', array('id' => $userid))) { - $ruser = fullname($user); - } - } - if ($this->respondenttype == 'anonymous') { - $ruser = '- '.get_string('anonymous', 'questionnaire').' -'; - } else { - // JR DEV comment following line out if you do NOT want time submitted displayed in Anonymous surveys - if ($resp->submitted) { - $timesubmitted = ' '.get_string('submitted', 'questionnaire').' '.userdate($resp->submitted); - } - } - } - if ($ruser) { - echo (get_string('respondent', 'questionnaire').': '.$ruser.''); - if ($this->survey->realm == 'public') { - /// For a public questionnaire, look for the course that used it. - $coursename = ''; - $sql = 'SELECT q.id, q.course, c.fullname '. - 'FROM {questionnaire} q, {questionnaire_attempts} qa, {course} c '. - 'WHERE qa.rid = ? AND q.id = qa.qid AND c.id = q.course'; - if ($record = $DB->get_record_sql($sql, array($rid))) { - $coursename = $record->fullname; - } - echo (' '.get_string('course'). ': '.$coursename); - } - echo ($groupname); - echo ($timesubmitted); - } - echo '

'.s($this->survey->title).'

'; - if ($section == 1) { - if ($this->survey->subtitle) { - echo '

'.(format_text($this->survey->subtitle, FORMAT_HTML)).'

'; - } - if ($this->survey->info) { - $infotext = file_rewrite_pluginfile_urls($this->survey->info, 'pluginfile.php', $this->context->id, 'mod_questionnaire', 'info', $this->survey->id); - echo '
'.format_text($infotext, FORMAT_HTML).'
'; - } - } - if($num_sections>1) { - $a = new stdClass(); - $a->page = $section; - $a->totpages = $num_sections; - echo '
 '.get_string('pageof', 'questionnaire', $a).'
'; - } - if ($message) { - echo '
'.$message.'
'; //JR - } - - } - - function print_survey_end($section, $num_sections) { - if($num_sections>1) { - $a = new stdClass(); - $a->page = $section; - $a->totpages = $num_sections; - echo get_string('pageof', 'questionnaire', $a).'  '; - } - } - - function survey_print_render($message = '', $referer='', $courseid, $blankquestionnaire=false) { - global $USER, $DB, $OUTPUT; - - $rid = optional_param('rid', 0, PARAM_INT); - - if (! $course = $DB->get_record("course", array("id" => $courseid))) { - print_error('incorrectcourseid', 'questionnaire'); - } - $this->course = $course; - - if ($this->resume && empty($rid)) { - $rid = $this->get_response($USER->id, $rid); - } - - if (!empty($rid)) { - // If we're viewing a response, use this method. - $this->view_response($rid, $blankquestionnaire); - return; - } - - if(empty($section)) { - $section = 1; - } - - $num_sections = isset($this->questionsbysec) ? count($this->questionsbysec) : 0; - if($section > $num_sections) - return(false); // invalid section - - $has_required = $this->has_required(); - - // find out what question number we are on $i - $i = 1; - for($j = 2; $j<=$section; $j++) { - $i += count($this->questionsbysec[$j-1]); - } - - echo $OUTPUT->box_start(); - $this->print_survey_start($message, 1, 1, $has_required); - /// Print all sections: - $formdata = new stdClass(); - if (data_submitted() && confirm_sesskey()) { - $formdata = data_submitted(); - } - foreach ($this->questionsbysec as $section) { - foreach ($section as $question) { - if ($question->type_id == QUESSECTIONTEXT) { - $i--; - } - $question->survey_display($formdata, $i++, $usehtmleditor=null); - } - if (!$blankquestionnaire) { - echo (get_string('sectionbreak', 'questionnaire').'

'); // print on preview questionaire page only - } - } - // end of questions - - echo $OUTPUT->box_end(); - return; - } - - function survey_update($sdata) { - global $DB; - - $errstr = ''; //TODO: notused! - - // new survey - if(empty($this->survey->id)) { - // create a new survey in the database - $fields = array('name','realm','title','subtitle','email','theme','thanks_page','thank_head','thank_body','info'); // theme field deprecated - $record = new Object(); - $record->id = 0; - $record->owner = $sdata->owner; - foreach($fields as $f) { - if(isset($sdata->$f)) { - $record->$f = $sdata->$f; - } - } - - $this->survey = new stdClass(); - $this->survey->id = $DB->insert_record('questionnaire_survey', $record); - $this->add_survey($this->survey->id); - - if(!$this->survey->id) { - $errstr = get_string('errnewname', 'questionnaire') .' [ : ]'; //TODO: notused! - return(false); - } - } else { - if(empty($sdata->name) || empty($sdata->title) - || empty($sdata->realm)) { - return(false); - } - - $fields = array('name','realm','title','subtitle','email','theme','thanks_page','thank_head','thank_body','info'); // theme field deprecated - - $name = $DB->get_field('questionnaire_survey', 'name', array('id' => $this->survey->id)); - - // trying to change survey name - if(trim($name) != trim(stripslashes($sdata->name))) { // $sdata will already have slashes added to it. - $count = $DB->count_records('questionnaire_survey', array('name' => $sdata->name)); - if($count != 0) { - $errstr = get_string('errnewname', 'questionnaire'); //TODO: notused! - return(false); - } - } - - // UPDATE the row in the DB with current values - $survey_record = new Object(); - $survey_record->id = $this->survey->id; - foreach($fields as $f) { - $survey_record->$f = trim($sdata->{$f}); - } - - $result = $DB->update_record('questionnaire_survey', $survey_record); - if(!$result) { - $errstr = get_string('warning', 'questionnaire').' [ : ]'; //TODO: notused! - return(false); - } - } - - return($this->survey->id); - } - - /* Creates an editable copy of a survey. */ - function survey_copy($owner) { - global $DB; - - // clear the sid, clear the creation date, change the name, and clear the status - // Since we're copying a data record, addslashes. - // 2.0 - don't need to do this now, since its handled by the $DB-> functions. - $survey = clone($this->survey); - - unset($survey->id); - $survey->owner = $owner; - // Make sure that the survey name is not larger than the field size (CONTRIB-2999). Leave room for extra chars. - $survey->name = substr($survey->name, 0, (64-10)); - $survey->name .= '_copy'; - $survey->status = 0; - - // check for 'name' conflict, and resolve - $i=0; - $name = $survey->name; - while ($DB->count_records('questionnaire_survey', array('name' => $name)) > 0) { - $name = $survey->name.(++$i); - } - if($i) { - $survey->name .= $i; - } - - // create new survey - if (!($new_sid = $DB->insert_record('questionnaire_survey', $survey))) { - return(false); - } - - // make copies of all the questions - $pos=1; - foreach ($this->questions as $question) { - // fix some fields first - unset($question->id); - $question->survey_id = $new_sid; - $question->position = $pos++; - $question->name = addslashes($question->name); - $question->content = addslashes($question->content); - - // copy question to new survey - if (!($new_qid = $DB->insert_record('questionnaire_question', $question))) { - return(false); - } - - foreach ($question->choices as $choice) { - unset($choice->id); - $choice->question_id = $new_qid; - $choice->content = addslashes($choice->content); - $choice->value = addslashes($choice->value); - if (!$DB->insert_record('questionnaire_quest_choice', $choice)) { - return(false); - } - } - } - - return($new_sid); - } - - function type_has_choices() { - global $DB; - - $has_choices = array(); - - if ($records = $DB->get_records('questionnaire_question_type', array(), 'typeid', 'typeid,has_choices')) { - foreach ($records as $record) { - if($record->has_choices == 'y') { - $has_choices[$record->typeid]=1; - } else { - $has_choices[$record->typeid]=0; - } - } - } else { - $has_choices = array(); - } - - return($has_choices); - } - - function array_to_insql($array) { - if (count($array)) - return("IN (".preg_replace("/([^,]+)/","'\\1'",join(",",$array)).")"); - return 'IS NULL'; - } - - // ---- RESPONSE LIBRARY - - function response_check_format($section, &$formdata, $qnum='') { - $missing = 0; - $strmissing = ''; // missing questions - $wrongformat = 0; - $strwrongformat = ''; // wrongly formatted questions (Numeric, 5:Check Boxes, Date) - $i = 1; - for($j = 2; $j<=$section; $j++) { - // ADDED A SIMPLE LOOP FOR MAKING SURE PAGE BREAKS (type 99) AND LABELS (type 100) ARE NOT ALLOWED - foreach ($this->questionsbysec[$j-1] as $sectionrecord) { - $tid = $sectionrecord->type_id; - if ($tid < 99) { - $i++; - } - } - } - $qnum = $i - 1; - - foreach ($this->questionsbysec[$section] as $record) { - - $qid = $record->id; - $tid = $record->type_id; - $lid = $record->length; - $pid = $record->precise; - if ($tid != 100) { - $qnum++; - } - if ( ($record->required == 'y') && ($record->deleted == 'n') && ((isset($formdata->{'q'.$qid}) && $formdata->{'q'.$qid} == '') || (!isset($formdata->{'q'.$qid}))) && $tid != 8 && $tid != 100 ) { - $missing++; - $strmissing .= get_string('num', 'questionnaire').$qnum.'. '; - } - - switch ($tid) { - - case 4: // Radio Buttons with !other field - if (!isset($formdata->{'q'.$qid})) { - break; - } - $resp = $formdata->{'q'.$qid}; - $pos = strpos($resp, 'other_'); - - // "other" choice is checked but text box is empty - if (is_int($pos) == true){ - $othercontent = "q".$qid.substr($resp, 5); - if ( !$formdata->$othercontent ) { - $wrongformat++; - $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; - break; - } - } - - if (is_int($pos) == true && $record->required == 'y') { - $resp = 'q'.$qid.''.substr($resp,5); - if (!$formdata->$resp) { - $missing++; - $strmissing .= get_string('num', 'questionnaire').$qnum.'. '; - } - } - break; - - case 5: // Check Boxes - if (!isset($formdata->{'q'.$qid})) { - break; - } - $resps = $formdata->{'q'.$qid}; - $nbrespchoices = 0; - foreach ($resps as $resp) { - $pos = strpos($resp, 'other_'); - - // "other" choice is checked but text box is empty - if (is_int($pos) == true){ - $othercontent = "q".$qid.substr($resp, 5); - if ( !$formdata->$othercontent ) { - $wrongformat++; - $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; - break; - } - } - - if (is_numeric($resp) || is_int($pos) == true) { //JR fixed bug CONTRIB-884 - $nbrespchoices++; - } - } - $nbquestchoices = count($record->choices); - $min = $lid; - $max = $pid; - if ($max == 0) { - $max = $nbquestchoices; - } - if ($min > $max) { - $min = $max; // sanity check - } - $min = min($nbquestchoices, $min); - // number of ticked boxes is not within min and max set limits - if ( $nbrespchoices && ($nbrespchoices < $min || $nbrespchoices > $max) ) { - $wrongformat++; - $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; - break; - } - break; - - case 6: // Drop - $resp = $formdata->{'q'.$qid}; - if (!$resp && $record->required == 'y') { - $missing++; - $strmissing .= get_string('num', 'questionnaire').$qnum.'. '; - } - break; - - case 8: // Rate - $num = 0; - $nbchoices = count($record->choices); - $na = get_string('notapplicable', 'questionnaire'); - foreach ($record->choices as $cid => $choice) { - // in case we have named degrees on the Likert scale, count them to substract from nbchoices - $nameddegrees = 0; - $content = $choice->content; - if (preg_match("/^[0-9]{1,3}=/", $content,$ndd)) { - $nameddegrees++; - } else { - $str = 'q'."{$record->id}_$cid"; - if (isset($formdata->$str) && $formdata->$str == $na) { - $formdata->$str = -1; - } - for ($j = 0; $j < $record->length; $j++) { - $num += (isset($formdata->$str) && ($j == $formdata->$str)); - } - $num += (($record->precise) && isset($formdata->$str) && ($formdata->$str == -1)); - } - $nbchoices -= $nameddegrees; - } - if ( $num == 0 && $record->required == 'y') { - $missing++; - $strmissing .= get_string('num', 'questionnaire').$qnum.'. '; - break; - } - // if nodupes and nb choice restricted, nbchoices may be > actual choices, so limit it to $record->length - $isrestricted = ($record->length < count($record->choices)) && $record->precise == 2; - if ($isrestricted) { - $nbchoices = min ($nbchoices, $record->length); - } - if ( $num != $nbchoices && $num!=0 ) { - $wrongformat++; - $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; - } - break; - - case 9: // Date - $checkdateresult = ''; - if ($formdata->{'q'.$qid} != '') { - $checkdateresult = questionnaire_check_date($formdata->{'q'.$qid}); - } - if (substr($checkdateresult,0,5) == 'wrong') { - $wrongformat++; - $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; - } - break; - - case 10: // Numeric - if ( ($formdata->{'q'.$qid} != '') && (!is_numeric($formdata->{'q'.$qid})) ) { - $wrongformat++; - $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; - } - break; - - default: - break; - } - } - $message =''; - if($missing) { - if ($missing == 1) { - $message = get_string('missingquestion', 'questionnaire').$strmissing; - } else { - $message = get_string('missingquestions', 'questionnaire').$strmissing; - } - if ($wrongformat) { - $message .= '
'; - } - } - if($wrongformat) { - if ($wrongformat == 1) { - $message .= get_string('wrongformat', 'questionnaire').$strwrongformat; - } else { - $message .= get_string('wrongformats', 'questionnaire').$strwrongformat; - } - } - return ($message); - } - - - function response_delete($rid, $sec = null) { - global $DB; - - if (empty($rid)) { - return; - } - - if ($sec != null) { - if ($sec < 1) { - return; - } - - /* get question_id's in this section */ - $qids = ''; - foreach ($this->questionsbysec[$sec] as $question) { - if (empty($qids)) { - $qids .= ' AND question_id IN ('.$question->id; - } else { - $qids .= ','.$question->id; - } - } - if (!empty($qids)) { - $qids .= ')'; - } else { - return; - } - } else { - /* delete all */ - $qids = ''; - } - - /* delete values */ - $select = 'response_id = \''.$rid.'\' '.$qids; - foreach (array('response_bool', 'resp_single', 'resp_multiple', 'response_rank', 'response_text', - 'response_other', 'response_date') as $tbl) { - $DB->delete_records_select('questionnaire_'.$tbl, $select); - } - } - - function response_import_sec($rid, $sec, &$varr) { - if ($sec < 1 || !isset($this->questionsbysec[$sec])) { - return; - } - $vals = $this->response_select($rid, 'content'); - reset($vals); - foreach ($vals as $id => $arr) { - if (isset($arr[0]) && is_array($arr[0])) { - // multiple - $varr->{'q'.$id} = array_map('array_pop', $arr); - } else { - $varr->{'q'.$id} = array_pop($arr); - } - } - } - - - - function response_import_all($rid, &$varr) { - $vals = $this->response_select($rid, 'content'); - reset($vals); - foreach ($vals as $id => $arr) { - if (strstr($id, '_') && isset($arr[4])) { // single OR multiple with !other choice selected - $varr->{'q'.$id} = $arr[4]; - } else { - if (isset($arr[0]) && is_array($arr[0])) { // multiple - $varr->{'q'.$id} = array_map('array_pop', $arr); - } else { // boolean, rate and other - $varr->{'q'.$id} = array_pop($arr); - } - } - } - } - - function response_commit($rid) { - global $DB; - - $record = new object; - $record->id = $rid; - $record->complete = 'y'; - $record->submitted = time(); - - if ($this->grade < 0) { - $record->grade = 1; /// Don't know what to do if its a scale... - } else { - $record->grade = $this->grade; - } - return $DB->update_record('questionnaire_response', $record); - } - - function get_response($username, $rid = 0) { - global $DB; - - $rid = intval($rid); - if ($rid != 0) { - // check for valid rid - $fields = 'id, username'; - $select = 'id = '.$rid.' AND survey_id = '.$this->sid.' AND username = \''.$username.'\' AND complete = \'n\''; - return ($DB->get_record_select('questionnaire_response', $select, null, $fields) !== false) ? $rid : ''; - - } else { - // find latest in progress rid - $select = 'survey_id = '.$this->sid.' AND complete = \'n\' AND username = \''.$username.'\''; - if ($records = $DB->get_records_select('questionnaire_response', $select, null, 'submitted DESC', - 'id,survey_id', 0, 1)) { - $rec = reset($records); - return $rec->id; - } else { - return ''; - } - } - } - - function response_select_max_sec($rid) { - global $DB; - - $pos = $this->response_select_max_pos($rid); - $select = 'survey_id = \''.$this->sid.'\' AND type_id = 99 AND position < '.$pos.' AND deleted = \'n\''; - $max = $DB->count_records_select('questionnaire_question', $select) + 1; - - return $max; - } - - function response_select_max_pos($rid) { - global $DB; - - $max = 0; - - foreach (array('response_bool', 'resp_single', 'resp_multiple', 'response_rank', 'response_text', - 'response_other', 'response_date') as $tbl) { - $sql = 'SELECT MAX(q.position) as num FROM {questionnaire_'.$tbl.'} a, {questionnaire_question} q '. - 'WHERE a.response_id = ? AND '. - 'q.id = a.question_id AND '. - 'q.survey_id = ? AND '. - 'q.deleted = \'n\''; - if ($record = $DB->get_record_sql($sql, array($rid, $this->sid))) { - $max = (int)$record->num; - } - } - return $max; - } - -/* {{{ proto array response_select_name(int survey_id, int response_id, array question_ids) - A wrapper around response_select(), that returns an array of - key/value pairs using the field name as the key. - $csvexport = true: a parameter to return a different response formatting for CSV export from normal report formatting - */ - function response_select_name($rid, $choicecodes, $choicetext) { - $res = $this->response_select($rid, 'position,type_id,name', true, $choicecodes, $choicetext); - $nam = array(); - reset($res); - $subqnum = 0; - $oldpos = ''; - while(list($qid, $arr) = each($res)) { - $qpos = $arr[0]; // question position (there may be "holes" in positions list) - $qtype = $arr[1]; // question type (1:bool,2:text,3:essay,4:radio,5:check,6:dropdn,7:rating(not used),8:rate,9:date,10:numeric) - $qname = $arr[2]; // variable name; (may be empty); for rate questions: 'variable group' name - $qchoice = $arr[3]; // modality; for rate questions: variable - - // strip potential html tags from modality name - if (!empty($qchoice)) { - $qchoice = strip_tags($arr[3]); - $qchoice = preg_replace("/[\r\n\t]/", ' ', $qchoice); - } - $q4 = ''; // for rate questions: modality; for multichoice: selected = 1; not selected = 0 - if (isset($arr[4])) { - $q4 = $arr[4]; - } - if (strstr($qid, '_')) { - if ($qtype == 4) { //single - $nam[$qpos][$qname.'_'.get_string('other', 'questionnaire')] = $q4; - continue; - } - // multiple OR rank - if ($oldpos != $qpos) { - $subqnum = 1; - $oldpos = $qpos; - } else { - $subqnum++; - } - if ($qtype == 8) { // rate - $qname .= "->$qchoice"; - if ($q4 == -1) { -// $q4 = get_string('notapplicable', 'questionnaire'); DEV JR choose one solution please - $q4 = ''; - } else { - if (is_numeric($q4)) { - $q4++; - } - } - } else { // multiple - $qname .= "->$qchoice"; - } - $nam[$qpos][$qname] = $q4; - continue; - } - $val = $qchoice; - $nam[$qpos][$qname] = $val; - } - return $nam; - } - - function response_send_email($rid, $userid=false) { - global $CFG, $USER, $DB; - - require_once($CFG->libdir.'/phpmailer/class.phpmailer.php'); - - $name = s($this->name); - if ($record = $DB->get_record('questionnaire_survey', array('id' => $this->survey->id))) { - $email = $record->email; - } else { - $email = ''; - } - - if(empty($email)) { - return(false); - } - $answers = $this->generate_csv($rid, $userid='', null, 1); - - // line endings for html and plaintext emails - $end_html = "\r\n
"; - $end_plaintext = "\r\n"; - - $subject = get_string('surveyresponse', 'questionnaire') .": $name [$rid]"; - $url = $CFG->wwwroot.'/mod/questionnaire/report.php?action=vresp&sid='.$this->survey->id. - '&rid='.$rid.'&instance='.$this->id; - - // html and plaintext body - $body_html = ''.$url.''.$end_html; - $body_plaintext = $url.$end_plaintext; - $body_html .= get_string('surveyresponse', 'questionnaire') .' "'.$name.'"'.$end_html; - $body_plaintext .= get_string('surveyresponse', 'questionnaire') .' "'.$name.'"'.$end_plaintext; - - reset($answers); - - for ($i = 0; $i < count($answers[0]); $i++) { - $sep = ' : '; - switch($i) { - case 1: - $sep = ' '; - break; - case 4: - $body_html .= get_string('user').' '; - $body_plaintext .= get_string('user').' '; - break; - case 6: - if ($this->respondenttype != 'anonymous') { - $body_html .= get_string('email').$sep.$USER->email. $end_html; - $body_plaintext .= get_string('email').$sep.$USER->email. $end_plaintext; - } - } - $body_html .= $answers[0][$i].$sep.$answers[1][$i]. $end_html; - $body_plaintext .= $answers[0][$i].$sep.$answers[1][$i]. $end_plaintext; - } - - // use plaintext version for altbody - $altbody = "\n$body_plaintext\n"; - - $return = true; - $mailaddresses = preg_split('/,|;/', $email); - foreach ($mailaddresses as $email) { - $userto = new Object(); - $userto->email = $email; - $userto->mailformat = 1; - $userfrom = $CFG->noreplyaddress; - if (email_to_user($userto, $userfrom, $subject, $altbody, $body_html)) { - $return = $return && true; - } else { - $return = false; +/// Constants + +define ('QUESTIONNAIRE_BGALT_COLOR1', '#FFFFFF'); +define ('QUESTIONNAIRE_BGALT_COLOR2', '#EEEEEE'); + +define ('QUESTIONNAIREUNLIMITED', 0); +define ('QUESTIONNAIREONCE', 1); +define ('QUESTIONNAIREDAILY', 2); +define ('QUESTIONNAIREWEEKLY', 3); +define ('QUESTIONNAIREMONTHLY', 4); + +define ('QUESTIONNAIRE_EDITING', 0); +define ('QUESTIONNAIRE_ACTIVE1', 1); +define ('QUESTIONNAIRE_ENDED', 3); +define ('QUESTIONNAIRE_ARCHIVED', 4); +define ('QUESTIONNAIRE_TESTING', 8); +define ('QUESTIONNAIRE_ACTIVE2', 9); + +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 + +global $QUESTIONNAIRE_TYPES; +$QUESTIONNAIRE_TYPES = 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 $QUESTIONNAIRE_RESPONDENTS; +$QUESTIONNAIRE_RESPONDENTS = array ('fullname' => get_string('respondenttypefullname', 'questionnaire'), + 'anonymous' => get_string('respondenttypeanonymous', 'questionnaire')); + +global $QUESTIONNAIRE_ELIGIBLES; +$QUESTIONNAIRE_ELIGIBLES = array ('all' => get_string('respondenteligibleall', 'questionnaire'), + 'students' => get_string('respondenteligiblestudents', 'questionnaire'), + 'teachers' => get_string('respondenteligibleteachers', 'questionnaire')); + +global $QUESTIONNAIRE_REALMS; +$QUESTIONNAIRE_REALMS = array ('private' => get_string('private', 'questionnaire'), + 'public' => get_string('public', 'questionnaire'), + 'template' => get_string('template', 'questionnaire')); + +global $QUESTIONNAIRE_RESPONSEVIEWERS; +$QUESTIONNAIRE_RESPONSEVIEWERS = + array ( QUESTIONNAIRE_STUDENTVIEWRESPONSES_NEVER => get_string('responseviewstudentsnever', 'questionnaire'), + QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENANSWERED => get_string('responseviewstudentswhenanswered', 'questionnaire'), + QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENCLOSED => get_string('responseviewstudentswhenclosed', 'questionnaire'), + QUESTIONNAIRE_STUDENTVIEWRESPONSES_ALWAYS => get_string('responseviewstudentsalways', 'questionnaire')); + +function questionnaire_check_date ($thisdate, $insert=false) { + $dateformat = get_string('strfdate', 'questionnaire'); + if (preg_match('/(%[mdyY])(.+)(%[mdyY])(.+)(%[mdyY])/', $dateformat, $matches)) { + $date_pieces = explode($matches[2], $thisdate); + foreach ($date_pieces as $datepiece) { + if (!is_numeric($datepiece)) { + return 'wrongdateformat'; + } + } + $pattern = "/[^dmy]/i"; + $dateorder = strtolower(preg_replace($pattern, '', $dateformat)); + $countpieces = count($date_pieces); + if ($countpieces == 1) { // assume only year entered + switch ($dateorder) { + case 'dmy': // most countries + case 'mdy': // USA + $date_pieces[2] = $date_pieces[0]; // year + $date_pieces[0] = '1'; // assumed 1st month of year + $date_pieces[1] = '1'; // assumed 1st day of month + break; + case 'ymd': // ISO 8601 standard + $date_pieces[1] = '1'; // assumed 1st month of year + $date_pieces[2] = '1'; // assumed 1st day of month + break; } } - return $return; - } - - function response_insert($sid, $section, $rid, $userid, $resume=false) { - global $DB, $USER; - - $record = new object; - $record->submitted = time(); - - if(empty($rid)) { - // create a uniqe id for this response - $record->survey_id = $sid; - $record->username = $userid; - $rid = $DB->insert_record('questionnaire_response', $record); - } else { - $record->id = $rid; - $DB->update_record('questionnaire_response', $record); - } - if ($resume) { - add_to_log($this->course->id, "questionnaire", "save", "view.php?id={$this->cm->id}", "{$this->name}", $this->cm->id, $USER->id); - } - - if (!empty($this->questionsbysec[$section])) { - foreach ($this->questionsbysec[$section] as $question) { - $question->insert_response($rid); + if ($countpieces == 2) { // assume only month and year entered + switch ($dateorder) { + case 'dmy': // most countries + $date_pieces[2] = $date_pieces[1]; //year + $date_pieces[1] = $date_pieces[0]; // month + $date_pieces[0] = '1'; // assumed 1st day of month + break; + case 'mdy': // USA + $date_pieces[2] = $date_pieces[1]; //year + $date_pieces[0] = $date_pieces[0]; // month + $date_pieces[1] = '1'; // assumed 1st day of month + break; + case 'ymd': // ISO 8601 standard + $date_pieces[2] = '1'; // assumed 1st day of month + break; } } - return($rid); - } - - function response_select($rid, $col = null, $csvexport = false, $choicecodes=0, $choicetext=1) { - global $DB; - - $sid = $this->survey->id; - $values = array(); - $stringother = get_string('other', 'questionnaire'); - if ($col == null) { - $col = ''; - } - if (!is_array($col) && !empty($col)) { - $col = explode(',', preg_replace("/\s/",'', $col)); - } - if (is_array($col) && count($col) > 0) { - $col = ',' . implode(',', array_map(create_function('$a','return "q.$a";'), $col)); - } - - // --------------------- response_bool (yes/no)--------------------- - $sql = 'SELECT q.id '.$col.', a.choice_id '. - 'FROM {questionnaire_response_bool} a, {questionnaire_question} q '. - 'WHERE a.response_id= ? AND a.question_id=q.id '; - if ($records = $DB->get_records_sql($sql, array($rid))) { - foreach ($records as $qid => $row) { - $choice = $row->choice_id; - if (isset ($row->name) && $row->name == '') { - $noname = TRUE; - } - unset ($row->id); - unset ($row->choice_id); - $row = (array)$row; - $newrow = array(); - foreach ($row as $key => $val) { - if (!is_numeric($key)) { - $newrow[] = $val; - } - } - $values[$qid] = $newrow; - array_push($values["$qid"], ($choice == 'y') ? '1' : '0'); - if (!$csvexport) { - array_push($values["$qid"], $choice); //DEV still needed for responses display - } + if (count($date_pieces) > 1) { + if ($matches[1] == '%m') $month = $date_pieces[0]; + if ($matches[1] == '%d') $day = $date_pieces[0]; + if ($matches[1] == '%y') $year = strftime('%C').$date_pieces[0]; + if ($matches[1] == '%Y') $year = $date_pieces[0]; + + if ($matches[3] == '%m') $month = $date_pieces[1]; + if ($matches[3] == '%d') $day = $date_pieces[1]; + if ($matches[3] == '%y') $year = strftime('%C').$date_pieces[1]; + if ($matches[3] == '%Y') $year = $date_pieces[1]; + + if ($matches[5] == '%m') $month = $date_pieces[2]; + if ($matches[5] == '%d') $day = $date_pieces[2]; + if ($matches[5] == '%y') $year = strftime('%C').$date_pieces[2]; + if ($matches[5] == '%Y') $year = $date_pieces[2]; + + $month = min(12,$month); + $month = max(1,$month); + if ($month == 2) { + $day = min(29, $day); + } else if ($month == 4 || $month == 6 || $month == 9 || $month == 11) { + $day = min(30, $day); + } else { + $day = min(31, $day); } - } - - // --------------------- response_single (radio button or dropdown)--------------------- - $sql = 'SELECT q.id '.$col.', q.type_id as q_type, c.content as ccontent,c.id as cid '. - 'FROM {questionnaire_resp_single} a, {questionnaire_question} q, {questionnaire_quest_choice} c '. - 'WHERE a.response_id = ? AND a.question_id=q.id AND a.choice_id=c.id '; - if ($records = $DB->get_records_sql($sql, array($rid))) { - foreach ($records as $qid => $row) { - $cid = $row->cid; - $qtype = $row->q_type; - if ($csvexport) { - static $i = 1; - $qrecords = $DB->get_records('questionnaire_quest_choice', array('question_id' => $qid)); - foreach($qrecords as $value) { - if ($value->id == $cid) { - $contents = questionnaire_choice_values($value->content); - if ($contents->modname) { - $row->ccontent = $contents->modname; - } else { - $content = $contents->text; - if (preg_match('/^!other/', $content)) { - $row->ccontent = get_string('other','questionnaire'); - } else if (($choicecodes == 1) && ($choicetext == 1)) { - $row->ccontent = "$i : $content"; - } else if ($choicecodes == 1) { - $row->ccontent = "$i"; - } else { - $row->ccontent = $content; - } - } - $i = 1; - break; - } - $i++; - } - } - unset($row->id); - unset($row->cid); - unset($row->q_type); - $arow = get_object_vars($row); - $newrow = array(); - foreach ($arow as $key => $val) { - if (!is_numeric($key)) { - $newrow[] = $val; - } - } - if (preg_match('/^!other/', $row->ccontent)) { - $newrow[] = 'other_' . $cid; + $day = max(1, $day); + if (!$thisdate = gmmktime(0, 0, 0, $month, $day, $year)) { + return 'wrongdaterange'; + } else { + if ($insert) { + $thisdate = trim(userdate ($thisdate, '%Y-%m-%d', '1', false)); } else { - $newrow[] = (int)$cid; + $thisdate = trim(userdate ($thisdate, $dateformat, '1', false)); } - $values[$qid] = $newrow; } + return $thisdate; } + } else return ('wrongdateformat'); +} - // --------------------- response_multiple --------------------- - $sql = 'SELECT a.id as aid, q.id as qid '.$col.',c.content as ccontent,c.id as cid '. - 'FROM {questionnaire_resp_multiple} a, {questionnaire_question} q, {questionnaire_quest_choice} c '. - 'WHERE a.response_id = ? AND a.question_id=q.id AND a.choice_id=c.id '. - 'ORDER BY a.id,a.question_id,c.id'; - $records = $DB->get_records_sql($sql, array($rid)); - if ($csvexport) { - $tmp = null; - - if (!empty($records)) { - $qids2 = array(); - $oldqid = ''; - foreach ($records as $qid => $row) { - if ($row->qid != $oldqid) { - $qids2[] = $row->qid; - $oldqid = $row->qid; - } - } - if (is_array($qids2)) { - $qids2 = 'question_id ' . $this->array_to_insql($qids2); - } else { - $qids2 = 'question_id= ' . $qids2; - } - $sql = 'SELECT * FROM {questionnaire_quest_choice} WHERE '.$qids2. - 'ORDER BY id'; - if ($records2 = $DB->get_records_sql($sql)) { - foreach ($records2 as $qid => $row2) { - $selected = '0'; - $qid2 = $row2->question_id; - $cid2 = $row2->id; - $c2 = $row2->content; - $otherend = false; - if ($c2 == '!other') { - $c2 = '!other='.get_string('other','questionnaire'); - } - if (preg_match('/^!other/', $c2)) { - $otherend = true; - } else { - $contents = questionnaire_choice_values($c2); - if ($contents->modname) { - $c2 = $contents->modname; - } elseif ($contents->title) { - $c2 = $contents->title; - } - } - $sql = 'SELECT a.name as name, a.type_id as q_type, a.position as pos ' . - 'FROM {questionnaire_question} a WHERE id = ?'; - if ($currentquestion = $DB->get_records_sql($sql, array($qid2))) { - foreach ($currentquestion as $question) { - $name1 = $question->name; - $type1 = $question->q_type; - } - } - $newrow = array(); - foreach ($records as $qid => $row1) { - $qid1 = $row1->qid; - $cid1 = $row1->cid; - // if available choice has been selected by student - if ($qid1 == $qid2 && $cid1 == $cid2) { - $selected = '1'; - } - } - if ($otherend) { - $newrow2 = array(); - $newrow2[] = $question->pos; - $newrow2[] = $type1; - $newrow2[] = $name1; - $newrow2[] = '['.get_string('other','questionnaire').']'; - $newrow2[] = $selected; - $tmp2 = $qid2.'_other'; - $values["$tmp2"]=$newrow2; - } - $newrow[] = $question->pos; - $newrow[] = $type1; - $newrow[] = $name1; - $newrow[] = $c2; - $newrow[] = $selected; - $tmp = $qid2.'_'.$cid2; - $values["$tmp"]=$newrow; - } - } - } - unset($tmp); - unset($row); +// .mform span.required .mform div.error +// a variant of Moodle's notify function, with a different formatting +function questionnaire_notify($message) { + $message = clean_text($message); + $errorstart = '
'; + $errorend = '
'; + $output = $errorstart.$message.$errorend; + echo $output; +} +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 ($count = preg_match('/(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 { - $arr = array(); - $tmp = null; - if (!empty($records)) { - foreach ($records as $aid => $row) { - $qid = $row->qid; - $cid = $row->cid; - unset($row->aid); - unset($row->qid); - unset($row->cid); - $arow = get_object_vars($row); - $newrow = array(); - foreach ($arow as $key => $val) { - if (!is_numeric($key)) { - $newrow[] = $val; - } - } - if (preg_match('/^!other/', $row->ccontent)) { - $newrow[] = 'other_' . $cid; - } else { - $newrow[] = (int)$cid; - } - if($tmp == $qid) { - $arr[] = $newrow; - continue; - } - if($tmp != null) { - $values["$tmp"]=$arr; - } - $tmp = $qid; - $arr = array($newrow); - } - } - if($tmp != null) { - $values["$tmp"]=$arr; - } - unset($arr); - unset($tmp); - unset($row); - - + // image has no title nor alt text: use its filename (without the extension) + preg_match("/.*\/(.*)\..*$/", $imageurl, $matches); + $contents->title = $matches[1]; } - - // --------------------- response_other --------------------- - // this will work even for multiple !other fields within one question AND for identical !other responses in different questions JR - $sql = 'SELECT c.id as cid, c.content as content, a.response as aresponse, q.id as qid, q.position as position, q.type_id as type_id, q.name as name '. - 'FROM {questionnaire_response_other} a, {questionnaire_question} q, {questionnaire_quest_choice} c '. - 'WHERE a.response_id= ? AND a.question_id=q.id AND a.choice_id=c.id '. - 'ORDER BY a.question_id,c.id '; - if ($records = $DB->get_records_sql($sql, array($rid))) { - foreach ($records as $record) { - $newrow = array(); - $position = $record->position; - $type_id = $record->type_id; - $name = $record->name; - $cid = $record->cid; - $qid = $record->qid; - $content = $record->content; - - //!other modality with no label - if ($content == '!other') { - $content = '!other='.$stringother; - } - $content = substr($content,7); - $aresponse = $record->aresponse; - // the first two empty values are needed for compatibility with "normal" (non !other) responses - // they are only needed for the CSV export, in fact - JR - $newrow[] = $position; - $newrow[] = $type_id; - $newrow[] = $name; - $content = $stringother; - $newrow[] = $content; - $newrow[] = $aresponse; - $values["${qid}_${cid}"] = $newrow; - } + // content has text or named modality plus an image + if (preg_match('/(.*)(get_records_sql($sql, array($rid))) { - foreach ($records as $row) { - /// Next two are 'qid' and 'cid', each with numeric and hash keys. - $osgood = false; - if ($row->precise == 3) { - $osgood = true; - } - $qid = $row->qid.'_'.$row->cid; - unset($row->aid); // get rid of the answer id. - unset($row->qid); - unset($row->cid); - unset($row->precise); - $row = (array)$row; - $newrow = array(); - foreach ($row as $key => $val) { - if ($key != 'content') { // no need to keep question text - ony keep choice text and rank - if ($key == 'ccontent') { - if ($osgood) { - list($contentleft, $contentright) = preg_split('/[|]/', $val); - $contents = questionnaire_choice_values($contentleft); - if ($contents->title) { - $contentleft = $contents->title; - } - $contents = questionnaire_choice_values($contentright); - if ($contents->title) { - $contentright = $contents->title; - } - $val = strip_tags($contentleft.'|'.$contentright); - $val = preg_replace("/[\r\n\t]/", ' ', $val); - } else { - $contents = questionnaire_choice_values($val); - if ($contents->modname) { - $val = $contents->modname; - } elseif ($contents->title) { - $val = $contents->title; - } elseif ($contents->text) { - $val = strip_tags($contents->text); - $val = preg_replace("/[\r\n\t]/", ' ', $val); - } - } - } - $newrow[] = $val; - } - } - $values[$qid] = $newrow; - } + } + // look for named modalities + $contents->text = $content; + if ($pos = strpos($content, '=')) { + // the equal sign used for named modalities must NOT be followed by a double quote + // because an equal sign followed by double quote might introduce e.g. a lang tag + if (substr($content, $pos + 1, 1) != '"') { + $contents->text = substr($content, $pos + 1); + $contents->modname =substr($content, 0, $pos); } + } + return $contents; +} - // --------------------- response_text --------------------- - $sql = 'SELECT q.id '.$col.',a.response as aresponse '. - 'FROM {questionnaire_response_text} a, {questionnaire_question} q '. - 'WHERE a.response_id=\''.$rid.'\' AND a.question_id=q.id '; - if ($records = $DB->get_records_sql($sql)) { - foreach ($records as $qid => $row) { - unset($row->id); - $row = (array)$row; - $newrow = array(); - foreach ($row as $key => $val) { - if (!is_numeric($key)) { - $newrow[] = $val; - } - } - $values["$qid"]=$newrow; - $val = array_pop($values["$qid"]); - array_push($values["$qid"], $val, $val); - } - } +/** + * Get the information about the standard questionnaire JavaScript module. + * @return array a standard jsmodule structure. + */ +function questionnaire_get_js_module() { + global $PAGE; + 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'), + ), + ); +} - // --------------------- response_date --------------------- - $sql = 'SELECT q.id '.$col.',a.response as aresponse '. - 'FROM {questionnaire_response_date} a, {questionnaire_question} q '. - 'WHERE a.response_id=\''.$rid.'\' AND a.question_id=q.id '; - if ($records = $DB->get_records_sql($sql)) { - $dateformat = get_string('strfdate', 'questionnaire'); - foreach ($records as $qid => $row) { - unset ($row->id); - $row = (array)$row; - $newrow = array(); - foreach ($row as $key => $val) { - if (!is_numeric($key)) { - $newrow[] = $val; - // convert date from yyyy-mm-dd database format to actual questionnaire dateformat - // does not work with dates prior to 1900 under Windows - if (preg_match('/\d\d\d\d-\d\d-\d\d/', $val)) { - $dateparts = preg_split('/-/', $val); - $val = make_timestamp($dateparts[0], $dateparts[1], $dateparts[2]); // Unix timestamp - $val = userdate ( $val, $dateformat); - $newrow[] = $val; - } - } - } - $values["$qid"]=$newrow; - $val = array_pop($values["$qid"]); - array_push($values["$qid"], '', '', $val); - } - } +/** + * Get all the questionnaire responses for a user + */ +function questionnaire_get_user_responses($surveyid, $userid, $complete=true) { + global $DB; + $andcomplete = ''; + if ($complete) { + $andcomplete = " AND complete = 'y' "; + } + return $DB->get_records_sql ("SELECT * + FROM {questionnaire_response} + WHERE survey_id = ? + AND username = ? + ".$andcomplete." + ORDER BY submitted ASC ", array($surveyid, $userid)); +} - // --------------------- return --------------------- - return($values); - } +/** + * 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 object; + $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->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->copysurveys = has_capability('mod/questionnaire:copysurveys', $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->viewhiddenactivities = has_capability('moodle/course:viewhiddenactivities', $context, NULL, false); + + return $cb; +} - function response_goto_thankyou() { - global $CFG, $USER, $DB; +/** + * 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; - $select = 'id = '.$this->survey->id; - $fields = 'thanks_page,thank_head,thank_body'; - if ($result = $DB->get_record_select('questionnaire_survey', $select, null, $fields)) { - $thank_url = $result->thanks_page; - $thank_head = $result->thank_head; - $thank_body = $result->thank_body; - } else { - $thank_url = ''; - $thank_head = ''; - $thank_body = ''; - } - if(!empty($thank_url)) { - if(!headers_sent()) { - header("Location: $thank_url"); - exit; - } - ?> - - - '.$thank_head.''.file_rewrite_pluginfile_urls(format_text($thank_body, FORMAT_HTML), 'pluginfile.php', - $this->context->id, 'mod_questionnaire', 'thankbody', $this->id); - echo ($message); - if ($this->capabilities->readownresponses) { - echo(''. - get_string("continue").''); - } else { - echo(''. - get_string("continue").''); - } - return; - } + if(isset($context)) return $context; - function response_goto_saved($url) { - ?> -
- '.get_string('resumesurvey', 'questionnaire').''); ?> -
-
-
- -   ' - .get_string("backto","moodle",$this->course->fullname).'  '); - ?> - survey->id.' AND complete = \'y\''; - if ($userid !== false) { - $select .= ' AND username = \''.$userid.'\''; - } - if (!($responses = $DB->get_records_select('questionnaire_response', $select, null, 'id', 'id,survey_id,submitted,username'))) { - return; - } - $total = count($responses); - if ($total == 1) { - return; - } - $rids = array(); - $ridssub = array(); - $ridsusername = array(); - $i = 0; - $curr_pos = -1; - foreach ($responses as $response) { - array_push($rids, $response->id); - array_push($ridssub, $response->submitted); - array_push($ridsusername, $response->username); - if ($response->id == $curr_rid) { - $curr_pos = $i; - } - $i++; + 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. // TODO + return true; +} - $prev_rid = ($curr_pos > 0) ? $rids[$curr_pos - 1] : null; - $next_rid = ($curr_pos < $total - 1) ? $rids[$curr_pos + 1] : null; - $rows_per_page = 1; - $pages = ceil($total / $rows_per_page); - - $url = $CFG->wwwroot.'/mod/questionnaire/report.php?action=vresp&sid='.$this->survey->id; +function questionnaire_record_submission(&$questionnaire, $userid, $rid=0) { + global $DB; - $mlink = create_function('$i,$r', 'return "$i";'); + $attempt['qid'] = $questionnaire->id; + $attempt['userid'] = $userid; + $attempt['rid'] = $rid; + $attempt['timemodified'] = time(); + return $DB->insert_record("questionnaire_attempts", (object)$attempt, false); +} - $linkarr = array(); +function questionnaire_delete_survey($sid, $qid) { + global $DB; +/// Until backup is implemented, just mark the survey as archived. - $display_pos = 1; - if ($prev_rid != null) { - array_push($linkarr, "".get_string('previous').''); - } - $ruser = ''; - for ($i = 0; $i < $curr_pos; $i++) { - if ($this->respondenttype != 'anonymous') { - if ($user = $DB->get_record('user', array('id' => $ridsusername[$i]))) { - $ruser = fullname($user); - } - } else { - $ruser = $stranonymous; - } - $title = userdate($ridssub[$i]).' | ' .$ruser; - array_push($linkarr, ''.$display_pos.''); - $display_pos++; - } - array_push($linkarr, ''.$display_pos.''); - for (++$i; $i < $total; $i++) { - if ($this->respondenttype != 'anonymous') { - if ($user = $DB->get_record('user', array('id' => $ridsusername[$i]))) { - $ruser = fullname($user); - } - } else { - $ruser = $stranonymous; - } - $title = userdate($ridssub[$i]).' | ' .$ruser; - $display_pos++; - array_push($linkarr, ''.$display_pos.''); + $status = true; + /// Delete all responses for the survey: + if ($responses = $DB->get_records('questionnaire_response', array('survey_id' => $sid), 'id')) { + foreach ($responses as $response) { + $status = $status && questionnaire_delete_response($response->id); } - if ($next_rid != null) { - array_push($linkarr, "".get_string('next').''); - } - echo implode(' | ', $linkarr); } - function survey_results_navbar_student($curr_rid, $userid, $instance, $resps, $reporttype='myreport', $sid='') { - global $DB; - - $stranonymous = get_string('anonymous', 'questionnaire'); - - $total = count($resps); - $rids = array(); - $ridssub = array(); - $ridsusers = array(); - $i = 0; - $curr_pos = -1; - $title = ''; - foreach ($resps as $response) { - array_push($rids, $response->id); - array_push($ridssub, $response->submitted); - $ruser = ''; - if ($reporttype == 'report') { - if ($this->respondenttype != 'anonymous') { - if ($user = $DB->get_record('user', array('id' => $response->username))) { - $ruser = ' | ' .fullname($user); - } - } else { - $ruser = ' | ' . $stranonymous; - } - } - array_push($ridsusers, $ruser); - if ($response->id == $curr_rid) { - $curr_pos = $i; - } - $i++; - } - $prev_rid = ($curr_pos > 0) ? $rids[$curr_pos - 1] : null; - $next_rid = ($curr_pos < $total - 1) ? $rids[$curr_pos + 1] : null; - $rows_per_page = 1; - $pages = ceil($total / $rows_per_page); + /// There really shouldn't be any more, but just to make sure... + $DB->delete_records('questionnaire_response', array('survey_id' => $sid)); + $DB->delete_records('questionnaire_attempts', array('qid' => $qid)); - if ($reporttype == 'myreport') { - $url = 'myreport.php?instance='.$instance.'&user='.$userid.'&action=vresp'; - } else { - $url = 'report.php?instance='.$instance.'&user='.$userid.'&action=vresp&byresponse=1&sid='.$sid; - } - $linkarr = array(); - $display_pos = 1; - if ($prev_rid != null) { - $title = userdate($ridssub[$curr_pos - 1].$ridsusers[$curr_pos - 1]); - array_push($linkarr, ''.get_string('previous').''); - } - for ($i = 0; $i < $curr_pos; $i++) { - $title = userdate($ridssub[$i]).$ridsusers[$i]; - array_push($linkarr, ''.$display_pos.''); - $display_pos++; - } - array_push($linkarr, ''.$display_pos.''); - for (++$i; $i < $total; $i++) { - $display_pos++; - $title = userdate($ridssub[$i]).$ridsusers[$i]; - array_push($linkarr, ''.$display_pos.''); - } - if ($next_rid != null) { -// $title = userdate($ridssub[$curr_pos]); - $title = userdate($ridssub[$curr_pos + 1]).$ridsusers[$curr_pos + 1]; - array_push($linkarr, ''.get_string('next').''); + /// Delete all question data for the survey: + if ($questions = $DB->get_records('questionnaire_question', array('survey_id' => $sid), 'id')) { + foreach ($questions as $question) { + $DB->delete_records('questionnaire_quest_choice', array('question_id' => $question->id)); } - echo implode(' | ', $linkarr); + $status = $status && $DB->delete_records('questionnaire_question', array('survey_id' => $sid)); } - /* {{{ proto string survey_results(int survey_id, int precision, bool show_totals, int question_id, array choice_ids, int response_id) - Builds HTML for the results for the survey. If a - question id and choice id(s) are given, then the results - are only calculated for respodants who chose from the - choice ids for the given question id. - Returns empty string on sucess, else returns an error - string. */ - function survey_results($precision = 1, $showTotals = 1, $qid = '', $cids = '', $rid = '', $guicross='', $uid=false, $groupid='', $sort='') { - global $SESSION, $DB; - - $SESSION->questionnaire->noresponses = false; - if(empty($precision)) { - $precision = 1; - } - if($showTotals === '') { - $showTotals = 1; - } + $status = $status && $DB->delete_records('questionnaire_survey', array('id' => $sid)); - if(is_int($cids)) { - $cids = array($cids); - } - if(is_string($cids)) { - $cids = preg_split("/ /",$cids); // turn space seperated list into array - } + return $status; +} - // set up things differently for cross analysis - $cross = !empty($qid); - if($cross) { - if(is_array($cids) && count($cids)>0) { - $cidstr = $this->array_to_insql($cids); - } else { - $cidstr = ''; - } - } +function questionnaire_delete_response($rid) { + global $DB; - // build associative array holding whether each question - // type has answer choices or not and the table the answers are in - /// TO DO - FIX BELOW TO USE STANDARD FUNCTIONS - $has_choices = array(); - $response_table = array(); - if (!($types = $DB->get_records('questionnaire_question_type', array(), 'typeid', 'typeid,has_choices,response_table'))) { - $errmsg = sprintf('%s [ %s: question_type ]', - get_string('errortable', 'questionnaire'), 'Table'); - return($errmsg); - } - foreach ($types as $type) { - $has_choices[$type->typeid]=$type->has_choices; - $response_table[$type->typeid]=$type->response_table; - } + $status = true; - // load survey title (and other globals) - if (empty($this->survey)) { - $errmsg = get_string('erroropening', 'questionnaire') ." [ ID:${sid} R:"; - return($errmsg); - } + /// Delete all of the survey response data: + $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)); - if (empty($this->questions)) { - $errmsg = get_string('erroropening', 'questionnaire') .' '. 'No questions found.' ." [ ID:${sid} ]"; - return($errmsg); - } + $status = $status && $DB->delete_records('questionnaire_response', array('id' => $rid)); + $status = $status && $DB->delete_records('questionnaire_attempts', array('rid' => $rid)); - // find out more about the question we are cross analyzing on (if any) - if($cross) { - $crossTable = $response_table[$DB->get_field('questionnaire_question', 'type_id', array('id' => $qid))]; - if(!in_array($crossTable, array('resp_single','response_bool','resp_multiple'))) { - $errmsg = get_string('errorcross', 'questionnaire') .' [ '. 'Table' .": ${crossTable} ]"; - return($errmsg); - } - } + return $status; +} - // find total number of survey responses - // and relevant response ID's - if (!empty($rid)) { - $rids = $rid; - if (is_array($rids)) { - $navbar = false; - } else { - $navbar = true; - } - $total = 1; +/// Functions to call directly into phpESP. +/// Make sure a "require_once('phpESP/admin/phpESP.ini.php')" line is included. +/// Don't need to include this for all library functions, so don't. +function questionnaire_get_survey_list($courseid=0, $type='') { + global $DB; + + if ($courseid == 0) { + if (isadmin()) { + $sql = "SELECT id,name,owner,realm,status " . + "{questionnaire_survey} " . + "ORDER BY realm,name "; + $params = null; } else { - $navbar = false; - $sql = ""; - $castsql = $DB->sql_cast_char2int('R.username'); - if($cross) { - if(!empty($cidstr)) - $sql = "SELECT A.response_id, R.id - FROM {questionnaire_".$crossTable."} A, - {questionnaire_response} R - WHERE A.response_id=R.id AND - R.complete='y' AND - A.question_id='${qid}' AND - A.choice_id ${cidstr} - ORDER BY A.response_id"; - else - $sql = "SELECT A.response_id, R.id - FROM {questionnaire_".$crossTable."} A, - {questionnaire_response} R - WHERE A.response_id=R.id AND - R.complete='y' AND - A.question_id='${qid}' AND - A.choice_id = 0 - ORDER BY A.response_id"; - } else if ($uid !== false) { // one participant only - $sql = "SELECT r.id, r.survey_id - FROM {questionnaire_response} r - WHERE r.survey_id='{$this->survey->id}' AND - r.username = $uid AND - r.complete='y' - ORDER BY r.id"; - } else if ($groupid == -1) { // all participants - $sql = "SELECT R.id, R.survey_id - FROM {questionnaire_response} R - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' - ORDER BY R.id"; - } else if ($groupid == -2) { // all members of any group - $sql = "SELECT R.id, R.survey_id - FROM {questionnaire_response} R, - {groups_members} GM - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' AND - GM.groupid>0 AND - ".$castsql."=GM.userid - ORDER BY R.id"; - } else if ($groupid == -3) { // not members of any group - $sql = "SELECT R.id, R.survey_id, U.id AS userid - FROM {questionnaire_response} R, - {user} U - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' AND - ".$castsql."=U.id - ORDER BY userid"; - } else { // members of a specific group - $sql = "SELECT R.id, R.survey_id - FROM {questionnaire_response} R, - {groups_members} GM - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' AND - GM.groupid=".$groupid." AND - ".$castsql."=GM.userid - ORDER BY R.id"; - } - if (!($rows = $DB->get_records_sql($sql))) { - echo (get_string('noresponses','questionnaire')); - $SESSION->questionnaire->noresponses = true; - return; - } - if ($groupid == -3) { // members of no group - foreach ($rows as $row=>$key) { - if (groups_has_membership($this->cm, $key->userid)) { - unset($rows[$row]); - } - } - } - $total = count($rows); - echo (' '.get_string('responses','questionnaire').": $total"); - if(empty($rows)) { - $errmsg = get_string('erroropening', 'questionnaire') .' '. get_string('noresponsedata', 'questionnaire'); - return($errmsg); - } - - $rids = array(); - foreach ($rows as $row) { - array_push($rids, $row->id); - } - } - - if ($navbar) { - // show response navigation bar - $this->survey_results_navbar($rid); - } - - ?> -

survey->title); ?>

- survey->subtitle) { - echo('

'.$this->survey->subtitle.'

'); - } - ?> - survey->info) { - $infotext = file_rewrite_pluginfile_urls($this->survey->info, 'pluginfile.php', - $this->context->id, 'mod_questionnaire', 'info', $this->survey->id); - echo '
'.format_text($infotext, FORMAT_HTML).'
'; - } - ?> - " ._('Cross analysis on QID:') ." ${qid}\n"); - } - ?> - - questions as $question) { - // process each question - - if ($question->type_id == 99) { - continue; - } - if ($question->type_id == 100) { - echo ("\n"); - continue; - } - echo ("\n"); - echo (" - - -
". format_text(file_rewrite_pluginfile_urls($question->content, 'pluginfile.php', $question->context->id, 'mod_questionnaire', - 'question', $question->id), FORMAT_HTML)."
"); - if ($question->type_id < 50) { - if (!empty($guicross)){ - echo ('
'); - echo (''); - echo (''); - echo ('
'); - echo ("\n\n"); - echo ("\n"); - echo (" \n"); - echo (" \n"); - echo (""); - echo ("\n"); - echo ("\n"); - echo ("\n"); - echo ("\n"); - echo ("\n"); - echo ("
\n"); - if ($question->type_id ==1 || $question->type_id ==4 || $question->type_id ==5 || $question->type_id ==6){ - echo ("
\n"); - echo (" id."\" />\n"); - echo ("
\n"); - } - echo ("
\n"); - } //end if empty($guicross) - echo ++$i; - echo (""); - echo ("
"); - - if (!empty($guicross)){ - echo ("
\n"); - if ($question->type_id ==1 || $question->type_id ==4 || $question->type_id ==5 || $question->type_id ==6){ - echo ("
\n"); - echo ("id."\" />\n"); - echo ("
\n"); - } - echo ("
\n"); - if ($question->type_id ==1 || $question->type_id ==4 || $question->type_id ==5 || $question->type_id ==6){ - echo ("
\n"); - echo ("id."\" />\n"); - echo ("
\n"); - } - echo ("
\n"); - } //end if empty($guicross) - } //end if ($question->type_id < 50) - - $counts = array(); - - // --------------------------------------------------------------------------- - echo format_text(file_rewrite_pluginfile_urls($question->content, 'pluginfile.php', $question->context->id, 'mod_questionnaire', - 'question', $question->id), FORMAT_HTML).''; // moved from $question->display_results - $question->display_results($rids, $guicross, $sort); - - ?> -
- questionnaire->currentgroupid)) { - $groupid = $SESSION->questionnaire->currentgroupid; - } else{ - $groupid = -1; - } - $output = array(); - $nbinfocols = 9; // change this if you want more info columns - $stringother = get_string('other', 'questionnaire'); - $columns = array( - get_string('response','questionnaire'), - get_string('submitted','questionnaire'), - get_string('institution'), - get_string('department'), - get_string('course'), - get_string('group'), - get_string('id','questionnaire'), - get_string('fullname'), - get_string('username') - ); - - $types = array( - 0, - 0, - 1, - 1, - 1, - 1, - 0, - 1, - 1, - ); - - $arr = array(); - // 0 = number; 1 = text - $id_to_csv_map = array( - '0', // 0: unused - '0', // 1: bool -> boolean - '1', // 2: text -> string - '1', // 3: essay -> string - '0', // 4: radio -> string - '0', // 5: check -> string - '0', // 6: dropdn -> string - '0', // 7: rating -> number - '0', // 8: rate -> number - '1', // 9: date -> string - '0' // 10: numeric -> number - ); - - if (!$survey = $DB->get_record('questionnaire_survey', array('id' => $this->survey->id))) { - print_error ('surveynotexists', 'questionnaire'); - } - - $select = 'survey_id = '.$this->survey->id.' AND deleted = \'n\' AND type_id < 50'; - $fields = 'id,name,type_id,position'; - if (!($records = $DB->get_records_select('questionnaire_question', $select, null, 'position', $fields))) { - $records = array(); - } - - $num = 1; - foreach ($records as $record) { - // establish the table's field names - $qid = $record->id; - $qpos = $record->position; - if ($record->name == '') { - } - $col = $record->name; - $type = $record->type_id; - if ($type == 4 || $type == 5 || $type == 8) { - /* single or multiple or rate */ - $sql = "SELECT c.id as cid, q.id as qid, q.precise AS precise, q.name, c.content - FROM {questionnaire_question} q ". - "LEFT JOIN {questionnaire_quest_choice} c ON question_id = q.id ". - 'WHERE q.id = '.$qid.' ORDER BY cid ASC'; - if (!($records2 = $DB->get_records_sql($sql))) { - $records2 = array(); - } - $subqnum = 0; - switch ($type) { - - case 4: // single - $columns[][$qpos] = $col; - array_push($types, $id_to_csv_map[$type]); - $thisnum = 1; - foreach ($records2 as $record2) { - $content = $record2->content; - if (preg_match('/^!other/', $content)) { - $col = $record2->name.'_'.$stringother; - $columns[][$qpos] = $col; - array_push($types, '0'); - } - } - break; - - case 5: // multiple - $thisnum = 1; - foreach ($records2 as $record2) { - $content = $record2->content; - $modality = ''; - if (preg_match('/^!other/', $content)) { - $content = $stringother; - $col = $record2->name.'->['.$content.']'; - $columns[][$qpos] = $col; - array_push($types, '0'); - } - $contents = questionnaire_choice_values($content); - if ($contents->modname) { - $modality = $contents->modname; - } elseif ($contents->title) { - $modality = $contents->title; - } else { - $modality = strip_tags($contents->text); - } - $col = $record2->name.'->'.$modality; - $columns[][$qpos] = $col; - array_push($types, '0'); - } - break; - - case 8: // rate - foreach ($records2 as $record2) { - $nameddegrees = 0; - $modality = ''; - $content = $record2->content; - $osgood = false; - if ($record2->precise == 3) { - $osgood = true; - } - if (preg_match("/^[0-9]{1,3}=/", $content,$ndd)) { - $nameddegrees++; - } else { - if ($osgood) { - list($contentleft, $contentright) = preg_split('/[|]/', $content); - $contents = questionnaire_choice_values($contentleft); - if ($contents->title) { - $contentleft = $contents->title; - } - $contents = questionnaire_choice_values($contentright); - if ($contents->title) { - $contentright = $contents->title; - } - $modality = strip_tags($contentleft.'|'.$contentright); - $modality = preg_replace("/[\r\n\t]/", ' ', $modality); - } else { - $contents = questionnaire_choice_values($content); - if ($contents->modname) { - $modality = $contents->modname; - } elseif ($contents->title) { - $modality = $contents->title; - } else { - $modality = strip_tags($contents->text); - $modality = preg_replace("/[\r\n\t]/", ' ', $modality); - } - } - $col = $record2->name.'->'.$modality; - $columns[][$qpos] = $col; - array_push($types, $id_to_csv_map[$type]); - } - } - break; - } - } else { - $columns[][$qpos] = $col; - array_push($types, $id_to_csv_map[$type]); - } - $num++; - } - array_push($output, $columns); - $numcols = count($output[0]); - - if ($rid) { // send e-mail for a unique response ($rid) - $select = 'survey_id = '.$this->survey->id.' AND complete=\'y\' AND id = '.$rid; - $fields = 'id,submitted,username'; - if (!($records = $DB->get_records_select('questionnaire_response', $select, null, 'submitted', $fields))) { - $records = array(); - } - } else if ($userid) { // download CSV for one user's own responses' - $sql = "SELECT R.id, R.survey_id, R.submitted, R.username - FROM {questionnaire_response} R - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' AND - R.username='$userid' - ORDER BY R.id"; - if (!($records = $DB->get_records_sql($sql))) { - $records = array(); - } - - } else { // download CSV for all participants (or groups if enabled) - $castsql = $DB->sql_cast_char2int('R.username'); - if ($groupid == -1) { // all participants - $sql = "SELECT R.id, R.survey_id, R.submitted, R.username - FROM {questionnaire_response} R - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' - ORDER BY R.id"; - } else if ($groupid == -2) { // all members of any group - $sql = "SELECT R.id, R.survey_id, R.submitted, R.username - FROM {questionnaire_response} R, - {groups_members} GM - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' AND - GM.groupid>0 AND - ".$castsql."=GM.userid - ORDER BY R.id"; - } else if ($groupid == -3) { // not members of any group - $sql = "SELECT R.id, R.survey_id, R.submitted, U.id AS username - FROM {questionnaire_response} R, - {user} U - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' AND - ".$castsql."=U.id - ORDER BY username"; - } else { // members of a specific group - $sql = "SELECT R.id, R.survey_id, R.submitted, R.username - FROM {questionnaire_response} R, - {groups_members} GM - WHERE R.survey_id='{$this->survey->id}' AND - R.complete='y' AND - GM.groupid=".$groupid." AND - ".$castsql."=GM.userid - ORDER BY R.id"; - } - if (!($records = $DB->get_records_sql($sql))) { - $records = array(); - } - if ($groupid == -3) { // members of no group - foreach ($records as $row=>$key) { - $userid = $key->username; - if (groups_has_membership($this->cm, $userid)) { - unset($records[$row]); - } - } - } + return false; } - $isanonymous = $this->respondenttype == 'anonymous'; - $format_options = new Object(); - $format_options->filter = false; // To prevent any filtering in CSV output... - foreach ($records as $record) { - // get the response - $response = $this->response_select_name($record->id, $choicecodes, $choicetext); - $qid = $record->id; - //JR for better compabitility & readability with Excel - $submitted = date(get_string('strfdateformatcsv', 'questionnaire'), $record->submitted); - $institution = ''; - $department = ''; - $username = $record->username; - if ($user = $DB->get_record('user', array('id' => $username))) { - $institution = $user->institution; - $department = $user->department; - } + } else if (!empty($type)) { + if ($type == 'public') { + $sql = "SELECT s.id,s.name,s.owner,s.realm,s.status,s.title,q.id as qid " . + "FROM {questionnaire} q " . + "INNER JOIN {questionnaire_survey} s ON s.id = q.sid " . + "WHERE status != ? AND realm = ? " . + "ORDER BY realm,name "; + $params = array(QUESTIONNAIRE_ARCHIVED, $type); + /// Any survey owned by the user or typed as 'template' can be copied. + } else if ($type == 'template') { + $sql = "SELECT s.id,s.name,s.owner,s.realm,s.status,s.title,q.id as qid " . + "FROM {questionnaire} q " . + "INNER JOIN {questionnaire_survey} s ON s.id = q.sid " . + "WHERE status != ? AND (realm = ? OR owner = ?) " . + "ORDER BY realm,name "; + $params = array(QUESTIONNAIRE_ARCHIVED, $type, $courseid); + } + } else { + $sql = "SELECT s.id,s.name,s.owner,s.realm,s.status,q.id as qid " . + "FROM {questionnaire} q " . + "INNER JOIN {questionnaire_survey} s ON s.id = q.sid " . + "WHERE status != ? AND owner = ? " . + "ORDER BY realm,name "; + $params = array(QUESTIONNAIRE_ARCHIVED, $courseid); + } + return $DB->get_records_sql($sql, $params); +} - /// Moodle: - // Get the course name that this questionnaire belongs to. - if ($survey->realm != 'public') { - $courseid = $this->course->id; - $coursename = $this->course->fullname; +function questionnaire_get_survey_select($instance, $courseid=0, $sid=0, $type='') { + global $OUTPUT; + + $surveylist = array(); + if ($surveys = questionnaire_get_survey_list($courseid, $type)) { + + $strpreview = get_string('preview'); + $strunknown = get_string('unknown', 'questionnaire'); + $strpublic = get_string('public', 'questionnaire'); + $strprivate = get_string('private', 'questionnaire'); + $strtemplate = get_string('template', 'questionnaire'); + $strviewresp = get_string('viewresponses', 'questionnaire'); + + foreach ($surveys as $survey) { + if (empty($survey->realm)) { + $stat = $strunknown; + } else if ($survey->realm == 'public') { + $stat = $strpublic; + } else if ($survey->realm == 'private') { + $stat = $strprivate; + } else if ($survey->realm == 'template') { + $stat = $strtemplate; } else { - /// For a public questionnaire, look for the course that used it. - $sql = 'SELECT q.id, q.course, c.fullname '. - 'FROM {questionnaire} q, {questionnaire_attempts} qa, {course} c '. - 'WHERE qa.rid = ? AND q.id = qa.qid AND c.id = q.course'; - if ($record = $DB->get_record_sql($sql, array($qid))) { - $courseid = $record->course; - $coursename = $record->fullname; - } else { - $courseid = $this->course->id; - $coursename = $this->course->fullname; - } - } - /// Moodle: - // If the username is numeric, try it as a Moodle user id. - if (is_numeric($username)) { - if ($user = $DB->get_record('user', array('id' => $username))) { - $uid = $username; - $fullname = fullname($user); - $username = $user->username; - } - } - - /// Moodle: - // Determine if the user is a member of a group in this course or not. - $groupname = ''; - if ($this->cm->groupmode > 0) { - if ($groupid > 0) { - $groupname = groups_get_group_name($groupid); - } else { - if ($uid) { - if ($groups = groups_get_all_groups($courseid, $uid)) { - foreach ($groups as $group) { - $groupname.= $group->name.', '; - } - $groupname = substr($groupname, 0, strlen($groupname) -2); - } else { - $groupname = ' ('.get_string('groupnonmembers').')'; - } - } - } - } - if ($isanonymous) { - $fullname = get_string('anonymous', 'questionnaire'); - $username = ''; - $uid = ''; + $stat = $strunknown; } - $arr = array(); - array_push($arr, $qid); - array_push($arr, $submitted); - array_push($arr, $institution); - array_push($arr, $department); - array_push($arr, $coursename); - array_push($arr, $groupname); - array_push($arr, $uid); - array_push($arr, $fullname); - array_push($arr, $username); - - // merge it - for($i = $nbinfocols; $i < $numcols; $i++) { - /*if (isset($response[$columns[$i]]) && is_array($response[$columns[$i]])) { - $response[$columns[$i]] = join(',', $response[$columns[$i]]); - }*/ - - $qpos = key($columns[$i]); - $qname = current($columns[$i]); - if (isset($response[$qpos][$qname]) && $response[$qpos][$qname] != '') { - $thisresponse = $response[$qpos][$qname]; + // prevent creation of a new questionnaire using a public questionnaire IN THE SAME COURSE! + if ($type == 'public' && $survey->owner == $courseid) { + continue; } else { - $thisresponse = ''; - } - - switch ($types[$i]) { - case 1: //string - // Excel seems to allow "\n" inside a quoted string, but - // "\r\n" is used as a record separator and so "\r" may - // not occur within a cell. So if one would like to preserve - // new-lines in a response, remove the "\n" from the - // regex below. - - // email format text is plain text for being displayed in Excel, etc. added by JR - // but it must be stripped of carriage returns - if ($thisresponse) { - $thisresponse = format_text($thisresponse, FORMAT_HTML, $format_options); - $thisresponse = preg_replace("/[\r\n\t]/", ' ', $thisresponse); - $thisresponse = preg_replace('/"/', '""', $thisresponse); - } - // fall through - case 0: //number - //array_push($arr,$thisresponse); - break; + $args = "sid={$survey->id}&popup=1"; + if (!empty($survey->qid)) { + $args .= "&qid={$survey->qid}"; } - array_push($arr,$thisresponse); - } - array_push($output, $arr); - } - - // change table headers to incorporate actual question numbers - $numcol = 0; - $numquestion = 0; - $out = ''; - $nbrespcols = count($output[0]); - $oldkey = 0; - - for ($i = $nbinfocols;$i < $nbrespcols; $i++) { - $sep = ''; - $thisoutput = current($output[0][$i]); - $thiskey = key($output[0][$i]); - // case of unnamed rate single possible answer (full stop char is used for support) - if (strstr($thisoutput,'->.')) { - $thisoutput = str_replace('->.','',$thisoutput); - } - - // if variable is not named no separator needed between Question number and potential sub-variables - if ($thisoutput == '' || strstr($thisoutput,'->.') || substr($thisoutput,0,2) == '->' || substr($thisoutput,0,1) == '_') { - $sep = ''; - } else { - $sep = '_'; - } - if ($thiskey > $oldkey) { - $oldkey = $thiskey; - $numquestion++; + $link = new moodle_url("/mod/questionnaire/preview.php?{$args}"); + $action = new popup_action('click', $link); + $label = $OUTPUT->action_link($link, $survey->title, $action, array('title'=>$survey->title)); + $surveylist[$type.'-'.$survey->id] = $label; } - // abbreviated modality name in multiple or rate questions (COLORS->blue=the color of the sky...) - $pos = strpos($thisoutput, '='); - if($pos) { - $thisoutput = substr($thisoutput, 0, $pos); - } - $other = $sep.$stringother; - $out = 'Q'.sprintf("%02d", $numquestion).$sep.$thisoutput; - $output[0][$i] = $out; } - return $output; } + return $surveylist; +} - /* {{{ proto bool survey_export_csv(int survey_id, string filename) - Exports the results of a survey to a CSV file. - Returns true on success. - */ - function export_csv($filename) { - $umask = umask(0077); - $fh = fopen($filename, 'w'); - umask($umask); - if(!$fh) - return 0; - - $data = survey_generate_csv($rid='', $userid='', $groupid=''); - - foreach ($data as $row) { - fputs($fh, join(',', $row) . "\n"); - } - - fflush($fh); - fclose($fh); - - return 1; +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 100: + return get_string('sectiontext', 'questionnaire'); + case 99: + return get_string('sectionbreak', 'questionnaire'); + default: + return $id; } +} - /** - * Function to move a question to a new position. - * - * @param int $moveqid The id of the question to be moved. - * @param int $movetopos The position to move before, or zero if the end. - * - */ - function move_question($moveqid, $movetopos) { - global $DB; - - /// If its moving to the last position (moveto = 0), or its moving to a higher position - /// No point in moving it to where it already is... - if (($movetopos == 0) || (($movetopos-1) > $this->questions[$moveqid]->position)) { - $found = false; - foreach ($this->questions as $qid => $question) { - if ($moveqid == $qid) { - $found = true; - continue; - } - if ($found) { - $DB->set_field('questionnaire_question', 'position', $question->position-1, array('id' => $qid)); - } - if ($question->position == ($movetopos-1)) { - break; - } - } - if ($movetopos == 0) { - $movetopos = count($this->questions); - } else { - $movetopos--; - } - $DB->set_field('questionnaire_question', 'position', $movetopos, array('id' => $moveqid)); - - } else if ($movetopos < $this->questions[$moveqid]->position) { - $found = false; - foreach ($this->questions as $qid => $question) { - if ($movetopos == $question->position) { - $found = true; - } - if (!$found) { - continue; - } else { - $DB->set_field('questionnaire_question', 'position', $question->position+1, array('id' => $qid)); - } - if ($question->position == ($this->questions[$moveqid]->position-1)) { - break; - } - } - $DB->set_field('questionnaire_question', 'position', $movetopos, array('id' => $moveqid)); +/** + * This creates new events given as opendate and closedate by $questionnaire. + * @param object $questionnaire + * @return void + */ + /* added by JR 16 march 2009 based on lesson_process_post_save script */ + +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) { + delete_event($event->id); + } + } + + // 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->timestart = $questionnaire->opendate; + $event->visible = instance_is_visible('questionnaire', $questionnaire); + $event->timeduration = ($questionnaire->closedate - $questionnaire->opendate); + + if ($questionnaire->closedate and $questionnaire->opendate and $event->timeduration <= QUESTIONNAIRE_MAX_EVENT_LENGTH) { + // Single event for the whole questionnaire. + $event->name = $questionnaire->name; + add_event($event); + } else { + // Separate start and end events. + $event->timeduration = 0; + if ($questionnaire->opendate) { + $event->name = $questionnaire->name.' ('.get_string('questionnaireopens', 'questionnaire').')'; + add_event($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->eventtype = 'close'; + add_event($event); } } } - /* {{{ proto void mkcrossformat (array weights, integer qid) - Builds HTML to allow for cross tabulation/analysis reporting. - */ + +// DEPRECATED FROM HERE ON + +/** {{{ proto void mkcrossformat (array weights, integer qid) + * Builds HTML to allow for cross tabulation/analysis reporting. + * @deprecated since Moodle 2.5 + */ function questionnaire_response_key_cmp($l, $r) { - $lx = explode('_', $l); + debugging('questionnaire_response_key_cmp() has been deprecated.'); + $lx = explode('_', $l); $rx = explode('_', $r); $lc = intval($lx[0]); $rc = intval($rx[0]); @@ -2891,196 +582,83 @@ function questionnaire_response_key_cmp($l, $r) { return ($lc > $rc) ? 1 : -1; } - function questionnaire_check_date ($thisdate, $insert=false) { - $dateformat = get_string('strfdate', 'questionnaire'); - if (preg_match('/(%[mdyY])(.+)(%[mdyY])(.+)(%[mdyY])/', $dateformat, $matches)) { - $date_pieces = explode($matches[2], $thisdate); - foreach ($date_pieces as $datepiece) { - if (!is_numeric($datepiece)) { - return 'wrongdateformat'; - } - } - $pattern = "/[^dmy]/i"; - $dateorder = strtolower(preg_replace($pattern, '', $dateformat)); - $countpieces = count($date_pieces); - if ($countpieces == 1) { // assume only year entered - switch ($dateorder) { - case 'dmy': // most countries - case 'mdy': // USA - $date_pieces[2] = $date_pieces[0]; // year - $date_pieces[0] = '1'; // assumed 1st month of year - $date_pieces[1] = '1'; // assumed 1st day of month - break; - case 'ymd': // ISO 8601 standard - $date_pieces[1] = '1'; // assumed 1st month of year - $date_pieces[2] = '1'; // assumed 1st day of month - break; - } - } - if ($countpieces == 2) { // assume only month and year entered - switch ($dateorder) { - case 'dmy': // most countries - $date_pieces[2] = $date_pieces[1]; //year - $date_pieces[1] = $date_pieces[0]; // month - $date_pieces[0] = '1'; // assumed 1st day of month - break; - case 'mdy': // USA - $date_pieces[2] = $date_pieces[1]; //year - $date_pieces[0] = $date_pieces[0]; // month - $date_pieces[1] = '1'; // assumed 1st day of month - break; - case 'ymd': // ISO 8601 standard - $date_pieces[2] = '1'; // assumed 1st day of month - break; - } - } - if (count($date_pieces) > 1) { - if ($matches[1] == '%m') $month = $date_pieces[0]; - if ($matches[1] == '%d') $day = $date_pieces[0]; - if ($matches[1] == '%y') $year = strftime('%C').$date_pieces[0]; - if ($matches[1] == '%Y') $year = $date_pieces[0]; - - if ($matches[3] == '%m') $month = $date_pieces[1]; - if ($matches[3] == '%d') $day = $date_pieces[1]; - if ($matches[3] == '%y') $year = strftime('%C').$date_pieces[1]; - if ($matches[3] == '%Y') $year = $date_pieces[1]; - - if ($matches[5] == '%m') $month = $date_pieces[2]; - if ($matches[5] == '%d') $day = $date_pieces[2]; - if ($matches[5] == '%y') $year = strftime('%C').$date_pieces[2]; - if ($matches[5] == '%Y') $year = $date_pieces[2]; - - $month = min(12,$month); - $month = max(1,$month); - if ($month == 2) { - $day = min(29, $day); - } else if ($month == 4 || $month == 6 || $month == 9 || $month == 11) { - $day = min(30, $day); - } else { - $day = min(31, $day); - } - $day = max(1, $day); - if (!$thisdate = gmmktime(0, 0, 0, $month, $day, $year)) { - return 'wrongdaterange'; - } else { - if ($insert) { - $thisdate = trim(userdate ($thisdate, '%Y-%m-%d', '1', false)); - } else { - $thisdate = trim(userdate ($thisdate, $dateformat, '1', false)); - } - } - return $thisdate; - } - } else return ('wrongdateformat'); - } - // .mform span.required .mform div.error - // a variant of Moodle's notify function, with a different formatting - function questionnaire_notify($message) { - $message = clean_text($message); - $errorstart = '
'; - $errorend = '
'; - $output = $errorstart.$message.$errorend; - echo $output; - } - - /// deprecated - function questionnaire_preview ($questionnaire) { - global $DB; - /// Print the page header - /// Templates may not have questionnaires yet... - $tempsid = $questionnaire->survey->id; // this is needed for Preview cases later on - - if (!isset($questionnaire->name)) { - $name = $DB->get_field('questionnaire_survey', 'name', array('id' => $tempsid)); - $questionnaire->sid = $tempsid; - $questionnaire->add_questions($tempsid); - } else { - $name = $questionnaire->name; - } - $qp = get_string('preview_questionnaire', 'questionnaire'); - $pq = get_string('previewing', 'questionnaire'); - $course = $questionnaire->course; - print_header($course->shortname.$qp, - $course->fullname.$pq.$name, '', '', '', false); +/** + * @deprecated since Moodle 2.5 + */ +function questionnaire_preview ($questionnaire) { + debugging('questionnaire_preview() has been deprecated.'); + global $DB; + /// Print the page header + /// Templates may not have questionnaires yet... + $tempsid = $questionnaire->survey->id; // this is needed for Preview cases later on + + if (!isset($questionnaire->name)) { + $name = $DB->get_field('questionnaire_survey', 'name', array('id' => $tempsid)); + $questionnaire->sid = $tempsid; + $questionnaire->add_questions($tempsid); + } else { + $name = $questionnaire->name; + } + $qp = get_string('preview_questionnaire', 'questionnaire'); + $pq = get_string('previewing', 'questionnaire'); + $course = $questionnaire->course; + print_header($course->shortname.$qp, + $course->fullname.$pq.$name, '', '', '', false); /// Print the main part of the page - $SESSION->questionnaire_survey_id = $tempsid; - if (isset($formdata->sid) && $formdata->sid != 0) { - $sid = $SESSION->questionnaire_survey_id = $formdata->sid; - } else { - $sid = $SESSION->questionnaire_survey_id; - } - $questionnaire->survey = $DB->get_record('questionnaire_survey', array('id' => $sid)); - $n = $DB->count_records('questionnaire_question', array('survey_id' => $sid, 'type_id' => '99', 'deleted' => 'n')); - for ($i=1; $i<$n+2 ; $i++) { - $questionnaire->survey_render($i, '', $formdata); - } - close_window_button(); - echo ''; - break; - } + $SESSION->questionnaire_survey_id = $tempsid; + if (isset($formdata->sid) && $formdata->sid != 0) { + $sid = $SESSION->questionnaire_survey_id = $formdata->sid; + } else { + $sid = $SESSION->questionnaire_survey_id; + } + $questionnaire->survey = $DB->get_record('questionnaire_survey', array('id' => $sid)); + $n = $DB->count_records('questionnaire_question', array('survey_id' => $sid, 'type_id' => '99', 'deleted' => 'n')); + for ($i=1; $i<$n+2 ; $i++) { + $questionnaire->survey_render($i, '', $formdata); + } + close_window_button(); + echo ''; + break; +} - 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 ($count = preg_match('/(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('/(.*)(text = $content; - if ($pos = strpos($content, '=')) { - // the equal sign used for named modalities must NOT be followed by a double quote - // because an equal sign followed by double quote might introduce e.g. a lang tag - if (substr($content, $pos + 1, 1) != '"') { - $contents->text = substr($content, $pos + 1); - $contents->modname =substr($content, 0, $pos); - } - } - return $contents; - } +/** + * @deprecated since Moodle 2.5 + */ +function questionnaire_get_active_surveys_menu() { + debugging('questionnaire_get_active_surveys_menu() has been deprecated.'); + global $DB; - /** - * Get the information about the standard questionnaire JavaScript module. - * @return array a standard jsmodule structure. - */ - function questionnaire_get_js_module() { - global $PAGE; - 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'), - ), - ); - } + $select = "status in (". QUESTIONNAIRE_ACTIVE1 . "," . QUESTIONNAIRE_ACTIVE2 . ")"; + return $DB->get_records_select_menu('questionnaire_survey', $select); +} + +/** + * @deprecated since Moodle 2.5 + */ +function questionnaire_get_surveys_menu($status=NULL) { + debugging('questionnaire_get_surveys_menu() has been deprecated.'); + global $DB; + + $field = ($status) ? 'status' : $status; + return $DB->get_records_menu('questionnaire_survey', array($field => $status)); +} + +/** + * @deprecated since Moodle 2.5 + */ +function questionnaire_survey_has_questions($sid) { + debugging('questionnaire_survey_has_questions() has been deprecated.'); + global $DB; + + return $DB->record_exists('questionnaire_question', array('survey_id' => $sid, 'deleted' => 'n')); +} + +/** + * @deprecated since Moodle 2.5 + */ +function questionnaire_survey_exists($sid) { + debugging('questionnaire_survey_exists() has been deprecated.'); + global $DB; + + return $DB->record_exists('questionnaire_survey', array('id' => $sid)); +} diff --git a/mod/questionnaire/mod_form.php b/mod/questionnaire/mod_form.php index c412bdc..ccb9e96 100644 --- a/mod/questionnaire/mod_form.php +++ b/mod/questionnaire/mod_form.php @@ -23,7 +23,8 @@ */ require_once ($CFG->dirroot.'/course/moodleform_mod.php'); -require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); +require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php'); +require_once($CFG->dirroot.'/mod/questionnaire/locallib.php'); class mod_questionnaire_mod_form extends moodleform_mod { diff --git a/mod/questionnaire/myreport.php b/mod/questionnaire/myreport.php index 32e369a..26d0238 100644 --- a/mod/questionnaire/myreport.php +++ b/mod/questionnaire/myreport.php @@ -17,7 +17,7 @@ /// This page shows results of a questionnaire to a student. require_once("../../config.php"); - require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); + require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php'); $strsummary = get_string('summary', 'questionnaire'); $strall = get_string('myresponses', 'questionnaire'); diff --git a/mod/questionnaire/preview.php b/mod/questionnaire/preview.php index a939c44..712d33d 100644 --- a/mod/questionnaire/preview.php +++ b/mod/questionnaire/preview.php @@ -17,7 +17,7 @@ /// This page displays a non-completable instance of questionnaire require_once("../../config.php"); - require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); + require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php'); $id = optional_param('id', 0, PARAM_INT); $sid = optional_param('sid', 0, PARAM_INT); diff --git a/mod/questionnaire/print.php b/mod/questionnaire/print.php index e532d1f..fb73811 100644 --- a/mod/questionnaire/print.php +++ b/mod/questionnaire/print.php @@ -15,7 +15,7 @@ // along with Moodle. If not, see . require_once("../../config.php"); - require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); + require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php'); $qid = required_param('qid', PARAM_INT); $rid = required_param('rid', PARAM_INT); @@ -45,6 +45,12 @@ print_error('nopermissions', 'moodle', $CFG->wwwroot.'/mod/questionnaire/view.php?id='.$cm->id); } + $url = new moodle_url($CFG->wwwroot.'/mod/questionnaire/print.php'); + $url->param('qid', $qid); + $url->param('rid', $rid); + $url->param('courseid', $courseid); + $url->param('sec', $sec); + $PAGE->set_url($url); $PAGE->set_title($questionnaire->survey->title); $PAGE->set_pagelayout('popup'); echo $OUTPUT->header(); diff --git a/mod/questionnaire/qsettings.php b/mod/questionnaire/qsettings.php index 553bb20..7f6aee2 100644 --- a/mod/questionnaire/qsettings.php +++ b/mod/questionnaire/qsettings.php @@ -17,9 +17,8 @@ /// This page prints a particular instance of questionnaire require_once("../../config.php"); -// JR moved further down after course_require_login -// require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); require_once($CFG->dirroot.'/mod/questionnaire/settings_form.php'); + require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php'); $id = required_param('id', PARAM_INT); // course module ID @@ -38,7 +37,6 @@ // needed here for forced language courses require_course_login($course, true, $cm); $context = get_context_instance(CONTEXT_MODULE, $cm->id); - require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); $url = new moodle_url($CFG->wwwroot.'/mod/questionnaire/qsettings.php', array('id' => $id)); $PAGE->set_url($url); diff --git a/mod/questionnaire/questionnaire.class.php b/mod/questionnaire/questionnaire.class.php new file mode 100644 index 0000000..4b06a69 --- /dev/null +++ b/mod/questionnaire/questionnaire.class.php @@ -0,0 +1,2853 @@ +. + +require_once('questiontypes/questiontypes.class.php'); + +class questionnaire { + +/// Class Properties + /** + * The survey record. + * @var object $survey + */ + var $survey; + +/// Class Methods + + /** + * The class constructor + * + */ + function __construct($id = 0, $questionnaire = null, &$course, &$cm, $addquestions = true) { + global $DB; + + if ($id) { + $questionnaire = $DB->get_record('questionnaire', array('id' => $id)); + } + + if (is_object($questionnaire)) { + $properties = get_object_vars($questionnaire); + foreach ($properties as $property => $value) { + $this->$property = $value; + } + } + + if (!empty($this->sid)) { + $this->add_survey($this->sid); + } + + $this->course = $course; + $this->cm = $cm; + /// When we are creating a brand new questionnaire, we will not yet have a context. + if (!empty($cm) && !empty($this->id)) { + $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); + } else { + $this->context = null; + } + + if ($addquestions && !empty($this->sid)) { + $this->add_questions($this->sid); + } + + $this->usehtmleditor = can_use_html_editor(); + + /// Load the capabilities for this user and questionnaire, if not creating a new one. + if (!empty($this->cm->id)) { + $this->capabilities = questionnaire_load_capabilities($this->cm->id); + } + } + + /** + * Adding a survey record to the object. + * + */ + function add_survey($sid = 0, $survey = null) { + global $DB; + + if ($sid) { + $this->survey = $DB->get_record('questionnaire_survey', array('id' => $sid)); + } else if (is_object($survey)) { + $this->survey = clone($survey); + } + } + + /** + * Adding questions to the object. + */ + function add_questions($sid = false, $section = false) { + global $DB; + + if ($sid === false) { + $sid = $this->sid; + } + + if (!isset($this->questions)) { + $this->questions = array(); + $this->questionsbysec = array(); + } + + $select = 'survey_id = '.$sid.' AND deleted != \'y\''; + if ($records = $DB->get_records_select('questionnaire_question', $select, null, 'position')) { + $sec = 1; + $isbreak = false; + foreach ($records as $record) { + $this->questions[$record->id] = new questionnaire_question(0, $record, $this->context); + if ($record->type_id != 99) { + $this->questionsbysec[$sec][$record->id] = &$this->questions[$record->id]; + $isbreak = false; + } else { + // sanity check: no section break allowed as first position, no 2 consecutive section breaks + if ($record->position != 1 && $isbreak == false) { + $sec++; + $isbreak = true; + } + } + } + } + } + + function view() { + global $CFG, $USER, $PAGE, $OUTPUT; + + $PAGE->set_title(format_string($this->name)); + $PAGE->set_heading(format_string($this->course->fullname)); + $PAGE->set_button(update_module_button($this->cm->id, $this->course->id, $this->strquestionnaire)); + // Initialise the JavaScript. + $PAGE->requires->js_init_call('M.mod_questionnaire.init_attempt_form', null, false, questionnaire_get_js_module()); + + echo $OUTPUT->header(); + + /// print the tabs + $questionnaire = $this; + include('tabs.php'); + + if (!$this->cm->visible && !$this->capabilities->viewhiddenactivities) { + notice(get_string("activityiscurrentlyhidden")); + } + + if (!$this->capabilities->view) { + echo('
'); + questionnaire_notify(get_string("guestsno", "questionnaire", $this->name)); + echo(''); + exit; + } + + /// Print the main part of the page + + if (!$this->is_active()) { + echo '
' + .get_string('notavail', 'questionnaire') + .'
'; + } + else if (!$this->is_open()) { + echo '
' + .get_string('notopen', 'questionnaire', userdate($this->opendate)) + .'
'; + } + else if ($this->is_closed()) { + echo '
' + .get_string('closed', 'questionnaire', userdate($this->closedate)) + .'
'; + } + else if (!$this->user_is_eligible($USER->id)) { + echo '
' + .get_string('noteligible', 'questionnaire') + .'
'; + } + else if ($this->user_can_take($USER->id)) { + $sid=$this->sid; + $quser = $USER->id; + + if ($this->survey->realm == 'template') { + print_string('templatenotviewable', 'questionnaire'); + echo $OUTPUT->footer($this->course); + exit(); + } + + if ((!empty($this->questions)) && $this->capabilities->printblank) { + // open print friendly as popup window + $image_url = $CFG->wwwroot.'/mod/questionnaire/images/'; + $linkname = 'Printer-friendly version'; + $title = get_string('printblanktooltip','questionnaire'); + $url = '/mod/questionnaire/print.php?qid='.$this->id.'&rid=0&'.'courseid='.$this->course->id.'&sec=1'; + $options = array('menubar' => true, 'location' => false, 'scrollbars' => true, 'resizable' => true, + 'height' => 600, 'width' => 800, 'title'=>$title); + $name = 'popup'; + $link = new moodle_url($url); + $action = new popup_action('click', $link, $name, $options); + $class = "floatprinticon"; + echo $OUTPUT->action_link($link, $linkname, $action, array('class'=>$class, 'title'=>$title)); + } + $msg = $this->print_survey($USER->id, $quser); + /// If Survey was submitted with all required fields completed ($msg is empty), + /// then record the submittal. + $viewform = data_submitted($CFG->wwwroot."/mod/questionnaire/view.php"); + if (!empty($viewform->rid)) { + $viewform->rid = (int)$viewform->rid; + } + if (!empty($viewform->sec)) { + $viewform->sec = (int)$viewform->sec; + } + if (data_submitted() && confirm_sesskey() && isset($viewform->submit) && isset($viewform->submittype) && + ($viewform->submittype == "Submit Survey") && empty($msg)) { + + $this->response_delete($viewform->rid, $viewform->sec); + $this->rid = $this->response_insert($this->survey->id, $viewform->sec, $viewform->rid, $quser); + $this->response_commit($this->rid); + + /// If it was a previous save, rid is in the form... + if (!empty($viewform->rid) && is_numeric($viewform->rid)) { + $rid = $viewform->rid; + + /// Otherwise its in this object. + } else { + $rid = $this->rid; + } + + questionnaire_record_submission($this, $USER->id, $rid); + + if ($this->grade != 0) { + $questionnaire = new Object(); + $questionnaire->id = $this->id; + $questionnaire->name = $this->name; + $questionnaire->grade = $this->grade; + $questionnaire->cmidnumber = $this->cm->idnumber; + $questionnaire->courseid = $this->course->id; + questionnaire_update_grades($questionnaire, $quser); + } + + add_to_log($this->course->id, "questionnaire", "submit", "view.php?id={$this->cm->id}", "{$this->name}", $this->cm->id, $USER->id); + + $this->response_send_email($this->rid); + $this->response_goto_thankyou(); + } + + } else { + switch ($this->qtype) { + case QUESTIONNAIREDAILY: + $msgstring = ' '.get_string('today', 'questionnaire'); + break; + case QUESTIONNAIREWEEKLY: + $msgstring = ' '.get_string('thisweek', 'questionnaire'); + break; + case QUESTIONNAIREMONTHLY: + $msgstring = ' '.get_string('thismonth', 'questionnaire'); + break; + default: + $msgstring = ''; + break; + } + echo ('
'.get_string("alreadyfilled", "questionnaire", $msgstring).'
'); + } + + /// Finish the page + echo $OUTPUT->footer($this->course); + } + + /** + * Function to view an entire responses data. + * + */ + function view_response($rid, $blankquestionnaire=false) { + global $OUTPUT; + + echo $OUTPUT->box_start(); + $this->print_survey_start('', 1, 1, 0, $rid, $blankquestionnaire); + + $data = new Object(); + $i = 1; + if (!$blankquestionnaire) { + $this->response_import_all($rid, $data); + } + foreach ($this->questions as $question) { + if ($question->type_id < QUESPAGEBREAK) { + $question->response_display($data, $i++); + } + } + + $this->print_survey_end(1, 1); + echo $OUTPUT->box_end(); + } + + /** + * Function to view an entire responses data. + * + */ + function view_all_responses($resps) { + global $QTYPENAMES, $OUTPUT; + echo $OUTPUT->box_start(); + $this->print_survey_start('', 1, 1, 0); + + foreach ($resps as $resp) { + $data[$resp->id] = new Object(); + $this->response_import_all($resp->id, $data[$resp->id]); + } + + $i = 1; + echo '
'; + foreach ($this->questions as $question) { + if ($question->type_id < QUESPAGEBREAK) { + $method = $QTYPENAMES[$question->type_id].'_response_display'; + if (method_exists($question, $method)) { + $question->questionstart_survey_display($i); + $numItems = count($data); + $inneri = 0; + foreach ($data as $respid => $respdata) { + echo '
'.userdate($resps[$respid]->submitted).'
'; + $question->$method($respdata); + $inneri++; + if ($inneri < $numItems) { + echo '
'; + } + } + $question->questionend_survey_display($i); + } else { + print_error('displaymethod', 'questionnaire'); + } + $i++; + } + } + echo '
'; + + $this->print_survey_end(1, 1); + echo $OUTPUT->box_end(); + } + +/// Access Methods + function is_active() { + return (!empty($this->survey)); + } + + function is_open() { + return ($this->opendate > 0) ? ($this->opendate < time()) : true; + } + + function is_closed() { + return ($this->closedate > 0) ? ($this->closedate < time()) : false; + } + + function user_can_take($userid) { + + if (!$this->is_active() || !$this->user_is_eligible($userid)) { + return false; + } + else if ($this->qtype == QUESTIONNAIREUNLIMITED) { + return true; + } + else if ($userid > 0){ + return $this->user_time_for_new_attempt($userid); + } + else { + return false; + } + } + + function user_is_eligible($userid) { + return ($this->capabilities->view && $this->capabilities->submit); + } + + function user_time_for_new_attempt($userid) { + global $DB; + + $select = 'qid = '.$this->id.' AND userid = '.$userid; + if (!($attempts = $DB->get_records_select('questionnaire_attempts', $select, null, 'timemodified DESC'))) { + return true; + } + + $attempt = reset($attempts); + $timenow = time(); + + switch ($this->qtype) { + + case QUESTIONNAIREUNLIMITED: + $cantake = true; + break; + + case QUESTIONNAIREONCE: + $cantake = false; + break; + + case QUESTIONNAIREDAILY: + $attemptyear = date('Y', $attempt->timemodified); + $currentyear = date('Y', $timenow); + $attemptdayofyear = date('z', $attempt->timemodified); + $currentdayofyear = date('z', $timenow); + $cantake = (($attemptyear < $currentyear) || + (($attemptyear == $currentyear) && ($attemptdayofyear < $currentdayofyear))); + break; + + case QUESTIONNAIREWEEKLY: + $attemptyear = date('Y', $attempt->timemodified); + $currentyear = date('Y', $timenow); + $attemptweekofyear = date('W', $attempt->timemodified); + $currentweekofyear = date('W', $timenow); + $cantake = (($attemptyear < $currentyear) || + (($attemptyear == $currentyear) && ($attemptweekofyear < $currentweekofyear))); + break; + + case QUESTIONNAIREMONTHLY: + $attemptyear = date('Y', $attempt->timemodified); + $currentyear = date('Y', $timenow); + $attemptmonthofyear = date('n', $attempt->timemodified); + $currentmonthofyear = date('n', $timenow); + $cantake = (($attemptyear < $currentyear) || + (($attemptyear == $currentyear) && ($attemptmonthofyear < $currentmonthofyear))); + break; + + default: + $cantake = false; + break; + } + + return $cantake; + } + + function is_survey_owner() { + return (!empty($this->survey->owner) && ($this->course->id == $this->survey->owner)); + } + + function can_view_response($rid) { + global $USER, $DB; + + if (!empty($rid)) { + $response = $DB->get_record('questionnaire_response', array('id' => $rid)); + + /// If the response was not found, can't view it. + if (empty($response)) { + return false; + } + + /// If the response belongs to a different survey than this one, can't view it. + if ($response->survey_id != $this->survey->id) { + return false; + } + + /// If you can view all responses always, then you can view it. + if ($this->capabilities->readallresponseanytime) { + return true; + } + + /// If you are allowed to view this response for another user. + if ($this->capabilities->readallresponses && + ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_ALWAYS || + ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENCLOSED && $this->is_closed()) || + ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENANSWERED && !$this->user_can_take($USER->id)))) { + return true; + } + + /// If you can read your own response + if (($response->username == $USER->id) && $this->capabilities->readownresponses && ($this->count_submissions($USER->id) > 0)) { + return true; + } + + } else { + /// If you can view all responses always, then you can view it. + if ($this->capabilities->readallresponseanytime) { + return true; + } + + /// If you are allowed to view this response for another user. + if ($this->capabilities->readallresponses && + ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_ALWAYS || + ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENCLOSED && $this->is_closed()) || + ($this->resp_view == QUESTIONNAIRE_STUDENTVIEWRESPONSES_WHENANSWERED && !$this->user_can_take($USER->id)))) { + return true; + } + + /// If you can read your own response + if ($this->capabilities->readownresponses && ($this->count_submissions($USER->id) > 0)) { + return true; + } + + } + } + + function count_submissions($userid=false) { + global $DB; + + if (!$userid) { + // provide for groups setting + return $DB->count_records('questionnaire_response', array('survey_id' => $this->sid, 'complete' => 'y')); + } else { + return $DB->count_records('questionnaire_response', array('survey_id' => $this->sid, 'username' => $userid, + 'complete' => 'y')); + } + } + + function has_required($section = 0) { + if (empty($this->questions)) { + return false; + } else if ($section <= 0) { + foreach ($this->questions as $question) { + if ($question->required == 'y') { + return true; + } + } + } else { + foreach ($this->questionsbysec[$section] as $question) { + if ($question->required == 'y') { + return true; + } + } + } + return false; + } +/// Display Methods + + function print_survey($userid=false, $quser) { + global $CFG; + + $formdata = new stdClass(); + if (data_submitted() && confirm_sesskey()) { + $formdata = data_submitted(); + } + $formdata->rid = $this->get_response($quser); + if (!empty($formdata->rid) && (empty($formdata->sec) || intval($formdata->sec) < 1)) { + $formdata->sec = $this->response_select_max_sec($formdata->rid); + } + if (empty($formdata->sec)) { + $formdata->sec = 1; + } else { + $formdata->sec = (intval($formdata->sec) > 0) ? intval($formdata->sec) : 1; + } + + $num_sections = isset($this->questionsbysec) ? count($this->questionsbysec) : 0; /// indexed by section. + $msg = ''; + $action = $CFG->wwwroot.'/mod/questionnaire/view.php?id='.$this->cm->id; + +/// TODO - Need to rework this. Too much crossover with ->view method. + if(!empty($formdata->submit)) { + $msg = $this->response_check_format($formdata->sec, $formdata); + if(empty($msg)) { + return; + } + } + + if(!empty($formdata->resume) && ($this->resume)) { + $this->response_delete($formdata->rid, $formdata->sec); + $formdata->rid = $this->response_insert($this->survey->id, $formdata->sec, $formdata->rid, $quser, $resume=true); + $this->response_goto_saved($action); + return; + } + // JR save each section 's $formdata somewhere in case user returns to that page when navigating the questionnaire... + if(!empty($formdata->next)) { + $this->response_delete($formdata->rid, $formdata->sec); + $formdata->rid = $this->response_insert($this->survey->id, $formdata->sec, $formdata->rid, $quser); + $msg = $this->response_check_format($formdata->sec, $formdata); + if ( $msg ) { + $formdata->next = ''; + } else { + $formdata->sec++; + } + } + if (!empty($formdata->prev) && ($this->navigate)) { + $this->response_delete($formdata->rid, $formdata->sec); + $formdata->rid = $this->response_insert($this->survey->id, $formdata->sec, $formdata->rid, $quser); + $msg = $this->response_check_format($formdata->sec, $formdata); + if ( $msg ) { + $formdata->prev = ''; + } else { + $formdata->sec--; + } + } + + if (!empty($formdata->rid)) { + $this->response_import_sec($formdata->rid, $formdata->sec, $formdata); + } + echo ' + + '; + + echo '
'; + + ?> +
+
+ + + + + + +
+ questions) && $num_sections) { // sanity check + $this->survey_render($formdata->sec, $msg, $formdata); + echo '
'; + if (($this->navigate) && ($formdata->sec > 1)) { + echo ''; + } + if ($this->resume) { + echo ''; + } + // Add a 'hidden' variable for the mod's 'view.php', and use a language variable for the submit button. + + if($formdata->sec == $num_sections) { + echo ' +
+
'; + } else { + echo '
'; + } + echo '
'; //divs notice & buttons + echo '
'; + + echo '
'; //div class="generalbox" + + return $msg; + } else { + echo '

'.get_string('noneinuse','questionnaire').'

'; + echo ''; + echo ''; //div class="generalbox" + } + } + + function survey_render($section = 1, $message = '', &$formdata) { + + $this->usehtmleditor = null; + + if(empty($section)) { + $section = 1; + } + + $num_sections = isset($this->questionsbysec) ? count($this->questionsbysec) : 0; /// indexed by section. + if($section > $num_sections) { + return(false); // invalid section + } + + // check to see if there are required questions + $has_required = $this->has_required($section); + + // find out what question number we are on $i New fix for question numbering + $i = 0; + if ($section > 1) { + for($j = 2; $j<=$section; $j++) { + foreach ($this->questionsbysec[$j-1] as $question) { + if ($question->type_id < 99) { + $i++; + } + } + } + } + + $this->print_survey_start($message, $section, $num_sections, $has_required); + foreach ($this->questionsbysec[$section] as $question) { + if ($question->type === 'Essay Box') { + $this->usehtmleditor = can_use_html_editor(); + } + if ($question->type_id != QUESSECTIONTEXT) { + $i++; + } + $question->survey_display($formdata, $i, $this->usehtmleditor); + /// Bug MDL-7292 - Don't count section text as a question number. + // process each question + } + // end of questions + echo ('
'); + $this->print_survey_end($section, $num_sections); + echo '
'; + return; + } + + function print_survey_start($message, $section, $num_sections, $has_required, $rid='', $blankquestionnaire=false) { + global $CFG; + global $DB; + require_once($CFG->libdir.'/filelib.php'); + + $userid = ''; + $resp = ''; + $groupname = ''; + $timesubmitted = ''; + //available group modes (0 = no groups; 1 = separate groups; 2 = visible groups) + + if ($rid) { + $courseid = $this->course->id; + if ($resp = $DB->get_record('questionnaire_response', array('id' => $rid)) ) { + if ($this->respondenttype == 'fullname') { + $userid = $resp->username; + // display name of group(s) that student belongs to... if questionnaire is set to Groups separate or visible + if ($this->cm->groupmode > 0) { + if ($groups = groups_get_all_groups($courseid, $resp->username)) { + if (count($groups) == 1) { + $group = current($groups); + $groupname = ' ('.get_string('group').': '.$group->name.')'; + } else { + $groupname = ' ('.get_string('groups').': '; + foreach ($groups as $group) { + $groupname.= $group->name.', '; + } + $groupname = substr($groupname, 0, strlen($groupname) -2).')'; + } + } else { + $groupname = ' ('.get_string('groupnonmembers').')'; + } + } + } + } + } + $ruser = ''; + if ($resp && !$blankquestionnaire) { + if ($userid) { + if ($user = $DB->get_record('user', array('id' => $userid))) { + $ruser = fullname($user); + } + } + if ($this->respondenttype == 'anonymous') { + $ruser = '- '.get_string('anonymous', 'questionnaire').' -'; + } else { + // JR DEV comment following line out if you do NOT want time submitted displayed in Anonymous surveys + if ($resp->submitted) { + $timesubmitted = ' '.get_string('submitted', 'questionnaire').' '.userdate($resp->submitted); + } + } + } + if ($ruser) { + echo (get_string('respondent', 'questionnaire').': '.$ruser.''); + if ($this->survey->realm == 'public') { + /// For a public questionnaire, look for the course that used it. + $coursename = ''; + $sql = 'SELECT q.id, q.course, c.fullname '. + 'FROM {questionnaire} q, {questionnaire_attempts} qa, {course} c '. + 'WHERE qa.rid = ? AND q.id = qa.qid AND c.id = q.course'; + if ($record = $DB->get_record_sql($sql, array($rid))) { + $coursename = $record->fullname; + } + echo (' '.get_string('course'). ': '.$coursename); + } + echo ($groupname); + echo ($timesubmitted); + } + echo '

'.s($this->survey->title).'

'; + if ($section == 1) { + if ($this->survey->subtitle) { + echo '

'.(format_text($this->survey->subtitle, FORMAT_HTML)).'

'; + } + if ($this->survey->info) { + $infotext = file_rewrite_pluginfile_urls($this->survey->info, 'pluginfile.php', $this->context->id, 'mod_questionnaire', 'info', $this->survey->id); + echo '
'.format_text($infotext, FORMAT_HTML).'
'; + } + } + if($num_sections>1) { + $a = new stdClass(); + $a->page = $section; + $a->totpages = $num_sections; + echo '
 '.get_string('pageof', 'questionnaire', $a).'
'; + } + if ($message) { + echo '
'.$message.'
'; //JR + } + + } + + function print_survey_end($section, $num_sections) { + if($num_sections>1) { + $a = new stdClass(); + $a->page = $section; + $a->totpages = $num_sections; + echo get_string('pageof', 'questionnaire', $a).'  '; + } + } + + function survey_print_render($message = '', $referer='', $courseid, $blankquestionnaire=false) { + global $USER, $DB, $OUTPUT; + + $rid = optional_param('rid', 0, PARAM_INT); + + if (! $course = $DB->get_record("course", array("id" => $courseid))) { + print_error('incorrectcourseid', 'questionnaire'); + } + $this->course = $course; + + if ($this->resume && empty($rid)) { + $rid = $this->get_response($USER->id, $rid); + } + + if (!empty($rid)) { + // If we're viewing a response, use this method. + $this->view_response($rid, $blankquestionnaire); + return; + } + + if(empty($section)) { + $section = 1; + } + + $num_sections = isset($this->questionsbysec) ? count($this->questionsbysec) : 0; + if($section > $num_sections) + return(false); // invalid section + + $has_required = $this->has_required(); + + // find out what question number we are on $i + $i = 1; + for($j = 2; $j<=$section; $j++) { + $i += count($this->questionsbysec[$j-1]); + } + + echo $OUTPUT->box_start(); + $this->print_survey_start($message, 1, 1, $has_required); + /// Print all sections: + $formdata = new stdClass(); + if (data_submitted() && confirm_sesskey()) { + $formdata = data_submitted(); + } + foreach ($this->questionsbysec as $section) { + foreach ($section as $question) { + if ($question->type_id == QUESSECTIONTEXT) { + $i--; + } + $question->survey_display($formdata, $i++, $usehtmleditor=null); + } + if (!$blankquestionnaire) { + echo (get_string('sectionbreak', 'questionnaire').'

'); // print on preview questionaire page only + } + } + // end of questions + + echo $OUTPUT->box_end(); + return; + } + + function survey_update($sdata) { + global $DB; + + $errstr = ''; //TODO: notused! + + // new survey + if(empty($this->survey->id)) { + // create a new survey in the database + $fields = array('name','realm','title','subtitle','email','theme','thanks_page','thank_head','thank_body','info'); // theme field deprecated + $record = new Object(); + $record->id = 0; + $record->owner = $sdata->owner; + foreach($fields as $f) { + if(isset($sdata->$f)) { + $record->$f = $sdata->$f; + } + } + + $this->survey = new stdClass(); + $this->survey->id = $DB->insert_record('questionnaire_survey', $record); + $this->add_survey($this->survey->id); + + if(!$this->survey->id) { + $errstr = get_string('errnewname', 'questionnaire') .' [ : ]'; //TODO: notused! + return(false); + } + } else { + if(empty($sdata->name) || empty($sdata->title) + || empty($sdata->realm)) { + return(false); + } + + $fields = array('name','realm','title','subtitle','email','theme','thanks_page','thank_head','thank_body','info'); // theme field deprecated + + $name = $DB->get_field('questionnaire_survey', 'name', array('id' => $this->survey->id)); + + // trying to change survey name + if(trim($name) != trim(stripslashes($sdata->name))) { // $sdata will already have slashes added to it. + $count = $DB->count_records('questionnaire_survey', array('name' => $sdata->name)); + if($count != 0) { + $errstr = get_string('errnewname', 'questionnaire'); //TODO: notused! + return(false); + } + } + + // UPDATE the row in the DB with current values + $survey_record = new Object(); + $survey_record->id = $this->survey->id; + foreach($fields as $f) { + $survey_record->$f = trim($sdata->{$f}); + } + + $result = $DB->update_record('questionnaire_survey', $survey_record); + if(!$result) { + $errstr = get_string('warning', 'questionnaire').' [ : ]'; //TODO: notused! + return(false); + } + } + + return($this->survey->id); + } + + /* Creates an editable copy of a survey. */ + function survey_copy($owner) { + global $DB; + + // clear the sid, clear the creation date, change the name, and clear the status + // Since we're copying a data record, addslashes. + // 2.0 - don't need to do this now, since its handled by the $DB-> functions. + $survey = clone($this->survey); + + unset($survey->id); + $survey->owner = $owner; + // Make sure that the survey name is not larger than the field size (CONTRIB-2999). Leave room for extra chars. + $survey->name = substr($survey->name, 0, (64-10)); + $survey->name .= '_copy'; + $survey->status = 0; + + // check for 'name' conflict, and resolve + $i=0; + $name = $survey->name; + while ($DB->count_records('questionnaire_survey', array('name' => $name)) > 0) { + $name = $survey->name.(++$i); + } + if($i) { + $survey->name .= $i; + } + + // create new survey + if (!($new_sid = $DB->insert_record('questionnaire_survey', $survey))) { + return(false); + } + + // make copies of all the questions + $pos=1; + foreach ($this->questions as $question) { + // fix some fields first + unset($question->id); + $question->survey_id = $new_sid; + $question->position = $pos++; + $question->name = addslashes($question->name); + $question->content = addslashes($question->content); + + // copy question to new survey + if (!($new_qid = $DB->insert_record('questionnaire_question', $question))) { + return(false); + } + + foreach ($question->choices as $choice) { + unset($choice->id); + $choice->question_id = $new_qid; + $choice->content = addslashes($choice->content); + $choice->value = addslashes($choice->value); + if (!$DB->insert_record('questionnaire_quest_choice', $choice)) { + return(false); + } + } + } + + return($new_sid); + } + + function type_has_choices() { + global $DB; + + $has_choices = array(); + + if ($records = $DB->get_records('questionnaire_question_type', array(), 'typeid', 'typeid,has_choices')) { + foreach ($records as $record) { + if($record->has_choices == 'y') { + $has_choices[$record->typeid]=1; + } else { + $has_choices[$record->typeid]=0; + } + } + } else { + $has_choices = array(); + } + + return($has_choices); + } + + function array_to_insql($array) { + if (count($array)) + return("IN (".preg_replace("/([^,]+)/","'\\1'",join(",",$array)).")"); + return 'IS NULL'; + } + + // ---- RESPONSE LIBRARY + + function response_check_format($section, &$formdata, $qnum='') { + $missing = 0; + $strmissing = ''; // missing questions + $wrongformat = 0; + $strwrongformat = ''; // wrongly formatted questions (Numeric, 5:Check Boxes, Date) + $i = 1; + for($j = 2; $j<=$section; $j++) { + // ADDED A SIMPLE LOOP FOR MAKING SURE PAGE BREAKS (type 99) AND LABELS (type 100) ARE NOT ALLOWED + foreach ($this->questionsbysec[$j-1] as $sectionrecord) { + $tid = $sectionrecord->type_id; + if ($tid < 99) { + $i++; + } + } + } + $qnum = $i - 1; + + foreach ($this->questionsbysec[$section] as $record) { + + $qid = $record->id; + $tid = $record->type_id; + $lid = $record->length; + $pid = $record->precise; + if ($tid != 100) { + $qnum++; + } + if ( ($record->required == 'y') && ($record->deleted == 'n') && ((isset($formdata->{'q'.$qid}) && $formdata->{'q'.$qid} == '') || (!isset($formdata->{'q'.$qid}))) && $tid != 8 && $tid != 100 ) { + $missing++; + $strmissing .= get_string('num', 'questionnaire').$qnum.'. '; + } + + switch ($tid) { + + case 4: // Radio Buttons with !other field + if (!isset($formdata->{'q'.$qid})) { + break; + } + $resp = $formdata->{'q'.$qid}; + $pos = strpos($resp, 'other_'); + + // "other" choice is checked but text box is empty + if (is_int($pos) == true){ + $othercontent = "q".$qid.substr($resp, 5); + if ( !$formdata->$othercontent ) { + $wrongformat++; + $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; + break; + } + } + + if (is_int($pos) == true && $record->required == 'y') { + $resp = 'q'.$qid.''.substr($resp,5); + if (!$formdata->$resp) { + $missing++; + $strmissing .= get_string('num', 'questionnaire').$qnum.'. '; + } + } + break; + + case 5: // Check Boxes + if (!isset($formdata->{'q'.$qid})) { + break; + } + $resps = $formdata->{'q'.$qid}; + $nbrespchoices = 0; + foreach ($resps as $resp) { + $pos = strpos($resp, 'other_'); + + // "other" choice is checked but text box is empty + if (is_int($pos) == true){ + $othercontent = "q".$qid.substr($resp, 5); + if ( !$formdata->$othercontent ) { + $wrongformat++; + $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; + break; + } + } + + if (is_numeric($resp) || is_int($pos) == true) { //JR fixed bug CONTRIB-884 + $nbrespchoices++; + } + } + $nbquestchoices = count($record->choices); + $min = $lid; + $max = $pid; + if ($max == 0) { + $max = $nbquestchoices; + } + if ($min > $max) { + $min = $max; // sanity check + } + $min = min($nbquestchoices, $min); + // number of ticked boxes is not within min and max set limits + if ( $nbrespchoices && ($nbrespchoices < $min || $nbrespchoices > $max) ) { + $wrongformat++; + $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; + break; + } + break; + + case 6: // Drop + $resp = $formdata->{'q'.$qid}; + if (!$resp && $record->required == 'y') { + $missing++; + $strmissing .= get_string('num', 'questionnaire').$qnum.'. '; + } + break; + + case 8: // Rate + $num = 0; + $nbchoices = count($record->choices); + $na = get_string('notapplicable', 'questionnaire'); + foreach ($record->choices as $cid => $choice) { + // in case we have named degrees on the Likert scale, count them to substract from nbchoices + $nameddegrees = 0; + $content = $choice->content; + if (preg_match("/^[0-9]{1,3}=/", $content,$ndd)) { + $nameddegrees++; + } else { + $str = 'q'."{$record->id}_$cid"; + if (isset($formdata->$str) && $formdata->$str == $na) { + $formdata->$str = -1; + } + for ($j = 0; $j < $record->length; $j++) { + $num += (isset($formdata->$str) && ($j == $formdata->$str)); + } + $num += (($record->precise) && isset($formdata->$str) && ($formdata->$str == -1)); + } + $nbchoices -= $nameddegrees; + } + if ( $num == 0 && $record->required == 'y') { + $missing++; + $strmissing .= get_string('num', 'questionnaire').$qnum.'. '; + break; + } + // if nodupes and nb choice restricted, nbchoices may be > actual choices, so limit it to $record->length + $isrestricted = ($record->length < count($record->choices)) && $record->precise == 2; + if ($isrestricted) { + $nbchoices = min ($nbchoices, $record->length); + } + if ( $num != $nbchoices && $num!=0 ) { + $wrongformat++; + $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; + } + break; + + case 9: // Date + $checkdateresult = ''; + if ($formdata->{'q'.$qid} != '') { + $checkdateresult = questionnaire_check_date($formdata->{'q'.$qid}); + } + if (substr($checkdateresult,0,5) == 'wrong') { + $wrongformat++; + $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; + } + break; + + case 10: // Numeric + if ( ($formdata->{'q'.$qid} != '') && (!is_numeric($formdata->{'q'.$qid})) ) { + $wrongformat++; + $strwrongformat .= get_string('num', 'questionnaire').$qnum.'. '; + } + break; + + default: + break; + } + } + $message =''; + if($missing) { + if ($missing == 1) { + $message = get_string('missingquestion', 'questionnaire').$strmissing; + } else { + $message = get_string('missingquestions', 'questionnaire').$strmissing; + } + if ($wrongformat) { + $message .= '
'; + } + } + if($wrongformat) { + if ($wrongformat == 1) { + $message .= get_string('wrongformat', 'questionnaire').$strwrongformat; + } else { + $message .= get_string('wrongformats', 'questionnaire').$strwrongformat; + } + } + return ($message); + } + + + function response_delete($rid, $sec = null) { + global $DB; + + if (empty($rid)) { + return; + } + + if ($sec != null) { + if ($sec < 1) { + return; + } + + /* get question_id's in this section */ + $qids = ''; + foreach ($this->questionsbysec[$sec] as $question) { + if (empty($qids)) { + $qids .= ' AND question_id IN ('.$question->id; + } else { + $qids .= ','.$question->id; + } + } + if (!empty($qids)) { + $qids .= ')'; + } else { + return; + } + } else { + /* delete all */ + $qids = ''; + } + + /* delete values */ + $select = 'response_id = \''.$rid.'\' '.$qids; + foreach (array('response_bool', 'resp_single', 'resp_multiple', 'response_rank', 'response_text', + 'response_other', 'response_date') as $tbl) { + $DB->delete_records_select('questionnaire_'.$tbl, $select); + } + } + + function response_import_sec($rid, $sec, &$varr) { + if ($sec < 1 || !isset($this->questionsbysec[$sec])) { + return; + } + $vals = $this->response_select($rid, 'content'); + reset($vals); + foreach ($vals as $id => $arr) { + if (isset($arr[0]) && is_array($arr[0])) { + // multiple + $varr->{'q'.$id} = array_map('array_pop', $arr); + } else { + $varr->{'q'.$id} = array_pop($arr); + } + } + } + + + + function response_import_all($rid, &$varr) { + $vals = $this->response_select($rid, 'content'); + reset($vals); + foreach ($vals as $id => $arr) { + if (strstr($id, '_') && isset($arr[4])) { // single OR multiple with !other choice selected + $varr->{'q'.$id} = $arr[4]; + } else { + if (isset($arr[0]) && is_array($arr[0])) { // multiple + $varr->{'q'.$id} = array_map('array_pop', $arr); + } else { // boolean, rate and other + $varr->{'q'.$id} = array_pop($arr); + } + } + } + } + + function response_commit($rid) { + global $DB; + + $record = new object; + $record->id = $rid; + $record->complete = 'y'; + $record->submitted = time(); + + if ($this->grade < 0) { + $record->grade = 1; /// Don't know what to do if its a scale... + } else { + $record->grade = $this->grade; + } + return $DB->update_record('questionnaire_response', $record); + } + + function get_response($username, $rid = 0) { + global $DB; + + $rid = intval($rid); + if ($rid != 0) { + // check for valid rid + $fields = 'id, username'; + $select = 'id = '.$rid.' AND survey_id = '.$this->sid.' AND username = \''.$username.'\' AND complete = \'n\''; + return ($DB->get_record_select('questionnaire_response', $select, null, $fields) !== false) ? $rid : ''; + + } else { + // find latest in progress rid + $select = 'survey_id = '.$this->sid.' AND complete = \'n\' AND username = \''.$username.'\''; + if ($records = $DB->get_records_select('questionnaire_response', $select, null, 'submitted DESC', + 'id,survey_id', 0, 1)) { + $rec = reset($records); + return $rec->id; + } else { + return ''; + } + } + } + + function response_select_max_sec($rid) { + global $DB; + + $pos = $this->response_select_max_pos($rid); + $select = 'survey_id = \''.$this->sid.'\' AND type_id = 99 AND position < '.$pos.' AND deleted = \'n\''; + $max = $DB->count_records_select('questionnaire_question', $select) + 1; + + return $max; + } + + function response_select_max_pos($rid) { + global $DB; + + $max = 0; + + foreach (array('response_bool', 'resp_single', 'resp_multiple', 'response_rank', 'response_text', + 'response_other', 'response_date') as $tbl) { + $sql = 'SELECT MAX(q.position) as num FROM {questionnaire_'.$tbl.'} a, {questionnaire_question} q '. + 'WHERE a.response_id = ? AND '. + 'q.id = a.question_id AND '. + 'q.survey_id = ? AND '. + 'q.deleted = \'n\''; + if ($record = $DB->get_record_sql($sql, array($rid, $this->sid))) { + $max = (int)$record->num; + } + } + return $max; + } + +/* {{{ proto array response_select_name(int survey_id, int response_id, array question_ids) + A wrapper around response_select(), that returns an array of + key/value pairs using the field name as the key. + $csvexport = true: a parameter to return a different response formatting for CSV export from normal report formatting + */ + function response_select_name($rid, $choicecodes, $choicetext) { + $res = $this->response_select($rid, 'position,type_id,name', true, $choicecodes, $choicetext); + $nam = array(); + reset($res); + $subqnum = 0; + $oldpos = ''; + while(list($qid, $arr) = each($res)) { + $qpos = $arr[0]; // question position (there may be "holes" in positions list) + $qtype = $arr[1]; // question type (1:bool,2:text,3:essay,4:radio,5:check,6:dropdn,7:rating(not used),8:rate,9:date,10:numeric) + $qname = $arr[2]; // variable name; (may be empty); for rate questions: 'variable group' name + $qchoice = $arr[3]; // modality; for rate questions: variable + + // strip potential html tags from modality name + if (!empty($qchoice)) { + $qchoice = strip_tags($arr[3]); + $qchoice = preg_replace("/[\r\n\t]/", ' ', $qchoice); + } + $q4 = ''; // for rate questions: modality; for multichoice: selected = 1; not selected = 0 + if (isset($arr[4])) { + $q4 = $arr[4]; + } + if (strstr($qid, '_')) { + if ($qtype == 4) { //single + $nam[$qpos][$qname.'_'.get_string('other', 'questionnaire')] = $q4; + continue; + } + // multiple OR rank + if ($oldpos != $qpos) { + $subqnum = 1; + $oldpos = $qpos; + } else { + $subqnum++; + } + if ($qtype == 8) { // rate + $qname .= "->$qchoice"; + if ($q4 == -1) { +// $q4 = get_string('notapplicable', 'questionnaire'); DEV JR choose one solution please + $q4 = ''; + } else { + if (is_numeric($q4)) { + $q4++; + } + } + } else { // multiple + $qname .= "->$qchoice"; + } + $nam[$qpos][$qname] = $q4; + continue; + } + $val = $qchoice; + $nam[$qpos][$qname] = $val; + } + return $nam; + } + + function response_send_email($rid, $userid=false) { + global $CFG, $USER, $DB; + + require_once($CFG->libdir.'/phpmailer/class.phpmailer.php'); + + $name = s($this->name); + if ($record = $DB->get_record('questionnaire_survey', array('id' => $this->survey->id))) { + $email = $record->email; + } else { + $email = ''; + } + + if(empty($email)) { + return(false); + } + $answers = $this->generate_csv($rid, $userid='', null, 1); + + // line endings for html and plaintext emails + $end_html = "\r\n
"; + $end_plaintext = "\r\n"; + + $subject = get_string('surveyresponse', 'questionnaire') .": $name [$rid]"; + $url = $CFG->wwwroot.'/mod/questionnaire/report.php?action=vresp&sid='.$this->survey->id. + '&rid='.$rid.'&instance='.$this->id; + + // html and plaintext body + $body_html = ''.$url.''.$end_html; + $body_plaintext = $url.$end_plaintext; + $body_html .= get_string('surveyresponse', 'questionnaire') .' "'.$name.'"'.$end_html; + $body_plaintext .= get_string('surveyresponse', 'questionnaire') .' "'.$name.'"'.$end_plaintext; + + reset($answers); + + for ($i = 0; $i < count($answers[0]); $i++) { + $sep = ' : '; + switch($i) { + case 1: + $sep = ' '; + break; + case 4: + $body_html .= get_string('user').' '; + $body_plaintext .= get_string('user').' '; + break; + case 6: + if ($this->respondenttype != 'anonymous') { + $body_html .= get_string('email').$sep.$USER->email. $end_html; + $body_plaintext .= get_string('email').$sep.$USER->email. $end_plaintext; + } + } + $body_html .= $answers[0][$i].$sep.$answers[1][$i]. $end_html; + $body_plaintext .= $answers[0][$i].$sep.$answers[1][$i]. $end_plaintext; + } + + // use plaintext version for altbody + $altbody = "\n$body_plaintext\n"; + + $return = true; + $mailaddresses = preg_split('/,|;/', $email); + foreach ($mailaddresses as $email) { + $userto = new Object(); + $userto->email = $email; + $userto->mailformat = 1; + $userfrom = $CFG->noreplyaddress; + if (email_to_user($userto, $userfrom, $subject, $altbody, $body_html)) { + $return = $return && true; + } else { + $return = false; + } + } + return $return; + } + + function response_insert($sid, $section, $rid, $userid, $resume=false) { + global $DB, $USER; + + $record = new object; + $record->submitted = time(); + + if(empty($rid)) { + // create a uniqe id for this response + $record->survey_id = $sid; + $record->username = $userid; + $rid = $DB->insert_record('questionnaire_response', $record); + } else { + $record->id = $rid; + $DB->update_record('questionnaire_response', $record); + } + if ($resume) { + add_to_log($this->course->id, "questionnaire", "save", "view.php?id={$this->cm->id}", "{$this->name}", $this->cm->id, $USER->id); + } + + if (!empty($this->questionsbysec[$section])) { + foreach ($this->questionsbysec[$section] as $question) { + $question->insert_response($rid); + } + } + return($rid); + } + + function response_select($rid, $col = null, $csvexport = false, $choicecodes=0, $choicetext=1) { + global $DB; + + $sid = $this->survey->id; + $values = array(); + $stringother = get_string('other', 'questionnaire'); + if ($col == null) { + $col = ''; + } + if (!is_array($col) && !empty($col)) { + $col = explode(',', preg_replace("/\s/",'', $col)); + } + if (is_array($col) && count($col) > 0) { + $col = ',' . implode(',', array_map(create_function('$a','return "q.$a";'), $col)); + } + + // --------------------- response_bool (yes/no)--------------------- + $sql = 'SELECT q.id '.$col.', a.choice_id '. + 'FROM {questionnaire_response_bool} a, {questionnaire_question} q '. + 'WHERE a.response_id= ? AND a.question_id=q.id '; + if ($records = $DB->get_records_sql($sql, array($rid))) { + foreach ($records as $qid => $row) { + $choice = $row->choice_id; + if (isset ($row->name) && $row->name == '') { + $noname = TRUE; + } + unset ($row->id); + unset ($row->choice_id); + $row = (array)$row; + $newrow = array(); + foreach ($row as $key => $val) { + if (!is_numeric($key)) { + $newrow[] = $val; + } + } + $values[$qid] = $newrow; + array_push($values["$qid"], ($choice == 'y') ? '1' : '0'); + if (!$csvexport) { + array_push($values["$qid"], $choice); //DEV still needed for responses display + } + } + } + + // --------------------- response_single (radio button or dropdown)--------------------- + $sql = 'SELECT q.id '.$col.', q.type_id as q_type, c.content as ccontent,c.id as cid '. + 'FROM {questionnaire_resp_single} a, {questionnaire_question} q, {questionnaire_quest_choice} c '. + 'WHERE a.response_id = ? AND a.question_id=q.id AND a.choice_id=c.id '; + if ($records = $DB->get_records_sql($sql, array($rid))) { + foreach ($records as $qid => $row) { + $cid = $row->cid; + $qtype = $row->q_type; + if ($csvexport) { + static $i = 1; + $qrecords = $DB->get_records('questionnaire_quest_choice', array('question_id' => $qid)); + foreach($qrecords as $value) { + if ($value->id == $cid) { + $contents = questionnaire_choice_values($value->content); + if ($contents->modname) { + $row->ccontent = $contents->modname; + } else { + $content = $contents->text; + if (preg_match('/^!other/', $content)) { + $row->ccontent = get_string('other','questionnaire'); + } else if (($choicecodes == 1) && ($choicetext == 1)) { + $row->ccontent = "$i : $content"; + } else if ($choicecodes == 1) { + $row->ccontent = "$i"; + } else { + $row->ccontent = $content; + } + } + $i = 1; + break; + } + $i++; + } + } + unset($row->id); + unset($row->cid); + unset($row->q_type); + $arow = get_object_vars($row); + $newrow = array(); + foreach ($arow as $key => $val) { + if (!is_numeric($key)) { + $newrow[] = $val; + } + } + if (preg_match('/^!other/', $row->ccontent)) { + $newrow[] = 'other_' . $cid; + } else { + $newrow[] = (int)$cid; + } + $values[$qid] = $newrow; + } + } + + // --------------------- response_multiple --------------------- + $sql = 'SELECT a.id as aid, q.id as qid '.$col.',c.content as ccontent,c.id as cid '. + 'FROM {questionnaire_resp_multiple} a, {questionnaire_question} q, {questionnaire_quest_choice} c '. + 'WHERE a.response_id = ? AND a.question_id=q.id AND a.choice_id=c.id '. + 'ORDER BY a.id,a.question_id,c.id'; + $records = $DB->get_records_sql($sql, array($rid)); + if ($csvexport) { + $tmp = null; + + if (!empty($records)) { + $qids2 = array(); + $oldqid = ''; + foreach ($records as $qid => $row) { + if ($row->qid != $oldqid) { + $qids2[] = $row->qid; + $oldqid = $row->qid; + } + } + if (is_array($qids2)) { + $qids2 = 'question_id ' . $this->array_to_insql($qids2); + } else { + $qids2 = 'question_id= ' . $qids2; + } + $sql = 'SELECT * FROM {questionnaire_quest_choice} WHERE '.$qids2. + 'ORDER BY id'; + if ($records2 = $DB->get_records_sql($sql)) { + foreach ($records2 as $qid => $row2) { + $selected = '0'; + $qid2 = $row2->question_id; + $cid2 = $row2->id; + $c2 = $row2->content; + $otherend = false; + if ($c2 == '!other') { + $c2 = '!other='.get_string('other','questionnaire'); + } + if (preg_match('/^!other/', $c2)) { + $otherend = true; + } else { + $contents = questionnaire_choice_values($c2); + if ($contents->modname) { + $c2 = $contents->modname; + } elseif ($contents->title) { + $c2 = $contents->title; + } + } + $sql = 'SELECT a.name as name, a.type_id as q_type, a.position as pos ' . + 'FROM {questionnaire_question} a WHERE id = ?'; + if ($currentquestion = $DB->get_records_sql($sql, array($qid2))) { + foreach ($currentquestion as $question) { + $name1 = $question->name; + $type1 = $question->q_type; + } + } + $newrow = array(); + foreach ($records as $qid => $row1) { + $qid1 = $row1->qid; + $cid1 = $row1->cid; + // if available choice has been selected by student + if ($qid1 == $qid2 && $cid1 == $cid2) { + $selected = '1'; + } + } + if ($otherend) { + $newrow2 = array(); + $newrow2[] = $question->pos; + $newrow2[] = $type1; + $newrow2[] = $name1; + $newrow2[] = '['.get_string('other','questionnaire').']'; + $newrow2[] = $selected; + $tmp2 = $qid2.'_other'; + $values["$tmp2"]=$newrow2; + } + $newrow[] = $question->pos; + $newrow[] = $type1; + $newrow[] = $name1; + $newrow[] = $c2; + $newrow[] = $selected; + $tmp = $qid2.'_'.$cid2; + $values["$tmp"]=$newrow; + } + } + } + unset($tmp); + unset($row); + + } else { + $arr = array(); + $tmp = null; + if (!empty($records)) { + foreach ($records as $aid => $row) { + $qid = $row->qid; + $cid = $row->cid; + unset($row->aid); + unset($row->qid); + unset($row->cid); + $arow = get_object_vars($row); + $newrow = array(); + foreach ($arow as $key => $val) { + if (!is_numeric($key)) { + $newrow[] = $val; + } + } + if (preg_match('/^!other/', $row->ccontent)) { + $newrow[] = 'other_' . $cid; + } else { + $newrow[] = (int)$cid; + } + if($tmp == $qid) { + $arr[] = $newrow; + continue; + } + if($tmp != null) { + $values["$tmp"]=$arr; + } + $tmp = $qid; + $arr = array($newrow); + } + } + if($tmp != null) { + $values["$tmp"]=$arr; + } + unset($arr); + unset($tmp); + unset($row); + + + } + + // --------------------- response_other --------------------- + // this will work even for multiple !other fields within one question AND for identical !other responses in different questions JR + $sql = 'SELECT c.id as cid, c.content as content, a.response as aresponse, q.id as qid, q.position as position, q.type_id as type_id, q.name as name '. + 'FROM {questionnaire_response_other} a, {questionnaire_question} q, {questionnaire_quest_choice} c '. + 'WHERE a.response_id= ? AND a.question_id=q.id AND a.choice_id=c.id '. + 'ORDER BY a.question_id,c.id '; + if ($records = $DB->get_records_sql($sql, array($rid))) { + foreach ($records as $record) { + $newrow = array(); + $position = $record->position; + $type_id = $record->type_id; + $name = $record->name; + $cid = $record->cid; + $qid = $record->qid; + $content = $record->content; + + //!other modality with no label + if ($content == '!other') { + $content = '!other='.$stringother; + } + $content = substr($content,7); + $aresponse = $record->aresponse; + // the first two empty values are needed for compatibility with "normal" (non !other) responses + // they are only needed for the CSV export, in fact - JR + $newrow[] = $position; + $newrow[] = $type_id; + $newrow[] = $name; + $content = $stringother; + $newrow[] = $content; + $newrow[] = $aresponse; + $values["${qid}_${cid}"] = $newrow; + } + } + + // --------------------- response_rank --------------------- + $sql = 'SELECT a.id as aid, q.id AS qid, q.precise AS precise, c.id AS cid '.$col.',c.content as ccontent,a.rank as arank '. + 'FROM {questionnaire_response_rank} a, {questionnaire_question} q, {questionnaire_quest_choice} c '. + 'WHERE a.response_id= ? AND a.question_id=q.id AND a.choice_id=c.id '. + 'ORDER BY aid, a.question_id,c.id'; + if ($records = $DB->get_records_sql($sql, array($rid))) { + foreach ($records as $row) { + /// Next two are 'qid' and 'cid', each with numeric and hash keys. + $osgood = false; + if ($row->precise == 3) { + $osgood = true; + } + $qid = $row->qid.'_'.$row->cid; + unset($row->aid); // get rid of the answer id. + unset($row->qid); + unset($row->cid); + unset($row->precise); + $row = (array)$row; + $newrow = array(); + foreach ($row as $key => $val) { + if ($key != 'content') { // no need to keep question text - ony keep choice text and rank + if ($key == 'ccontent') { + if ($osgood) { + list($contentleft, $contentright) = preg_split('/[|]/', $val); + $contents = questionnaire_choice_values($contentleft); + if ($contents->title) { + $contentleft = $contents->title; + } + $contents = questionnaire_choice_values($contentright); + if ($contents->title) { + $contentright = $contents->title; + } + $val = strip_tags($contentleft.'|'.$contentright); + $val = preg_replace("/[\r\n\t]/", ' ', $val); + } else { + $contents = questionnaire_choice_values($val); + if ($contents->modname) { + $val = $contents->modname; + } elseif ($contents->title) { + $val = $contents->title; + } elseif ($contents->text) { + $val = strip_tags($contents->text); + $val = preg_replace("/[\r\n\t]/", ' ', $val); + } + } + } + $newrow[] = $val; + } + } + $values[$qid] = $newrow; + } + } + + // --------------------- response_text --------------------- + $sql = 'SELECT q.id '.$col.',a.response as aresponse '. + 'FROM {questionnaire_response_text} a, {questionnaire_question} q '. + 'WHERE a.response_id=\''.$rid.'\' AND a.question_id=q.id '; + if ($records = $DB->get_records_sql($sql)) { + foreach ($records as $qid => $row) { + unset($row->id); + $row = (array)$row; + $newrow = array(); + foreach ($row as $key => $val) { + if (!is_numeric($key)) { + $newrow[] = $val; + } + } + $values["$qid"]=$newrow; + $val = array_pop($values["$qid"]); + array_push($values["$qid"], $val, $val); + } + } + + // --------------------- response_date --------------------- + $sql = 'SELECT q.id '.$col.',a.response as aresponse '. + 'FROM {questionnaire_response_date} a, {questionnaire_question} q '. + 'WHERE a.response_id=\''.$rid.'\' AND a.question_id=q.id '; + if ($records = $DB->get_records_sql($sql)) { + $dateformat = get_string('strfdate', 'questionnaire'); + foreach ($records as $qid => $row) { + unset ($row->id); + $row = (array)$row; + $newrow = array(); + foreach ($row as $key => $val) { + if (!is_numeric($key)) { + $newrow[] = $val; + // convert date from yyyy-mm-dd database format to actual questionnaire dateformat + // does not work with dates prior to 1900 under Windows + if (preg_match('/\d\d\d\d-\d\d-\d\d/', $val)) { + $dateparts = preg_split('/-/', $val); + $val = make_timestamp($dateparts[0], $dateparts[1], $dateparts[2]); // Unix timestamp + $val = userdate ( $val, $dateformat); + $newrow[] = $val; + } + } + } + $values["$qid"]=$newrow; + $val = array_pop($values["$qid"]); + array_push($values["$qid"], '', '', $val); + } + } + + // --------------------- return --------------------- + return($values); + } + + function response_goto_thankyou() { + global $CFG, $USER, $DB; + + $select = 'id = '.$this->survey->id; + $fields = 'thanks_page,thank_head,thank_body'; + if ($result = $DB->get_record_select('questionnaire_survey', $select, null, $fields)) { + $thank_url = $result->thanks_page; + $thank_head = $result->thank_head; + $thank_body = $result->thank_body; + } else { + $thank_url = ''; + $thank_head = ''; + $thank_body = ''; + } + if(!empty($thank_url)) { + if(!headers_sent()) { + header("Location: $thank_url"); + exit; + } + ?> + + + '.$thank_head.''.file_rewrite_pluginfile_urls(format_text($thank_body, FORMAT_HTML), 'pluginfile.php', + $this->context->id, 'mod_questionnaire', 'thankbody', $this->id); + echo ($message); + if ($this->capabilities->readownresponses) { + echo(''. + get_string("continue").''); + } else { + echo(''. + get_string("continue").''); + } + return; + } + + function response_goto_saved($url) { + ?> +
+ '.get_string('resumesurvey', 'questionnaire').''); ?> +
+
+
+ +   ' + .get_string("backto","moodle",$this->course->fullname).'  '); + ?> + survey->id.' AND complete = \'y\''; + if ($userid !== false) { + $select .= ' AND username = \''.$userid.'\''; + } + if (!($responses = $DB->get_records_select('questionnaire_response', $select, null, 'id', 'id,survey_id,submitted,username'))) { + return; + } + $total = count($responses); + if ($total == 1) { + return; + } + $rids = array(); + $ridssub = array(); + $ridsusername = array(); + $i = 0; + $curr_pos = -1; + foreach ($responses as $response) { + array_push($rids, $response->id); + array_push($ridssub, $response->submitted); + array_push($ridsusername, $response->username); + if ($response->id == $curr_rid) { + $curr_pos = $i; + } + $i++; + } + + $prev_rid = ($curr_pos > 0) ? $rids[$curr_pos - 1] : null; + $next_rid = ($curr_pos < $total - 1) ? $rids[$curr_pos + 1] : null; + $rows_per_page = 1; + $pages = ceil($total / $rows_per_page); + + $url = $CFG->wwwroot.'/mod/questionnaire/report.php?action=vresp&sid='.$this->survey->id; + + $mlink = create_function('$i,$r', 'return "$i";'); + + $linkarr = array(); + + $display_pos = 1; + if ($prev_rid != null) { + array_push($linkarr, "".get_string('previous').''); + } + $ruser = ''; + for ($i = 0; $i < $curr_pos; $i++) { + if ($this->respondenttype != 'anonymous') { + if ($user = $DB->get_record('user', array('id' => $ridsusername[$i]))) { + $ruser = fullname($user); + } + } else { + $ruser = $stranonymous; + } + $title = userdate($ridssub[$i]).' | ' .$ruser; + array_push($linkarr, ''.$display_pos.''); + $display_pos++; + } + array_push($linkarr, ''.$display_pos.''); + for (++$i; $i < $total; $i++) { + if ($this->respondenttype != 'anonymous') { + if ($user = $DB->get_record('user', array('id' => $ridsusername[$i]))) { + $ruser = fullname($user); + } + } else { + $ruser = $stranonymous; + } + $title = userdate($ridssub[$i]).' | ' .$ruser; + $display_pos++; + array_push($linkarr, ''.$display_pos.''); + + } + if ($next_rid != null) { + array_push($linkarr, "".get_string('next').''); + } + echo implode(' | ', $linkarr); + } + + function survey_results_navbar_student($curr_rid, $userid, $instance, $resps, $reporttype='myreport', $sid='') { + global $DB; + + $stranonymous = get_string('anonymous', 'questionnaire'); + + $total = count($resps); + $rids = array(); + $ridssub = array(); + $ridsusers = array(); + $i = 0; + $curr_pos = -1; + $title = ''; + foreach ($resps as $response) { + array_push($rids, $response->id); + array_push($ridssub, $response->submitted); + $ruser = ''; + if ($reporttype == 'report') { + if ($this->respondenttype != 'anonymous') { + if ($user = $DB->get_record('user', array('id' => $response->username))) { + $ruser = ' | ' .fullname($user); + } + } else { + $ruser = ' | ' . $stranonymous; + } + } + array_push($ridsusers, $ruser); + if ($response->id == $curr_rid) { + $curr_pos = $i; + } + $i++; + } + $prev_rid = ($curr_pos > 0) ? $rids[$curr_pos - 1] : null; + $next_rid = ($curr_pos < $total - 1) ? $rids[$curr_pos + 1] : null; + $rows_per_page = 1; + $pages = ceil($total / $rows_per_page); + + if ($reporttype == 'myreport') { + $url = 'myreport.php?instance='.$instance.'&user='.$userid.'&action=vresp'; + } else { + $url = 'report.php?instance='.$instance.'&user='.$userid.'&action=vresp&byresponse=1&sid='.$sid; + } + $linkarr = array(); + $display_pos = 1; + if ($prev_rid != null) { + $title = userdate($ridssub[$curr_pos - 1].$ridsusers[$curr_pos - 1]); + array_push($linkarr, ''.get_string('previous').''); + } + for ($i = 0; $i < $curr_pos; $i++) { + $title = userdate($ridssub[$i]).$ridsusers[$i]; + array_push($linkarr, ''.$display_pos.''); + $display_pos++; + } + array_push($linkarr, ''.$display_pos.''); + for (++$i; $i < $total; $i++) { + $display_pos++; + $title = userdate($ridssub[$i]).$ridsusers[$i]; + array_push($linkarr, ''.$display_pos.''); + } + if ($next_rid != null) { +// $title = userdate($ridssub[$curr_pos]); + $title = userdate($ridssub[$curr_pos + 1]).$ridsusers[$curr_pos + 1]; + array_push($linkarr, ''.get_string('next').''); + } + echo implode(' | ', $linkarr); + } + + /* {{{ proto string survey_results(int survey_id, int precision, bool show_totals, int question_id, array choice_ids, int response_id) + Builds HTML for the results for the survey. If a + question id and choice id(s) are given, then the results + are only calculated for respodants who chose from the + choice ids for the given question id. + Returns empty string on sucess, else returns an error + string. */ + function survey_results($precision = 1, $showTotals = 1, $qid = '', $cids = '', $rid = '', $guicross='', $uid=false, $groupid='', $sort='') { + global $SESSION, $DB; + + $SESSION->questionnaire->noresponses = false; + if(empty($precision)) { + $precision = 1; + } + if($showTotals === '') { + $showTotals = 1; + } + + if(is_int($cids)) { + $cids = array($cids); + } + if(is_string($cids)) { + $cids = preg_split("/ /",$cids); // turn space seperated list into array + } + + // set up things differently for cross analysis + $cross = !empty($qid); + if($cross) { + if(is_array($cids) && count($cids)>0) { + $cidstr = $this->array_to_insql($cids); + } else { + $cidstr = ''; + } + } + + // build associative array holding whether each question + // type has answer choices or not and the table the answers are in + /// TO DO - FIX BELOW TO USE STANDARD FUNCTIONS + $has_choices = array(); + $response_table = array(); + if (!($types = $DB->get_records('questionnaire_question_type', array(), 'typeid', 'typeid,has_choices,response_table'))) { + $errmsg = sprintf('%s [ %s: question_type ]', + get_string('errortable', 'questionnaire'), 'Table'); + return($errmsg); + } + foreach ($types as $type) { + $has_choices[$type->typeid]=$type->has_choices; + $response_table[$type->typeid]=$type->response_table; + } + + // load survey title (and other globals) + if (empty($this->survey)) { + $errmsg = get_string('erroropening', 'questionnaire') ." [ ID:${sid} R:"; + return($errmsg); + } + + if (empty($this->questions)) { + $errmsg = get_string('erroropening', 'questionnaire') .' '. 'No questions found.' ." [ ID:${sid} ]"; + return($errmsg); + } + + // find out more about the question we are cross analyzing on (if any) + if($cross) { + $crossTable = $response_table[$DB->get_field('questionnaire_question', 'type_id', array('id' => $qid))]; + if(!in_array($crossTable, array('resp_single','response_bool','resp_multiple'))) { + $errmsg = get_string('errorcross', 'questionnaire') .' [ '. 'Table' .": ${crossTable} ]"; + return($errmsg); + } + } + + // find total number of survey responses + // and relevant response ID's + if (!empty($rid)) { + $rids = $rid; + if (is_array($rids)) { + $navbar = false; + } else { + $navbar = true; + } + $total = 1; + } else { + $navbar = false; + $sql = ""; + $castsql = $DB->sql_cast_char2int('R.username'); + if($cross) { + if(!empty($cidstr)) + $sql = "SELECT A.response_id, R.id + FROM {questionnaire_".$crossTable."} A, + {questionnaire_response} R + WHERE A.response_id=R.id AND + R.complete='y' AND + A.question_id='${qid}' AND + A.choice_id ${cidstr} + ORDER BY A.response_id"; + else + $sql = "SELECT A.response_id, R.id + FROM {questionnaire_".$crossTable."} A, + {questionnaire_response} R + WHERE A.response_id=R.id AND + R.complete='y' AND + A.question_id='${qid}' AND + A.choice_id = 0 + ORDER BY A.response_id"; + } else if ($uid !== false) { // one participant only + $sql = "SELECT r.id, r.survey_id + FROM {questionnaire_response} r + WHERE r.survey_id='{$this->survey->id}' AND + r.username = $uid AND + r.complete='y' + ORDER BY r.id"; + } else if ($groupid == -1) { // all participants + $sql = "SELECT R.id, R.survey_id + FROM {questionnaire_response} R + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' + ORDER BY R.id"; + } else if ($groupid == -2) { // all members of any group + $sql = "SELECT R.id, R.survey_id + FROM {questionnaire_response} R, + {groups_members} GM + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' AND + GM.groupid>0 AND + ".$castsql."=GM.userid + ORDER BY R.id"; + } else if ($groupid == -3) { // not members of any group + $sql = "SELECT R.id, R.survey_id, U.id AS userid + FROM {questionnaire_response} R, + {user} U + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' AND + ".$castsql."=U.id + ORDER BY userid"; + } else { // members of a specific group + $sql = "SELECT R.id, R.survey_id + FROM {questionnaire_response} R, + {groups_members} GM + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' AND + GM.groupid=".$groupid." AND + ".$castsql."=GM.userid + ORDER BY R.id"; + } + if (!($rows = $DB->get_records_sql($sql))) { + echo (get_string('noresponses','questionnaire')); + $SESSION->questionnaire->noresponses = true; + return; + } + if ($groupid == -3) { // members of no group + foreach ($rows as $row=>$key) { + if (groups_has_membership($this->cm, $key->userid)) { + unset($rows[$row]); + } + } + } + $total = count($rows); + echo (' '.get_string('responses','questionnaire').": $total"); + if(empty($rows)) { + $errmsg = get_string('erroropening', 'questionnaire') .' '. get_string('noresponsedata', 'questionnaire'); + return($errmsg); + } + + $rids = array(); + foreach ($rows as $row) { + array_push($rids, $row->id); + } + } + + if ($navbar) { + // show response navigation bar + $this->survey_results_navbar($rid); + } + + ?> +

survey->title); ?>

+ survey->subtitle) { + echo('

'.$this->survey->subtitle.'

'); + } + ?> + survey->info) { + $infotext = file_rewrite_pluginfile_urls($this->survey->info, 'pluginfile.php', + $this->context->id, 'mod_questionnaire', 'info', $this->survey->id); + echo '
'.format_text($infotext, FORMAT_HTML).'
'; + } + ?> + " ._('Cross analysis on QID:') ." ${qid}\n"); + } + ?> + + questions as $question) { + // process each question + + if ($question->type_id == 99) { + continue; + } + if ($question->type_id == 100) { + echo ("\n"); + continue; + } + echo ("\n"); + echo (" + + +
". format_text(file_rewrite_pluginfile_urls($question->content, 'pluginfile.php', $question->context->id, 'mod_questionnaire', + 'question', $question->id), FORMAT_HTML)."
"); + if ($question->type_id < 50) { + if (!empty($guicross)){ + echo ('
'); + echo (''); + echo (''); + echo ('
'); + echo ("\n\n"); + echo ("\n"); + echo (" \n"); + echo (" \n"); + echo (""); + echo ("\n"); + echo ("\n"); + echo ("\n"); + echo ("\n"); + echo ("\n"); + echo ("
\n"); + if ($question->type_id ==1 || $question->type_id ==4 || $question->type_id ==5 || $question->type_id ==6){ + echo ("
\n"); + echo (" id."\" />\n"); + echo ("
\n"); + } + echo ("
\n"); + } //end if empty($guicross) + echo ++$i; + echo (""); + echo ("
"); + + if (!empty($guicross)){ + echo ("
\n"); + if ($question->type_id ==1 || $question->type_id ==4 || $question->type_id ==5 || $question->type_id ==6){ + echo ("
\n"); + echo ("id."\" />\n"); + echo ("
\n"); + } + echo ("
\n"); + if ($question->type_id ==1 || $question->type_id ==4 || $question->type_id ==5 || $question->type_id ==6){ + echo ("
\n"); + echo ("id."\" />\n"); + echo ("
\n"); + } + echo ("
\n"); + } //end if empty($guicross) + } //end if ($question->type_id < 50) + + $counts = array(); + + // --------------------------------------------------------------------------- + echo format_text(file_rewrite_pluginfile_urls($question->content, 'pluginfile.php', $question->context->id, 'mod_questionnaire', + 'question', $question->id), FORMAT_HTML).''; // moved from $question->display_results + $question->display_results($rids, $guicross, $sort); + + ?> +
+ questionnaire->currentgroupid)) { + $groupid = $SESSION->questionnaire->currentgroupid; + } else{ + $groupid = -1; + } + $output = array(); + $nbinfocols = 9; // change this if you want more info columns + $stringother = get_string('other', 'questionnaire'); + $columns = array( + get_string('response','questionnaire'), + get_string('submitted','questionnaire'), + get_string('institution'), + get_string('department'), + get_string('course'), + get_string('group'), + get_string('id','questionnaire'), + get_string('fullname'), + get_string('username') + ); + + $types = array( + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + ); + + $arr = array(); + // 0 = number; 1 = text + $id_to_csv_map = array( + '0', // 0: unused + '0', // 1: bool -> boolean + '1', // 2: text -> string + '1', // 3: essay -> string + '0', // 4: radio -> string + '0', // 5: check -> string + '0', // 6: dropdn -> string + '0', // 7: rating -> number + '0', // 8: rate -> number + '1', // 9: date -> string + '0' // 10: numeric -> number + ); + + if (!$survey = $DB->get_record('questionnaire_survey', array('id' => $this->survey->id))) { + print_error ('surveynotexists', 'questionnaire'); + } + + $select = 'survey_id = '.$this->survey->id.' AND deleted = \'n\' AND type_id < 50'; + $fields = 'id,name,type_id,position'; + if (!($records = $DB->get_records_select('questionnaire_question', $select, null, 'position', $fields))) { + $records = array(); + } + + $num = 1; + foreach ($records as $record) { + // establish the table's field names + $qid = $record->id; + $qpos = $record->position; + if ($record->name == '') { + } + $col = $record->name; + $type = $record->type_id; + if ($type == 4 || $type == 5 || $type == 8) { + /* single or multiple or rate */ + $sql = "SELECT c.id as cid, q.id as qid, q.precise AS precise, q.name, c.content + FROM {questionnaire_question} q ". + "LEFT JOIN {questionnaire_quest_choice} c ON question_id = q.id ". + 'WHERE q.id = '.$qid.' ORDER BY cid ASC'; + if (!($records2 = $DB->get_records_sql($sql))) { + $records2 = array(); + } + $subqnum = 0; + switch ($type) { + + case 4: // single + $columns[][$qpos] = $col; + array_push($types, $id_to_csv_map[$type]); + $thisnum = 1; + foreach ($records2 as $record2) { + $content = $record2->content; + if (preg_match('/^!other/', $content)) { + $col = $record2->name.'_'.$stringother; + $columns[][$qpos] = $col; + array_push($types, '0'); + } + } + break; + + case 5: // multiple + $thisnum = 1; + foreach ($records2 as $record2) { + $content = $record2->content; + $modality = ''; + if (preg_match('/^!other/', $content)) { + $content = $stringother; + $col = $record2->name.'->['.$content.']'; + $columns[][$qpos] = $col; + array_push($types, '0'); + } + $contents = questionnaire_choice_values($content); + if ($contents->modname) { + $modality = $contents->modname; + } elseif ($contents->title) { + $modality = $contents->title; + } else { + $modality = strip_tags($contents->text); + } + $col = $record2->name.'->'.$modality; + $columns[][$qpos] = $col; + array_push($types, '0'); + } + break; + + case 8: // rate + foreach ($records2 as $record2) { + $nameddegrees = 0; + $modality = ''; + $content = $record2->content; + $osgood = false; + if ($record2->precise == 3) { + $osgood = true; + } + if (preg_match("/^[0-9]{1,3}=/", $content,$ndd)) { + $nameddegrees++; + } else { + if ($osgood) { + list($contentleft, $contentright) = preg_split('/[|]/', $content); + $contents = questionnaire_choice_values($contentleft); + if ($contents->title) { + $contentleft = $contents->title; + } + $contents = questionnaire_choice_values($contentright); + if ($contents->title) { + $contentright = $contents->title; + } + $modality = strip_tags($contentleft.'|'.$contentright); + $modality = preg_replace("/[\r\n\t]/", ' ', $modality); + } else { + $contents = questionnaire_choice_values($content); + if ($contents->modname) { + $modality = $contents->modname; + } elseif ($contents->title) { + $modality = $contents->title; + } else { + $modality = strip_tags($contents->text); + $modality = preg_replace("/[\r\n\t]/", ' ', $modality); + } + } + $col = $record2->name.'->'.$modality; + $columns[][$qpos] = $col; + array_push($types, $id_to_csv_map[$type]); + } + } + break; + } + } else { + $columns[][$qpos] = $col; + array_push($types, $id_to_csv_map[$type]); + } + $num++; + } + array_push($output, $columns); + $numcols = count($output[0]); + + if ($rid) { // send e-mail for a unique response ($rid) + $select = 'survey_id = '.$this->survey->id.' AND complete=\'y\' AND id = '.$rid; + $fields = 'id,submitted,username'; + if (!($records = $DB->get_records_select('questionnaire_response', $select, null, 'submitted', $fields))) { + $records = array(); + } + } else if ($userid) { // download CSV for one user's own responses' + $sql = "SELECT R.id, R.survey_id, R.submitted, R.username + FROM {questionnaire_response} R + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' AND + R.username='$userid' + ORDER BY R.id"; + if (!($records = $DB->get_records_sql($sql))) { + $records = array(); + } + + } else { // download CSV for all participants (or groups if enabled) + $castsql = $DB->sql_cast_char2int('R.username'); + if ($groupid == -1) { // all participants + $sql = "SELECT R.id, R.survey_id, R.submitted, R.username + FROM {questionnaire_response} R + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' + ORDER BY R.id"; + } else if ($groupid == -2) { // all members of any group + $sql = "SELECT R.id, R.survey_id, R.submitted, R.username + FROM {questionnaire_response} R, + {groups_members} GM + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' AND + GM.groupid>0 AND + ".$castsql."=GM.userid + ORDER BY R.id"; + } else if ($groupid == -3) { // not members of any group + $sql = "SELECT R.id, R.survey_id, R.submitted, U.id AS username + FROM {questionnaire_response} R, + {user} U + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' AND + ".$castsql."=U.id + ORDER BY username"; + } else { // members of a specific group + $sql = "SELECT R.id, R.survey_id, R.submitted, R.username + FROM {questionnaire_response} R, + {groups_members} GM + WHERE R.survey_id='{$this->survey->id}' AND + R.complete='y' AND + GM.groupid=".$groupid." AND + ".$castsql."=GM.userid + ORDER BY R.id"; + } + if (!($records = $DB->get_records_sql($sql))) { + $records = array(); + } + if ($groupid == -3) { // members of no group + foreach ($records as $row=>$key) { + $userid = $key->username; + if (groups_has_membership($this->cm, $userid)) { + unset($records[$row]); + } + } + } + } + $isanonymous = $this->respondenttype == 'anonymous'; + $format_options = new Object(); + $format_options->filter = false; // To prevent any filtering in CSV output... + foreach ($records as $record) { + // get the response + $response = $this->response_select_name($record->id, $choicecodes, $choicetext); + $qid = $record->id; + //JR for better compabitility & readability with Excel + $submitted = date(get_string('strfdateformatcsv', 'questionnaire'), $record->submitted); + $institution = ''; + $department = ''; + $username = $record->username; + if ($user = $DB->get_record('user', array('id' => $username))) { + $institution = $user->institution; + $department = $user->department; + } + + /// Moodle: + // Get the course name that this questionnaire belongs to. + if ($survey->realm != 'public') { + $courseid = $this->course->id; + $coursename = $this->course->fullname; + } else { + /// For a public questionnaire, look for the course that used it. + $sql = 'SELECT q.id, q.course, c.fullname '. + 'FROM {questionnaire} q, {questionnaire_attempts} qa, {course} c '. + 'WHERE qa.rid = ? AND q.id = qa.qid AND c.id = q.course'; + if ($record = $DB->get_record_sql($sql, array($qid))) { + $courseid = $record->course; + $coursename = $record->fullname; + } else { + $courseid = $this->course->id; + $coursename = $this->course->fullname; + } + } + /// Moodle: + // If the username is numeric, try it as a Moodle user id. + if (is_numeric($username)) { + if ($user = $DB->get_record('user', array('id' => $username))) { + $uid = $username; + $fullname = fullname($user); + $username = $user->username; + } + } + + /// Moodle: + // Determine if the user is a member of a group in this course or not. + $groupname = ''; + if ($this->cm->groupmode > 0) { + if ($groupid > 0) { + $groupname = groups_get_group_name($groupid); + } else { + if ($uid) { + if ($groups = groups_get_all_groups($courseid, $uid)) { + foreach ($groups as $group) { + $groupname.= $group->name.', '; + } + $groupname = substr($groupname, 0, strlen($groupname) -2); + } else { + $groupname = ' ('.get_string('groupnonmembers').')'; + } + } + } + } + if ($isanonymous) { + $fullname = get_string('anonymous', 'questionnaire'); + $username = ''; + $uid = ''; + } + $arr = array(); + array_push($arr, $qid); + array_push($arr, $submitted); + array_push($arr, $institution); + array_push($arr, $department); + array_push($arr, $coursename); + array_push($arr, $groupname); + array_push($arr, $uid); + array_push($arr, $fullname); + array_push($arr, $username); + + // merge it + for($i = $nbinfocols; $i < $numcols; $i++) { + /*if (isset($response[$columns[$i]]) && is_array($response[$columns[$i]])) { + $response[$columns[$i]] = join(',', $response[$columns[$i]]); + }*/ + + $qpos = key($columns[$i]); + $qname = current($columns[$i]); + if (isset($response[$qpos][$qname]) && $response[$qpos][$qname] != '') { + $thisresponse = $response[$qpos][$qname]; + } else { + $thisresponse = ''; + } + + switch ($types[$i]) { + case 1: //string + // Excel seems to allow "\n" inside a quoted string, but + // "\r\n" is used as a record separator and so "\r" may + // not occur within a cell. So if one would like to preserve + // new-lines in a response, remove the "\n" from the + // regex below. + + // email format text is plain text for being displayed in Excel, etc. added by JR + // but it must be stripped of carriage returns + if ($thisresponse) { + $thisresponse = format_text($thisresponse, FORMAT_HTML, $format_options); + $thisresponse = preg_replace("/[\r\n\t]/", ' ', $thisresponse); + $thisresponse = preg_replace('/"/', '""', $thisresponse); + } + // fall through + case 0: //number + //array_push($arr,$thisresponse); + break; + } + array_push($arr,$thisresponse); + } + array_push($output, $arr); + } + + // change table headers to incorporate actual question numbers + $numcol = 0; + $numquestion = 0; + $out = ''; + $nbrespcols = count($output[0]); + $oldkey = 0; + + for ($i = $nbinfocols;$i < $nbrespcols; $i++) { + $sep = ''; + $thisoutput = current($output[0][$i]); + $thiskey = key($output[0][$i]); + // case of unnamed rate single possible answer (full stop char is used for support) + if (strstr($thisoutput,'->.')) { + $thisoutput = str_replace('->.','',$thisoutput); + } + + // if variable is not named no separator needed between Question number and potential sub-variables + if ($thisoutput == '' || strstr($thisoutput,'->.') || substr($thisoutput,0,2) == '->' || substr($thisoutput,0,1) == '_') { + $sep = ''; + } else { + $sep = '_'; + } + if ($thiskey > $oldkey) { + $oldkey = $thiskey; + $numquestion++; + } + // abbreviated modality name in multiple or rate questions (COLORS->blue=the color of the sky...) + $pos = strpos($thisoutput, '='); + if($pos) { + $thisoutput = substr($thisoutput, 0, $pos); + } + $other = $sep.$stringother; + $out = 'Q'.sprintf("%02d", $numquestion).$sep.$thisoutput; + $output[0][$i] = $out; + } + return $output; + } + + /* {{{ proto bool survey_export_csv(int survey_id, string filename) + Exports the results of a survey to a CSV file. + Returns true on success. + */ + function export_csv($filename) { + $umask = umask(0077); + $fh = fopen($filename, 'w'); + umask($umask); + if(!$fh) + return 0; + + $data = survey_generate_csv($rid='', $userid='', $groupid=''); + + foreach ($data as $row) { + fputs($fh, join(',', $row) . "\n"); + } + + fflush($fh); + fclose($fh); + + return 1; + } + + /** + * Function to move a question to a new position. + * + * @param int $moveqid The id of the question to be moved. + * @param int $movetopos The position to move before, or zero if the end. + * + */ + function move_question($moveqid, $movetopos) { + global $DB; + + /// If its moving to the last position (moveto = 0), or its moving to a higher position + /// No point in moving it to where it already is... + if (($movetopos == 0) || (($movetopos-1) > $this->questions[$moveqid]->position)) { + $found = false; + foreach ($this->questions as $qid => $question) { + if ($moveqid == $qid) { + $found = true; + continue; + } + if ($found) { + $DB->set_field('questionnaire_question', 'position', $question->position-1, array('id' => $qid)); + } + if ($question->position == ($movetopos-1)) { + break; + } + } + if ($movetopos == 0) { + $movetopos = count($this->questions); + } else { + $movetopos--; + } + $DB->set_field('questionnaire_question', 'position', $movetopos, array('id' => $moveqid)); + + } else if ($movetopos < $this->questions[$moveqid]->position) { + $found = false; + foreach ($this->questions as $qid => $question) { + if ($movetopos == $question->position) { + $found = true; + } + if (!$found) { + continue; + } else { + $DB->set_field('questionnaire_question', 'position', $question->position+1, array('id' => $qid)); + } + if ($question->position == ($this->questions[$moveqid]->position-1)) { + break; + } + } + $DB->set_field('questionnaire_question', 'position', $movetopos, array('id' => $moveqid)); + } + } +} \ No newline at end of file diff --git a/mod/questionnaire/questions.php b/mod/questionnaire/questions.php index e5abcf4..c59c840 100644 --- a/mod/questionnaire/questions.php +++ b/mod/questionnaire/questions.php @@ -17,8 +17,8 @@ /// This page prints a particular instance of questionnaire require_once("../../config.php"); - require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); require_once($CFG->dirroot.'/mod/questionnaire/questions_form.php'); + require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php'); $id = required_param('id', PARAM_INT); // course module ID $action = optional_param('action', 'main', PARAM_ALPHA); // screen diff --git a/mod/questionnaire/questions_form.php b/mod/questionnaire/questions_form.php index a5b5f3e..a5d2893 100644 --- a/mod/questionnaire/questions_form.php +++ b/mod/questionnaire/questions_form.php @@ -23,7 +23,7 @@ */ require_once ($CFG->dirroot.'/course/moodleform_mod.php'); -require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); +require_once($CFG->dirroot.'/mod/questionnaire/questiontypes/questiontypes.class.php'); class questionnaire_questions_form extends moodleform { diff --git a/mod/questionnaire/report.php b/mod/questionnaire/report.php index f65c63e..6ef208f 100644 --- a/mod/questionnaire/report.php +++ b/mod/questionnaire/report.php @@ -18,7 +18,7 @@ /// This page prints a particular instance of questionnaire global $SESSION, $CFG; require_once("../../config.php"); - require_once($CFG->dirroot.'/mod/questionnaire/lib.php'); + require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php'); $instance = optional_param('instance', false, PARAM_INT); // questionnaire ID $action = optional_param('action', 'vall', PARAM_ALPHA); diff --git a/mod/questionnaire/view.php b/mod/questionnaire/view.php index 5eae82e..ca76431 100644 --- a/mod/questionnaire/view.php +++ b/mod/questionnaire/view.php @@ -17,8 +17,8 @@ /// This page prints a particular instance of questionnaire require_once("../../config.php"); - require_once("lib.php"); require_once($CFG->libdir . '/completionlib.php'); + require_once($CFG->dirroot.'/mod/questionnaire/questionnaire.class.php'); if (!isset($SESSION->questionnaire)) { $SESSION->questionnaire = new stdClass();