Example #1
0
 /**
  * Handle's the page request
  *
  */
 public function handle_request()
 {
     global $DB, $USER, $PAGE;
     // first check if there are questions or not.  If there are no questions display that message instead,
     // regardless of action.
     if (count($this->RTQ->get_questionmanager()->get_questions()) === 0) {
         $this->pagevars['action'] = 'noquestions';
         $this->pageurl->param('action', '');
         // remove the action
     }
     switch ($this->pagevars['action']) {
         case 'noquestions':
             $this->RTQ->get_renderer()->view_header();
             $this->RTQ->get_renderer()->no_questions($this->RTQ->is_instructor());
             $this->RTQ->get_renderer()->view_footer();
             break;
         case 'quizstart':
             // case for the quiz start landing page
             // set the quiz view page to the base layout for 1 column layout
             $PAGE->set_pagelayout('base');
             if ($this->session->get_session() === false) {
                 // redirect them to the default page with a quick message first
                 $redirurl = clone $this->pageurl;
                 $redirurl->remove_params('action');
                 redirect($redirurl, get_string('nosession', 'activequiz'), 5);
             } else {
                 // this is here to help prevent race conditions for multiple group members trying to take the
                 // quiz at the same time
                 $cantakequiz = false;
                 if ($this->RTQ->group_mode()) {
                     if (!$this->RTQ->is_instructor() && $this->pagevars['group'] == 0) {
                         print_error('invalidgroupid', 'mod_activequiz');
                     }
                     // check if the user can take the quiz for the group
                     if ($this->session->can_take_quiz_for_group($this->pagevars['group'])) {
                         $cantakequiz = true;
                     }
                 } else {
                     // if no group mode, user will always be able to take quiz
                     $cantakequiz = true;
                 }
                 if ($cantakequiz) {
                     if (!$this->session->init_attempts($this->RTQ->is_instructor(), $this->pagevars['group'], $this->pagevars['groupmembers'])) {
                         print_error('cantinitattempts', 'activequiz');
                     }
                     // set the session as running
                     if ($this->RTQ->is_instructor() && $this->session->get_session()->status == 'notrunning') {
                         $this->session->set_status('running');
                     }
                     // get the current attempt an initialize the head contributions
                     $attempt = $this->session->get_open_attempt();
                     $attempt->get_html_head_contributions();
                     $attempt->setStatus('inprogress');
                     // now show the quiz start landing page
                     $this->RTQ->get_renderer()->view_header(true);
                     $this->RTQ->get_renderer()->render_quiz($attempt, $this->session);
                     $this->RTQ->get_renderer()->view_footer();
                 } else {
                     $this->RTQ->get_renderer()->view_header();
                     $this->RTQ->get_renderer()->group_session_started();
                     $this->RTQ->get_renderer()->view_footer();
                 }
             }
             break;
         case 'selectgroupmembers':
             if (empty($this->pagevars['group'])) {
                 $viewhome = clone $this->pageurl;
                 $viewhome->remove_params('action');
                 redirect($viewhome, get_string('invalid_group_selected', 'activequiz'), 5);
             } else {
                 $this->pageurl->param('group', $this->pagevars['group']);
                 $groupselectform = new \mod_activequiz\forms\view\groupselectmembers($this->pageurl, array('rtq' => $this->RTQ, 'selectedgroup' => $this->pagevars['group']));
                 if ($data = $groupselectform->get_data()) {
                     // basically we want to get all gm* fields
                     $gmemnum = 1;
                     $groupmembers = array();
                     $data = get_object_vars($data);
                     while (isset($data['gm' . $gmemnum])) {
                         if ($data['gm' . $gmemnum] != 0) {
                             $groupmembers[] = $data['gm' . $gmemnum];
                         }
                         $gmemnum++;
                     }
                     $this->pageurl->param('groupmembers', implode(',', $groupmembers));
                     $this->pageurl->param('action', 'quizstart');
                     // redirect to the quiz start page
                     redirect($this->pageurl, null, 0);
                 } else {
                     $this->RTQ->get_renderer()->view_header();
                     $this->RTQ->get_renderer()->group_member_select($groupselectform);
                     $this->RTQ->get_renderer()->view_footer();
                 }
             }
             break;
         default:
             // default is to show view to start quiz (for instructors/quiz controllers) or join quiz (for everyone else)
             // trigger event for course module viewed
             $event = \mod_activequiz\event\course_module_viewed::create(array('objectid' => $PAGE->cm->instance, 'context' => $PAGE->context));
             $event->add_record_snapshot('course', $this->RTQ->getCourse());
             $event->add_record_snapshot($PAGE->cm->modname, $this->RTQ->getRTQ());
             $event->trigger();
             // determine home display based on role
             if ($this->RTQ->is_instructor()) {
                 $startsessionform = new \mod_activequiz\forms\view\start_session($this->pageurl);
                 if ($data = $startsessionform->get_data()) {
                     // create a new quiz session
                     // first check to see if there are any open sessions
                     // this shouldn't occur, but never hurts to check
                     $sessions = $DB->get_records('activequiz_sessions', array('activequizid' => $this->RTQ->getRTQ()->id, 'sessionopen' => 1));
                     if (!empty($sessions)) {
                         // error out with that there are existing sessions
                         $this->RTQ->get_renderer()->setMessage(get_string('alreadyexisting_sessions', 'activequiz'), 'error');
                         $this->RTQ->get_renderer()->view_header();
                         $this->RTQ->get_renderer()->view_inst_home($startsessionform, $this->session->get_session());
                         $this->RTQ->get_renderer()->view_footer();
                         break;
                     } else {
                         if (!$this->session->create_session($data->sessionname)) {
                             // error handling
                             $this->RTQ->get_renderer()->setMessage(get_string('unabletocreate_session', 'activequiz'), 'error');
                             $this->RTQ->get_renderer()->view_header();
                             $this->RTQ->get_renderer()->view_inst_home($startsessionform, $this->session->get_session());
                             $this->RTQ->get_renderer()->view_footer();
                             break;
                             // break out of the switch
                         }
                     }
                     // redirect to the quiz start
                     $quizstarturl = clone $this->pageurl;
                     $quizstarturl->param('action', 'quizstart');
                     redirect($quizstarturl, null, 0);
                 } else {
                     $this->RTQ->get_renderer()->view_header();
                     $this->RTQ->get_renderer()->view_inst_home($startsessionform, $this->session->get_session());
                     $this->RTQ->get_renderer()->view_footer();
                 }
             } else {
                 // check to see if the group already started a quiz
                 $validgroups = array();
                 if ($this->RTQ->group_mode()) {
                     // if there is already an attempt for this session for this group for this user don't allow them to start another
                     $validgroups = $this->session->check_attempt_for_group();
                     if (empty($validgroups) && $validgroups !== false) {
                         $this->RTQ->get_renderer()->view_header();
                         $this->RTQ->get_renderer()->group_session_started();
                         $this->RTQ->get_renderer()->view_footer();
                         break;
                     } else {
                         if ($validgroups === false) {
                             $validgroups = array();
                         }
                     }
                 }
                 $studentstartformparams = array('rtq' => $this->RTQ, 'validgroups' => $validgroups);
                 $studentstartform = new \mod_activequiz\forms\view\student_start_form($this->pageurl, $studentstartformparams);
                 if ($data = $studentstartform->get_data()) {
                     $quizstarturl = clone $this->pageurl;
                     $quizstarturl->param('action', 'quizstart');
                     // if data redirect to the quiz start url with the group selected if we're in group mode
                     if ($this->RTQ->group_mode()) {
                         $groupid = $data->group;
                         $quizstarturl->param('group', $groupid);
                         // check if the group attendance feature is enabled
                         // if so redirect to the group member select form
                         // don't send to group attendance form if an attempt is already started
                         if ($this->RTQ->getRTQ()->groupattendance == 1 && !$this->session->get_open_attempt_for_current_user()) {
                             $quizstarturl->param('action', 'selectgroupmembers');
                         }
                         redirect($quizstarturl, null, 0);
                     } else {
                         redirect($quizstarturl, null, 0);
                     }
                 } else {
                     // display student home.  (form will display only if there is an active session
                     $this->RTQ->get_renderer()->view_header();
                     $this->RTQ->get_renderer()->view_student_home($studentstartform, $this->session);
                     $this->RTQ->get_renderer()->view_footer();
                 }
             }
             break;
     }
 }
    /**
     * Initializes quiz javascript and strings for javascript when on the
     * quiz view page, or the "quizstart" action
     *
     * @param \mod_activequiz\activequiz_attempt $attempt
     * @param \mod_activequiz\activequiz_session $session
     * @throws moodle_exception throws exception when invalid question on the attempt is found
     */
    public function init_quiz_js($attempt, $session)
    {
        global $USER, $CFG;
        // include classList javascript to add the class List HTML5 for compatibility
        // below IE 10
        $this->page->requires->js('/mod/activequiz/js/classList.js');
        $this->page->requires->js('/mod/activequiz/js/core.js');
        // add window.onload script manually to handle removing the loading mask
        echo html_writer::start_tag('script', array('type' => 'text/javascript'));
        echo <<<EOD
            (function preLoad(){
                window.addEventListener('load', function(){activequiz.quiz_page_loaded();}, false);
            }());
EOD;
        echo html_writer::end_tag('script');
        if ($this->rtq->is_instructor()) {
            $this->page->requires->js('/mod/activequiz/js/instructor.js');
        } else {
            $this->page->requires->js('/mod/activequiz/js/student.js');
        }
        // next set up a class to pass to js for js info
        $jsinfo = new stdClass();
        $jsinfo->sesskey = sesskey();
        $jsinfo->siteroot = $CFG->wwwroot;
        $jsinfo->rtqid = $this->rtq->getRTQ()->id;
        $jsinfo->sessionid = $session->get_session()->id;
        $jsinfo->attemptid = $attempt->id;
        $jsinfo->slots = $attempt->getSlots();
        $jsinfo->isinstructor = $this->rtq->is_instructor() ? 'true' : 'false';
        // manually create the questions stdClass as we can't support JsonSerializable yet
        $questions = array();
        foreach ($attempt->get_questions() as $q) {
            /** @var \mod_activequiz\activequiz_question $q */
            $question = new stdClass();
            $question->id = $q->getId();
            $question->questiontime = $q->getQuestionTime();
            $question->tries = $q->getTries();
            $question->question = $q->getQuestion();
            $question->slot = $attempt->get_question_slot($q);
            // if the slot is false, throw exception for invalid question on quiz attempt
            if ($question->slot === false) {
                $a = new stdClass();
                $a->questionname = $q->getQuestion()->name;
                throw new moodle_exception('invalidquestionattempt', 'mod_activequiz', '', $a, 'invalid slot when building questions array on quiz renderer');
            }
            $questions[$question->slot] = $question;
        }
        $jsinfo->questions = $questions;
        // resuming quiz feature
        // this will check if the session has started already and print out
        $jsinfo->resumequiz = 'false';
        if ($session->get_session()->status != 'notrunning') {
            $sessionstatus = $session->get_session()->status;
            $currentquestion = $session->get_session()->currentquestion;
            $nextstarttime = $session->get_session()->nextstarttime;
            if ($sessionstatus == 'running' && !empty($currentquestion)) {
                // we're in a currently running question
                $jsinfo->resumequiz = 'true';
                $jsinfo->resumequizstatus = $sessionstatus;
                $jsinfo->resumequizcurrentquestion = $currentquestion;
                $nextQuestion = $this->rtq->get_questionmanager()->get_question_with_slot($session->get_session()->currentqnum, $attempt);
                if ($nextstarttime > time()) {
                    // we're wating for question
                    $jsinfo->resumequizaction = 'waitforquestion';
                    $jsinfo->resumequizdelay = $session->get_session()->nextstarttime - time();
                    $jsinfo->resumequizquestiontime = $nextQuestion->getQuestionTime();
                } else {
                    $jsinfo->resumequizaction = 'startquestion';
                    // how much time has elapsed since start time
                    // first check if the question has a time limit
                    if ($nextQuestion->getNoTime()) {
                        $jsinfo->resumequizquestiontime = 0;
                    } else {
                        // otherwise figure out how much time is left
                        $timeelapsed = time() - $nextstarttime;
                        $timeLeft = $nextQuestion->getQuestionTime() - $timeelapsed;
                        $jsinfo->resumequizquestiontime = $timeLeft;
                    }
                }
            } else {
                if ($sessionstatus == 'reviewing' || $sessionstatus == 'endquestion') {
                    // if we're reviewing, resume with quiz info of reviewing and just let
                    // set interval capture next question start time
                    $jsinfo->resumequiz = 'true';
                    $jsinfo->resumequizaction = 'reviewing';
                    $jsinfo->resumequizstatus = $sessionstatus;
                    $jsinfo->resumequizcurrentquestion = $currentquestion;
                    if ($attempt->lastquestion) {
                        $jsinfo->lastquestion = 'true';
                    } else {
                        $jsinfo->lastquestion = 'false';
                    }
                }
            }
        }
        // print jsinfo to javascript
        echo html_writer::start_tag('script', array('type' => 'text/javascript'));
        echo "rtqinitinfo = " . json_encode($jsinfo);
        echo html_writer::end_tag('script');
        // add strings for js
        $this->page->requires->strings_for_js(array('waitforquestion', 'gatheringresults', 'feedbackintro', 'nofeedback', 'closingsession', 'sessionclosed', 'trycount', 'timertext', 'waitforrevewingend', 'show_correct_answer', 'hide_correct_answer', 'hidestudentresponses', 'showstudentresponses', 'hidenotresponded', 'shownotresponded'), 'activequiz');
        $this->page->requires->strings_for_js(array('seconds'), 'moodle');
        // finally allow question modifiers to add their own css/js
        $this->rtq->call_question_modifiers('add_js', null);
    }
 /**
  * Handles the incoming request
  *
  */
 public function handle_request()
 {
     global $USER;
     switch ($this->action) {
         case 'startquiz':
             // only allow instructors to perform this action
             if ($this->RTQ->is_instructor()) {
                 $firstquestion = $this->session->start_quiz();
                 $this->jsonlib->set('status', 'startedquiz');
                 $this->jsonlib->set('questionid', $firstquestion->get_slot());
                 $this->jsonlib->set('nextstarttime', $this->session->get_session()->nextstarttime);
                 $this->jsonlib->set('notime', $firstquestion->getNoTime());
                 if ($firstquestion->getNoTime() == 0) {
                     // this question has a time limit
                     if ($firstquestion->getQuestionTime() == 0) {
                         $questiontime = $this->RTQ->getRTQ()->defaultquestiontime;
                     } else {
                         $questiontime = $firstquestion->getQuestionTime();
                     }
                     $this->jsonlib->set('questiontime', $questiontime);
                 } else {
                     $this->jsonlib->set('questiontime', 0);
                 }
                 $delay = $this->session->get_session()->nextstarttime - time();
                 $this->jsonlib->set('delay', $delay);
                 $qattempt = $this->session->get_open_attempt();
                 $this->jsonlib->set('lastquestion', $qattempt->lastquestion ? 'true' : 'false');
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'savequestion':
             // check if we're working on the current question for the session
             $currentquestion = $this->session->get_session()->currentquestion;
             $jscurrentquestion = required_param('questionid', PARAM_INT);
             if ($currentquestion != $jscurrentquestion) {
                 $this->jsonlib->send_error('invalid question');
             }
             // if we pass attempt to save the question
             $qattempt = $this->session->get_open_attempt();
             // make sure the attempt belongs to the current user
             if ($qattempt->userid != $USER->id) {
                 $this->jsonlib->send_error('invalid user');
             }
             if ($qattempt->save_question()) {
                 $this->jsonlib->set('status', 'success');
                 $this->jsonlib->set('feedback', $qattempt->get_question_feedback());
                 // next we need to send back the updated sequence check for javascript to update
                 // the sequence check on the question form.  this allows the question to be resubmitted again
                 list($seqname, $seqvalue) = $qattempt->get_sequence_check($this->session->get_session()->currentqnum);
                 $this->jsonlib->set('seqcheckname', $seqname);
                 $this->jsonlib->set('seqcheckval', $seqvalue);
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('unable to save question');
             }
             break;
         case 'getresults':
             // only allow instructors to perform this action
             if ($this->RTQ->is_instructor()) {
                 $this->session->set_status('reviewing');
                 // get the current question results
                 $responses = $this->session->get_question_results();
                 $this->jsonlib->set('responses', $responses);
                 $this->jsonlib->set('status', 'success');
                 $this->jsonlib->set('qtype', $this->RTQ->get_questionmanager()->get_questiontype_byqnum($this->session->get_session()->currentqnum));
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'getcurrentresults':
             // case to get the results of the question currently going
             if ($this->RTQ->is_instructor()) {
                 $responses = $this->session->get_question_results();
                 $this->jsonlib->set('responses', $responses);
                 $this->jsonlib->set('status', 'success');
                 $this->jsonlib->set('qtype', $this->RTQ->get_questionmanager()->get_questiontype_byqnum($this->session->get_session()->currentqnum));
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'getnotresponded':
             // only allow instructors to perform this action
             if ($this->RTQ->is_instructor()) {
                 $notrespondedHTML = $this->session->get_not_responded();
                 $this->jsonlib->set('notresponded', $notrespondedHTML);
                 $this->jsonlib->set('status', 'success');
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'nextquestion':
             // only allow instructors to perform this action
             if ($this->RTQ->is_instructor()) {
                 $nextquestion = $this->session->next_question();
                 $this->session->set_status('running');
                 $this->jsonlib->set('status', 'startedquestion');
                 $qattempt = $this->session->get_open_attempt();
                 $this->jsonlib->set('lastquestion', $qattempt->lastquestion ? 'true' : 'false');
                 $this->jsonlib->set('questionid', $nextquestion->get_slot());
                 $this->jsonlib->set('nextstarttime', $this->session->get_session()->nextstarttime);
                 $this->jsonlib->set('notime', $nextquestion->getNoTime());
                 if ($nextquestion->getNoTime() == 0) {
                     // this question has a time limit
                     if ($nextquestion->getQuestionTime() == 0) {
                         $questiontime = $this->RTQ->getRTQ()->defaultquestiontime;
                     } else {
                         $questiontime = $nextquestion->getQuestionTime();
                     }
                     $this->jsonlib->set('questiontime', $questiontime);
                 } else {
                     $this->jsonlib->set('questiontime', 0);
                 }
                 $delay = $this->session->get_session()->nextstarttime - time();
                 $this->jsonlib->set('delay', $delay);
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'repollquestion':
             if ($this->RTQ->is_instructor()) {
                 $repollquestion = $this->session->repoll_question();
                 $this->session->set_status('running');
                 $this->jsonlib->set('status', 'startedquestion');
                 $qattempt = $this->session->get_open_attempt();
                 $this->jsonlib->set('lastquestion', $qattempt->lastquestion ? 'true' : 'false');
                 $this->jsonlib->set('questionid', $repollquestion->get_slot());
                 $this->jsonlib->set('nextstarttime', $this->session->get_session()->nextstarttime);
                 $this->jsonlib->set('notime', $repollquestion->getNoTime());
                 if ($repollquestion->getNoTime() == 0) {
                     // this question has a time limit
                     if ($repollquestion->getQuestionTime() == 0) {
                         $questiontime = $this->RTQ->getRTQ()->defaultquestiontime;
                     } else {
                         $questiontime = $repollquestion->getQuestionTime();
                     }
                     $this->jsonlib->set('questiontime', $questiontime);
                 } else {
                     $this->jsonlib->set('questiontime', 0);
                 }
                 $delay = $this->session->get_session()->nextstarttime - time();
                 $this->jsonlib->set('delay', $delay);
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'gotoquestion':
             if ($this->RTQ->is_instructor()) {
                 $qnum = optional_param('qnum', '', PARAM_INT);
                 if (empty($qnum)) {
                     $this->jsonlib->send_error('invalid question number');
                 }
                 if (!($question = $this->session->goto_question($qnum))) {
                     $this->jsonlib->send_error('invalid question number');
                 }
                 $this->session->set_status('running');
                 $this->jsonlib->set('status', 'startedquestion');
                 $qattempt = $this->session->get_open_attempt();
                 $this->jsonlib->set('lastquestion', $qattempt->lastquestion ? 'true' : 'false');
                 $this->jsonlib->set('questionid', $question->get_slot());
                 $this->jsonlib->set('nextstarttime', $this->session->get_session()->nextstarttime);
                 $this->jsonlib->set('notime', $question->getNoTime());
                 if ($question->getNoTime() == 0) {
                     // this question has a time limit
                     if ($question->getQuestionTime() == 0) {
                         $questiontime = $this->RTQ->getRTQ()->defaultquestiontime;
                     } else {
                         $questiontime = $question->getQuestionTime();
                     }
                     $this->jsonlib->set('questiontime', $questiontime);
                 } else {
                     $this->jsonlib->set('questiontime', 0);
                 }
                 $delay = $this->session->get_session()->nextstarttime - time();
                 $this->jsonlib->set('delay', $delay);
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'endquestion':
             // update the session status to say that we're ending the question (this will in turn update students
             if ($this->RTQ->is_instructor()) {
                 $this->session->end_question();
                 $this->jsonlib->set('status', 'success');
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'getrightresponse':
             if ($this->RTQ->is_instructor()) {
                 $rightresponsequestion = $this->session->get_question_right_response();
                 $this->jsonlib->set('rightanswer', $rightresponsequestion);
                 $this->jsonlib->set('status', 'success');
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         case 'closesession':
             // only allow instructors to perform this action
             if ($this->RTQ->is_instructor()) {
                 $this->session->end_session();
                 // next calculate and save grades
                 if (!$this->RTQ->get_grader()->save_all_grades()) {
                     $this->jsonlib->send_error('can\'t save grades');
                 }
                 $this->jsonlib->set('status', 'success');
                 $this->jsonlib->send_response();
             } else {
                 $this->jsonlib->send_error('invalidaction');
             }
             break;
         default:
             $this->jsonlib->send_error('invalidaction');
             break;
     }
 }