diff --git a/classes/question/bank/studentquiz_bank_view.php b/classes/question/bank/studentquiz_bank_view.php index 7b76bcb9..34620521 100755 --- a/classes/question/bank/studentquiz_bank_view.php +++ b/classes/question/bank/studentquiz_bank_view.php @@ -491,10 +491,17 @@ public function create_new_question_form($categoryid, $canadd) { $qtypecontainer = \html_writer::div( print_choose_qtype_to_add_form(array(), $allowedtypes, true ), '', array('id' => 'qtypechoicecontainer')); - $output .= \html_writer::div( - $OUTPUT->render(new \single_button($url, $caption, 'get', true)) . - $qtypecontainer, 'createnewquestion' - ); + $questionsubmissionbutton = new \single_button($url, $caption, 'get', true); + + list($message, $questionsubmissionallow) = mod_studentquiz_check_availability($this->studentquiz->opensubmissionfrom, + $this->studentquiz->closesubmissionfrom, 'submission'); + + $questionsubmissionbutton->disabled = !$questionsubmissionallow; + $output .= \html_writer::div($OUTPUT->render($questionsubmissionbutton) . $qtypecontainer, 'createnewquestion'); + + if (!empty($message)) { + $output .= $this->renderer->render_availability_message($message, 'mod_studentquiz_submission_info'); + } } else { $output .= get_string('nopermissionadd', 'question'); } diff --git a/db/install.xml b/db/install.xml index 499cd31f..655d7f5d 100644 --- a/db/install.xml +++ b/db/install.xml @@ -24,6 +24,10 @@ + + + + diff --git a/db/upgrade.php b/db/upgrade.php index ec002c92..e15fcde0 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -451,5 +451,19 @@ function xmldb_studentquiz_upgrade($oldversion) { upgrade_mod_savepoint(true, 2018121800, 'studentquiz'); } + if ($oldversion < 2018122500) { + $table = new xmldb_table('studentquiz'); + $fieldnames = ['opensubmissionfrom', 'closesubmissionfrom', 'openansweringfrom', 'closeansweringfrom']; + + foreach ($fieldnames as $fieldname) { + $field = new xmldb_field($fieldname, XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + } + + upgrade_mod_savepoint(true, 2018122500, 'studentquiz'); + } + return true; } diff --git a/lang/en/studentquiz.php b/lang/en/studentquiz.php index b4859a3b..5f3574d5 100644 --- a/lang/en/studentquiz.php +++ b/lang/en/studentquiz.php @@ -92,6 +92,12 @@ $string['progress_bar_caption'] = 'Your progress in this StudentQuiz activity'; $string['no_questions_filter'] = 'None of the questions matched your filter criteria. Reset the filter to see all.'; $string['no_questions_add'] = 'There are no questions in this StudentQuiz. Feel free to add some questions.'; +$string['before_submission_start_date'] = 'Open for question submission from {$a}.'; +$string['before_submission_end_date'] = 'This StudentQuiz closes for question submission on {$a}.'; +$string['after_submission_end_date'] = 'This StudentQuiz closed for question submission on {$a}.'; +$string['before_answering_start_date'] = 'Open for answering from {$a}.'; +$string['before_answering_end_date'] = 'This StudentQuiz closes for answering on {$a}.'; +$string['after_answering_end_date'] = 'This StudentQuiz closed for answering on {$a}.'; // Blocks. $string['statistic_block_title'] = 'My Progress'; @@ -167,10 +173,16 @@ $string['settings_allowedqtypes'] = 'Allowed question types'; $string['settings_allowedqtypes_help'] = 'Here you specify the type of questions that are allowed'; $string['settings_qtypes_default_new_activity'] = 'The following are default for a new activity'; +$string['settings_availability_open_submission_from'] = 'Open for question submission from'; +$string['settings_availability_close_submission_from'] = 'Closed for question submission from'; +$string['settings_availability_open_answering_from'] = 'Open for answering from'; +$string['settings_availability_close_answering_from'] = 'Close for answering from'; // Error messages. $string['needtoallowatleastoneqtype'] = 'You need to allow at least one question type'; $string['please_enrole_message'] = 'Please enroll in this course to see your personal progress'; +$string['submissionendbeforestart'] = 'Submissions deadline can not be specified before the open for submissions date'; +$string['answeringndbeforestart'] = 'Answering deadline can not be specified before the open for answering date'; // Admin settings. $string['rankingsettingsheader'] = 'Ranking settings'; diff --git a/locallib.php b/locallib.php index 30071fc9..8fd1e6a8 100644 --- a/locallib.php +++ b/locallib.php @@ -1301,3 +1301,32 @@ function mod_studentquiz_fix_wrong_parent_in_question_categories() { } } } + +/** + * Check that StudentQuiz is allowing answering or not. + * + * @param int $openform Open date + * @param int $closefrom Close date + * @param string $type submission or answering + * @return array Message and Allow or not + */ +function mod_studentquiz_check_availability($openform, $closefrom, $type) { + $message = ''; + $availabilityallow = true; + + if (time() < $openform) { + $availabilityallow = false; + $message = get_string('before_' . $type . '_start_date', 'studentquiz', + userdate($openform, get_string('strftimedatetimeshort', 'langconfig'))); + } else if (time() < $closefrom) { + $message = get_string('before_' . $type . '_end_date', 'studentquiz', + userdate($closefrom, get_string('strftimedatetimeshort', 'langconfig'))); + } + if ($closefrom && time() >= $closefrom) { + $availabilityallow = false; + $message = get_string('after_' . $type . '_end_date', 'studentquiz', + userdate($closefrom, get_string('strftimedatetimeshort', 'langconfig'))); + } + + return [$message, $availabilityallow]; +} diff --git a/mod_form.php b/mod_form.php index 19953b2f..bc037374 100644 --- a/mod_form.php +++ b/mod_form.php @@ -150,6 +150,17 @@ public function definition() { $mform->disabledIf('allowedqtypes', "allowedqtypes[ALL]", 'checked'); $mform->addHelpButton('allowedqtypes', 'settings_allowedqtypes', 'studentquiz'); + // Availability. + $mform->addElement('header', 'availability', get_string('availability', 'moodle')); + $mform->addElement('date_time_selector', 'opensubmissionfrom', + get_string('settings_availability_open_submission_from', 'studentquiz'), ['optional' => true]); + $mform->addElement('date_time_selector', 'closesubmissionfrom', + get_string('settings_availability_close_submission_from', 'studentquiz'), ['optional' => true]); + $mform->addElement('date_time_selector', 'openansweringfrom', + get_string('settings_availability_open_answering_from', 'studentquiz'), ['optional' => true]); + $mform->addElement('date_time_selector', 'closeansweringfrom', + get_string('settings_availability_close_answering_from', 'studentquiz'), ['optional' => true]); + // Add standard elements, common to all modules. $this->standard_coursemodule_elements(); @@ -183,6 +194,14 @@ public function validation($data, $files) { if (!isset($data['allowedqtypes'])) { $errors['allowedqtypes'] = get_string('needtoallowatleastoneqtype', 'studentquiz'); } + if ($data['opensubmissionfrom'] > 0 && $data['closesubmissionfrom'] > 0 && + $data['opensubmissionfrom'] >= $data['closesubmissionfrom']) { + $errors['closesubmissionfrom'] = get_string('submissionendbeforestart', 'studentquiz'); + } + if ($data['openansweringfrom'] > 0 && $data['closeansweringfrom'] > 0 && + $data['openansweringfrom'] >= $data['closeansweringfrom']) { + $errors['closeansweringfrom'] = get_string('answeringndbeforestart', 'studentquiz'); + } return $errors; } } diff --git a/pix/info.svg b/pix/info.svg new file mode 100644 index 00000000..69bd1018 --- /dev/null +++ b/pix/info.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/renderer.php b/renderer.php index 6c55d74c..fbd9b4e7 100755 --- a/renderer.php +++ b/renderer.php @@ -1095,13 +1095,23 @@ public function render_control_buttons($catcontext, $hasquestionincategory, $add $output .= html_writer::tag('strong', ' ' . get_string('withselected', 'question') . ':'); $output .= html_writer::empty_tag('br'); + $studentquiz = mod_studentquiz_load_studentquiz($this->page->url->get_param('cmid'), $this->page->context->id); + list($message, $answeringallow) = mod_studentquiz_check_availability( + $studentquiz->openansweringfrom, $studentquiz->closeansweringfrom, 'answering'); + if ($hasquestionincategory) { - $output .= html_writer::empty_tag('input', [ - 'class' => 'btn btn-primary form-submit', - 'type' => 'submit', - 'name' => 'startquiz', - 'value' => get_string('start_quiz_button', 'studentquiz') - ]); + $params = [ + 'class' => 'btn btn-primary form-submit', + 'type' => 'submit', + 'name' => 'startquiz', + 'value' => get_string('start_quiz_button', 'studentquiz') + ]; + + if (!$answeringallow) { + $params['disabled'] = 'disabled'; + } + + $output .= html_writer::empty_tag('input', $params); } if ($caneditall) { @@ -1132,6 +1142,11 @@ public function render_control_buttons($catcontext, $hasquestionincategory, $add ob_end_clean(); } + if (!empty($message)) { + $output .= $this->render_availability_message($message, 'mod_studentquiz_answering_info'); + } + + $output .= html_writer::end_div(); return $output; @@ -1238,6 +1253,24 @@ private function generate_hidden_input($name, $value) { return $output; } + /** + * Render the availability message + * + * @param string $message Message to show + * @param string $class Class of the message + * @return string HTML string + */ + public function render_availability_message($message, $class) { + $output = ''; + + if (!empty($message)) { + $icon = new \pix_icon('info', get_string('info'), 'studentquiz'); + $output = \html_writer::div($this->output->render($icon) . $message, $class); + } + + return $output; + } + } class mod_studentquiz_attempt_renderer extends mod_studentquiz_renderer { diff --git a/tests/behat/behat_mod_studentquiz.php b/tests/behat/behat_mod_studentquiz.php new file mode 100644 index 00000000..e58685ff --- /dev/null +++ b/tests/behat/behat_mod_studentquiz.php @@ -0,0 +1,72 @@ +. + +/** + * Steps definitions related to mod_studentquiz. + * + * @package mod_studentquiz + * @category test + * @copyright 2019 HSR (http://www.hsr.ch) + * @author 2019 Huong Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../../../../lib/behat/behat_base.php'); + +/** + * Steps definitions related to mod_studentquiz. + * + * @package mod_studentquiz + * @category test + * @copyright 2019 HSR (http://www.hsr.ch) + * @author 2019 Huong Nguyen + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_mod_studentquiz extends behat_base { + + /** + * @Given /^I set the availability field "(?P(?:[^"]|\\")*)" to "(?P(?:[^"]|\\")*)" days from now$/ + * @param string $field Field name. + * @param string $days Number of days from now. + */ + public function i_set_availability_field_to($field, $days) { + $date = strtotime($days . ' day'); + $day = date('j', $date); + $month = date('F', $date); + $year = date('Y', $date); + $this->set_field_value('id_' . $field . '_day', $day); + $this->set_field_value('id_' . $field . '_month', $month); + $this->set_field_value('id_' . $field . '_year', $year); + } + + /** + * Generic field setter. + * + * Internal API method, a generic *I set "VALUE" to "FIELD" field* + * could be created based on it. + * + * @param string $fieldlocator The pointer to the field, it will depend on the field type. + * @param string $value + * @return void + */ + protected function set_field_value($fieldlocator, $value) { + // We delegate to behat_form_field class, it will + // guess the type properly as it is a select tag. + $field = behat_field_manager::get_form_field_from_label($fieldlocator, $this); + $field->set_value($value); + } + +} diff --git a/tests/behat/question_submission_answering_phases.feature b/tests/behat/question_submission_answering_phases.feature new file mode 100644 index 00000000..2eed19ee --- /dev/null +++ b/tests/behat/question_submission_answering_phases.feature @@ -0,0 +1,183 @@ +@mod @mod_studentquiz +Feature: Question submission and answering will follow the availability setting + In order to allow users to submission questions or answering questions only in a limited period + As a teacher + I need availability setting for question submission and question answering + + Background: + Given the following "courses" exist: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "activities" exist: + | activity | name | intro | course | idnumber | + | studentquiz | StudentQuiz Test | StudentQuiz description | C1 | studentquiz1 | + And the following "questions" exist: + | questioncategory | qtype | name | + | Default for StudentQuiz Test | truefalse | Question 1 | + | Default for StudentQuiz Test | truefalse | Question 2 | + | Default for StudentQuiz Test | truefalse | Question 3 | + + @javascript + Scenario: New availability settings should exist + Given I log in as "admin" + And I am on "Course 1" course homepage + And I turn editing mode on + And I add a "StudentQuiz" to section "1" + When I expand all fieldsets + Then I should see "Open for question submission from" + And I should see "Closed for question submission from" + And I should see "Open for answering from" + And I should see "Close for answering from" + + @javascript + Scenario: Availability settings validation + Given I log in as "admin" + And I am on "Course 1" course homepage + And I turn editing mode on + And I add a "StudentQuiz" to section "1" + And I expand all fieldsets + And I set the field "id_name" to "StudentQuiz Test Availability" + + # Submissions deadline can not be specified before the open for submissions date + And I set the field "id_opensubmissionfrom_enabled" to "1" + And I set the field "id_closesubmissionfrom_enabled" to "1" + And I set the availability field "closesubmissionfrom" to "-1" days from now + When I press "Save and display" + Then I should see "Submissions deadline can not be specified before the open for submissions date" + + # Answering deadline can not be specified before the open for answering date + And I set the field "id_opensubmissionfrom_enabled" to "0" + And I set the field "id_closesubmissionfrom_enabled" to "0" + And I set the field "id_openansweringfrom_enabled" to "1" + And I set the field "id_closeansweringfrom_enabled" to "1" + And I set the availability field "closeansweringfrom" to "-1" days from now + When I press "Save and display" + Then I should see "Answering deadline can not be specified before the open for answering date" + + @javascript + Scenario: Availability settings for question submission + Given I log in as "admin" + And I am on "Course 1" course homepage + When I follow "StudentQuiz Test" + Then the "Create new question" "button" should be enabled + + # Enable only for Open for question submission (Future) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the field "id_opensubmissionfrom_enabled" to "1" + And I set the availability field "opensubmissionfrom" to "+5" days from now + When I press "Save and display" + Then the "Create new question" "button" should be disabled + And I should see "Open for question submission from" + + # Enable only for Open for question submission (Past) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the availability field "opensubmissionfrom" to "-5" days from now + When I press "Save and display" + Then the "Create new question" "button" should be enabled + And I should not see "Open for question submission from" + + # Enable only for Close for question submission (Past) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the field "id_opensubmissionfrom_enabled" to "0" + And I set the field "id_closesubmissionfrom_enabled" to "1" + And I set the availability field "closesubmissionfrom" to "-5" days from now + When I press "Save and display" + Then the "Create new question" "button" should be disabled + And I should see "This StudentQuiz closed for question submission on" + + # Enable only for Close for question submission (Future) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the availability field "closesubmissionfrom" to "+5" days from now + When I press "Save and display" + Then the "Create new question" "button" should be enabled + And I should see "This StudentQuiz closes for question submission on" + + # Enable both Open and Close for question submission (Open in the Past) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the field "id_opensubmissionfrom_enabled" to "1" + And I set the field "id_closesubmissionfrom_enabled" to "1" + And I set the availability field "opensubmissionfrom" to "-5" days from now + And I set the availability field "closesubmissionfrom" to "+5" days from now + When I press "Save and display" + Then the "Create new question" "button" should be enabled + And I should see "This StudentQuiz closes for question submission on" + + # Enable both Open and Close for question submission (Open in the Future) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the field "id_opensubmissionfrom_enabled" to "1" + And I set the field "id_closesubmissionfrom_enabled" to "1" + And I set the availability field "opensubmissionfrom" to "+5" days from now + And I set the availability field "closesubmissionfrom" to "+10" days from now + When I press "Save and display" + Then the "Create new question" "button" should be disabled + And I should see "Open for question submission from" + + @javascript + Scenario: Availability settings for question answering + Given I log in as "admin" + And I am on "Course 1" course homepage + When I follow "StudentQuiz Test" + Then the "Start Quiz" "button" should be enabled + + # Enable only for Open for question answering (Future) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the field "id_openansweringfrom_enabled" to "1" + And I set the availability field "openansweringfrom" to "+5" days from now + When I press "Save and display" + Then the "Start Quiz" "button" should be disabled + And I should see "Open for answering from" + + # Enable only for Open for question answering (Past) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the availability field "openansweringfrom" to "-5" days from now + When I press "Save and display" + Then the "Start Quiz" "button" should be enabled + And I should not see "Open for answering from" + + # Enable only for Close for question answering (Past) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the field "id_openansweringfrom_enabled" to "0" + And I set the field "id_closeansweringfrom_enabled" to "1" + And I set the availability field "closeansweringfrom" to "-5" days from now + When I press "Save and display" + Then the "Start Quiz" "button" should be disabled + And I should see "This StudentQuiz closed for answering on" + + # Enable only for Close for question answering (Future) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the availability field "closeansweringfrom" to "+5" days from now + When I press "Save and display" + Then the "Start Quiz" "button" should be enabled + And I should see "This StudentQuiz closes for answering on" + + # Enable both Open and Close for question answering (Open in the Past) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the field "id_openansweringfrom_enabled" to "1" + And I set the field "id_closeansweringfrom_enabled" to "1" + And I set the availability field "openansweringfrom" to "-5" days from now + And I set the availability field "closeansweringfrom" to "+5" days from now + When I press "Save and display" + Then the "Start Quiz" "button" should be enabled + And I should see "This StudentQuiz closes for answering on" + + # Enable both Open and Close for question answering (Open in the Future) + And I navigate to "Edit settings" in current page administration + And I expand all fieldsets + And I set the field "id_openansweringfrom_enabled" to "1" + And I set the field "id_closeansweringfrom_enabled" to "1" + And I set the availability field "openansweringfrom" to "+5" days from now + And I set the availability field "closeansweringfrom" to "+10" days from now + When I press "Save and display" + Then the "Start Quiz" "button" should be disabled + And I should see "Open for answering from" diff --git a/version.php b/version.php index a952f442..1c314b10 100644 --- a/version.php +++ b/version.php @@ -28,7 +28,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_studentquiz'; -$plugin->version = 2018121800; +$plugin->version = 2018122500; $plugin->release = 'v3.2.1'; $plugin->requires = 2017111306; // Version MOODLE_31, 3.1.0. $plugin->maturity = MATURITY_STABLE;