first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+151
View File
@@ -0,0 +1,151 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides support for the conversion of moodle1 backup to the moodle2 format
*
* @package mod_choice
* @copyright 2011 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Choice conversion handler
*/
class moodle1_mod_choice_handler extends moodle1_mod_handler {
/** @var moodle1_file_manager */
protected $fileman = null;
/** @var int cmid */
protected $moduleid = null;
/**
* Declare the paths in moodle.xml we are able to convert
*
* The method returns list of {@link convert_path} instances.
* For each path returned, the corresponding conversion method must be
* defined.
*
* Note that the path /MOODLE_BACKUP/COURSE/MODULES/MOD/CHOICE does not
* actually exist in the file. The last element with the module name was
* appended by the moodle1_converter class.
*
* @return array of {@link convert_path} instances
*/
public function get_paths() {
return array(
new convert_path(
'choice', '/MOODLE_BACKUP/COURSE/MODULES/MOD/CHOICE',
array(
'renamefields' => array(
'text' => 'intro',
'format' => 'introformat',
),
'newfields' => array(
'completionsubmit' => 0,
),
'dropfields' => array(
'modtype'
),
)
),
new convert_path('choice_options', '/MOODLE_BACKUP/COURSE/MODULES/MOD/CHOICE/OPTIONS'),
new convert_path('choice_option', '/MOODLE_BACKUP/COURSE/MODULES/MOD/CHOICE/OPTIONS/OPTION'),
);
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/CHOICE
* data available
*/
public function process_choice($data) {
// get the course module id and context id
$instanceid = $data['id'];
$cminfo = $this->get_cminfo($instanceid);
$this->moduleid = $cminfo['id'];
$contextid = $this->converter->get_contextid(CONTEXT_MODULE, $this->moduleid);
// get a fresh new file manager for this instance
$this->fileman = $this->converter->get_file_manager($contextid, 'mod_choice');
// convert course files embedded into the intro
$this->fileman->filearea = 'intro';
$this->fileman->itemid = 0;
$data['intro'] = moodle1_converter::migrate_referenced_files($data['intro'], $this->fileman);
// start writing choice.xml
$this->open_xml_writer("activities/choice_{$this->moduleid}/choice.xml");
$this->xmlwriter->begin_tag('activity', array('id' => $instanceid, 'moduleid' => $this->moduleid,
'modulename' => 'choice', 'contextid' => $contextid));
$this->xmlwriter->begin_tag('choice', array('id' => $instanceid));
foreach ($data as $field => $value) {
if ($field <> 'id') {
$this->xmlwriter->full_tag($field, $value);
}
}
return $data;
}
/**
* This is executed when the parser reaches the <OPTIONS> opening element
*/
public function on_choice_options_start() {
$this->xmlwriter->begin_tag('options');
}
/**
* This is executed every time we have one /MOODLE_BACKUP/COURSE/MODULES/MOD/CHOICE/OPTIONS/OPTION
* data available
*/
public function process_choice_option($data) {
$this->write_xml('option', $data, array('/option/id'));
}
/**
* This is executed when the parser reaches the closing </OPTIONS> element
*/
public function on_choice_options_end() {
$this->xmlwriter->end_tag('options');
}
/**
* This is executed when we reach the closing </MOD> tag of our 'choice' path
*/
public function on_choice_end() {
// finalize choice.xml
$this->xmlwriter->end_tag('choice');
$this->xmlwriter->end_tag('activity');
$this->close_xml_writer();
// write inforef.xml
$this->open_xml_writer("activities/choice_{$this->moduleid}/inforef.xml");
$this->xmlwriter->begin_tag('inforef');
$this->xmlwriter->begin_tag('fileref');
foreach ($this->fileman->get_fileids() as $fileid) {
$this->write_xml('file', array('id' => $fileid));
}
$this->xmlwriter->end_tag('fileref');
$this->xmlwriter->end_tag('inforef');
$this->close_xml_writer();
}
}
@@ -0,0 +1,71 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines backup_choice_activity_task class
*
* @package mod_choice
* @category backup
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/choice/backup/moodle2/backup_choice_stepslib.php');
require_once($CFG->dirroot . '/mod/choice/backup/moodle2/backup_choice_settingslib.php');
/**
* Provides the steps to perform one complete backup of the Choice instance
*/
class backup_choice_activity_task extends backup_activity_task {
/**
* No specific settings for this activity
*/
protected function define_my_settings() {
}
/**
* Defines a backup step to store the instance data in the choice.xml file
*/
protected function define_my_steps() {
$this->add_step(new backup_choice_activity_structure_step('choice_structure', 'choice.xml'));
}
/**
* Encodes URLs to the index.php and view.php scripts
*
* @param string $content some HTML text that eventually contains URLs to the activity instance scripts
* @return string the content with the URLs encoded
*/
public static function encode_content_links($content) {
global $CFG;
$base = preg_quote($CFG->wwwroot,"/");
// Link to the list of choices
$search="/(".$base."\/mod\/choice\/index.php\?id\=)([0-9]+)/";
$content= preg_replace($search, '$@CHOICEINDEX*$2@$', $content);
// Link to choice view by moduleid
$search="/(".$base."\/mod\/choice\/view.php\?id\=)([0-9]+)/";
$content= preg_replace($search, '$@CHOICEVIEWBYID*$2@$', $content);
return $content;
}
}
@@ -0,0 +1,27 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @package mod_choice
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// This activity has not particular settings but the inherited from the generic
// backup_activity_task so here there isn't any class definition, like the ones
// existing in /backup/moodle2/backup_settingslib.php (activities section)
@@ -0,0 +1,82 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @package mod_choice
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Define all the backup steps that will be used by the backup_choice_activity_task
*/
/**
* Define the complete choice structure for backup, with file and id annotations
*/
class backup_choice_activity_structure_step extends backup_activity_structure_step {
protected function define_structure() {
// To know if we are including userinfo
$userinfo = $this->get_setting_value('userinfo');
// Define each element separated
$choice = new backup_nested_element('choice', array('id'), array(
'name', 'intro', 'introformat', 'publish',
'showresults', 'display', 'allowupdate', 'allowmultiple', 'showunanswered',
'limitanswers', 'timeopen', 'timeclose', 'timemodified',
'completionsubmit', 'showpreview', 'includeinactive', 'showavailable'));
$options = new backup_nested_element('options');
$option = new backup_nested_element('option', array('id'), array(
'text', 'maxanswers', 'timemodified'));
$answers = new backup_nested_element('answers');
$answer = new backup_nested_element('answer', array('id'), array(
'userid', 'optionid', 'timemodified'));
// Build the tree
$choice->add_child($options);
$options->add_child($option);
$choice->add_child($answers);
$answers->add_child($answer);
// Define sources
$choice->set_source_table('choice', array('id' => backup::VAR_ACTIVITYID));
$option->set_source_table('choice_options', array('choiceid' => backup::VAR_PARENTID), 'id ASC');
// All the rest of elements only happen if we are including user info
if ($userinfo) {
$answer->set_source_table('choice_answers', array('choiceid' => '../../id'));
}
// Define id annotations
$answer->annotate_ids('user', 'userid');
// Define file annotations
$choice->annotate_files('mod_choice', 'intro', null); // This file area hasn't itemid
// Return the root element (choice), wrapped into standard activity structure
return $this->prepare_activity_structure($choice);
}
}
@@ -0,0 +1,115 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @package mod_choice
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/choice/backup/moodle2/restore_choice_stepslib.php'); // Because it exists (must)
/**
* choice restore task that provides all the settings and steps to perform one
* complete restore of the activity
*/
class restore_choice_activity_task extends restore_activity_task {
/**
* Define (add) particular settings this activity can have
*/
protected function define_my_settings() {
// No particular settings for this activity
}
/**
* Define (add) particular steps this activity can have
*/
protected function define_my_steps() {
// Choice only has one structure step
$this->add_step(new restore_choice_activity_structure_step('choice_structure', 'choice.xml'));
}
/**
* Define the contents in the activity that must be
* processed by the link decoder
*/
public static function define_decode_contents() {
$contents = array();
$contents[] = new restore_decode_content('choice', array('intro'), 'choice');
return $contents;
}
/**
* Define the decoding rules for links belonging
* to the activity to be executed by the link decoder
*/
public static function define_decode_rules() {
$rules = array();
$rules[] = new restore_decode_rule('CHOICEVIEWBYID', '/mod/choice/view.php?id=$1', 'course_module');
$rules[] = new restore_decode_rule('CHOICEINDEX', '/mod/choice/index.php?id=$1', 'course');
return $rules;
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* choice logs. It must return one array
* of {@link restore_log_rule} objects
*/
public static function define_restore_log_rules() {
$rules = array();
$rules[] = new restore_log_rule('choice', 'add', 'view.php?id={course_module}', '{choice}');
$rules[] = new restore_log_rule('choice', 'update', 'view.php?id={course_module}', '{choice}');
$rules[] = new restore_log_rule('choice', 'view', 'view.php?id={course_module}', '{choice}');
$rules[] = new restore_log_rule('choice', 'choose', 'view.php?id={course_module}', '{choice}');
$rules[] = new restore_log_rule('choice', 'choose again', 'view.php?id={course_module}', '{choice}');
$rules[] = new restore_log_rule('choice', 'report', 'report.php?id={course_module}', '{choice}');
return $rules;
}
/**
* Define the restore log rules that will be applied
* by the {@link restore_logs_processor} when restoring
* course logs. It must return one array
* of {@link restore_log_rule} objects
*
* Note this rules are applied when restoring course logs
* by the restore final task, but are defined here at
* activity level. All them are rules not linked to any module instance (cmid = 0)
*/
public static function define_restore_log_rules_for_course() {
$rules = array();
// Fix old wrong uses (missing extension)
$rules[] = new restore_log_rule('choice', 'view all', 'index?id={course}', null,
null, null, 'index.php?id={course}');
$rules[] = new restore_log_rule('choice', 'view all', 'index.php?id={course}', null);
return $rules;
}
}
@@ -0,0 +1,97 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @package mod_choice
* @subpackage backup-moodle2
* @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Define all the restore steps that will be used by the restore_choice_activity_task
*/
/**
* Structure step to restore one choice activity
*/
class restore_choice_activity_structure_step extends restore_activity_structure_step {
protected function define_structure() {
$paths = array();
$userinfo = $this->get_setting_value('userinfo');
$paths[] = new restore_path_element('choice', '/activity/choice');
$paths[] = new restore_path_element('choice_option', '/activity/choice/options/option');
if ($userinfo) {
$paths[] = new restore_path_element('choice_answer', '/activity/choice/answers/answer');
}
// Return the paths wrapped into standard activity structure
return $this->prepare_activity_structure($paths);
}
protected function process_choice($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->course = $this->get_courseid();
// Any changes to the list of dates that needs to be rolled should be same during course restore and course reset.
// See MDL-9367.
$data->timeopen = $this->apply_date_offset($data->timeopen);
$data->timeclose = $this->apply_date_offset($data->timeclose);
// insert the choice record
$newitemid = $DB->insert_record('choice', $data);
// immediately after inserting "activity" record, call this
$this->apply_activity_instance($newitemid);
}
protected function process_choice_option($data) {
global $DB;
$data = (object)$data;
$oldid = $data->id;
$data->choiceid = $this->get_new_parentid('choice');
$newitemid = $DB->insert_record('choice_options', $data);
$this->set_mapping('choice_option', $oldid, $newitemid);
}
protected function process_choice_answer($data) {
global $DB;
$data = (object)$data;
$data->choiceid = $this->get_new_parentid('choice');
$data->optionid = $this->get_mappingid('choice_option', $data->optionid);
$data->userid = $this->get_mappingid('user', $data->userid);
$newitemid = $DB->insert_record('choice_answers', $data);
// No need to save this mapping as far as nothing depend on it
// (child paths, file areas nor links decoder)
}
protected function after_execute() {
// Add choice related files, no need to match by itemname (just internally handled context)
$this->add_related_files('mod_choice', 'intro', null);
}
}
@@ -0,0 +1,81 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Activity base class.
*
* @package mod_choice
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Activity base class.
*
* @package mod_choice
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class activity_base extends \core_analytics\local\indicator\community_of_inquiry_activity {
/**
* feedback_viewed_events
*
* @return string[]
*/
protected function feedback_viewed_events() {
return array('\mod_choice\event\course_module_viewed', '\mod_choice\event\answer_updated');
}
/**
* feedback_viewed
*
* @param \cm_info $cm
* @param int $contextid
* @param int $userid
* @param int $after
* @return bool
*/
protected function feedback_viewed(\cm_info $cm, $contextid, $userid, $after = null) {
// If results are shown after they answer a write action counts as feedback viewed.
if ($this->instancedata[$cm->instance]->showresults == 1) {
// The user id will be enough for any_write_log.
$user = (object)['id' => $userid];
return $this->any_write_log($contextid, $user);
}
$after = null;
if ($this->instancedata[$cm->instance]->timeclose) {
$after = $this->instancedata[$cm->instance]->timeclose;
}
return $this->feedback_post_action($cm, $contextid, $userid, $this->feedback_viewed_events(), $after);
}
/**
* Returns the name of the field that controls activity availability.
*
* @return null|string
*/
protected function get_timeclose_field() {
return 'timeclose';
}
}
@@ -0,0 +1,63 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Cognitive depth indicator - choice.
*
* @package mod_choice
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Cognitive depth indicator - choice.
*
* @package mod_choice
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cognitive_depth extends activity_base {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:cognitivedepth', 'mod_choice');
}
public function get_indicator_type() {
return self::INDICATOR_COGNITIVE;
}
public function get_cognitive_depth_level(\cm_info $cm) {
$this->fill_instance_data($cm);
if ($this->instancedata[$cm->instance]->showresults == 0 || $this->instancedata[$cm->instance]->showresults == 4) {
// Results are not shown to students or are always shown.
return self::COGNITIVE_LEVEL_2;
}
return self::COGNITIVE_LEVEL_3;
}
}
@@ -0,0 +1,57 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Social breadth indicator - choice.
*
* @package mod_choice
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* Social breadth indicator - choice.
*
* @package mod_choice
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class social_breadth extends activity_base {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:socialbreadth', 'mod_choice');
}
public function get_indicator_type() {
return self::INDICATOR_SOCIAL;
}
public function get_social_breadth_level(\cm_info $cm) {
$this->fill_instance_data($cm);
return self::SOCIAL_LEVEL_2;
}
}
@@ -0,0 +1,84 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types = 1);
namespace mod_choice\completion;
use core_completion\activity_custom_completion;
/**
* Activity custom completion subclass for the choice activity.
*
* Class for defining mod_choice's custom completion rules and fetching the completion statuses
* of the custom completion rules for a given choice instance and a user.
*
* @package mod_choice
* @copyright 2021 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_completion extends activity_custom_completion {
/**
* Fetches the completion state for a given completion rule.
*
* @param string $rule The completion rule.
* @return int The completion state.
*/
public function get_state(string $rule): int {
global $DB;
$this->validate_rule($rule);
// Choice only supports completionsubmit as a custom rule.
$status = $DB->record_exists('choice_answers', ['choiceid' => $this->cm->instance, 'userid' => $this->userid]);
return $status ? COMPLETION_COMPLETE : COMPLETION_INCOMPLETE;
}
/**
* Fetch the list of custom completion rules that this module defines.
*
* @return array
*/
public static function get_defined_custom_rules(): array {
return [
'completionsubmit'
];
}
/**
* Returns an associative array of the descriptions of custom completion rules.
*
* @return array
*/
public function get_custom_rule_descriptions(): array {
return [
'completionsubmit' => get_string('completiondetail:submit', 'choice')
];
}
/**
* Returns an array of all completion rules, in the order they should be displayed to users.
*
* @return array
*/
public function get_sort_order(): array {
return [
'completionview',
'completionsubmit',
];
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains the class for fetching the important dates in mod_choice for a given module instance and a user.
*
* @package mod_choice
* @copyright Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_choice;
use core\activity_dates;
/**
* Class for fetching the important dates in mod_choice for a given module instance and a user.
*
* @copyright Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dates extends activity_dates {
/**
* Returns a list of important dates in mod_choice
*
* @return array
*/
protected function get_dates(): array {
$timeopen = $this->cm->customdata['timeopen'] ?? null;
$timeclose = $this->cm->customdata['timeclose'] ?? null;
$now = time();
$dates = [];
if ($timeopen) {
$openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened';
$dates[] = [
'dataid' => 'timeopen',
'label' => get_string($openlabelid, 'course'),
'timestamp' => (int) $timeopen,
];
}
if ($timeclose) {
$closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed';
$dates[] = [
'dataid' => 'timeclose',
'label' => get_string($closelabelid, 'course'),
'timestamp' => (int) $timeclose,
];
}
return $dates;
}
}
+154
View File
@@ -0,0 +1,154 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_choice answer created event.
*
* @package mod_choice
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_choice answer created event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int choiceid: id of choice.
* - int optionid: id of the option.
* }
*
* @package mod_choice
* @since Moodle 3.2
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class answer_created extends \core\event\base {
/**
* Creates an instance of the event from the records
*
* @param stdClass $choiceanswer record from 'choice_answers' table
* @param stdClass $choice record from 'choice' table
* @param stdClass $cm record from 'course_modules' table
* @param stdClass $course
* @return self
*/
public static function create_from_object($choiceanswer, $choice, $cm, $course) {
global $USER;
$eventdata = array();
$eventdata['objectid'] = $choiceanswer->id;
$eventdata['context'] = \context_module::instance($cm->id);
$eventdata['userid'] = $USER->id;
$eventdata['courseid'] = $course->id;
$eventdata['relateduserid'] = $choiceanswer->userid;
$eventdata['other'] = array();
$eventdata['other']['choiceid'] = $choice->id;
$eventdata['other']['optionid'] = $choiceanswer->optionid;
$event = self::create($eventdata);
$event->add_record_snapshot('course', $course);
$event->add_record_snapshot('course_modules', $cm);
$event->add_record_snapshot('choice', $choice);
$event->add_record_snapshot('choice_answers', $choiceanswer);
return $event;
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has added the option with id '" . $this->other['optionid'] . "' for the
user with id '$this->relateduserid' from the choice activity with course module id '$this->contextinstanceid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventanswercreated', 'mod_choice');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/choice/view.php', array('id' => $this->contextinstanceid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'choice_answers';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['choiceid'])) {
throw new \coding_exception('The \'choiceid\' value must be set in other.');
}
if (!isset($this->other['optionid'])) {
throw new \coding_exception('The \'optionid\' value must be set in other.');
}
}
/**
* This is used when restoring course logs where it is required that we
* map the objectid to it's new value in the new course.
*
* @return string the name of the restore mapping the objectid links to
*/
public static function get_objectid_mapping() {
return array('db' => 'choice_answers', 'restore' => 'answer');
}
/**
* This is used when restoring course logs where it is required that we
* map the information in 'other' to it's new value in the new course.
*
* @return array an array of other values and their corresponding mapping
*/
public static function get_other_mapping() {
$othermapped = array();
$othermapped['choiceid'] = array('db' => 'choice', 'restore' => 'choice');
$othermapped['optionid'] = array('db' => 'choice_options', 'restore' => 'choice_option');
return $othermapped;
}
}
+142
View File
@@ -0,0 +1,142 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_choice answer deleted event.
*
* @package mod_choice
* @copyright 2016 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_choice answer deleted event class.
*
* @property-read array $other {
* Extra information about event.
*
* - int choiceid: id of choice.
* - int optionid: id of the option.
* }
*
* @package mod_choice
* @since Moodle 3.1
* @copyright 2016 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class answer_deleted extends \core\event\base {
/**
* Creates an instance of the event from the records
*
* @param stdClass $choiceanswer record from 'choice_answers' table
* @param stdClass $choice record from 'choice' table
* @param stdClass $cm record from 'course_modules' table
* @param stdClass $course
* @return self
*/
public static function create_from_object($choiceanswer, $choice, $cm, $course) {
global $USER;
$eventdata = array();
$eventdata['objectid'] = $choiceanswer->id;
$eventdata['context'] = \context_module::instance($cm->id);
$eventdata['userid'] = $USER->id;
$eventdata['courseid'] = $course->id;
$eventdata['relateduserid'] = $choiceanswer->userid;
$eventdata['other'] = array();
$eventdata['other']['choiceid'] = $choice->id;
$eventdata['other']['optionid'] = $choiceanswer->optionid;
$event = self::create($eventdata);
$event->add_record_snapshot('course', $course);
$event->add_record_snapshot('course_modules', $cm);
$event->add_record_snapshot('choice', $choice);
$event->add_record_snapshot('choice_answers', $choiceanswer);
return $event;
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has deleted the option with id '" . $this->other['optionid'] . "' for the
user with id '$this->relateduserid' from the choice activity with course module id '$this->contextinstanceid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventanswerdeleted', 'mod_choice');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/choice/view.php', array('id' => $this->contextinstanceid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['objecttable'] = 'choice_answers';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
if (!isset($this->other['choiceid'])) {
throw new \coding_exception('The \'choiceid\' value must be set in other.');
}
if (!isset($this->other['optionid'])) {
throw new \coding_exception('The \'optionid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'choice_answers', 'restore' => \core\event\base::NOT_MAPPED);
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['choiceid'] = array('db' => 'choice', 'restore' => 'choice');
$othermapped['optionid'] = array('db' => 'choice_options', 'restore' => 'choice_option');
return $othermapped;
}
}
@@ -0,0 +1,136 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_choice answer submitted event.
*
* @package mod_choice
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_choice answer submitted event class.
*
* This event is deprecated in Moodle 3.2, it can no longer be triggered, do not
* write event observers for it. This event can only be initiated during
* restore from previous Moodle versions and appear in the logs.
*
* Event observers should listen to mod_choice\event\answer_created instead that
* will be triggered once for each option selected
*
* @property-read array $other {
* Extra information about event.
*
* - int choiceid: id of choice.
* - int optionid: (optional) id of option.
* }
*
* @deprecated since 3.2
* @package mod_choice
* @since Moodle 2.6
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class answer_submitted extends \core\event\base {
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' made the choice with id '$this->objectid' in the choice activity
with course module id '$this->contextinstanceid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventanswersubmitted', 'mod_choice');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/choice/view.php', array('id' => $this->contextinstanceid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
// The objecttable here is wrong. We are submitting an answer, not a choice activity.
// This also makes the description misleading as it states we made a choice with id
// '$this->objectid' which just refers to the 'choice' table. The trigger for
// this event should be triggered after we insert to the 'choice_answers' table.
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
$this->data['objecttable'] = 'choice';
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
debugging('Event \\mod_choice\event\\answer_submitted should not be used '
. 'any more for triggering new events and can only be initiated during restore. '
. 'For new events please use \\mod_choice\\event\\answer_created', DEBUG_DEVELOPER);
if (!isset($this->other['choiceid'])) {
throw new \coding_exception('The \'choiceid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'choice', 'restore' => 'choice');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['choiceid'] = array('db' => 'choice', 'restore' => 'choice');
// The 'optionid' is being passed as an array, so we can't map it. The event is
// triggered each time a choice is answered, where it may be possible to select
// multiple choices, so the value is converted to an array, which is then passed
// to the event. Ideally this event should be triggered every time we insert to the
// 'choice_answers' table so this will only be an int.
$othermapped['optionid'] = \core\event\base::NOT_MAPPED;
return $othermapped;
}
public static function is_deprecated() {
return true;
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_choice answer updated event.
*
* @package mod_choice
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_choice answer updated event class.
*
* This event is deprecated in Moodle 3.2, it can no longer be triggered, do not
* write event observers for it. This event can only be initiated during
* restore from previous Moodle versions and appear in the logs.
*
* Event observers should listen to mod_choice\event\answer_created and
* mod_choice\event\answer_deleted instead, these events will be triggered for
* each option that was user has selected or unselected
*
* @property-read array $other {
* Extra information about event.
*
* - int choiceid: id of choice.
* - int optionid: (optional) id of option.
* }
*
* @deprecated since 3.2
* @package mod_choice
* @since Moodle 2.6
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class answer_updated extends \core\event\base {
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' updated their choice with id '$this->objectid' in the choice activity
with course module id '$this->contextinstanceid'.";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventanswerupdated', 'mod_choice');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/choice/view.php', array('id' => $this->contextinstanceid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
// The objecttable here is wrong. We are updating an answer, not a choice activity.
// This also makes the description misleading as it states we made a choice with id
// '$this->objectid' which just refers to the 'choice' table. The trigger for
// this event should be triggered after we update the 'choice_answers' table.
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
$this->data['objecttable'] = 'choice';
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
debugging('Event \\mod_choice\event\\answer_updated should not be used '
. 'any more for triggering new events and can only be initiated during restore. '
. 'For new events please use \\mod_choice\\event\\answer_created '
. 'and \\mod_choice\\event\\answer_deleted', DEBUG_DEVELOPER);
if (!isset($this->other['choiceid'])) {
throw new \coding_exception('The \'choiceid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return array('db' => 'choice', 'restore' => 'choice');
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['choiceid'] = array('db' => 'choice', 'restore' => 'choice');
// The 'optionid' is being passed as an array, so we can't map it. The event is
// triggered each time a choice is answered, where it may be possible to select
// multiple choices, so the value is converted to an array, which is then passed
// to the event. Ideally this event should be triggered every time we update the
// 'choice_answers' table so this will only be an int.
$othermapped['optionid'] = \core\event\base::NOT_MAPPED;
return $othermapped;
}
public static function is_deprecated() {
return true;
}
}
@@ -0,0 +1,38 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_choice instance list viewed event.
*
* @package mod_choice
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_choice instance list viewed event class.
*
* @package mod_choice
* @since Moodle 2.7
* @copyright 2013 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_instance_list_viewed extends \core\event\course_module_instance_list_viewed {
}
@@ -0,0 +1,50 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_choice course module viewed event.
*
* @package mod_choice
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_choice course module viewed event class.
*
* @package mod_choice
* @since Moodle 2.6
* @copyright 2013 Adrian Greeve
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_module_viewed extends \core\event\course_module_viewed {
/**
* Init method.
*/
protected function init() {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
$this->data['objecttable'] = 'choice';
}
public static function get_objectid_mapping() {
return array('db' => 'choice', 'restore' => 'choice');
}
}
@@ -0,0 +1,114 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_choice report viewed event.
*
* @package mod_choice
* @copyright 2016 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_choice report viewed event class.
*
* @property-read array $other {
* Extra information about the event.
*
* - string content: The content we are viewing.
* - string format: The report format
* - int choiced: The id of the choice
* }
*
* @package mod_choice
* @since Moodle 3.1
* @copyright 2016 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class report_downloaded extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_TEACHING;
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventreportdownloaded', 'mod_choice');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has downloaded the report in the '".$this->other['format']."' format for
the choice activity with course module id '$this->contextinstanceid'";
}
/**
* Returns relevant URL.
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/choice/report.php', array('id' => $this->contextinstanceid));
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
parent::validate_data();
// Report format downloaded.
if (!isset($this->other['content'])) {
throw new \coding_exception('The \'content\' value must be set in other.');
}
// Report format downloaded.
if (!isset($this->other['format'])) {
throw new \coding_exception('The \'format\' value must be set in other.');
}
// ID of the choice activity.
if (!isset($this->other['choiceid'])) {
throw new \coding_exception('The \'choiceid\' value must be set in other.');
}
}
public static function get_objectid_mapping() {
return false;
}
public static function get_other_mapping() {
$othermapped = array();
$othermapped['choiceid'] = array('db' => 'choice', 'restore' => 'choice');
return $othermapped;
}
}
@@ -0,0 +1,88 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The mod_choice report viewed event.
*
* @package mod_choice
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
/**
* The mod_choice report viewed event class.
*
* @property-read array $other {
* Extra information about the event.
*
* - string content: (optional) The content we are viewing.
* }
*
* @package mod_choice
* @since Moodle 2.6
* @copyright 2013 Adrian Greeve
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class report_viewed extends \core\event\base {
/**
* Init method.
*/
protected function init() {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_TEACHING;
$this->data['objecttable'] = 'choice';
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventreportviewed', 'mod_choice');
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "The user with id '$this->userid' has viewed the report for the choice activity with course module id
'$this->contextinstanceid'";
}
/**
* Returns relevant URL.
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/mod/choice/report.php', array('id' => $this->contextinstanceid));
}
public static function get_objectid_mapping() {
return array('db' => 'choice', 'restore' => 'choice');
}
public static function get_other_mapping() {
// No need to map the 'content' value.
return false;
}
}
+685
View File
@@ -0,0 +1,685 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Choice module external API
*
* @package mod_choice
* @category external
* @copyright 2015 Costantino Cito <ccito@cvaconsulting.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.0
*/
use core_course\external\helper_for_get_mods_by_courses;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_external\util;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot . '/mod/choice/lib.php');
/**
* Choice module external functions
*
* @package mod_choice
* @category external
* @copyright 2015 Costantino Cito <ccito@cvaconsulting.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.0
*/
class mod_choice_external extends external_api {
/**
* Describes the parameters for get_choices_by_courses.
*
* @return external_function_parameters
* @since Moodle 3.0
*/
public static function get_choice_results_parameters() {
return new external_function_parameters (array('choiceid' => new external_value(PARAM_INT, 'choice instance id')));
}
/**
* Returns user's results for a specific choice
* and a list of those users that did not answered yet.
*
* @param int $choiceid the choice instance id
* @return array of responses details
* @since Moodle 3.0
*/
public static function get_choice_results($choiceid) {
global $USER, $PAGE;
$params = self::validate_parameters(self::get_choice_results_parameters(), array('choiceid' => $choiceid));
if (!$choice = choice_get_choice($params['choiceid'])) {
throw new moodle_exception("invalidcoursemodule", "error");
}
list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
$context = context_module::instance($cm->id);
self::validate_context($context);
$groupmode = groups_get_activity_groupmode($cm);
// Check if we have to include responses from inactive users.
$onlyactive = $choice->includeinactive ? false : true;
$users = choice_get_response_data($choice, $cm, $groupmode, $onlyactive);
// Show those who haven't answered the question.
if (!empty($choice->showunanswered)) {
$choice->option[0] = get_string('notanswered', 'choice');
$choice->maxanswers[0] = 0;
}
$results = prepare_choice_show_results($choice, $course, $cm, $users);
$options = array();
$fullnamecap = has_capability('moodle/site:viewfullnames', $context);
foreach ($results->options as $optionid => $option) {
$userresponses = array();
$numberofuser = 0;
$percentageamount = 0;
if (property_exists($option, 'user') and
(has_capability('mod/choice:readresponses', $context) or choice_can_view_results($choice))) {
$numberofuser = count($option->user);
$percentageamount = ((float)$numberofuser / (float)$results->numberofuser) * 100.0;
if ($choice->publish) {
foreach ($option->user as $userresponse) {
$response = array();
$response['userid'] = $userresponse->id;
$response['fullname'] = fullname($userresponse, $fullnamecap);
$userpicture = new user_picture($userresponse);
$userpicture->size = 1; // Size f1.
$response['profileimageurl'] = $userpicture->get_url($PAGE)->out(false);
// Add optional properties.
foreach (array('answerid', 'timemodified') as $field) {
if (property_exists($userresponse, 'answerid')) {
$response[$field] = $userresponse->$field;
}
}
$userresponses[] = $response;
}
}
}
$options[] = array('id' => $optionid,
'text' => \core_external\util::format_string($option->text, $context->id),
'maxanswer' => $option->maxanswer,
'userresponses' => $userresponses,
'numberofuser' => $numberofuser,
'percentageamount' => $percentageamount
);
}
$warnings = array();
return array(
'options' => $options,
'warnings' => $warnings
);
}
/**
* Describes the get_choice_results return value.
*
* @return external_single_structure
* @since Moodle 3.0
*/
public static function get_choice_results_returns() {
return new external_single_structure(
array(
'options' => new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'choice instance id'),
'text' => new external_value(PARAM_RAW, 'text of the choice'),
'maxanswer' => new external_value(PARAM_INT, 'maximum number of answers'),
'userresponses' => new external_multiple_structure(
new external_single_structure(
array(
'userid' => new external_value(PARAM_INT, 'user id'),
'fullname' => new external_value(PARAM_NOTAGS, 'user full name'),
'profileimageurl' => new external_value(PARAM_URL, 'profile user image url'),
'answerid' => new external_value(PARAM_INT, 'answer id', VALUE_OPTIONAL),
'timemodified' => new external_value(PARAM_INT, 'time of modification', VALUE_OPTIONAL),
), 'User responses'
)
),
'numberofuser' => new external_value(PARAM_INT, 'number of users answers'),
'percentageamount' => new external_value(PARAM_FLOAT, 'percentage of users answers')
), 'Options'
)
),
'warnings' => new external_warnings(),
)
);
}
/**
* Describes the parameters for mod_choice_get_choice_options.
*
* @return external_function_parameters
* @since Moodle 3.0
*/
public static function get_choice_options_parameters() {
return new external_function_parameters (array('choiceid' => new external_value(PARAM_INT, 'choice instance id')));
}
/**
* Returns options for a specific choice
*
* @param int $choiceid the choice instance id
* @return array of options details
* @since Moodle 3.0
*/
public static function get_choice_options($choiceid) {
global $USER;
$warnings = array();
$params = self::validate_parameters(self::get_choice_options_parameters(), array('choiceid' => $choiceid));
if (!$choice = choice_get_choice($params['choiceid'])) {
throw new moodle_exception("invalidcoursemodule", "error");
}
list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
$context = context_module::instance($cm->id);
self::validate_context($context);
require_capability('mod/choice:choose', $context);
$groupmode = groups_get_activity_groupmode($cm);
$onlyactive = $choice->includeinactive ? false : true;
$allresponses = choice_get_response_data($choice, $cm, $groupmode, $onlyactive);
$timenow = time();
$choiceopen = true;
$showpreview = false;
if (!empty($choice->timeopen) && ($choice->timeopen > $timenow)) {
$choiceopen = false;
$warnings[1] = get_string("notopenyet", "choice", userdate($choice->timeopen));
if ($choice->showpreview) {
$warnings[2] = get_string('previewonly', 'choice', userdate($choice->timeopen));
$showpreview = true;
}
}
if (!empty($choice->timeclose) && ($timenow > $choice->timeclose)) {
$choiceopen = false;
$warnings[3] = get_string("expired", "choice", userdate($choice->timeclose));
}
$optionsarray = array();
if ($choiceopen or $showpreview) {
$options = choice_prepare_options($choice, $USER, $cm, $allresponses);
foreach ($options['options'] as $option) {
$optionarr = array();
$optionarr['id'] = $option->attributes->value;
$optionarr['text'] = \core_external\util::format_string($option->text, $context->id);
$optionarr['maxanswers'] = $option->maxanswers;
$optionarr['displaylayout'] = $option->displaylayout;
$optionarr['countanswers'] = $option->countanswers;
foreach (array('checked', 'disabled') as $field) {
if (property_exists($option->attributes, $field) and $option->attributes->$field == 1) {
$optionarr[$field] = 1;
} else {
$optionarr[$field] = 0;
}
}
// When showpreview is active, we show options as disabled.
if ($showpreview or ($optionarr['checked'] == 1 and !$choice->allowupdate)) {
$optionarr['disabled'] = 1;
}
$optionsarray[] = $optionarr;
}
}
foreach ($warnings as $key => $message) {
$warnings[$key] = array(
'item' => 'choice',
'itemid' => $cm->id,
'warningcode' => $key,
'message' => $message
);
}
return array(
'options' => $optionsarray,
'warnings' => $warnings
);
}
/**
* Describes the get_choice_results return value.
*
* @return external_multiple_structure
* @since Moodle 3.0
*/
public static function get_choice_options_returns() {
return new external_single_structure(
array(
'options' => new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'option id'),
'text' => new external_value(PARAM_RAW, 'text of the choice'),
'maxanswers' => new external_value(PARAM_INT, 'maximum number of answers'),
'displaylayout' => new external_value(PARAM_BOOL, 'true for orizontal, otherwise vertical'),
'countanswers' => new external_value(PARAM_INT, 'number of answers'),
'checked' => new external_value(PARAM_BOOL, 'we already answered'),
'disabled' => new external_value(PARAM_BOOL, 'option disabled'),
)
), 'Options'
),
'warnings' => new external_warnings(),
)
);
}
/**
* Describes the parameters for submit_choice_response.
*
* @return external_function_parameters
* @since Moodle 3.0
*/
public static function submit_choice_response_parameters() {
return new external_function_parameters (
array(
'choiceid' => new external_value(PARAM_INT, 'choice instance id'),
'responses' => new external_multiple_structure(
new external_value(PARAM_INT, 'answer id'),
'Array of response ids'
),
)
);
}
/**
* Submit choice responses
*
* @param int $choiceid the choice instance id
* @param array $responses the response ids
* @return array answers information and warnings
* @since Moodle 3.0
*/
public static function submit_choice_response($choiceid, $responses) {
global $USER;
$warnings = array();
$params = self::validate_parameters(self::submit_choice_response_parameters(),
array(
'choiceid' => $choiceid,
'responses' => $responses
));
if (!$choice = choice_get_choice($params['choiceid'])) {
throw new moodle_exception("invalidcoursemodule", "error");
}
list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
$context = context_module::instance($cm->id);
self::validate_context($context);
require_capability('mod/choice:choose', $context);
$timenow = time();
if (!empty($choice->timeopen) && ($choice->timeopen > $timenow)) {
throw new moodle_exception("notopenyet", "choice", '', userdate($choice->timeopen));
} else if (!empty($choice->timeclose) && ($timenow > $choice->timeclose)) {
throw new moodle_exception("expired", "choice", '', userdate($choice->timeclose));
}
if (!choice_get_my_response($choice) or $choice->allowupdate) {
// When a single response is given, we convert the array to a simple variable
// in order to avoid choice_user_submit_response to check with allowmultiple even
// for a single response.
if (count($params['responses']) == 1) {
$params['responses'] = reset($params['responses']);
}
choice_user_submit_response($params['responses'], $choice, $USER->id, $course, $cm);
} else {
throw new moodle_exception('missingrequiredcapability', 'webservice', '', 'allowupdate');
}
$answers = choice_get_my_response($choice);
return array(
'answers' => $answers,
'warnings' => $warnings
);
}
/**
* Describes the submit_choice_response return value.
*
* @return external_multiple_structure
* @since Moodle 3.0
*/
public static function submit_choice_response_returns() {
return new external_single_structure(
array(
'answers' => new external_multiple_structure(
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'answer id'),
'choiceid' => new external_value(PARAM_INT, 'choiceid'),
'userid' => new external_value(PARAM_INT, 'user id'),
'optionid' => new external_value(PARAM_INT, 'optionid'),
'timemodified' => new external_value(PARAM_INT, 'time of last modification')
), 'Answers'
)
),
'warnings' => new external_warnings(),
)
);
}
/**
* Returns description of method parameters
*
* @return external_function_parameters
* @since Moodle 3.0
*/
public static function view_choice_parameters() {
return new external_function_parameters(
array(
'choiceid' => new external_value(PARAM_INT, 'choice instance id')
)
);
}
/**
* Trigger the course module viewed event and update the module completion status.
*
* @param int $choiceid the choice instance id
* @return array of warnings and status result
* @since Moodle 3.0
* @throws moodle_exception
*/
public static function view_choice($choiceid) {
global $CFG;
$params = self::validate_parameters(self::view_choice_parameters(),
array(
'choiceid' => $choiceid
));
$warnings = array();
// Request and permission validation.
if (!$choice = choice_get_choice($params['choiceid'])) {
throw new moodle_exception("invalidcoursemodule", "error");
}
list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
$context = context_module::instance($cm->id);
self::validate_context($context);
// Trigger course_module_viewed event and completion.
choice_view($choice, $course, $cm, $context);
$result = array();
$result['status'] = true;
$result['warnings'] = $warnings;
return $result;
}
/**
* Returns description of method result value
*
* @return \core_external\external_description
* @since Moodle 3.0
*/
public static function view_choice_returns() {
return new external_single_structure(
array(
'status' => new external_value(PARAM_BOOL, 'status: true if success'),
'warnings' => new external_warnings()
)
);
}
/**
* Describes the parameters for get_choices_by_courses.
*
* @return external_function_parameters
* @since Moodle 3.0
*/
public static function get_choices_by_courses_parameters() {
return new external_function_parameters (
array(
'courseids' => new external_multiple_structure(
new external_value(PARAM_INT, 'course id'), 'Array of course ids', VALUE_DEFAULT, array()
),
)
);
}
/**
* Returns a list of choices in a provided list of courses,
* if no list is provided all choices that the user can view will be returned.
*
* @param array $courseids the course ids
* @return array of choices details
* @since Moodle 3.0
*/
public static function get_choices_by_courses($courseids = array()) {
$returnedchoices = array();
$warnings = array();
$params = self::validate_parameters(self::get_choices_by_courses_parameters(), array('courseids' => $courseids));
$courses = array();
if (empty($params['courseids'])) {
$courses = enrol_get_my_courses();
$params['courseids'] = array_keys($courses);
}
// Ensure there are courseids to loop through.
if (!empty($params['courseids'])) {
list($courses, $warnings) = util::validate_courses($params['courseids'], $courses);
// Get the choices in this course, this function checks users visibility permissions.
// We can avoid then additional validate_context calls.
$choices = get_all_instances_in_courses("choice", $courses);
foreach ($choices as $choice) {
$context = context_module::instance($choice->coursemodule);
$choicedetails = helper_for_get_mods_by_courses::standard_coursemodule_element_values($choice, 'mod_choice');
if (has_capability('mod/choice:choose', $context)) {
$choicedetails['publish'] = $choice->publish;
$choicedetails['showresults'] = $choice->showresults;
$choicedetails['showpreview'] = $choice->showpreview;
$choicedetails['timeopen'] = $choice->timeopen;
$choicedetails['timeclose'] = $choice->timeclose;
$choicedetails['display'] = $choice->display;
$choicedetails['allowupdate'] = $choice->allowupdate;
$choicedetails['allowmultiple'] = $choice->allowmultiple;
$choicedetails['limitanswers'] = $choice->limitanswers;
$choicedetails['showunanswered'] = $choice->showunanswered;
$choicedetails['includeinactive'] = $choice->includeinactive;
$choicedetails['showavailable'] = $choice->showavailable;
}
if (has_capability('moodle/course:manageactivities', $context)) {
$choicedetails['timemodified'] = $choice->timemodified;
$choicedetails['completionsubmit'] = $choice->completionsubmit;
}
$returnedchoices[] = $choicedetails;
}
}
$result = array();
$result['choices'] = $returnedchoices;
$result['warnings'] = $warnings;
return $result;
}
/**
* Describes the mod_choice_get_choices_by_courses return value.
*
* @return external_single_structure
* @since Moodle 3.0
*/
public static function get_choices_by_courses_returns() {
return new external_single_structure(
array(
'choices' => new external_multiple_structure(
new external_single_structure(array_merge(
helper_for_get_mods_by_courses::standard_coursemodule_elements_returns(),
[
'publish' => new external_value(PARAM_BOOL, 'If choice is published', VALUE_OPTIONAL),
'showresults' => new external_value(PARAM_INT, '0 never, 1 after answer, 2 after close, 3 always',
VALUE_OPTIONAL),
'display' => new external_value(PARAM_INT, 'Display mode (vertical, horizontal)', VALUE_OPTIONAL),
'allowupdate' => new external_value(PARAM_BOOL, 'Allow update', VALUE_OPTIONAL),
'allowmultiple' => new external_value(PARAM_BOOL, 'Allow multiple choices', VALUE_OPTIONAL),
'showunanswered' => new external_value(PARAM_BOOL, 'Show users who not answered yet', VALUE_OPTIONAL),
'includeinactive' => new external_value(PARAM_BOOL, 'Include inactive users', VALUE_OPTIONAL),
'limitanswers' => new external_value(PARAM_BOOL, 'Limit unswers', VALUE_OPTIONAL),
'timeopen' => new external_value(PARAM_INT, 'Date of opening validity', VALUE_OPTIONAL),
'timeclose' => new external_value(PARAM_INT, 'Date of closing validity', VALUE_OPTIONAL),
'showpreview' => new external_value(PARAM_BOOL, 'Show preview before timeopen', VALUE_OPTIONAL),
'timemodified' => new external_value(PARAM_INT, 'Time of last modification', VALUE_OPTIONAL),
'completionsubmit' => new external_value(PARAM_BOOL, 'Completion on user submission', VALUE_OPTIONAL),
'showavailable' => new external_value(PARAM_BOOL, 'Show available spaces', VALUE_OPTIONAL),
]
), 'Choices')
),
'warnings' => new external_warnings(),
)
);
}
/**
* Describes the parameters for delete_choice_responses.
*
* @return external_function_parameters
* @since Moodle 3.0
*/
public static function delete_choice_responses_parameters() {
return new external_function_parameters (
array(
'choiceid' => new external_value(PARAM_INT, 'choice instance id'),
'responses' => new external_multiple_structure(
new external_value(PARAM_INT, 'response id'),
'Array of response ids, empty for deleting all the current user responses.',
VALUE_DEFAULT,
array()
),
)
);
}
/**
* Delete the given submitted responses in a choice
*
* @param int $choiceid the choice instance id
* @param array $responses the response ids, empty for deleting all the current user responses
* @return array status information and warnings
* @throws moodle_exception
* @since Moodle 3.0
*/
public static function delete_choice_responses($choiceid, $responses = array()) {
$status = false;
$warnings = array();
$params = self::validate_parameters(self::delete_choice_responses_parameters(),
array(
'choiceid' => $choiceid,
'responses' => $responses
));
if (!$choice = choice_get_choice($params['choiceid'])) {
throw new moodle_exception("invalidcoursemodule", "error");
}
list($course, $cm) = get_course_and_cm_from_instance($choice, 'choice');
$context = context_module::instance($cm->id);
self::validate_context($context);
require_capability('mod/choice:choose', $context);
$candeleteall = has_capability('mod/choice:deleteresponses', $context);
if ($candeleteall || $choice->allowupdate) {
// Check if we can delete our own responses.
if (!$candeleteall) {
$timenow = time();
if (!empty($choice->timeclose) && ($timenow > $choice->timeclose)) {
throw new moodle_exception("expired", "choice", '', userdate($choice->timeclose));
}
}
if (empty($params['responses'])) {
// No responses indicated so delete only my responses.
$todelete = array_keys(choice_get_my_response($choice));
} else {
// Fill an array with the responses that can be deleted for this choice.
if ($candeleteall) {
// Teacher/managers can delete any.
$allowedresponses = array_keys(choice_get_all_responses($choice));
} else {
// Students can delete only their own responses.
$allowedresponses = array_keys(choice_get_my_response($choice));
}
$todelete = array();
foreach ($params['responses'] as $response) {
if (!in_array($response, $allowedresponses)) {
$warnings[] = array(
'item' => 'response',
'itemid' => $response,
'warningcode' => 'nopermissions',
'message' => 'Invalid response id, the response does not exist or you are not allowed to delete it.'
);
} else {
$todelete[] = $response;
}
}
}
$status = choice_delete_responses($todelete, $choice, $cm, $course);
} else {
// The user requires the capability to delete responses.
throw new required_capability_exception($context, 'mod/choice:deleteresponses', 'nopermissions', '');
}
return array(
'status' => $status,
'warnings' => $warnings
);
}
/**
* Describes the delete_choice_responses return value.
*
* @return external_multiple_structure
* @since Moodle 3.0
*/
public static function delete_choice_responses_returns() {
return new external_single_structure(
array(
'status' => new external_value(PARAM_BOOL, 'status, true if everything went right'),
'warnings' => new external_warnings(),
)
);
}
}
+283
View File
@@ -0,0 +1,283 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for mod_choice.
*
* @package mod_choice
* @category privacy
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\privacy;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\deletion_criteria;
use core_privacy\local\request\helper;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
defined('MOODLE_INTERNAL') || die();
/**
* Implementation of the privacy subsystem plugin provider for the choice activity module.
*
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
// This plugin stores personal data.
\core_privacy\local\metadata\provider,
// This plugin is a core_user_data_provider.
\core_privacy\local\request\plugin\provider,
// This plugin is capable of determining which users have data within it.
\core_privacy\local\request\core_userlist_provider {
/**
* Return the fields which contain personal data.
*
* @param collection $items a reference to the collection to use to store the metadata.
* @return collection the updated collection of metadata items.
*/
public static function get_metadata(collection $items): collection {
$items->add_database_table(
'choice_answers',
[
'choiceid' => 'privacy:metadata:choice_answers:choiceid',
'optionid' => 'privacy:metadata:choice_answers:optionid',
'userid' => 'privacy:metadata:choice_answers:userid',
'timemodified' => 'privacy:metadata:choice_answers:timemodified',
],
'privacy:metadata:choice_answers'
);
return $items;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid the userid.
* @return contextlist the list of contexts containing user info for the user.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
// Fetch all choice answers.
$sql = "SELECT c.id
FROM {context} c
INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
INNER JOIN {choice} ch ON ch.id = cm.instance
INNER JOIN {choice_options} co ON co.choiceid = ch.id
INNER JOIN {choice_answers} ca ON ca.optionid = co.id AND ca.choiceid = ch.id
WHERE ca.userid = :userid";
$params = [
'modname' => 'choice',
'contextlevel' => CONTEXT_MODULE,
'userid' => $userid,
];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!$context instanceof \context_module) {
return;
}
// Fetch all choice answers.
$sql = "SELECT ca.userid
FROM {course_modules} cm
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
JOIN {choice} ch ON ch.id = cm.instance
JOIN {choice_options} co ON co.choiceid = ch.id
JOIN {choice_answers} ca ON ca.optionid = co.id AND ca.choiceid = ch.id
WHERE cm.id = :cmid";
$params = [
'cmid' => $context->instanceid,
'modname' => 'choice',
];
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export personal data for the given approved_contextlist. User and context information is contained within the contextlist.
*
* @param approved_contextlist $contextlist a list of contexts approved for export.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$user = $contextlist->get_user();
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$sql = "SELECT cm.id AS cmid,
co.text as answer,
ca.timemodified
FROM {context} c
INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
INNER JOIN {choice} ch ON ch.id = cm.instance
INNER JOIN {choice_options} co ON co.choiceid = ch.id
INNER JOIN {choice_answers} ca ON ca.optionid = co.id AND ca.choiceid = ch.id
WHERE c.id {$contextsql}
AND ca.userid = :userid
ORDER BY cm.id";
$params = ['modname' => 'choice', 'contextlevel' => CONTEXT_MODULE, 'userid' => $user->id] + $contextparams;
// Reference to the choice activity seen in the last iteration of the loop. By comparing this with the current record, and
// because we know the results are ordered, we know when we've moved to the answers for a new choice activity and therefore
// when we can export the complete data for the last activity.
$lastcmid = null;
$choiceanswers = $DB->get_recordset_sql($sql, $params);
foreach ($choiceanswers as $choiceanswer) {
// If we've moved to a new choice, then write the last choice data and reinit the choice data array.
if ($lastcmid != $choiceanswer->cmid) {
if (!empty($choicedata)) {
$context = \context_module::instance($lastcmid);
self::export_choice_data_for_user($choicedata, $context, $user);
}
$choicedata = [
'answer' => [],
'timemodified' => \core_privacy\local\request\transform::datetime($choiceanswer->timemodified),
];
}
$choicedata['answer'][] = $choiceanswer->answer;
$lastcmid = $choiceanswer->cmid;
}
$choiceanswers->close();
// The data for the last activity won't have been written yet, so make sure to write it now!
if (!empty($choicedata)) {
$context = \context_module::instance($lastcmid);
self::export_choice_data_for_user($choicedata, $context, $user);
}
}
/**
* Export the supplied personal data for a single choice activity, along with any generic data or area files.
*
* @param array $choicedata the personal data to export for the choice.
* @param \context_module $context the context of the choice.
* @param \stdClass $user the user record
*/
protected static function export_choice_data_for_user(array $choicedata, \context_module $context, \stdClass $user) {
// Fetch the generic module data for the choice.
$contextdata = helper::get_context_data($context, $user);
// Merge with choice data and write it.
$contextdata = (object)array_merge((array)$contextdata, $choicedata);
writer::with_context($context)->export_data([], $contextdata);
// Write generic module intro files.
helper::export_context_files($context, $user);
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context the context to delete in.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
global $DB;
if (!$context instanceof \context_module) {
return;
}
if ($cm = get_coursemodule_from_id('choice', $context->instanceid)) {
$DB->delete_records('choice_answers', ['choiceid' => $cm->instance]);
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist a list of contexts approved for deletion.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$userid = $contextlist->get_user()->id;
foreach ($contextlist->get_contexts() as $context) {
if (!$context instanceof \context_module) {
continue;
}
$instanceid = $DB->get_field('course_modules', 'instance', ['id' => $context->instanceid]);
if (!$instanceid) {
continue;
}
$DB->delete_records('choice_answers', ['choiceid' => $instanceid, 'userid' => $userid]);
}
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
if (!$context instanceof \context_module) {
return;
}
$cm = get_coursemodule_from_id('choice', $context->instanceid);
if (!$cm) {
// Only choice module will be handled.
return;
}
$userids = $userlist->get_userids();
list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$select = "choiceid = :choiceid AND userid $usersql";
$params = ['choiceid' => $cm->instance] + $userparams;
$DB->delete_records_select('choice_answers', $select, $params);
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Search area for mod_choice activities.
*
* @package mod_choice
* @copyright 2015 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\search;
defined('MOODLE_INTERNAL') || die();
/**
* Search area for mod_choice activities.
*
* @package mod_choice
* @copyright 2015 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activity extends \core_search\base_activity {
/**
* Returns true if this area uses file indexing.
*
* @return bool
*/
public function uses_file_indexing() {
return true;
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Plugin capabilities
*
* @package mod_choice
* @copyright 2006 Martin Dougiamas
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'mod/choice:addinstance' => array(
'riskbitmask' => RISK_XSS,
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/course:manageactivities'
),
'mod/choice:choose' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'student' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW
)
),
'mod/choice:readresponses' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/choice:deleteresponses' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/choice:downloadresponses' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
),
'mod/choice:view' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'user' => CAP_ALLOW,
'guest' => CAP_ALLOW
)
)
);
+67
View File
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/choice/db" VERSION="20200616" COMMENT="XMLDB file for Moodle mod/choice"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="choice" COMMENT="Available choices are stored here">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="intro" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="publish" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="showresults" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="display" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="allowupdate" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="allowmultiple" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="showunanswered" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="includeinactive" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="1" SEQUENCE="false"/>
<FIELD NAME="limitanswers" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timeopen" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timeclose" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="showpreview" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completionsubmit" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If this field is set to 1, then the activity will be automatically marked as 'complete' once the user submits their choice."/>
<FIELD NAME="showavailable" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="If this field is set to 1, then the the number of available space on choice options will be shown, given limitanswers is set to 1."/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
</INDEXES>
</TABLE>
<TABLE NAME="choice_options" COMMENT="available options to choice">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="choiceid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="text" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="maxanswers" TYPE="int" LENGTH="10" NOTNULL="false" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="choiceid" TYPE="foreign" FIELDS="choiceid" REFTABLE="choice" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="choice_answers" COMMENT="choices performed by users">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="choiceid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="optionid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="choiceid" TYPE="foreign" FIELDS="choiceid" REFTABLE="choice" REFFIELDS="id"/>
<KEY NAME="optionid" TYPE="foreign" FIELDS="optionid" REFTABLE="choice_options" REFFIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>
+36
View File
@@ -0,0 +1,36 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Definition of log events
*
* @package mod_choice
* @category log
* @copyright 2010 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$logs = array(
array('module'=>'choice', 'action'=>'view', 'mtable'=>'choice', 'field'=>'name'),
array('module'=>'choice', 'action'=>'update', 'mtable'=>'choice', 'field'=>'name'),
array('module'=>'choice', 'action'=>'add', 'mtable'=>'choice', 'field'=>'name'),
array('module'=>'choice', 'action'=>'report', 'mtable'=>'choice', 'field'=>'name'),
array('module'=>'choice', 'action'=>'choose', 'mtable'=>'choice', 'field'=>'name'),
array('module'=>'choice', 'action'=>'choose again', 'mtable'=>'choice', 'field'=>'name'),
);
+85
View File
@@ -0,0 +1,85 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Choice external functions and service definitions.
*
* @package mod_choice
* @category external
* @copyright 2015 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.0
*/
defined('MOODLE_INTERNAL') || die;
$functions = array(
'mod_choice_get_choice_results' => array(
'classname' => 'mod_choice_external',
'methodname' => 'get_choice_results',
'description' => 'Retrieve users results for a given choice.',
'type' => 'read',
'capabilities' => '',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_choice_get_choice_options' => array(
'classname' => 'mod_choice_external',
'methodname' => 'get_choice_options',
'description' => 'Retrieve options for a specific choice.',
'type' => 'read',
'capabilities' => 'mod/choice:choose',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_choice_submit_choice_response' => array(
'classname' => 'mod_choice_external',
'methodname' => 'submit_choice_response',
'description' => 'Submit responses to a specific choice item.',
'type' => 'write',
'capabilities' => 'mod/choice:choose',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_choice_view_choice' => array(
'classname' => 'mod_choice_external',
'methodname' => 'view_choice',
'description' => 'Trigger the course module viewed event and update the module completion status.',
'type' => 'write',
'capabilities' => '',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_choice_get_choices_by_courses' => array(
'classname' => 'mod_choice_external',
'methodname' => 'get_choices_by_courses',
'description' => 'Returns a list of choice instances in a provided set of courses,
if no courses are provided then all the choice instances the user has access to will be returned.',
'type' => 'read',
'capabilities' => '',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
'mod_choice_delete_choice_responses' => array(
'classname' => 'mod_choice_external',
'methodname' => 'delete_choice_responses',
'description' => 'Delete the given submitted responses in a choice',
'type' => 'write',
'capabilities' => 'mod/choice:choose',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
);
+56
View File
@@ -0,0 +1,56 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file keeps track of upgrades to the choice module
*
* Sometimes, changes between versions involve
* alterations to database structures and other
* major things that may break installations.
*
* The upgrade function in this file will attempt
* to perform all the necessary actions to upgrade
* your older installation to the current version.
*
* If there's something it cannot do itself, it
* will tell you what you need to do.
*
* The commands in here will all be database-neutral,
* using the methods of database_manager class
*
* Please do not forget to use upgrade_set_timeout()
* before any action that may take longer time to finish.
*
* @package mod_choice
* @copyright 2006 Eloy Lafuente (stronk7) {@link http://stronk7.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function xmldb_choice_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+31
View File
@@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* List of deprecated mod_choice functions.
*
* @package mod_choice
* @copyright 2021 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @deprecated since Moodle 3.11
*/
function choice_get_completion_state() {
$completionclass = \mod_choice\completion\custom_completion::class;
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
}
+107
View File
@@ -0,0 +1,107 @@
<?php
require_once("../../config.php");
require_once("lib.php");
$id = required_param('id',PARAM_INT); // course
$PAGE->set_url('/mod/choice/index.php', array('id'=>$id));
if (!$course = $DB->get_record('course', array('id'=>$id))) {
throw new \moodle_exception('invalidcourseid');
}
require_course_login($course);
$PAGE->set_pagelayout('incourse');
$eventdata = array('context' => context_course::instance($id));
$event = \mod_choice\event\course_module_instance_list_viewed::create($eventdata);
$event->add_record_snapshot('course', $course);
$event->trigger();
$strchoice = get_string("modulename", "choice");
$strchoices = get_string("modulenameplural", "choice");
$PAGE->set_title($strchoices);
$PAGE->set_heading($course->fullname);
$PAGE->navbar->add($strchoices);
echo $OUTPUT->header();
if (! $choices = get_all_instances_in_course("choice", $course)) {
notice(get_string('thereareno', 'moodle', $strchoices), "../../course/view.php?id=$course->id");
}
$usesections = course_format_uses_sections($course->format);
$sql = "SELECT cha.*
FROM {choice} ch, {choice_answers} cha
WHERE cha.choiceid = ch.id AND
ch.course = ? AND cha.userid = ?";
$answers = array () ;
if (isloggedin() and !isguestuser() and $allanswers = $DB->get_records_sql($sql, array($course->id, $USER->id))) {
foreach ($allanswers as $aa) {
$answers[$aa->choiceid] = $aa;
}
unset($allanswers);
}
$timenow = time();
$table = new html_table();
if ($usesections) {
$strsectionname = get_string('sectionname', 'format_'.$course->format);
$table->head = array ($strsectionname, get_string("question"), get_string("answer"));
$table->align = array ("center", "left", "left");
} else {
$table->head = array (get_string("question"), get_string("answer"));
$table->align = array ("left", "left");
}
$currentsection = "";
foreach ($choices as $choice) {
if (!empty($answers[$choice->id])) {
$answer = $answers[$choice->id];
} else {
$answer = "";
}
if (!empty($answer->optionid)) {
$aa = format_string(choice_get_option_text($choice, $answer->optionid));
} else {
$aa = "";
}
if ($usesections) {
$printsection = "";
if ($choice->section !== $currentsection) {
if ($choice->section) {
$printsection = get_section_name($course, $choice->section);
}
if ($currentsection !== "") {
$table->data[] = 'hr';
}
$currentsection = $choice->section;
}
}
//Calculate the href
if (!$choice->visible) {
//Show dimmed if the mod is hidden
$tt_href = "<a class=\"dimmed\" href=\"view.php?id=$choice->coursemodule\">".format_string($choice->name,true)."</a>";
} else {
//Show normal if the mod is visible
$tt_href = "<a href=\"view.php?id=$choice->coursemodule\">".format_string($choice->name,true)."</a>";
}
if ($usesections) {
$table->data[] = array ($printsection, $tt_href, $aa);
} else {
$table->data[] = array ($tt_href, $aa);
}
}
echo "<br />";
echo html_writer::table($table);
echo $OUTPUT->footer();
+161
View File
@@ -0,0 +1,161 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'choice', language 'en', branch 'MOODLE_20_STABLE'
*
* @package mod_choice
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['addmorechoices'] = 'Add more choices';
$string['allowupdate'] = 'Allow choice to be updated';
$string['allowmultiple'] = 'Allow more than one choice to be selected';
$string['answered'] = 'Answered';
$string['calendarend'] = '{$a} closes';
$string['calendarstart'] = '{$a} opens';
$string['cannotsubmit'] = 'Sorry, there was a problem submitting your choice. Please try again.';
$string['closebeforeopen'] = 'You have specified a close date before the open date.';
$string['completiondetail:submit'] = 'Make a choice';
$string['completionsubmit'] = 'Make a choice';
$string['displayhorizontal'] = 'Display horizontally';
$string['displaymode'] = 'Display mode for the options';
$string['displayvertical'] = 'Display vertically';
$string['eventanswercreated'] = 'Choice answer added';
$string['eventanswerdeleted'] = 'Choice answer deleted';
$string['eventanswersubmitted'] = 'Choice made';
$string['eventanswerupdated'] = 'Choice updated';
$string['eventreportdownloaded'] = 'Choice report downloaded';
$string['eventreportviewed'] = 'Choice report viewed';
$string['expired'] = 'This activity closed on {$a}.';
$string['atleastoneoption'] = 'You need to provide at least one possible answer.';
$string['full'] = '(Full)';
$string['havetologin'] = 'You have to log in before you can submit your choice';
$string['choice'] = 'Choice';
$string['choice:addinstance'] = 'Add a new choice';
$string['choiceclose'] = 'Allow responses until';
$string['choice:deleteresponses'] = 'Modify and delete responses';
$string['choice:downloadresponses'] = 'Download responses';
$string['choicefull'] = 'One or more of the options you have selected have already been filled. Your response has not been saved. Please make another selection.';
$string['choice:choose'] = 'Record a choice';
$string['choicename'] = 'Choice name';
$string['choiceopen'] = 'Allow responses from';
$string['choiceoptions'] = 'Choice options';
$string['choiceoptions_help'] = 'Here is where you specify the options that participants have to choose from.
You can fill in any number of these. If you leave some of the options blank, they will not be displayed. If you need more than 8 options, click the "Add 3 fields to form" button.';
$string['limitanswers_help'] = 'This option allows you to limit the number of participants that can select each choice option. When the limit is reached then no-one else can select that option.
Please note that if you use this activity in group mode, then the limit is per group. For example, if the limit is set to 5, then 5 members of each group can select the option. If there are 3 groups, this would mean that up to 5 x 3 = 15 participants could select the option.
If limits are disabled then any number of participants can select each of the options.';
$string['choice:readresponses'] = 'View responses';
$string['choicesaved'] = 'Your choice has been saved';
$string['choicetext'] = 'Choice text';
$string['choice:view'] = 'View choice activity';
$string['chooseaction'] = 'Choose an action ...';
$string['chooseoption'] = 'Choose: {$a}';
$string['description'] = 'Description';
$string['deselectalloption'] = 'Deselect all "{$a}"';
$string['includeinactive'] = 'Include responses from inactive/suspended users';
$string['indicator:cognitivedepth'] = 'Choice cognitive';
$string['indicator:cognitivedepth_help'] = 'This indicator is based on the cognitive depth reached by the student in a Choice activity.';
$string['indicator:cognitivedepthdef'] = 'Choice cognitive';
$string['indicator:cognitivedepthdef_help'] = 'The participant has reached this percentage of the cognitive engagement offered by the Choice activities during this analysis interval (Levels = No view, View, Submit, View feedback)';
$string['indicator:cognitivedepthdef_link'] = 'Learning_analytics_indicators#Cognitive_depth';
$string['indicator:socialbreadth'] = 'Choice social';
$string['indicator:socialbreadth_help'] = 'This indicator is based on the social breadth reached by the student in a Choice activity.';
$string['indicator:socialbreadthdef'] = 'Choice social';
$string['indicator:socialbreadthdef_help'] = 'The participant has reached this percentage of the social engagement offered by the Choice activities during this analysis interval (Levels = No participation, Participant alone, Participant with others)';
$string['indicator:socialbreadthdef_link'] = 'Learning_analytics_indicators#Social_breadth';
$string['limit'] = 'Limit';
$string['limita'] = 'Limit: {$a}';
$string['limitno'] = 'Limit {no}';
$string['limitanswers'] = 'Limit the number of responses allowed';
$string['modulename'] = 'Choice';
$string['modulename_help'] = 'The choice activity module enables a teacher to ask a single question and offer a selection of possible responses.
Choice results may be published after students have answered, after a certain date, or not at all. Results may be published with student names or anonymously (though teachers always see student names and their responses).
A choice activity may be used
* As a quick poll to stimulate thinking about a topic
* To quickly test students\' understanding
* To facilitate student decision-making, for example allowing students to vote on a direction for the course';
$string['modulename_link'] = 'mod/choice/view';
$string['modulenameplural'] = 'Choices';
$string['moveselectedusersto'] = 'Move selected users to...';
$string['multiplenotallowederror'] = 'Multiple answers are not allowed in this choice';
$string['mustchooseone'] = 'You must choose an answer before saving. Nothing was saved.';
$string['noguestchoose'] = 'Sorry, guests are not allowed to make choices.';
$string['noresultsviewable'] = 'The results are not currently viewable.';
$string['notanswered'] = 'Not answered yet';
$string['notenrolledchoose'] = 'Sorry, only enrolled users are allowed to make choices.';
$string['notopenyet'] = 'This activity is not available until {$a}.';
$string['numberofuser'] = 'Number of responses';
$string['numberofuserinpercentage'] = 'Percentage of responses';
$string['openafterclose'] = 'You have specified an open date after the close date';
$string['option'] = 'Option';
$string['optionno'] = 'Option {no}';
$string['options'] = 'Options';
$string['page-mod-choice-x'] = 'Any choice module page';
$string['pluginadministration'] = 'Choice administration';
$string['pluginname'] = 'Choice';
$string['previewing'] = 'This is just a preview of the available options for this activity. You will be able to make a choice when it opens.';
$string['previewonly'] = 'This is just a preview of the available options for this activity. You will not be able to submit your choice until {$a}.';
$string['privacy'] = 'Privacy of results';
$string['privacy:metadata:choice_answers'] = 'Information about the user\'s chosen answer(s) for a given choice activity';
$string['privacy:metadata:choice_answers:choiceid'] = 'The ID of the choice activity';
$string['privacy:metadata:choice_answers:optionid'] = 'The ID of the option that the user selected.';
$string['privacy:metadata:choice_answers:userid'] = 'The ID of the user answering this choice activity';
$string['privacy:metadata:choice_answers:timemodified'] = 'The timestamp indicating when the choice was modified by the user';
$string['publish'] = 'Publish results';
$string['publishafteranswer'] = 'Show results to students after they answer';
$string['publishafterclose'] = 'Show results to students only after the choice is closed';
$string['publishalways'] = 'Always show results to students';
$string['publishanonymous'] = 'Publish anonymous results, do not show student names';
$string['publishinfoanonafter'] = 'Anonymous results will be published after you answer.';
$string['publishinfoanonclose'] = 'Anonymous results will be published after the activity is closed.';
$string['publishinfofullafter'] = 'Full results, showing everyone\'s choices, will be published after you answer.';
$string['publishinfofullclose'] = 'Full results, showing everyone\'s choices, will be published after the activity is closed.';
$string['publishinfonever'] = 'The results of this activity will not be published after you answer.';
$string['publishnames'] = 'Publish full results, showing names and their choices';
$string['publishnot'] = 'Do not publish results to students';
$string['removemychoice'] = 'Remove my choice';
$string['removeresponses'] = 'Remove all responses';
$string['responses'] = 'Responses';
$string['responsesresultgraphheader'] = 'Graph display';
$string['responsesa'] = 'Responses: {$a}';
$string['responsesto'] = 'Responses to {$a}';
$string['results'] = 'Results';
$string['savemychoice'] = 'Save my choice';
$string['search:activity'] = 'Choice - activity information';
$string['selectalloption'] = 'Select all "{$a}"';
$string['showavailable'] = 'Show available spaces';
$string['showavailable_help'] = 'Show participants the limit for each option and the number of responses for it so far.';
$string['showpreview'] = 'Show preview';
$string['showpreview_help'] = 'Allow students to preview the available options before the choice is opened for submission.';
$string['showunanswered'] = 'Show column for unanswered';
$string['spaceleft'] = 'space available';
$string['spacesleft'] = 'spaces available';
$string['taken'] = 'Taken';
$string['viewallresponses'] = 'View {$a} responses';
$string['viewchoices'] = 'View choices';
$string['withselected'] = 'With selected';
$string['userchoosethisoption'] = 'Users who chose this option';
$string['yourselection'] = 'Your selection';
+1372
View File
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Internal library of functions for choice module.
*
* All the choice specific functions, needed to implement the module
* logic, should go here. Never include this file from your lib.php!
*
* @package mod_choice
* @copyright 2016 Stephen Bourget
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* This creates new calendar events given as timeopen and timeclose by $choice.
*
* @param stdClass $choice
* @return void
*/
function choice_set_events($choice) {
global $DB, $CFG;
require_once($CFG->dirroot.'/calendar/lib.php');
// Get CMID if not sent as part of $choice.
if (!isset($choice->coursemodule)) {
$cm = get_coursemodule_from_instance('choice', $choice->id, $choice->course);
$choice->coursemodule = $cm->id;
}
// Choice start calendar events.
$event = new stdClass();
$event->eventtype = CHOICE_EVENT_TYPE_OPEN;
// The CHOICE_EVENT_TYPE_OPEN event should only be an action event if no close time is specified.
$event->type = empty($choice->timeclose) ? CALENDAR_EVENT_TYPE_ACTION : CALENDAR_EVENT_TYPE_STANDARD;
if ($event->id = $DB->get_field('event', 'id',
array('modulename' => 'choice', 'instance' => $choice->id, 'eventtype' => $event->eventtype))) {
if ((!empty($choice->timeopen)) && ($choice->timeopen > 0)) {
// Calendar event exists so update it.
$event->name = get_string('calendarstart', 'choice', $choice->name);
$event->description = format_module_intro('choice', $choice, $choice->coursemodule, false);
$event->format = FORMAT_HTML;
$event->timestart = $choice->timeopen;
$event->timesort = $choice->timeopen;
$event->visible = instance_is_visible('choice', $choice);
$event->timeduration = 0;
$calendarevent = calendar_event::load($event->id);
$calendarevent->update($event, false);
} else {
// Calendar event is on longer needed.
$calendarevent = calendar_event::load($event->id);
$calendarevent->delete();
}
} else {
// Event doesn't exist so create one.
if ((!empty($choice->timeopen)) && ($choice->timeopen > 0)) {
$event->name = get_string('calendarstart', 'choice', $choice->name);
$event->description = format_module_intro('choice', $choice, $choice->coursemodule, false);
$event->format = FORMAT_HTML;
$event->courseid = $choice->course;
$event->groupid = 0;
$event->userid = 0;
$event->modulename = 'choice';
$event->instance = $choice->id;
$event->timestart = $choice->timeopen;
$event->timesort = $choice->timeopen;
$event->visible = instance_is_visible('choice', $choice);
$event->timeduration = 0;
calendar_event::create($event, false);
}
}
// Choice end calendar events.
$event = new stdClass();
$event->type = CALENDAR_EVENT_TYPE_ACTION;
$event->eventtype = CHOICE_EVENT_TYPE_CLOSE;
if ($event->id = $DB->get_field('event', 'id',
array('modulename' => 'choice', 'instance' => $choice->id, 'eventtype' => $event->eventtype))) {
if ((!empty($choice->timeclose)) && ($choice->timeclose > 0)) {
// Calendar event exists so update it.
$event->name = get_string('calendarend', 'choice', $choice->name);
$event->description = format_module_intro('choice', $choice, $choice->coursemodule, false);
$event->format = FORMAT_HTML;
$event->timestart = $choice->timeclose;
$event->timesort = $choice->timeclose;
$event->visible = instance_is_visible('choice', $choice);
$event->timeduration = 0;
$calendarevent = calendar_event::load($event->id);
$calendarevent->update($event, false);
} else {
// Calendar event is on longer needed.
$calendarevent = calendar_event::load($event->id);
$calendarevent->delete();
}
} else {
// Event doesn't exist so create one.
if ((!empty($choice->timeclose)) && ($choice->timeclose > 0)) {
$event->name = get_string('calendarend', 'choice', $choice->name);
$event->description = format_module_intro('choice', $choice, $choice->coursemodule, false);
$event->format = FORMAT_HTML;
$event->courseid = $choice->course;
$event->groupid = 0;
$event->userid = 0;
$event->modulename = 'choice';
$event->instance = $choice->id;
$event->timestart = $choice->timeclose;
$event->timesort = $choice->timeclose;
$event->visible = instance_is_visible('choice', $choice);
$event->timeduration = 0;
calendar_event::create($event, false);
}
}
}
+184
View File
@@ -0,0 +1,184 @@
<?php
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
require_once ($CFG->dirroot.'/course/moodleform_mod.php');
class mod_choice_mod_form extends moodleform_mod {
function definition() {
global $CFG, $CHOICE_SHOWRESULTS, $CHOICE_PUBLISH, $CHOICE_DISPLAY, $DB;
$mform =& $this->_form;
//-------------------------------------------------------------------------------
$mform->addElement('header', 'general', get_string('general', 'form'));
$mform->addElement('text', 'name', get_string('choicename', 'choice'), array('size'=>'64'));
if (!empty($CFG->formatstringstriptags)) {
$mform->setType('name', PARAM_TEXT);
} else {
$mform->setType('name', PARAM_CLEANHTML);
}
$mform->addRule('name', null, 'required', null, 'client');
$mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$this->standard_intro_elements(get_string('description', 'choice'));
$mform->addElement('select', 'display', get_string("displaymode","choice"), $CHOICE_DISPLAY);
//-------------------------------------------------------------------------------
$mform->addElement('header', 'optionhdr', get_string('options', 'choice'));
$mform->addElement('selectyesno', 'allowupdate', get_string("allowupdate", "choice"));
$mform->addElement('selectyesno', 'allowmultiple', get_string('allowmultiple', 'choice'));
if ($this->_instance) {
if ($DB->count_records('choice_answers', array('choiceid' => $this->_instance)) > 0) {
// Prevent user from toggeling the number of allowed answers once there are submissions.
$mform->freeze('allowmultiple');
}
}
$mform->addElement('selectyesno', 'limitanswers', get_string('limitanswers', 'choice'));
$mform->addHelpButton('limitanswers', 'limitanswers', 'choice');
$mform->addElement('selectyesno', 'showavailable', get_string('showavailable', 'choice'));
$mform->addHelpButton('showavailable', 'showavailable', 'choice');
$mform->hideIf('showavailable', 'limitanswers', 'eq', 0);
$repeatarray = array();
$repeatarray[] = $mform->createElement('text', 'option', get_string('optionno', 'choice'));
$repeatarray[] = $mform->createElement('text', 'limit', get_string('limitno', 'choice'));
$repeatarray[] = $mform->createElement('hidden', 'optionid', 0);
if ($this->_instance){
$repeatno = $DB->count_records('choice_options', array('choiceid'=>$this->_instance));
$repeatno += 2;
} else {
$repeatno = 5;
}
$repeateloptions = array();
$repeateloptions['limit']['default'] = 0;
$repeateloptions['limit']['hideif'] = array('limitanswers', 'eq', 0);
$repeateloptions['limit']['rule'] = 'numeric';
$repeateloptions['limit']['type'] = PARAM_INT;
$repeateloptions['option']['helpbutton'] = array('choiceoptions', 'choice');
$mform->setType('option', PARAM_CLEANHTML);
$mform->setType('optionid', PARAM_INT);
$this->repeat_elements($repeatarray, $repeatno,
$repeateloptions, 'option_repeats', 'option_add_fields', 3, null, true);
// Make the first option required
if ($mform->elementExists('option[0]')) {
$mform->addRule('option[0]', get_string('atleastoneoption', 'choice'), 'required', null, 'client');
}
//-------------------------------------------------------------------------------
$mform->addElement('header', 'availabilityhdr', get_string('availability'));
$mform->addElement('date_time_selector', 'timeopen', get_string("choiceopen", "choice"),
array('optional' => true));
$mform->addElement('date_time_selector', 'timeclose', get_string("choiceclose", "choice"),
array('optional' => true));
$mform->addElement('advcheckbox', 'showpreview', get_string('showpreview', 'choice'));
$mform->addHelpButton('showpreview', 'showpreview', 'choice');
$mform->disabledIf('showpreview', 'timeopen[enabled]');
//-------------------------------------------------------------------------------
$mform->addElement('header', 'resultshdr', get_string('results', 'choice'));
$mform->addElement('select', 'showresults', get_string("publish", "choice"), $CHOICE_SHOWRESULTS);
$mform->addElement('select', 'publish', get_string("privacy", "choice"), $CHOICE_PUBLISH);
$mform->hideIf('publish', 'showresults', 'eq', 0);
$mform->addElement('selectyesno', 'showunanswered', get_string("showunanswered", "choice"));
$mform->addElement('selectyesno', 'includeinactive', get_string('includeinactive', 'choice'));
$mform->setDefault('includeinactive', 0);
//-------------------------------------------------------------------------------
$this->standard_coursemodule_elements();
//-------------------------------------------------------------------------------
$this->add_action_buttons();
}
function data_preprocessing(&$default_values){
global $DB;
if (!empty($this->_instance) && ($options = $DB->get_records_menu('choice_options',array('choiceid'=>$this->_instance), 'id', 'id,text'))
&& ($options2 = $DB->get_records_menu('choice_options', array('choiceid'=>$this->_instance), 'id', 'id,maxanswers')) ) {
$choiceids=array_keys($options);
$options=array_values($options);
$options2=array_values($options2);
foreach (array_keys($options) as $key){
$default_values['option['.$key.']'] = $options[$key];
$default_values['limit['.$key.']'] = $options2[$key];
$default_values['optionid['.$key.']'] = $choiceids[$key];
}
}
}
/**
* Allows module to modify the data returned by form get_data().
* This method is also called in the bulk activity completion form.
*
* Only available on moodleform_mod.
*
* @param stdClass $data the form data to be modified.
*/
public function data_postprocessing($data) {
parent::data_postprocessing($data);
// Set up completion section even if checkbox is not ticked.
if (!empty($data->completionunlocked)) {
$suffix = $this->get_suffix();
if (empty($data->{'completionsubmit' . $suffix})) {
$data->{'completionsubmit' . $suffix} = 0;
}
}
}
/**
* Enforce validation rules here
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @return array
**/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
// Check open and close times are consistent.
if ($data['timeopen'] && $data['timeclose'] &&
$data['timeclose'] < $data['timeopen']) {
$errors['timeclose'] = get_string('closebeforeopen', 'choice');
}
return $errors;
}
public function add_completion_rules() {
$mform =& $this->_form;
$suffix = $this->get_suffix();
$completionsubmitel = 'completionsubmit' . $suffix;
$mform->addElement('checkbox', $completionsubmitel, '', get_string('completionsubmit', 'choice'));
// Enable this completion rule by default.
$mform->setDefault($completionsubmitel, 1);
return [$completionsubmitel];
}
public function completion_rule_enabled($data) {
$suffix = $this->get_suffix();
return !empty($data['completionsubmit' . $suffix]);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512" preserveAspectRatio="xMinYMid meet"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 96C0 60.7 28.7 32 64 32H448c35.3 0 64 28.7 64 64V416c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V96zm64 64V416H224V160H64zm384 0H288V416H448V160z"/></svg>

After

Width:  |  Height:  |  Size: 449 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.64999 3.14337C6.79305 3.00054 7.00805 2.95791 7.19478 3.03535C7.38152 3.11278 7.50325 3.29505 7.50325 3.49721V4.49723C9.24865 4.50145 10.8331 5.19273 11.9998 6.31366C13.167 5.19219 14.7511 4.50145 16.4964 4.49723V3.49721C16.4964 3.29505 16.6182 3.11278 16.8049 3.03535C16.9916 2.95791 17.2066 3.00054 17.3497 3.14337L20.8569 6.64482C20.9508 6.73861 21.0036 6.86591 21.0036 6.99866C21.0036 7.13141 20.9508 7.25871 20.8569 7.3525L17.3497 10.854C17.2066 10.9968 16.9916 11.0394 16.8049 10.962C16.6182 10.8845 16.4964 10.7023 16.4964 10.5001V8.4959C15.1202 8.51134 14.0091 9.63243 14.0091 11.0126V19.0253C13.9941 20.12 13.1018 21.0028 12.0035 21.0028C10.9052 21.0028 10.0131 20.12 9.99807 19.0253L9.99788 11.0126H9.99058C9.99058 9.63243 8.87945 8.51134 7.50325 8.4959V10.5001C7.50325 10.7023 7.38152 10.8845 7.19478 10.962C7.00805 11.0394 6.79305 10.9968 6.64999 10.854L3.14283 7.3525C3.04888 7.25871 2.99609 7.13141 2.99609 6.99866C2.99609 6.86591 3.04888 6.73861 3.14283 6.64482L6.64999 3.14337ZM10.3284 8.95812C10.5579 8.26681 10.9001 7.62687 11.3332 7.0601C10.349 6.1018 9.0093 5.50882 7.53076 5.49738C7.52423 5.49763 7.51767 5.49776 7.51108 5.49776H7.00325C6.72711 5.49776 6.50325 5.2739 6.50325 4.99776V4.70293L4.20378 6.99866L6.50325 9.2944V7.9935C6.50325 7.71735 6.72711 7.4935 7.00325 7.4935H7.48716C7.50534 7.4935 7.5233 7.49447 7.54098 7.49636C8.68879 7.51766 9.70204 8.0892 10.3284 8.95812ZM10.9979 11.0126C10.9979 7.98141 13.4434 5.52083 16.4689 5.49738C16.4754 5.49763 16.482 5.49776 16.4886 5.49776H16.9964C17.2726 5.49776 17.4964 5.2739 17.4964 4.99776V4.70293L19.7959 6.99866L17.4964 9.2944V7.9935C17.4964 7.71735 17.2726 7.4935 16.9964 7.4935H16.5125C16.4943 7.4935 16.4764 7.49447 16.4587 7.49636C14.5473 7.53184 13.0091 9.09307 13.0091 11.0126V18.9972C13.0091 19.5526 12.5589 20.0028 12.0035 20.0028C11.4481 20.0028 10.9979 19.5526 10.9979 18.9972V11.0126Z" fill="#212529"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 192 512" preserveAspectRatio="xMinYMid meet"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M48 80a48 48 0 1 1 96 0A48 48 0 1 1 48 80zM0 224c0-17.7 14.3-32 32-32H96c17.7 0 32 14.3 32 32V448h32c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H64V256H32c-17.7 0-32-14.3-32-32z"/></svg>

After

Width:  |  Height:  |  Size: 507 B

+412
View File
@@ -0,0 +1,412 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Moodle renderer used to display special elements of the lesson module
*
* @package mod_choice
* @copyright 2010 Rossiani Wijaya
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
**/
class mod_choice_renderer extends plugin_renderer_base {
/**
* Returns HTML to display choices of option
* @param object $options
* @param int $coursemoduleid
* @param bool $vertical
* @return string
*/
public function display_options($options, $coursemoduleid, $vertical = false, $multiple = false) {
$layoutclass = 'horizontal';
if ($vertical) {
$layoutclass = 'vertical';
}
$target = new moodle_url('/mod/choice/view.php');
$attributes = array('method'=>'POST', 'action'=>$target, 'class'=> $layoutclass);
$disabled = empty($options['previewonly']) ? array() : array('disabled' => 'disabled');
$html = html_writer::start_tag('form', $attributes);
$html .= html_writer::start_tag('ul', array('class' => 'choices list-unstyled unstyled'));
$availableoption = count($options['options']);
$choicecount = 0;
foreach ($options['options'] as $option) {
$choicecount++;
$html .= html_writer::start_tag('li', array('class' => 'option mr-3'));
if ($multiple) {
$option->attributes->name = 'answer[]';
$option->attributes->type = 'checkbox';
} else {
$option->attributes->name = 'answer';
$option->attributes->type = 'radio';
}
$option->attributes->id = 'choice_'.$choicecount;
$option->attributes->class = 'mx-1';
$labeltext = $option->text;
if (!empty($option->attributes->disabled)) {
$labeltext .= ' ' . get_string('full', 'choice');
$availableoption--;
}
if (!empty($options['limitanswers']) && !empty($options['showavailable'])) {
$labeltext .= html_writer::empty_tag('br');
$labeltext .= get_string("responsesa", "choice", $option->countanswers);
$labeltext .= html_writer::empty_tag('br');
$labeltext .= get_string("limita", "choice", $option->maxanswers);
}
$html .= html_writer::empty_tag('input', (array)$option->attributes + $disabled);
$html .= html_writer::tag('label', $labeltext, array('for'=>$option->attributes->id));
$html .= html_writer::end_tag('li');
}
$html .= html_writer::tag('li','', array('class'=>'clearfloat'));
$html .= html_writer::end_tag('ul');
$html .= html_writer::tag('div', '', array('class'=>'clearfloat'));
$html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey()));
$html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'action', 'value'=>'makechoice'));
$html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=>$coursemoduleid));
if (empty($options['previewonly'])) {
if (!empty($options['hascapability']) && ($options['hascapability'])) {
if ($availableoption < 1) {
$html .= html_writer::tag('label', get_string('choicefull', 'choice'));
} else {
$html .= html_writer::empty_tag('input', array(
'type' => 'submit',
'value' => get_string('savemychoice', 'choice'),
'class' => 'btn btn-primary'
));
}
if (!empty($options['allowupdate']) && ($options['allowupdate'])) {
$url = new moodle_url('view.php',
array('id' => $coursemoduleid, 'action' => 'delchoice', 'sesskey' => sesskey()));
$html .= html_writer::link($url, get_string('removemychoice', 'choice'), array('class' => 'ml-1'));
}
} else {
$html .= html_writer::tag('label', get_string('havetologin', 'choice'));
}
}
$html .= html_writer::end_tag('ul');
$html .= html_writer::end_tag('form');
return $html;
}
/**
* Returns HTML to display choices result
* @param object $choices
* @param bool $forcepublish
* @return string
*/
public function display_result($choices, $forcepublish = false) {
if (empty($forcepublish)) { //allow the publish setting to be overridden
$forcepublish = $choices->publish;
}
$displaylayout = $choices->display;
if ($forcepublish) { //CHOICE_PUBLISH_NAMES
return $this->display_publish_name_vertical($choices);
} else {
return $this->display_publish_anonymous($choices, $displaylayout);
}
}
/**
* Returns HTML to display choices result
* @param object $choices
* @return string
*/
public function display_publish_name_vertical($choices) {
$html ='';
$attributes = array('method'=>'POST');
$attributes['action'] = new moodle_url($this->page->url);
$attributes['id'] = 'attemptsform';
if ($choices->viewresponsecapability) {
$html .= html_writer::start_tag('form', $attributes);
$html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=> $choices->coursemoduleid));
$html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=> sesskey()));
$html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'mode', 'value'=>'overview'));
}
$table = new html_table();
$table->cellpadding = 0;
$table->cellspacing = 0;
$table->attributes['class'] = 'results names table table-bordered';
$table->tablealign = 'center';
$table->summary = get_string('responsesto', 'choice', format_string($choices->name));
$table->data = array();
$count = 0;
ksort($choices->options);
$columns = array();
$celldefault = new html_table_cell();
$celldefault->attributes['class'] = 'data';
// This extra cell is needed in order to support accessibility for screenreader. MDL-30816
$accessiblecell = new html_table_cell();
$accessiblecell->scope = 'row';
$accessiblecell->text = get_string('choiceoptions', 'choice');
$columns['options'][] = $accessiblecell;
$usernumberheader = clone($celldefault);
$usernumberheader->header = true;
$usernumberheader->attributes['class'] = 'header data';
$usernumberheader->text = get_string('numberofuser', 'choice');
$columns['usernumber'][] = $usernumberheader;
$optionsnames = [];
foreach ($choices->options as $optionid => $options) {
$celloption = clone($celldefault);
$cellusernumber = clone($celldefault);
if ($choices->showunanswered && $optionid == 0) {
$headertitle = get_string('notanswered', 'choice');
} else if ($optionid > 0) {
$headertitle = format_string($choices->options[$optionid]->text);
if (!empty($choices->options[$optionid]->user) && count($choices->options[$optionid]->user) > 0) {
if (
$choices->limitanswers &&
(count($choices->options[$optionid]->user) == $choices->options[$optionid]->maxanswer)
) {
$headertitle .= ' ' . get_string('full', 'choice');
}
}
}
$celltext = $headertitle;
// Render select/deselect all checkbox for this option.
if ($choices->viewresponsecapability && $choices->deleterepsonsecapability) {
// Build the select/deselect all for this option.
$selectallid = 'select-response-option-' . $optionid;
$togglegroup = 'responses response-option-' . $optionid;
$selectalltext = get_string('selectalloption', 'choice', $headertitle);
$deselectalltext = get_string('deselectalloption', 'choice', $headertitle);
$mastercheckbox = new \core\output\checkbox_toggleall($togglegroup, true, [
'id' => $selectallid,
'name' => $selectallid,
'value' => 1,
'selectall' => $selectalltext,
'deselectall' => $deselectalltext,
'label' => $selectalltext,
'labelclasses' => 'accesshide',
]);
$celltext .= html_writer::div($this->output->render($mastercheckbox));
}
$numberofuser = 0;
if (!empty($options->user) && count($options->user) > 0) {
$numberofuser = count($options->user);
}
if (($choices->limitanswers) && ($choices->showavailable)) {
$numberofuser .= html_writer::empty_tag('br');
$numberofuser .= get_string("limita", "choice", $options->maxanswer);
}
$celloption->text = html_writer::div($celltext, 'text-center');
$optionsnames[$optionid] = $celltext;
$cellusernumber->text = html_writer::div($numberofuser, 'text-center');
$columns['options'][] = $celloption;
$columns['usernumber'][] = $cellusernumber;
}
$table->head = $columns['options'];
$table->data[] = new html_table_row($columns['usernumber']);
$columns = array();
// This extra cell is needed in order to support accessibility for screenreader. MDL-30816
$accessiblecell = new html_table_cell();
$accessiblecell->text = get_string('userchoosethisoption', 'choice');
$accessiblecell->header = true;
$accessiblecell->scope = 'row';
$accessiblecell->attributes['class'] = 'header data';
$columns[] = $accessiblecell;
foreach ($choices->options as $optionid => $options) {
$cell = new html_table_cell();
$cell->attributes['class'] = 'data';
if ($choices->showunanswered || $optionid > 0) {
if (!empty($options->user)) {
$optionusers = '';
foreach ($options->user as $user) {
$data = '';
if (empty($user->imagealt)) {
$user->imagealt = '';
}
$userfullname = fullname($user, $choices->fullnamecapability);
$checkbox = '';
if ($choices->viewresponsecapability && $choices->deleterepsonsecapability) {
$checkboxid = 'attempt-user' . $user->id . '-option' . $optionid;
if ($optionid > 0) {
$checkboxname = 'attemptid[]';
$checkboxvalue = $user->answerid;
} else {
$checkboxname = 'userid[]';
$checkboxvalue = $user->id;
}
$togglegroup = 'responses response-option-' . $optionid;
$slavecheckbox = new \core\output\checkbox_toggleall($togglegroup, false, [
'id' => $checkboxid,
'name' => $checkboxname,
'classes' => 'mr-1',
'value' => $checkboxvalue,
'label' => $userfullname . ' ' . $options->text,
'labelclasses' => 'accesshide',
]);
$checkbox = $this->output->render($slavecheckbox);
}
$userimage = $this->output->user_picture($user, array('courseid' => $choices->courseid, 'link' => false));
$profileurl = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $choices->courseid));
$profilelink = html_writer::link($profileurl, $userimage . $userfullname);
$data .= html_writer::div($checkbox . $profilelink, 'mb-1');
$optionusers .= $data;
}
$cell->text = $optionusers;
}
}
$columns[] = $cell;
$count++;
}
$row = new html_table_row($columns);
$table->data[] = $row;
$html .= html_writer::tag('div', html_writer::table($table), array('class'=>'response'));
$actiondata = '';
if ($choices->viewresponsecapability && $choices->deleterepsonsecapability) {
// Build the select/deselect all for all of options.
$selectallid = 'select-all-responses';
$togglegroup = 'responses';
$selectallcheckbox = new \core\output\checkbox_toggleall($togglegroup, true, [
'id' => $selectallid,
'name' => $selectallid,
'value' => 1,
'label' => get_string('selectall'),
'classes' => 'btn-secondary mr-1'
], true);
$actiondata .= $this->output->render($selectallcheckbox);
$actionurl = new moodle_url($this->page->url,
['sesskey' => sesskey(), 'action' => 'delete_confirmation()']);
$actionoptions = array('delete' => get_string('delete'));
foreach ($choices->options as $optionid => $option) {
if ($optionid > 0) {
$actionoptions['choose_'.$optionid] = get_string('chooseoption', 'choice', $option->text);
}
}
$selectattributes = [
'data-action' => 'toggle',
'data-togglegroup' => 'responses',
'data-toggle' => 'action',
];
$selectnothing = ['' => get_string('chooseaction', 'choice')];
$select = new single_select($actionurl, 'action', $actionoptions, null, $selectnothing, 'attemptsform');
$select->set_label(get_string('withselected', 'choice'));
$select->disabled = true;
$select->attributes = $selectattributes;
$actiondata .= $this->output->render($select);
}
$html .= html_writer::tag('div', $actiondata, array('class'=>'responseaction'));
if ($choices->viewresponsecapability) {
$html .= html_writer::end_tag('form');
}
return $html;
}
/**
* Returns HTML to display choices result
* @deprecated since 3.2
* @param object $choices
* @return string
*/
public function display_publish_anonymous_horizontal($choices) {
debugging(__FUNCTION__.'() is deprecated. Please use mod_choice_renderer::display_publish_anonymous() instead.',
DEBUG_DEVELOPER);
return $this->display_publish_anonymous($choices, CHOICE_DISPLAY_VERTICAL);
}
/**
* Returns HTML to display choices result
* @deprecated since 3.2
* @param object $choices
* @return string
*/
public function display_publish_anonymous_vertical($choices) {
debugging(__FUNCTION__.'() is deprecated. Please use mod_choice_renderer::display_publish_anonymous() instead.',
DEBUG_DEVELOPER);
return $this->display_publish_anonymous($choices, CHOICE_DISPLAY_HORIZONTAL);
}
/**
* Generate the choice result chart.
*
* Can be displayed either in the vertical or horizontal position.
*
* @param stdClass $choices Choices responses object.
* @param int $displaylayout The constants CHOICE_DISPLAY_HORIZONTAL or CHOICE_DISPLAY_VERTICAL.
* @return string the rendered chart.
*/
public function display_publish_anonymous($choices, $displaylayout) {
$count = 0;
$data = [];
$numberofuser = 0;
$percentageamount = 0;
foreach ($choices->options as $optionid => $option) {
if (!empty($option->user)) {
$numberofuser = count($option->user);
}
if($choices->numberofuser > 0) {
$percentageamount = ((float)$numberofuser / (float)$choices->numberofuser) * 100.0;
}
$data['labels'][$count] = $option->text;
$data['series'][$count] = $numberofuser;
$data['series_labels'][$count] = $numberofuser . ' (' . format_float($percentageamount, 1) . '%)';
$count++;
$numberofuser = 0;
}
$chart = new \core\chart_bar();
if ($displaylayout == CHOICE_DISPLAY_VERTICAL) {
$chart->set_horizontal(true); // Horizontal bars when choices are vertical.
}
$series = new \core\chart_series(format_string(get_string("responses", "choice")), $data['series']);
$series->set_labels($data['series_labels']);
$chart->add_series($series);
$chart->set_labels($data['labels']);
$yaxis = $chart->get_yaxis(0, true);
$yaxis->set_stepsize(max(1, round(max($data['series']) / 10)));
return $this->output->render($chart);
}
}
+309
View File
@@ -0,0 +1,309 @@
<?php
require_once("../../config.php");
require_once("lib.php");
$id = required_param('id', PARAM_INT); //moduleid
$download = optional_param('download', '', PARAM_ALPHA);
$action = optional_param('action', '', PARAM_ALPHANUMEXT);
$attemptids = optional_param_array('attemptid', array(), PARAM_INT); // Get array of responses to delete or modify.
$userids = optional_param_array('userid', array(), PARAM_INT); // Get array of users whose choices need to be modified.
$url = new moodle_url('/mod/choice/report.php', array('id'=>$id));
if ($download !== '') {
$url->param('download', $download);
}
if ($action !== '') {
$url->param('action', $action);
}
$PAGE->set_url($url);
if (! $cm = get_coursemodule_from_id('choice', $id)) {
throw new \moodle_exception("invalidcoursemodule");
}
if (! $course = $DB->get_record("course", array("id" => $cm->course))) {
throw new \moodle_exception("coursemisconf");
}
require_login($course, false, $cm);
$context = context_module::instance($cm->id);
require_capability('mod/choice:readresponses', $context);
if (!$choice = choice_get_choice($cm->instance)) {
throw new \moodle_exception('invalidcoursemodule');
}
$strchoice = get_string("modulename", "choice");
$strchoices = get_string("modulenameplural", "choice");
$strresponses = get_string("responses", "choice");
$eventdata = array();
$eventdata['objectid'] = $choice->id;
$eventdata['context'] = $context;
$eventdata['courseid'] = $course->id;
$eventdata['other']['content'] = 'choicereportcontentviewed';
$event = \mod_choice\event\report_viewed::create($eventdata);
$event->trigger();
if (data_submitted() && has_capability('mod/choice:deleteresponses', $context) && confirm_sesskey()) {
if ($action === 'delete') {
// Delete responses of other users.
choice_delete_responses($attemptids, $choice, $cm, $course);
redirect("report.php?id=$cm->id");
}
if (preg_match('/^choose_(\d+)$/', $action, $actionmatch)) {
// Modify responses of other users.
$newoptionid = (int)$actionmatch[1];
choice_modify_responses($userids, $attemptids, $newoptionid, $choice, $cm, $course);
redirect("report.php?id=$cm->id");
}
}
$groupmode = groups_get_activity_groupmode($cm);
if (!$download) {
$PAGE->set_title(format_string($choice->name).": $strresponses");
$PAGE->set_heading($course->fullname);
$PAGE->activityheader->set_attrs([
'hidecompletion' => true,
'description' => ''
]);
echo $OUTPUT->header();
echo $OUTPUT->heading($strresponses);
/// Check to see if groups are being used in this choice
if ($groupmode) {
groups_get_activity_group($cm, true);
$groupsactivitymenu = groups_print_activity_menu($cm, $CFG->wwwroot . '/mod/choice/report.php?id=' . $id,
true);
echo html_writer::div($groupsactivitymenu, 'mb-2');
}
} else {
// Trigger the report downloaded event.
$eventdata = array();
$eventdata['context'] = $context;
$eventdata['courseid'] = $course->id;
$eventdata['other']['content'] = 'choicereportcontentviewed';
$eventdata['other']['format'] = $download;
$eventdata['other']['choiceid'] = $choice->id;
$event = \mod_choice\event\report_downloaded::create($eventdata);
$event->trigger();
}
// Check if we want to include responses from inactive users.
$onlyactive = $choice->includeinactive ? false : true;
$users = choice_get_response_data($choice, $cm, $groupmode, $onlyactive);
// TODO Does not support custom user profile fields (MDL-70456).
$extrafields = \core_user\fields::get_identity_fields($context, false);
if ($download == "ods" && has_capability('mod/choice:downloadresponses', $context)) {
require_once("$CFG->libdir/odslib.class.php");
/// Calculate file name
$shortname = format_string($course->shortname, true, array('context' => $context));
$choicename = format_string($choice->name, true, array('context' => $context));
$filename = clean_filename("$shortname " . strip_tags($choicename)) . '.ods';
/// Creating a workbook
$workbook = new MoodleODSWorkbook("-");
/// Send HTTP headers
$workbook->send($filename);
/// Creating the first worksheet
$myxls = $workbook->add_worksheet($strresponses);
/// Print names of all the fields
$i = 0;
$myxls->write_string(0, $i++, get_string("lastname"));
$myxls->write_string(0, $i++, get_string("firstname"));
// Add headers for extra user fields.
foreach ($extrafields as $field) {
$myxls->write_string(0, $i++, \core_user\fields::get_display_name($field));
}
$myxls->write_string(0, $i++, get_string("group"));
$myxls->write_string(0, $i++, get_string("choice", "choice"));
// Generate the data for the body of the spreadsheet.
$row = 1;
if ($users) {
foreach ($users as $option => $userid) {
if (!($choice->showunanswered == 0 && $option == 0)) {
$option_text = choice_get_option_text($choice, $option);
foreach ($userid as $user) {
$i = 0;
$myxls->write_string($row, $i++, $user->lastname);
$myxls->write_string($row, $i++, $user->firstname);
foreach ($extrafields as $field) {
$myxls->write_string($row, $i++, $user->$field);
}
$ug2 = '';
if ($usergrps = groups_get_all_groups($course->id, $user->id)) {
foreach ($usergrps as $ug) {
$ug2 = $ug2 . $ug->name;
}
}
$myxls->write_string($row, $i++, $ug2);
if (isset($option_text)) {
$myxls->write_string($row, $i++, format_string($option_text, true));
}
$row++;
}
}
}
}
/// Close the workbook
$workbook->close();
exit;
}
//print spreadsheet if one is asked for:
if ($download == "xls" && has_capability('mod/choice:downloadresponses', $context)) {
require_once("$CFG->libdir/excellib.class.php");
/// Calculate file name
$shortname = format_string($course->shortname, true, array('context' => $context));
$choicename = format_string($choice->name, true, array('context' => $context));
$filename = clean_filename("$shortname " . strip_tags($choicename)) . '.xls';
/// Creating a workbook
$workbook = new MoodleExcelWorkbook("-");
/// Send HTTP headers
$workbook->send($filename);
/// Creating the first worksheet
$myxls = $workbook->add_worksheet($strresponses);
/// Print names of all the fields
$i = 0;
$myxls->write_string(0, $i++, get_string("lastname"));
$myxls->write_string(0, $i++, get_string("firstname"));
// Add headers for extra user fields.
foreach ($extrafields as $field) {
$myxls->write_string(0, $i++, \core_user\fields::get_display_name($field));
}
$myxls->write_string(0, $i++, get_string("group"));
$myxls->write_string(0, $i++, get_string("choice", "choice"));
// Generate the data for the body of the spreadsheet.
$row = 1;
if ($users) {
foreach ($users as $option => $userid) {
$i = 0;
if (!($choice->showunanswered == 0 && $option == 0)) {
$option_text = choice_get_option_text($choice, $option);
foreach($userid as $user) {
$i = 0;
$myxls->write_string($row, $i++, $user->lastname);
$myxls->write_string($row, $i++, $user->firstname);
foreach ($extrafields as $field) {
$myxls->write_string($row, $i++, $user->$field);
}
$ug2 = '';
if ($usergrps = groups_get_all_groups($course->id, $user->id)) {
foreach ($usergrps as $ug) {
$ug2 = $ug2 . $ug->name;
}
}
$myxls->write_string($row, $i++, $ug2);
if (isset($option_text)) {
$myxls->write_string($row, $i++, format_string($option_text, true));
}
$row++;
}
}
}
}
/// Close the workbook
$workbook->close();
exit;
}
// print text file
if ($download == "txt" && has_capability('mod/choice:downloadresponses', $context)) {
$shortname = format_string($course->shortname, true, array('context' => $context));
$choicename = format_string($choice->name, true, array('context' => $context));
$filename = clean_filename("$shortname " . strip_tags($choicename)) . '.txt';
header("Content-Type: application/download\n");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Expires: 0");
header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
header("Pragma: public");
/// Print names of all the fields
echo get_string("lastname") . "\t" . get_string("firstname") . "\t";
// Add headers for extra user fields.
foreach ($extrafields as $field) {
echo \core_user\fields::get_display_name($field) . "\t";
}
echo get_string("group"). "\t";
echo get_string("choice","choice"). "\n";
/// generate the data for the body of the spreadsheet
$i=0;
if ($users) {
foreach ($users as $option => $userid) {
if (!($choice->showunanswered == 0 && $option == 0)) {
$option_text = choice_get_option_text($choice, $option);
foreach($userid as $user) {
echo $user->lastname . "\t";
echo $user->firstname . "\t";
foreach ($extrafields as $field) {
echo $user->$field . "\t";
}
$ug2 = '';
if ($usergrps = groups_get_all_groups($course->id, $user->id)) {
foreach ($usergrps as $ug) {
$ug2 = $ug2. $ug->name;
}
}
echo $ug2. "\t";
if (isset($option_text)) {
echo format_string($option_text,true);
}
echo "\n";
}
}
}
}
exit;
}
$results = prepare_choice_show_results($choice, $course, $cm, $users);
$renderer = $PAGE->get_renderer('mod_choice');
echo $renderer->display_result($results, true);
//now give links for downloading spreadsheets.
if (!empty($users) && has_capability('mod/choice:downloadresponses',$context)) {
$downloadoptions = array();
$options = array();
$options["id"] = "$cm->id";
$options["download"] = "ods";
$button = $OUTPUT->single_button(new moodle_url("report.php", $options), get_string("downloadods"));
$downloadoptions[] = html_writer::tag('li', $button, array('class' => 'reportoption list-inline-item'));
$options["download"] = "xls";
$button = $OUTPUT->single_button(new moodle_url("report.php", $options), get_string("downloadexcel"));
$downloadoptions[] = html_writer::tag('li', $button, array('class' => 'reportoption list-inline-item'));
$options["download"] = "txt";
$button = $OUTPUT->single_button(new moodle_url("report.php", $options), get_string("downloadtext"));
$downloadoptions[] = html_writer::tag('li', $button, array('class' => 'reportoption list-inline-item'));
$downloadlist = html_writer::tag('ul', implode('', $downloadoptions), array('class' => 'list-inline inline'));
$downloadlist .= html_writer::tag('div', '', array('class' => 'clearfloat'));
echo html_writer::tag('div',$downloadlist, array('class' => 'downloadreport mt-1'));
}
echo $OUTPUT->footer();
@@ -0,0 +1,65 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_choice\backup;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php");
/**
* Restore date tests.
*
* @package mod_choice
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_date_test extends \restore_date_testcase {
public function test_restore_dates(): void {
global $DB, $USER;
$time = 100000;
$record = ['timeopen' => $time, 'timeclose' => $time + 1];
list($course, $choice) = $this->create_course_and_module('choice', $record);
$options = $DB->get_records('choice_options', ['choiceid' => $choice->id]);
$DB->set_field('choice_options', 'timemodified', $time);
$option = reset($options);
$cm = $DB->get_record('course_modules', ['id' => $choice->cmid]);
choice_user_submit_response($option->id, $choice, $USER->id, $course, $cm);
$answer = $DB->get_record('choice_answers', ['choiceid' => $choice->id]);
// Do backup and restore.
$newcourseid = $this->backup_and_restore($course);
$newchoice = $DB->get_record('choice', ['course' => $newcourseid]);
$newoptions = $DB->get_records('choice_options', ['choiceid' => $newchoice->id]);
$this->assertFieldsNotRolledForward($choice, $newchoice, ['timemodified']);
$props = ['timeopen', 'timeclose'];
$this->assertFieldsRolledForward($choice, $newchoice, $props);
// Options check.
foreach ($newoptions as $newoption) {
$this->assertEquals($time, $newoption->timemodified);
}
// Answers check.
$newanswer = $DB->get_record('choice_answers', ['choiceid' => $newchoice->id]);
$this->assertEquals($answer->timemodified, $newanswer->timemodified);
}
}
@@ -0,0 +1,55 @@
@mod @mod_choice @core_completion
Feature: Automatic completion in the choice activity
In order for me to know what to do to complete the choice activity
As a student
I need to be able to see the completion requirements of the choice activity
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
| category | 0 |
| enablecompletion | 1 |
And the following "activity" exists:
| activity | choice |
| name | What to drink? |
| intro | Friday drinks, anyone? |
| course | C1 |
| idnumber | choice1 |
| completion | 2 |
| completionview | 1 |
| completionsubmit | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| teacher1 | C1 | editingteacher |
Scenario: Viewing a choice activity with automatic completion as a student
When I am on the "What to drink?" "choice activity" page logged in as student1
Then the "View" completion condition of "What to drink?" is displayed as "done"
And the "Make a choice" completion condition of "What to drink?" is displayed as "todo"
And I set the field "Beer" to "1"
And I press "Save my choice"
And the "View" completion condition of "What to drink?" is displayed as "done"
And the "Make a choice" completion condition of "What to drink?" is displayed as "done"
Scenario: Viewing a choice activity with automatic completion as a teacher
When I am on the "What to drink?" "choice activity" page logged in as teacher1
Then "What to drink?" should have the "View" completion condition
And "What to drink?" should have the "Make a choice" completion condition
@javascript
Scenario: Overriding automatic choice completion for a user
Given I am on the "Course 1" course page logged in as teacher1
And I navigate to "Reports" in current page administration
And I click on "Activity completion" "link"
And I click on "Student 1, What to drink?: Not completed" "link"
And I press "Save changes"
And I log out
When I am on the "What to drink?" "choice activity" page logged in as student1
Then the "View" completion condition of "What to drink?" overridden by "Teacher 1" is displayed as "done"
And the "Make a choice" completion condition of "What to drink?" overridden by "Teacher 1" is displayed as "done"
@@ -0,0 +1,81 @@
@mod @mod_choice @core_completion
Feature: Manual completion in the choice activity
To avoid navigating from the choice activity to the course homepage to mark the choice activity as complete
As a student
I need to be able to mark the choice activity as complete within the choice activity itself
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
| category | 0 |
| enablecompletion | 1 |
And the following "activity" exists:
| activity | choice |
| name | What to drink? |
| intro | Friday drinks, anyone? |
| course | C1 |
| idnumber | choice1 |
| completion | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| teacher1 | C1 | editingteacher |
@javascript
Scenario: Toggle manual completion as a student
Given I am on the "What to drink?" "choice activity" page logged in as student1
And the manual completion button of "What to drink?" is displayed as "Mark as done"
When I toggle the manual completion state of "What to drink?"
Then the manual completion button of "What to drink?" is displayed as "Done"
But "Mark as done" "button" should not exist
# Just make sure that the change persisted.
And I reload the page
And I wait until the page is ready
And I should not see "Mark as done"
And the manual completion button of "What to drink?" is displayed as "Done"
And I toggle the manual completion state of "What to drink?"
And the manual completion button of "What to drink?" is displayed as "Mark as done"
But "Done" "button" should not exist
# Just make sure that the change persisted.
And I reload the page
And the manual completion button of "What to drink?" is displayed as "Mark as done"
But "Done" "button" should not exist
Scenario: Viewing a choice activity with manual completion as a teacher
When I am on the "What to drink?" "choice activity" page logged in as teacher1
Then the manual completion button for "What to drink?" should be disabled
@javascript
Scenario: Overriding a manual choice completion for a user to done
Given I am on the "Course 1" course page logged in as teacher1
And I navigate to "Reports" in current page administration
And I click on "Activity completion" "link"
And I click on "Student 1, What to drink?: Not completed" "link"
And I press "Save changes"
And I log out
When I am on the "What to drink?" "choice activity" page logged in as student1
Then the manual completion button of "What to drink?" overridden by "Teacher 1" is displayed as "Done"
And I toggle the manual completion state of "What to drink?"
And the manual completion button of "What to drink?" is displayed as "Mark as done"
@javascript
Scenario: Overriding a manual choice completion for a user to not done
Given I am on the "What to drink?" "choice activity" page logged in as student1
And I press "Mark as done"
And I wait until the page is ready
And I log out
And I am on the "Course 1" course page logged in as teacher1
And I navigate to "Reports" in current page administration
And I click on "Activity completion" "link"
And I click on "Student 1, What to drink?: Completed" "link"
And I press "Save changes"
And I log out
Given I am on the "What to drink?" "choice activity" page logged in as student1
Then the manual completion button of "What to drink?" overridden by "Teacher 1" is displayed as "Mark as done"
And I toggle the manual completion state of "What to drink?"
And the manual completion button of "What to drink?" is displayed as "Done"
+26
View File
@@ -0,0 +1,26 @@
@mod @mod_choice
Feature: Add choice activity
In order to ask questions as a choice of multiple responses
As a teacher
I need to add choice activities to courses
Scenario: Add a choice activity and complete the activity as a student
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Choice name | Choice Description | C1 | choice1 | Option 1, Option 2 | 1 |
When I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Choice name" choice activity
Then I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
@@ -0,0 +1,51 @@
@mod @mod_choice
Feature: Allow choice preview
In order to allow students to preview options before a choice activity is opened for submission
As a teacher
I need to enable the choice preview option
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice name |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2 |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
Scenario: Enable the choice preview option and view the activity as a student before the opening time
And I follow "Choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| timeopen[enabled] | 1 |
| timeclose[enabled] | 1 |
| timeopen[day] | 30 |
| timeopen[month] | December |
| timeopen[year] | 2037 |
| timeclose[day] | 31 |
| timeclose[month] | December |
| timeclose[year] | 2037 |
| Show preview | 1 |
And I press "Save and return to course"
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Choice name"
Then I should see "This is just a preview of the available options for this activity"
And the "choice_1" "radio" should be disabled
And the "choice_2" "radio" should be disabled
And "Save my choice" "button" should not exist
@@ -0,0 +1,86 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Steps definitions for choice activity.
*
* @package mod_choice
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
/**
* Choice activity definitions.
*
* @package mod_choice
* @category test
* @copyright 2013 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_mod_choice extends behat_base {
/**
* Chooses the specified option from the choice activity named as specified. You should be located in the activity's course page.
*
* @Given /^I choose "(?P<option_string>(?:[^"]|\\")*)" from "(?P<choice_activity_string>(?:[^"]|\\")*)" choice activity$/
* @param string $option
* @param string $choiceactivity
* @return array
*/
public function I_choose_option_from_activity($option, $choiceactivity) {
$this->execute('behat_navigation::i_am_on_page_instance', [$this->escape($choiceactivity), 'choice activity']);
$this->execute('behat_forms::i_set_the_field_to', array( $this->escape($option), 1));
$this->execute("behat_forms::press_button", get_string('savemychoice', 'choice'));
}
/**
* Chooses the specified option from the choice activity named as specified and save the choice.
* You should be located in the activity's course page.
*
* @Given /^I choose options (?P<option_string>"(?:[^"]|\\")*"(?:,"(?:[^"]|\\")*")*) from "(?P<choice_activity_string>(?:[^"]|\\")*)" choice activity$/
* @param string $option
* @param string $choiceactivity
* @return array
*/
public function I_choose_options_from_activity($option, $choiceactivity) {
// Get Behat general and forms contexts.
$behatgeneral = behat_context_helper::get('behat_general');
$behatforms = behat_context_helper::get('behat_forms');
// Go to choice activity.
$this->execute("behat_navigation::i_am_on_page_instance", [$this->escape($choiceactivity), 'choice activity']);
// Wait for page to be loaded.
$this->wait_for_pending_js();
// Set all options.
$options = explode('","', trim($option, '"'));
foreach ($options as $option) {
$behatforms->i_set_the_field_to($this->escape($option), '1');
}
// Save choice.
$behatforms->press_button(get_string('savemychoice', 'choice'));
}
}
@@ -0,0 +1,105 @@
@mod @mod_choice
Feature: Editing choice block
In order to customise choice page
As a teacher or admin
I need to add remove block from the choice page
# This tests that the hacky block editing is not borked by legacy forms in choice activity.
Scenario: Add a choice activity as admin and check blog menu block should contain link.
Given the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice name 1 |
| intro | Choice Description 1 |
| section | 1 |
| option | Option 1, Option 2 |
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| blog_menu | Activity module | choice1 | mod-choice-* | side-pre |
And I log in as "admin"
And I am on "Course 1" course homepage with editing mode on
And I follow "Choice name 1"
And I should see "View all entries about this Choice"
When I configure the "Blog menu" block
And I press "Save changes"
Then I should see "View all entries about this Choice"
And I open the "Blog menu" blocks action menu
And I click on "Delete" "link" in the "Blog menu" "block"
And I press "Yes"
And I should not see "View all entries about this Choice"
And I should see "Choice Description 1"
Scenario: Add a choice activity as teacher and check blog menu block contain choice link.
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice name 1 |
| intro | Choice Description 1 |
| section | 1 |
| option | Option 1, Option 2 |
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| blog_menu | Activity module | choice1 | mod-choice-* | side-pre |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I follow "Choice name 1"
And I should see "View all entries about this Choice"
When I configure the "Blog menu" block
And I press "Save changes"
Then I should see "View all entries about this Choice"
And I open the "Blog menu" blocks action menu
And I click on "Delete" "link" in the "Blog menu" "block"
And I press "Yes"
And I should not see "View all entries about this Choice"
And I should see "Choice Description 1"
Scenario: Add a choice activity as teacher (with dual role) and check blog menu block contain choice link.
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher1 | C1 | student |
And the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice name 1 |
| intro | Choice Description 1 |
| section | 1 |
| option | Option 1, Option 2 |
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| blog_menu | Activity module | choice1 | mod-choice-* | side-pre |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I follow "Choice name 1"
And I should see "View all entries about this Choice"
When I configure the "Blog menu" block
And I press "Save changes"
Then I should see "View all entries about this Choice"
And I open the "Blog menu" blocks action menu
And I click on "Delete" "link" in the "Blog menu" "block"
And I press "Yes"
And I should not see "View all entries about this Choice"
And I should see "Choice Description 1"
@@ -0,0 +1,56 @@
@mod @mod_choice
Feature: Teacher can choose whether to allow students to change their choice response
In order to allow students to change their choice
As a teacher
I need to enable the option to change the choice
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
Scenario: Change a choice response as a student
Given the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | Choice name |
| name | Choice name |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2 |
| allowupdate | 0 |
When I am on the "Course 1" course page logged in as student1
And I choose "Option 1" from "Choice name" choice activity
And I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
Then "Save my choice" "button" should not exist
Scenario: Change a choice response as a student
Given the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | Choice name |
| name | Choice name |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2 |
| allowupdate | 1 |
When I am on the "Course 1" course page logged in as student1
And I choose "Option 1" from "Choice name" choice activity
And I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
Then I should see "Your selection: Option 1"
And "Save my choice" "button" should exist
And "Remove my choice" "link" should exist
And I set the field "Option 2" to "1"
And I press "Save my choice"
And I should see "Your choice has been saved"
And I should see "Your selection: Option 2"
@@ -0,0 +1,104 @@
@mod @mod_choice
Feature: Restrict availability of the choice module to a deadline
In order to limit the time a student can mace a selection
As a teacher
I need to restrict answering to within a time period
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
Scenario: Enable the choice activity with a start deadline in the future
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Choice name | Choice Description | C1 | choice1 | Option 1, Option 2 | 1 |
And I am on "Course 1" course homepage
And I follow "Choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| timeopen[enabled] | 1 |
| timeopen[day] | 30 |
| timeopen[month] | December |
| timeopen[year] | 2037 |
And I press "Save and return to course"
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Choice name"
Then "choice_1" "radio" should not exist
And "choice_2" "radio" should not exist
And "Save my choice" "button" should not exist
Scenario: Enable the choice activity with a start deadline in the past
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Choice name | Choice Description | C1 | choice1 | Option 1, Option 2 | 1 |
And I am on "Course 1" course homepage
And I follow "Choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| timeopen[enabled] | 1 |
| timeopen[day] | 30 |
| timeopen[month] | December |
| timeopen[year] | 2007 |
And I press "Save and return to course"
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Choice name"
And "choice_1" "radio" should exist
And "choice_2" "radio" should exist
And "Save my choice" "button" should exist
Scenario: Enable the choice activity with a end deadline in the future
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Choice name | Choice Description | C1 | choice1 | Option 1, Option 2 | 1 |
And I am on "Course 1" course homepage
And I follow "Choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| timeclose[enabled] | 1 |
| timeclose[day] | 30 |
| timeclose[month] | December |
| timeclose[year] | 2037 |
And I press "Save and return to course"
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Choice name"
And "choice_1" "radio" should exist
And "choice_2" "radio" should exist
And "Save my choice" "button" should exist
Scenario: Enable the choice activity with a end deadline in the past
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Choice name | Choice Description | C1 | choice1 | Option 1, Option 2 | 1 |
And I am on "Course 1" course homepage
And I follow "Choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| timeclose[enabled] | 1 |
| timeclose[day] | 30 |
| timeclose[month] | December |
| timeclose[year] | 2007 |
And I press "Save and return to course"
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Choice name"
Then "choice_1" "radio" should not exist
And "choice_2" "radio" should not exist
And "Save my choice" "button" should not exist
@@ -0,0 +1,51 @@
@mod @mod_choice
Feature: Choice with no calendar capabilites
In order to allow work effectively
As a teacher
I need to be able to create choices even when I cannot edit calendar events
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And I log in as "admin"
And I am on the "Course 1" "permissions" page
And I override the system permissions of "Teacher" role with:
| capability | permission |
| moodle/calendar:manageentries | Prohibit |
And I log out
Scenario: Editing a choice
And the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Test choice name | Test choice description | C1 | choice1 | Option 1, Option 2 | 1 |
Given I log in as "admin"
And I am on "Course 1" course homepage
And I follow "Test choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| id_timeopen_enabled | 1 |
| id_timeopen_day | 1 |
| id_timeopen_month | 1 |
| id_timeopen_year | 2017 |
| id_timeclose_enabled | 1 |
| id_timeclose_day | 1 |
| id_timeclose_month | 2 |
| id_timeclose_year | 2017 |
And I press "Save and return to course"
And I log out
When I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I follow "Test choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| id_timeopen_year | 2018 |
| id_timeclose_year | 2018 |
And I press "Save and return to course"
Then I should see "Test choice name"
+60
View File
@@ -0,0 +1,60 @@
@mod @mod_choice
Feature: Group choice
In order to view choice responses for large courses
As a user
I need to filter the responses to a choice by group
Background:
And the following "courses" exist:
| fullname | shortname |
| Test Course 1 | C1 |
And the following "groups" exist:
| name | course | idnumber | participation |
| Group 1 | C1 | G1 | 1 |
| Group 2 | C1 | G2 | 1 |
| Group 3 | C1 | G3 | 0 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | TeacherG1 | 1 | teacher1@example.com |
| user1 | User1G1 | 1 | user1@example.com |
| user2 | User2G2 | 2 | user2@example.com |
| user3 | User3None | 3 | user3@example.com |
| user4 | User4NPgroup | 4 | user4@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| user1 | C1 | student |
| user2 | C1 | student |
| user3 | C1 | student |
| user4 | C1 | student |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| user1 | G1 |
| user2 | G2 |
| user4 | G3 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | showresults | publish | groupmode |
| choice | Separate Choice | Choice with separate groups | C1 | choice1 | 3 | 1 | 1 |
| choice | Visible Choice | Choice with visible groups | C1 | choice2 | 3 | 1 | 2 |
Scenario Outline: Users should see their own participation groups in "separate groups" mode, and all
participation groups in "visible groups" mode.
Given I am on the "<choice>" "choice activity" page logged in as "<user>"
Then I <all> "All participants"
And I <G1> "Group 1"
And I <G2> "Group 2"
And I should not see "Group 3"
Examples:
| choice | user | all | G1 | G2 |
| choice1 | teacher1 | should see | should see | should see |
| choice1 | user1 | should not see | should see | should not see |
| choice1 | user2 | should not see | should not see | should see |
| choice1 | user3 | should see | should not see | should not see |
| choice1 | user4 | should see | should not see | should not see |
| choice2 | teacher1 | should see | should see | should see |
| choice2 | user1 | should see | should see | should see |
| choice2 | user2 | should see | should see | should see |
| choice2 | user3 | should see | should see | should see |
| choice2 | user4 | should see | should see | should see |
@@ -0,0 +1,134 @@
@mod @mod_choice
Feature: Include responses from inactive users
In order to view responses from inactive or suspended users in choice results
As a teacher
I need to enable the choice include inactive option
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
Scenario: Enable the choice include inactive option and check that responses from inactive students are visible
Given the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice name |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2, Option 3 |
| includeinactive | 1 |
And I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Choice name" choice activity
And I log out
And I log in as "student2"
And I am on "Course 1" course homepage
And I choose "Option 2" from "Choice name" choice activity
And I log out
And I log in as "student3"
And I am on "Course 1" course homepage
And I choose "Option 3" from "Choice name" choice activity
And I log out
And the following "course enrolments" exist:
| user | course | role | status |
| student1 | C1 | student | 1 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I click on "Choice name" "link" in the "region-main" "region"
And I navigate to "Responses" in current page administration
Then I should see "Student 1"
And I should see "Student 2"
And I should see "Student 3"
And I log out
And the following "course enrolments" exist:
| user | course | role | timestart |
| student2 | C1 | student | 2145830400 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I click on "Choice name" "link" in the "region-main" "region"
Then I navigate to "Responses" in current page administration
And I should see "Student 1"
And I should see "Student 2"
And I should see "Student 3"
And I log out
And the following "course enrolments" exist:
| user | course | role | timeend |
| student3 | C1 | student | 1425168000 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I click on "Choice name" "link" in the "region-main" "region"
Then I navigate to "Responses" in current page administration
And I should see "Student 1"
And I should see "Student 2"
And I should see "Student 3"
And I log out
Scenario: Disable the choice include inactive option and check that responses from inactive students are not visible
Given the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice name |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2, Option 3 |
| includeinactive | 0 |
And I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Choice name" choice activity
And I log out
And I log in as "student2"
And I am on "Course 1" course homepage
And I choose "Option 2" from "Choice name" choice activity
And I log out
And I log in as "student3"
And I am on "Course 1" course homepage
And I choose "Option 3" from "Choice name" choice activity
And I log out
And the following "course enrolments" exist:
| user | course | role | status |
| student1 | C1 | student | 1 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I click on "Choice name" "link" in the "region-main" "region"
Then I navigate to "Responses" in current page administration
And I should not see "Student 1"
And I should see "Student 2"
And I should see "Student 3"
And I log out
And the following "course enrolments" exist:
| user | course | role | timestart |
| student2 | C1 | student | 2145830400 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I click on "Choice name" "link" in the "region-main" "region"
Then I navigate to "Responses" in current page administration
And I should not see "Student 1"
And I should not see "Student 2"
And I should see "Student 3"
And I log out
And the following "course enrolments" exist:
| user | course | role | timeend |
| student3 | C1 | student | 1425168000 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I click on "Choice name" "link" in the "region-main" "region"
Then I navigate to "Responses" in current page administration
And I should not see "Student 1"
And I should not see "Student 2"
And I should not see "Student 3"
And I log out
@@ -0,0 +1,60 @@
@mod @mod_choice
Feature: Limit choice responses
In order to restrict students from selecting a response more than a specified number of times
As a teacher
I need to limit the choice responses
Scenario: Limit the number of responses allowed for a choice activity and verify the result as students
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
And the following "activities" exist:
| activity | name | intro | course | idnumber | option | showavailable | limitanswers |
| choice | Choice name | Choice description | C1 | choice1 | Option 1, Option 2 | 1 | 1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Choice name"
And I navigate to "Settings" in current page administration
And I set the field "Limit 1" to "1"
And I press "Save and display"
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Choice name"
And I choose "Option 1" from "Choice name" choice activity
Then I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
And I log out
And I log in as "student2"
And I am on "Course 1" course homepage
And I follow "Choice name"
And I should see "Option 1 (Full)"
And I should see "Responses: 1"
And I should see "Limit: 1"
And the "choice_1" "radio" should be disabled
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Limit the number of responses allowed | No |
And I press "Save and return to course"
And I log out
And I log in as "student3"
And I am on "Course 1" course homepage
And I follow "Choice name"
Then I should not see "Limit: 1"
And the "choice_1" "radio" should be enabled
@@ -0,0 +1,121 @@
@mod @mod_choice
Feature: Teacher can modify choices of the students
In order to have all students choices
As a teacher
I need to be able to make choice for studnets
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
And the following "activities" exist:
| activity | name | intro | course | idnumber | option |
| choice | Choice name | Test choice description | C1 | choice1 | Option 1, Option 2, Option 3 |
@javascript
Scenario: Delete students choice response as a teacher
Given I am on the "Choice name" "choice activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the field "Show column for unanswered" to "Yes"
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Choice name" choice activity
Then I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
And I log out
And I log in as "teacher1"
And I change window size to "large"
And I am on the "Choice name" "choice activity" page
And I navigate to "Responses" in current page administration
And I click on "Student 1 Option 1" "checkbox"
And I select "Delete" from the "With selected" singleselect
And "Student 1 Option 1" "checkbox" should not exist
And "Student 1 Not answered yet" "checkbox" should exist
@javascript
Scenario: Teacher set answers of students who did not respond or change existing answers
Given I am on the "Choice name" "choice activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the field "Show column for unanswered" to "Yes"
And I press "Save and return to course"
And I log out
And I am on the "Course 1" course page logged in as student1
And I choose "Option 1" from "Choice name" choice activity
Then I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
And I log out
And I change window size to "large"
And I am on the "Choice name" "choice activity" page logged in as teacher1
And I navigate to "Responses" in current page administration
And I click on "Student 1 Option 1" "checkbox"
And I click on "Student 2 Not answered yet" "checkbox"
And I click on "Student 3 Not answered yet" "checkbox"
And I select "Choose: Option 2" from the "With selected" singleselect
And "Student 1 Option 1" "checkbox" should not exist
And "Student 2 Not answered yet" "checkbox" should not exist
And "Student 3 Not answered yet" "checkbox" should not exist
And "Student 1 Option 2" "checkbox" should exist
And "Student 2 Option 2" "checkbox" should exist
And "Student 3 Option 2" "checkbox" should exist
@javascript
Scenario: Teacher can delete answers in the multiple answer choice
Given I am on the "Choice name" "choice activity editing" page logged in as teacher1
And I set the field "Allow more than one choice to be selected" to "Yes"
And I press "Save and return to course"
And I log out
And I am on the "Course 1" course page logged in as student1
And I choose options "Option 1","Option 2" from "Choice name" choice activity
And I should see "Your selection: Option 1; Option 2"
And I should see "Your choice has been saved"
And I log out
And I change window size to "large"
And I am on the "Choice name" "choice activity" page logged in as teacher1
And I navigate to "Responses" in current page administration
And I click on "Student 1 Option 2" "checkbox"
And I select "Delete" from the "With selected" singleselect
And I click on "Student 1 Option 1" "checkbox"
And I select "Choose: Option 3" from the "With selected" singleselect
And I log out
And I am on the "Choice name" "choice activity" page logged in as student1
And I should see "Your selection: Option 1; Option 3"
@javascript
Scenario: Teacher can manage answers on view page if the names are displayed
Given I am on the "Course 1" course page logged in as student1
And I choose "Option 1" from "Choice name" choice activity
Then I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
And I log out
And I change window size to "large"
And I am on the "Choice name" "choice activity editing" page logged in as teacher1
And I set the following fields to these values:
| Publish results | Always show results to students |
| Privacy of results | Publish full results, showing names and their choices |
| Show column for unanswered | Yes |
And I press "Save and display"
And I click on "Student 1 Option 1" "checkbox"
And I click on "Student 2 Not answered yet" "checkbox"
And I select "Choose: Option 3" from the "With selected" singleselect
And "Student 1 Option 1" "checkbox" should not exist
And "Student 1 Option 3" "checkbox" should exist
And "Student 2 Not answered yet" "checkbox" should not exist
And "Student 2 Option 3" "checkbox" should exist
And I click on "Student 1 Option 3" "checkbox"
And I select "Delete" from the "With selected" singleselect
And "Student 1 Option 3" "checkbox" should not exist
And "Student 1 Not answered yet" "checkbox" should exist
@@ -0,0 +1,87 @@
@mod @mod_choice
Feature: Multiple option choice response
In order to ask questions as a choice of multiple responses
As a teacher
I need to add choice activities to courses with multiple options enabled
Scenario: Complete a choice with multiple options enabled
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exist:
| activity | name | intro | course | idnumber | option | allowmultiple |
| choice | Choice name | Choice description | C1 | 00001 | Option 1, Option 2, Option 3 | 1 |
When I log in as "student1"
And I am on "Course 1" course homepage
And I choose options "Option 1","Option 2" from "Choice name" choice activity
Then I should see "Your selection: Option 1; Option 2"
And I should see "Your choice has been saved"
Scenario: Complete a choice with multiple options enabled and limited responses set
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activity" exist:
| activity | name | intro | course | idnumber | option | allowmultiple | showavailable | limitanswers |
| choice | Choice name | Choice description | C1 | choice1 | Option 1, Option 2, Option 3 | 1 | 1 | 1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
And I follow "Choice name"
And I follow "Settings"
And I set the following fields to these values:
| Limit 1 | 1 |
| Limit 2 | 1 |
| Limit 3 | 1 |
And I press "Save and display"
And I log out
When I log in as "student1"
And I am on "Course 1" course homepage
And I choose options "Option 1","Option 2" from "Choice name" choice activity
Then I should see "Your selection: Option 1; Option 2"
And I should see "Your choice has been saved"
And I log out
And I log in as "student2"
And I am on "Course 1" course homepage
And I follow "Choice name"
And I should see "Option 1 (Full)"
And I should see "Option 2 (Full)"
And I should see "Option 3"
And the "#choice_1" "css_element" should be disabled
And the "#choice_2" "css_element" should be disabled
And the "#choice_3" "css_element" should be enabled
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Choice name"
And I navigate to "Responses" in current page administration
Then I should see "Option 1 (Full)"
And I should see "Limit: 1"
And I am on "Course 1" course homepage with editing mode on
And I follow "Choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Limit the number of responses allowed | No |
And I press "Save and return to course"
And I am on "Course 1" course homepage
And I follow "Choice name"
And I navigate to "Responses" in current page administration
Then I should not see "Limit: 1"
And I log out
+54
View File
@@ -0,0 +1,54 @@
@mod @mod_choice
Feature: Test the display of the choice module on my home
In order to know my status in a choice activity
As a user
I need to see it in My dashboard.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Test choice name | Test choice description | C1 | choice1 | Option 1, Option 2 | 1 |
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Test choice name"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| timeopen[enabled] | 1 |
| timeclose[enabled] | 1 |
| timeclose[day] | 1 |
| timeclose[month] | January |
| timeclose[year] | 2030 |
| timeclose[hour] | 08 |
| timeclose[minute] | 00 |
| Allow choice to be updated | No |
And I press "Save and return to course"
And I log out
Scenario: View my home as a student after answering the choice
Given I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Test choice name" choice activity
And I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
And "Save my choice" "button" should not exist
And I log out
Scenario: View my home as a teacher
Given I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Test choice name" choice activity
And I should see "Your selection: Option 1"
And I should see "Your choice has been saved"
And "Save my choice" "button" should not exist
And I log out
+117
View File
@@ -0,0 +1,117 @@
@mod @mod_choice
Feature: A student can see how the results of the choice activity will be published
In order to put my mind at ease when it comes to answering a choice
As a student
I need to learn how my choice will be handled and published to the other course participants.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "teacher1"
Scenario: Results will not be published to the students
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option |
| choice | Choice 1 | Choice description | C1 | choice1 | Option 1, Option 2 |
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Publish results | Do not publish results to students |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
Then I should see "The results of this activity will not be published after you answer."
Scenario: Full results will be shown to the students after they answer
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option |
| choice | Choice 1 | Choice description | C1 | choice1 | Option 1, Option 2 |
And I am on "Course 1" course homepage
When I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Publish results | Show results to students after they answer |
| Privacy of results | Publish full results, showing names and their choices |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
Then I should see "Full results, showing everyone's choices, will be published after you answer."
Scenario: Anonymous results will be shown to students after they answer
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option |
| choice | Choice 1 | Choice description | C1 | choice1 | Option 1, Option 2 |
And I am on "Course 1" course homepage
When I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the field "Publish results" to "Show results to students after they answer"
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
Then I should see "Anonymous results will be published after you answer."
Scenario: Full results will be shown to students only after the choice is closed
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option |
| choice | Choice 1 | Choice description | C1 | choice1 | Option 1, Option 2 |
And I am on "Course 1" course homepage
When I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the field "Publish results" to "Show results to students only after the choice is closed"
And I set the field "Privacy of results" to "Publish full results, showing names and their choices"
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
Then I should see "Full results, showing everyone's choices, will be published after the activity is closed."
Scenario: Anonymous results will be shown to students only after the choice is closed
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option |
| choice | Choice 1 | Choice description | C1 | choice1 | Option 1, Option 2 |
And I am on "Course 1" course homepage
When I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the field "Publish results" to "Show results to students only after the choice is closed"
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
Then I should see "Anonymous results will be published after the activity is closed."
Scenario: Results will always be shown to students
And the following "activities" exist:
| activity | name | intro | course | idnumber | option |
| choice | Choice 1 | Choice description | C1 | choice1 | Option 1, Option 2 |
And I am on "Course 1" course homepage
When I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the field "Publish results" to "Always show results to students"
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
Then I should not see "Full results, showing everyone's choices, will be published after you answer."
And I should not see "Full results, showing everyone's choices, will be published after the activity is closed."
And I should not see "Anonymous results will be published after you answer."
And I should not see "Anonymous results will be published after the activity is closed."
And I should not see "The results of this activity will not be published after you answer."
@@ -0,0 +1,132 @@
@mod @mod_choice
Feature: A teacher can choose one of 4 options for publishing choice results
In order to display choice activities outcomes
As a teacher
I need to publish the choice activity results in different ways
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "teacher1"
And I am on "Course 1" course homepage with editing mode on
Scenario: Do not publish results to students
Given the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice 1 |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2 |
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Publish results | Do not publish results to students |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I choose "Option 1" from "Choice 1" choice activity
Then I should see "Your selection: Option 1"
And I should not see "Responses"
And I should not see "Graph display"
Scenario: Show results to students after they answer
Given the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice 1 |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2 |
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Publish results | Show results to students after they answer |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
Then I should not see "Responses"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Choice 1" choice activity
And I should see "Your selection: Option 1"
And I should see "Responses"
Scenario: Show results to students only after the choice is closed
Given the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice 1 |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2 |
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Publish results | Show results to students only after the choice is closed |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
Then I should not see "Responses"
And I choose "Option 1" from "Choice 1" choice activity
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I follow "Settings"
And I expand all fieldsets
And I set the following fields to these values:
| timeopen[enabled] | 1 |
| timeopen[day] | 1 |
| timeopen[month] | January |
| timeopen[year] | 2010 |
| timeclose[enabled] | 1 |
| timeclose[day] | 2 |
| timeclose[month] | January |
| timeclose[year] | 2010 |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I should see "Responses"
Scenario: Always show results to students
Given the following "activity" exists:
| activity | choice |
| course | C1 |
| idnumber | choice1 |
| name | Choice 1 |
| intro | Choice Description |
| section | 1 |
| option | Option 1, Option 2 |
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Publish results | Always show results to students |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
When I follow "Choice 1"
And I should see "Responses"
@@ -0,0 +1,65 @@
@mod @mod_choice
Feature: A teacher can choose whether to publish choice activity results anonymously or showing names
In order to keep students privacy or to give more info to students
As a teacher
I need to select whether I want other students to know who selected what option
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And I log in as "teacher1"
Scenario: Publish anonymous results
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Choice 1 | Choice Description | C1 | choice1 | Option 1, Option 2 | 1 |
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Publish results | Always show results to students |
| Privacy of results | Publish anonymous results, do not show student names |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Choice 1" choice activity
And I log out
When I log in as "student2"
And I am on "Course 1" course homepage
And I follow "Choice 1"
Then I should not see "Student 1"
And I should not see "Users who chose this option"
Scenario: Publish full results
Given the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Choice 1 | Choice Description | C1 | choice1 | Option 1, Option 2 | 1 |
And I am on "Course 1" course homepage
And I follow "Choice 1"
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Publish results | Always show results to students |
| Privacy of results | Publish full results, showing names and their choices |
And I press "Save and return to course"
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 1" from "Choice 1" choice activity
And I log out
When I log in as "student2"
And I am on "Course 1" course homepage
And I follow "Choice 1"
Then I should see "Student 1"
And I should see "Users who chose this option"
@@ -0,0 +1,43 @@
@mod @mod_choice
Feature: Update a choice activity removing options
In order to remove incorrect or unwanted options
As a teacher
I need to update the choice activity
Scenario: Update a choice activity that has student responses.
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | name | intro | course | idnumber | option | section |
| choice | Choice name | Choice Description | C1 | choice1 | Option 1, Option 2, Option 3 | 1 |
And I log in as "student1"
And I am on "Course 1" course homepage
And I choose "Option 3" from "Choice name" choice activity
And I should see "Your selection: Option 3"
And I should see "Your choice has been saved"
And I log out
And I log in as "student2"
And I am on "Course 1" course homepage
And I choose "Option 2" from "Choice name" choice activity
And I should see "Your selection: Option 2"
And I should see "Your choice has been saved"
And I log out
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I click on "Choice name" "link" in the "region-main" "region"
And I navigate to "Settings" in current page administration
And I set the field "option[2]" to ""
And I press "Save and display"
Then I navigate to "Responses" in current page administration
And I should see "1" in the "Number of responses" "table_row"
+209
View File
@@ -0,0 +1,209 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
declare(strict_types = 1);
namespace mod_choice;
use advanced_testcase;
use cm_info;
use coding_exception;
use mod_choice\completion\custom_completion;
use moodle_exception;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/completionlib.php');
/**
* Class for unit testing mod_choice/custom_completion.
*
* @package mod_choice
* @copyright 2021 Jun Pataleta <jun@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_completion_test extends advanced_testcase {
/**
* Data provider for get_state().
*
* @return array[]
*/
public function get_state_provider(): array {
return [
'Undefined rule' => [
'somenonexistentrule', COMPLETION_DISABLED, false, null, coding_exception::class
],
'Rule not available' => [
'completionsubmit', COMPLETION_DISABLED, false, null, moodle_exception::class
],
'Rule available, user has not submitted' => [
'completionsubmit', COMPLETION_ENABLED, false, COMPLETION_INCOMPLETE, null
],
'Rule available, user has submitted' => [
'completionsubmit', COMPLETION_ENABLED, true, COMPLETION_COMPLETE, null
],
];
}
/**
* Test for get_state().
*
* @dataProvider get_state_provider
* @param string $rule The custom completion rule.
* @param int $available Whether this rule is available.
* @param bool $submitted Whether the user has made a choice.
* @param int|null $status Expected status.
* @param string|null $exception Expected exception.
*/
public function test_get_state(string $rule, int $available, ?bool $submitted, ?int $status, ?string $exception): void {
global $DB;
if (!is_null($exception)) {
$this->expectException($exception);
}
// Custom completion rule data for cm_info::customdata.
$customdataval = [
'customcompletionrules' => [
$rule => $available
]
];
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Mock the return of the magic getter method when fetching the cm_info object's customdata and instance values.
$mockcminfo->expects($this->any())
->method('__get')
->will($this->returnValueMap([
['customdata', $customdataval],
['instance', 1],
]));
// Mock the DB calls.
$DB = $this->createMock(get_class($DB));
$DB->expects($this->atMost(1))
->method('record_exists')
->willReturn($submitted);
$customcompletion = new custom_completion($mockcminfo, 2);
$this->assertEquals($status, $customcompletion->get_state($rule));
}
/**
* Test for get_defined_custom_rules().
*/
public function test_get_defined_custom_rules(): void {
$rules = custom_completion::get_defined_custom_rules();
$this->assertCount(1, $rules);
$this->assertEquals('completionsubmit', reset($rules));
}
/**
* Test for get_defined_custom_rule_descriptions().
*/
public function test_get_custom_rule_descriptions(): void {
// Get defined custom rules.
$rules = custom_completion::get_defined_custom_rules();
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Instantiate a custom_completion object using the mocked cm_info.
$customcompletion = new custom_completion($mockcminfo, 1);
// Get custom rule descriptions.
$ruledescriptions = $customcompletion->get_custom_rule_descriptions();
// Confirm that defined rules and rule descriptions are consistent with each other.
$this->assertEquals(count($rules), count($ruledescriptions));
foreach ($rules as $rule) {
$this->assertArrayHasKey($rule, $ruledescriptions);
}
}
/**
* Test for is_defined().
*/
public function test_is_defined(): void {
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->getMock();
$customcompletion = new custom_completion($mockcminfo, 1);
// Rule is defined.
$this->assertTrue($customcompletion->is_defined('completionsubmit'));
// Undefined rule.
$this->assertFalse($customcompletion->is_defined('somerandomrule'));
}
/**
* Data provider for test_get_available_custom_rules().
*
* @return array[]
*/
public function get_available_custom_rules_provider(): array {
return [
'Completion submit available' => [
COMPLETION_ENABLED, ['completionsubmit']
],
'Completion submit not available' => [
COMPLETION_DISABLED, []
],
];
}
/**
* Test for get_available_custom_rules().
*
* @dataProvider get_available_custom_rules_provider
* @param int $status
* @param array $expected
*/
public function test_get_available_custom_rules(int $status, array $expected): void {
$customdataval = [
'customcompletionrules' => [
'completionsubmit' => $status
]
];
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Mock the return of magic getter for the customdata attribute.
$mockcminfo->expects($this->any())
->method('__get')
->with('customdata')
->willReturn($customdataval);
$customcompletion = new custom_completion($mockcminfo, 1);
$this->assertEquals($expected, $customcompletion->get_available_custom_rules());
}
}
+122
View File
@@ -0,0 +1,122 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains unit tests for mod_choice\dates.
*
* @package mod_choice
* @category test
* @copyright Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_choice;
use advanced_testcase;
use cm_info;
use core\activity_dates;
/**
* Class for unit testing mod_choice\dates.
*
* @copyright Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dates_test extends advanced_testcase {
/**
* Data provider for get_dates_for_module().
* @return array[]
*/
public function get_dates_for_module_provider(): array {
$now = time();
$before = $now - DAYSECS;
$earlier = $before - DAYSECS;
$after = $now + DAYSECS;
$later = $after + DAYSECS;
return [
'without any dates' => [
null, null, []
],
'only with opening time' => [
$after, null, [
['label' => 'Opens:', 'timestamp' => $after, 'dataid' => 'timeopen'],
]
],
'only with closing time' => [
null, $after, [
['label' => 'Closes:', 'timestamp' => $after, 'dataid' => 'timeclose'],
]
],
'with both times' => [
$after, $later, [
['label' => 'Opens:', 'timestamp' => $after, 'dataid' => 'timeopen'],
['label' => 'Closes:', 'timestamp' => $later, 'dataid' => 'timeclose'],
]
],
'between the dates' => [
$before, $after, [
['label' => 'Opened:', 'timestamp' => $before, 'dataid' => 'timeopen'],
['label' => 'Closes:', 'timestamp' => $after, 'dataid' => 'timeclose'],
]
],
'dates are past' => [
$earlier, $before, [
['label' => 'Opened:', 'timestamp' => $earlier, 'dataid' => 'timeopen'],
['label' => 'Closed:', 'timestamp' => $before, 'dataid' => 'timeclose'],
]
],
];
}
/**
* Test for get_dates_for_module().
*
* @dataProvider get_dates_for_module_provider
* @param int|null $timeopen Time of opening the choice
* @param int|null $timeclose Time of closing the choice
* @param array $expected The expected value of calling get_dates_for_module()
*/
public function test_get_dates_for_module(?int $timeopen, ?int $timeclose, array $expected): void {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user->id, $course->id);
$data = ['course' => $course->id];
if ($timeopen) {
$data['timeopen'] = $timeopen;
}
if ($timeclose) {
$data['timeclose'] = $timeclose;
}
$choice = $this->getDataGenerator()->create_module('choice', $data);
$this->setUser($user);
$cm = get_coursemodule_from_instance('choice', $choice->id);
// Make sure we're using a cm_info object.
$cm = cm_info::create($cm);
$dates = activity_dates::get_dates_for_module($cm, (int)$user->id);
$this->assertEquals($expected, $dates);
}
}
+393
View File
@@ -0,0 +1,393 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Events tests.
*
* @package mod_choice
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\event;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/choice/lib.php');
/**
* Events tests class.
*
* @package mod_choice
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class events_test extends \advanced_testcase {
/** @var choice_object */
protected $choice;
/** @var course_object */
protected $course;
/** @var cm_object Course module object. */
protected $cm;
/** @var context_object */
protected $context;
/**
* Setup often used objects for the following tests.
*/
protected function setUp(): void {
global $DB;
$this->resetAfterTest();
$this->course = $this->getDataGenerator()->create_course();
$this->choice = $this->getDataGenerator()->create_module('choice', array('course' => $this->course->id));
$this->cm = $DB->get_record('course_modules', array('id' => $this->choice->cmid));
$this->context = \context_module::instance($this->choice->cmid);
}
/**
* Test to ensure that event data is being stored correctly.
*/
public function test_answer_created(): void {
global $DB;
// Generate user data.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$optionids = array_keys($DB->get_records('choice_options', array('choiceid' => $this->choice->id)));
// Redirect event.
$sink = $this->redirectEvents();
choice_user_submit_response($optionids[3], $this->choice, $user->id, $this->course, $this->cm);
$events = $sink->get_events();
$answer = $DB->get_record('choice_answers', ['userid' => $user->id, 'choiceid' => $this->choice->id]);
// Data checking.
$this->assertCount(1, $events);
$this->assertInstanceOf('\mod_choice\event\answer_created', $events[0]);
$this->assertEquals($user->id, $events[0]->userid);
$this->assertEquals($user->id, $events[0]->relateduserid);
$this->assertEquals(\context_module::instance($this->choice->cmid), $events[0]->get_context());
$this->assertEquals($answer->id, $events[0]->objectid);
$this->assertEquals($this->choice->id, $events[0]->other['choiceid']);
$this->assertEquals($optionids[3], $events[0]->other['optionid']);
$this->assertEventContextNotUsed($events[0]);
$sink->close();
}
/**
* Test to ensure that event data is being stored correctly.
*/
public function test_answer_submitted_by_another_user(): void {
global $DB, $USER;
// Generate user data.
$user = $this->getDataGenerator()->create_user();
$optionids = array_keys($DB->get_records('choice_options', array('choiceid' => $this->choice->id)));
// Redirect event.
$sink = $this->redirectEvents();
choice_user_submit_response($optionids[3], $this->choice, $user->id, $this->course, $this->cm);
$events = $sink->get_events();
$answer = $DB->get_record('choice_answers', ['userid' => $user->id, 'choiceid' => $this->choice->id]);
// Data checking.
$this->assertCount(1, $events);
$this->assertInstanceOf('\mod_choice\event\answer_created', $events[0]);
$this->assertEquals($USER->id, $events[0]->userid);
$this->assertEquals($user->id, $events[0]->relateduserid);
$this->assertEquals(\context_module::instance($this->choice->cmid), $events[0]->get_context());
$this->assertEquals($answer->id, $events[0]->objectid);
$this->assertEquals($this->choice->id, $events[0]->other['choiceid']);
$this->assertEquals($optionids[3], $events[0]->other['optionid']);
$this->assertEventContextNotUsed($events[0]);
$sink->close();
}
/**
* Test to ensure that multiple choice data is being stored correctly.
*/
public function test_answer_created_multiple(): void {
global $DB;
// Generate user data.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
// Create multiple choice.
$choice = $this->getDataGenerator()->create_module('choice', array('course' => $this->course->id,
'allowmultiple' => 1));
$cm = $DB->get_record('course_modules', array('id' => $choice->cmid));
$context = \context_module::instance($choice->cmid);
$optionids = array_keys($DB->get_records('choice_options', array('choiceid' => $choice->id)));
$submittedoptionids = array($optionids[1], $optionids[3]);
// Redirect event.
$sink = $this->redirectEvents();
choice_user_submit_response($submittedoptionids, $choice, $user->id, $this->course, $cm);
$events = $sink->get_events();
$answers = $DB->get_records('choice_answers', ['userid' => $user->id, 'choiceid' => $choice->id], 'id');
$answers = array_values($answers);
// Data checking.
$this->assertCount(2, $events);
$this->assertInstanceOf('\mod_choice\event\answer_created', $events[0]);
$this->assertEquals($user->id, $events[0]->userid);
$this->assertEquals($user->id, $events[0]->relateduserid);
$this->assertEquals(\context_module::instance($choice->cmid), $events[0]->get_context());
$this->assertEquals($answers[0]->id, $events[0]->objectid);
$this->assertEquals($choice->id, $events[0]->other['choiceid']);
$this->assertEquals($optionids[1], $events[0]->other['optionid']);
$this->assertEventContextNotUsed($events[0]);
$this->assertInstanceOf('\mod_choice\event\answer_created', $events[1]);
$this->assertEquals($user->id, $events[1]->userid);
$this->assertEquals($user->id, $events[1]->relateduserid);
$this->assertEquals(\context_module::instance($choice->cmid), $events[1]->get_context());
$this->assertEquals($answers[1]->id, $events[1]->objectid);
$this->assertEquals($choice->id, $events[1]->other['choiceid']);
$this->assertEquals($optionids[3], $events[1]->other['optionid']);
$this->assertEventContextNotUsed($events[1]);
$sink->close();
}
/**
* Test custom validations.
*/
public function test_answer_created_other_exception(): void {
// Generate user data.
$user = $this->getDataGenerator()->create_user();
$eventdata = array();
$eventdata['context'] = $this->context;
$eventdata['objectid'] = 2;
$eventdata['userid'] = $user->id;
$eventdata['courseid'] = $this->course->id;
$eventdata['other'] = array();
// Make sure content identifier is always set.
$this->expectException(\coding_exception::class);
$event = \mod_choice\event\answer_created::create($eventdata);
$event->trigger();
$this->assertEventContextNotUsed($event);
}
/**
* Test to ensure that event data is being stored correctly.
*/
public function test_answer_updated(): void {
global $DB;
// Generate user data.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$optionids = array_keys($DB->get_records('choice_options', array('choiceid' => $this->choice->id)));
// Create the first answer.
choice_user_submit_response($optionids[2], $this->choice, $user->id, $this->course, $this->cm);
$oldanswer = $DB->get_record('choice_answers', ['userid' => $user->id, 'choiceid' => $this->choice->id]);
// Redirect event.
$sink = $this->redirectEvents();
// Now choose a different answer.
choice_user_submit_response($optionids[3], $this->choice, $user->id, $this->course, $this->cm);
$newanswer = $DB->get_record('choice_answers', ['userid' => $user->id, 'choiceid' => $this->choice->id]);
$events = $sink->get_events();
// Data checking.
$this->assertCount(2, $events);
$this->assertInstanceOf('\mod_choice\event\answer_deleted', $events[0]);
$this->assertEquals($user->id, $events[0]->userid);
$this->assertEquals(\context_module::instance($this->choice->cmid), $events[0]->get_context());
$this->assertEquals($oldanswer->id, $events[0]->objectid);
$this->assertEquals($this->choice->id, $events[0]->other['choiceid']);
$this->assertEquals($optionids[2], $events[0]->other['optionid']);
$this->assertEventContextNotUsed($events[0]);
$this->assertInstanceOf('\mod_choice\event\answer_created', $events[1]);
$this->assertEquals($user->id, $events[1]->userid);
$this->assertEquals(\context_module::instance($this->choice->cmid), $events[1]->get_context());
$this->assertEquals($newanswer->id, $events[1]->objectid);
$this->assertEquals($this->choice->id, $events[1]->other['choiceid']);
$this->assertEquals($optionids[3], $events[1]->other['optionid']);
$this->assertEventContextNotUsed($events[1]);
$sink->close();
}
/**
* Test to ensure that event data is being stored correctly.
*/
public function test_answer_deleted(): void {
global $DB, $USER;
// Generate user data.
$user = $this->getDataGenerator()->create_user();
$optionids = array_keys($DB->get_records('choice_options', array('choiceid' => $this->choice->id)));
// Create the first answer.
choice_user_submit_response($optionids[2], $this->choice, $user->id, $this->course, $this->cm);
// Get the users response.
$answer = $DB->get_record('choice_answers', array('userid' => $user->id, 'choiceid' => $this->choice->id),
'*', $strictness = IGNORE_MULTIPLE);
// Redirect event.
$sink = $this->redirectEvents();
// Now delete the answer.
choice_delete_responses(array($answer->id), $this->choice, $this->cm, $this->course);
// Get our event event.
$events = $sink->get_events();
$event = reset($events);
// Data checking.
$this->assertInstanceOf('\mod_choice\event\answer_deleted', $event);
$this->assertEquals($USER->id, $event->userid);
$this->assertEquals($user->id, $event->relateduserid);
$this->assertEquals(\context_module::instance($this->choice->cmid), $event->get_context());
$this->assertEquals($this->choice->id, $event->other['choiceid']);
$this->assertEquals($answer->optionid, $event->other['optionid']);
$this->assertEventContextNotUsed($event);
$sink->close();
}
/**
* Test to ensure that event data is being stored correctly.
*/
public function test_report_viewed(): void {
global $USER;
$this->resetAfterTest();
// Generate user data.
$this->setAdminUser();
$eventdata = array();
$eventdata['objectid'] = $this->choice->id;
$eventdata['context'] = $this->context;
$eventdata['courseid'] = $this->course->id;
$eventdata['other']['content'] = 'choicereportcontentviewed';
// This is fired in a page view so we can't run this through a function.
$event = \mod_choice\event\report_viewed::create($eventdata);
// Redirect event.
$sink = $this->redirectEvents();
$event->trigger();
$event = $sink->get_events();
// Data checking.
$this->assertCount(1, $event);
$this->assertInstanceOf('\mod_choice\event\report_viewed', $event[0]);
$this->assertEquals($USER->id, $event[0]->userid);
$this->assertEquals(\context_module::instance($this->choice->cmid), $event[0]->get_context());
$sink->close();
}
/**
* Test to ensure that event data is being stored correctly.
*/
public function test_report_downloaded(): void {
global $USER;
$this->resetAfterTest();
// Generate user data.
$this->setAdminUser();
$eventdata = array();
$eventdata['context'] = $this->context;
$eventdata['courseid'] = $this->course->id;
$eventdata['other']['content'] = 'choicereportcontentviewed';
$eventdata['other']['format'] = 'csv';
$eventdata['other']['choiceid'] = $this->choice->id;
// This is fired in a page view so we can't run this through a function.
$event = \mod_choice\event\report_downloaded::create($eventdata);
// Redirect event.
$sink = $this->redirectEvents();
$event->trigger();
$event = $sink->get_events();
// Data checking.
$this->assertCount(1, $event);
$this->assertInstanceOf('\mod_choice\event\report_downloaded', $event[0]);
$this->assertEquals($USER->id, $event[0]->userid);
$this->assertEquals(\context_module::instance($this->choice->cmid), $event[0]->get_context());
$this->assertEquals('csv', $event[0]->other['format']);
$this->assertEquals($this->choice->id, $event[0]->other['choiceid']);
$this->assertEventContextNotUsed($event[0]);
$sink->close();
}
/**
* Test to ensure that event data is being stored correctly.
*/
public function test_course_module_viewed(): void {
global $USER;
// Generate user data.
$this->setAdminUser();
$eventdata = array();
$eventdata['objectid'] = $this->choice->id;
$eventdata['context'] = $this->context;
$eventdata['courseid'] = $this->course->id;
$eventdata['other']['content'] = 'pageresourceview';
// This is fired in a page view so we can't run this through a function.
$event = \mod_choice\event\course_module_viewed::create($eventdata);
// Redirect event.
$sink = $this->redirectEvents();
$event->trigger();
$event = $sink->get_events();
// Data checking.
$this->assertCount(1, $event);
$this->assertInstanceOf('\mod_choice\event\course_module_viewed', $event[0]);
$this->assertEquals($USER->id, $event[0]->userid);
$this->assertEquals(\context_module::instance($this->choice->cmid), $event[0]->get_context());
$sink->close();
}
/**
* Test to ensure that event data is being stored correctly.
*/
public function test_course_module_instance_list_viewed_viewed(): void {
global $USER;
// Not much can be tested here as the event is only triggered on a page load,
// let's just check that the event contains the expected basic information.
$this->setAdminUser();
$params = array('context' => \context_course::instance($this->course->id));
$event = \mod_choice\event\course_module_instance_list_viewed::create($params);
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
$this->assertInstanceOf('\mod_choice\event\course_module_instance_list_viewed', $event);
$this->assertEquals($USER->id, $event->userid);
$this->assertEquals(\context_course::instance($this->course->id), $event->get_context());
}
}
+580
View File
@@ -0,0 +1,580 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_choice;
use core_external\external_api;
use externallib_advanced_testcase;
use mod_choice_external;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
require_once($CFG->dirroot . '/mod/choice/lib.php');
/**
* External choice functions unit tests
*
* @package mod_choice
* @category external
* @copyright 2015 Costantino Cito <ccito@cvaconsulting.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class externallib_test extends externallib_advanced_testcase {
/**
* Test get_choice_results
*/
public function test_get_choice_results(): void {
global $DB;
$this->resetAfterTest(true);
$course = self::getDataGenerator()->create_course();
$params = new \stdClass();
$params->course = $course->id;
$params->option = array('fried rice', 'spring rolls', 'sweet and sour pork', 'satay beef', 'gyouza');
$params->name = 'First Choice Activity';
$params->showresults = CHOICE_SHOWRESULTS_AFTER_ANSWER;
$params->publish = 1;
$params->allowmultiple = 1;
$params->showunanswered = 1;
$choice = self::getDataGenerator()->create_module('choice', $params);
$cm = get_coursemodule_from_id('choice', $choice->cmid);
$choiceinstance = choice_get_choice($cm->instance);
$options = array_keys($choiceinstance->option);
$student1 = $this->getDataGenerator()->create_user();
$student2 = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Enroll Students in Course1.
self::getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
self::getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
$this->setUser($student1);
$myanswer = $options[2];
choice_user_submit_response($myanswer, $choice, $student1->id, $course, $cm);
$results = mod_choice_external::get_choice_results($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_results_returns(), $results);
// Create an array with optionID as Key.
$resultsarr = array();
foreach ($results['options'] as $option) {
$resultsarr[$option['id']] = $option['userresponses'];
}
// The stundent1 is the userid who choosed the myanswer(option 3).
$this->assertEquals($resultsarr[$myanswer][0]['userid'], $student1->id);
// The stundent2 is the userid who didn't answered yet.
$this->assertEquals($resultsarr[0][0]['userid'], $student2->id);
// As Stundent2 we cannot see results (until we answered).
$this->setUser($student2);
$results = mod_choice_external::get_choice_results($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_results_returns(), $results);
// We do not retrieve any response!
foreach ($results['options'] as $option) {
$this->assertCount(0, $option['userresponses']);
}
$timenow = time();
// We can see results only after activity close (even if we didn't answered).
$choice->showresults = CHOICE_SHOWRESULTS_AFTER_CLOSE;
// Set timeopen and timeclose in the past.
$choice->timeopen = $timenow - (60 * 60 * 24 * 3);
$choice->timeclose = $timenow + (60 * 60 * 24 * 2);
$DB->update_record('choice', $choice);
$results = mod_choice_external::get_choice_results($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_results_returns(), $results);
// We do not retrieve any response (activity is still open).
foreach ($results['options'] as $option) {
$this->assertCount(0, $option['userresponses']);
}
// We close the activity (setting timeclose in the past).
$choice->timeclose = $timenow - (60 * 60 * 24 * 2);
$DB->update_record('choice', $choice);
// Now as Stundent2 we will see results!
$results = mod_choice_external::get_choice_results($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_results_returns(), $results);
// Create an array with optionID as Key.
$resultsarr = array();
foreach ($results['options'] as $option) {
$resultsarr[$option['id']] = $option['userresponses'];
}
// The stundent1 is the userid who choosed the myanswer(option 3).
$this->assertEquals($resultsarr[$myanswer][0]['userid'], $student1->id);
// The stundent2 is the userid who didn't answered yet.
$this->assertEquals($resultsarr[0][0]['userid'], $student2->id);
// Do not publish user names!
$choice->publish = 0;
$DB->update_record('choice', $choice);
$results = mod_choice_external::get_choice_results($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_results_returns(), $results);
// Create an array with optionID as Key.
$resultsarr = array();
// Does not show any user response!
foreach ($results['options'] as $option) {
$this->assertCount(0, $option['userresponses']);
$resultsarr[$option['id']] = $option;
}
// But we can see totals and percentages.
$this->assertEquals(1, $resultsarr[$myanswer]['numberofuser']);
}
/**
* Test get_choice_options
*/
public function test_get_choice_options(): void {
global $DB;
// Warningcodes.
$notopenyet = 1;
$previewonly = 2;
$expired = 3;
$this->resetAfterTest(true);
$timenow = time();
$timeopen = $timenow + (60 * 60 * 24 * 2);
$timeclose = $timenow + (60 * 60 * 24 * 7);
$course = self::getDataGenerator()->create_course();
$possibleoptions = array('fried rice', 'spring rolls', 'sweet and sour pork', 'satay beef', 'gyouza');
$params = array();
$params['course'] = $course->id;
$params['option'] = $possibleoptions;
$params['name'] = 'First Choice Activity';
$params['showpreview'] = 0;
$generator = $this->getDataGenerator()->get_plugin_generator('mod_choice');
$choice = $generator->create_instance($params);
$student1 = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Enroll Students in Course.
self::getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
$this->setUser($student1);
$results = mod_choice_external::get_choice_options($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_options_returns(), $results);
// We should retrieve all options.
$this->assertCount(count($possibleoptions), $results['options']);
// Here we force timeopen/close in the future.
$choice->timeopen = $timeopen;
$choice->timeclose = $timeclose;
$DB->update_record('choice', $choice);
$results = mod_choice_external::get_choice_options($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_options_returns(), $results);
// We should retrieve no options.
$this->assertCount(0, $results['options']);
$this->assertEquals($notopenyet, $results['warnings'][0]['warningcode']);
// Here we see the options because of preview!
$choice->showpreview = 1;
$DB->update_record('choice', $choice);
$results = mod_choice_external::get_choice_options($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_options_returns(), $results);
// We should retrieve all options.
$this->assertCount(count($possibleoptions), $results['options']);
foreach ($results['options'] as $option) {
// Each option is disabled as this is only the preview!
$this->assertEquals(1, $option['disabled']);
}
$warnings = array();
foreach ($results['warnings'] as $warning) {
$warnings[$warning['warningcode']] = $warning['message'];
}
$this->assertTrue(isset($warnings[$previewonly]));
$this->assertTrue(isset($warnings[$notopenyet]));
// Simulate activity as opened!
$choice->timeopen = $timenow - (60 * 60 * 24 * 3);
$choice->timeclose = $timenow + (60 * 60 * 24 * 2);
$DB->update_record('choice', $choice);
$cm = get_coursemodule_from_id('choice', $choice->cmid);
$choiceinstance = choice_get_choice($cm->instance);
$optionsids = array_keys($choiceinstance->option);
$myanswerid = $optionsids[2];
choice_user_submit_response($myanswerid, $choice, $student1->id, $course, $cm);
$results = mod_choice_external::get_choice_options($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_options_returns(), $results);
// We should retrieve all options.
$this->assertCount(count($possibleoptions), $results['options']);
foreach ($results['options'] as $option) {
// When we answered and we cannot update our choice.
if ($option['id'] == $myanswerid and !$choice->allowupdate) {
$this->assertEquals(1, $option['disabled']);
$this->assertEquals(1, $option['checked']);
} else {
$this->assertEquals(0, $option['disabled']);
}
}
// Set timeopen and timeclose as older than today!
// We simulate what happens when the activity is closed.
$choice->timeopen = $timenow - (60 * 60 * 24 * 3);
$choice->timeclose = $timenow - (60 * 60 * 24 * 2);
$DB->update_record('choice', $choice);
$results = mod_choice_external::get_choice_options($choice->id);
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::get_choice_options_returns(), $results);
// We should retrieve no options.
$this->assertCount(0, $results['options']);
$this->assertEquals($expired, $results['warnings'][0]['warningcode']);
}
/**
* Test submit_choice_response
*/
public function test_submit_choice_response(): void {
global $DB;
$this->resetAfterTest(true);
$course = self::getDataGenerator()->create_course();
$params = new \stdClass();
$params->course = $course->id;
$params->option = array('fried rice', 'spring rolls', 'sweet and sour pork', 'satay beef', 'gyouza');
$params->name = 'First Choice Activity';
$params->showresults = CHOICE_SHOWRESULTS_ALWAYS;
$params->allowmultiple = 1;
$params->showunanswered = 1;
$choice = self::getDataGenerator()->create_module('choice', $params);
$cm = get_coursemodule_from_id('choice', $choice->cmid);
$choiceinstance = choice_get_choice($cm->instance);
$options = array_keys($choiceinstance->option);
$student1 = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Enroll Students in Course1.
self::getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
$this->setUser($student1);
$myresponse = $options[2];
$results = mod_choice_external::submit_choice_response($choice->id, array($myresponse));
// We need to execute the return values cleaning process to simulate the web service server.
$results = external_api::clean_returnvalue(mod_choice_external::submit_choice_response_returns(), $results);
$myanswers = $DB->get_records('choice_answers', array('choiceid' => $choice->id, 'userid' => $student1->id));
$myanswer = reset($myanswers);
$this->assertEquals($results['answers'][0]['id'], $myanswer->id);
$this->assertEquals($results['answers'][0]['choiceid'], $myanswer->choiceid);
$this->assertEquals($results['answers'][0]['userid'], $myanswer->userid);
$this->assertEquals($results['answers'][0]['timemodified'], $myanswer->timemodified);
}
/**
* Test view_choice
*/
public function test_view_choice(): void {
global $DB;
$this->resetAfterTest(true);
// Setup test data.
$course = $this->getDataGenerator()->create_course();
$choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
$context = \context_module::instance($choice->cmid);
$cm = get_coursemodule_from_instance('choice', $choice->id);
// Test invalid instance id.
try {
mod_choice_external::view_choice(0);
$this->fail('Exception expected due to invalid mod_choice instance id.');
} catch (\moodle_exception $e) {
$this->assertEquals('invalidcoursemodule', $e->errorcode);
}
// Test not-enrolled user.
$user = self::getDataGenerator()->create_user();
$this->setUser($user);
try {
mod_choice_external::view_choice($choice->id);
$this->fail('Exception expected due to not enrolled user.');
} catch (\moodle_exception $e) {
$this->assertEquals('requireloginerror', $e->errorcode);
}
// Test user with full capabilities.
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$result = mod_choice_external::view_choice($choice->id);
$result = external_api::clean_returnvalue(mod_choice_external::view_choice_returns(), $result);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = array_shift($events);
// Checking that the event contains the expected values.
$this->assertInstanceOf('\mod_choice\event\course_module_viewed', $event);
$this->assertEquals($context, $event->get_context());
$moodlechoice = new \moodle_url('/mod/choice/view.php', array('id' => $cm->id));
$this->assertEquals($moodlechoice, $event->get_url());
$this->assertEventContextNotUsed($event);
$this->assertNotEmpty($event->get_name());
}
/**
* Test get_choices_by_courses
*/
public function test_get_choices_by_courses(): void {
global $DB;
$this->resetAfterTest(true);
// As admin.
$this->setAdminUser();
$course1 = self::getDataGenerator()->create_course();
$choiceoptions1 = array(
'course' => $course1->id,
'name' => 'First IMSCP'
);
$choice1 = self::getDataGenerator()->create_module('choice', $choiceoptions1);
$course2 = self::getDataGenerator()->create_course();
$choiceoptions2 = array(
'course' => $course2->id,
'name' => 'Second IMSCP'
);
$choice2 = self::getDataGenerator()->create_module('choice', $choiceoptions2);
$student1 = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Enroll Student1 in Course1.
self::getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id);
$this->setUser($student1);
$choices = mod_choice_external::get_choices_by_courses(array());
$choices = external_api::clean_returnvalue(mod_choice_external::get_choices_by_courses_returns(), $choices);
$this->assertCount(1, $choices['choices']);
$this->assertEquals('First IMSCP', $choices['choices'][0]['name']);
// As Student you cannot see some IMSCP properties like 'section'.
$this->assertFalse(isset($choices['choices'][0]['section']));
// Student1 is not enrolled in this Course.
// The webservice will give a warning!
$choices = mod_choice_external::get_choices_by_courses(array($course2->id));
$choices = external_api::clean_returnvalue(mod_choice_external::get_choices_by_courses_returns(), $choices);
$this->assertCount(0, $choices['choices']);
$this->assertEquals(1, $choices['warnings'][0]['warningcode']);
// Now as admin.
$this->setAdminUser();
// As Admin we can see this IMSCP.
$choices = mod_choice_external::get_choices_by_courses(array($course2->id));
$choices = external_api::clean_returnvalue(mod_choice_external::get_choices_by_courses_returns(), $choices);
$this->assertCount(1, $choices['choices']);
$this->assertEquals('Second IMSCP', $choices['choices'][0]['name']);
// As an Admin you can see some IMSCP properties like 'section'.
$this->assertEquals(0, $choices['choices'][0]['section']);
// Now, prohibit capabilities.
$this->setUser($student1);
$contextcourse1 = \context_course::instance($course1->id);
// Prohibit capability = mod:choice:choose on Course1 for students.
assign_capability('mod/choice:choose', CAP_PROHIBIT, $studentrole->id, $contextcourse1->id);
accesslib_clear_all_caches_for_unit_testing();
$choices = mod_choice_external::get_choices_by_courses(array($course1->id));
$choices = external_api::clean_returnvalue(mod_choice_external::get_choices_by_courses_returns(), $choices);
$this->assertFalse(isset($choices['choices'][0]['timeopen']));
}
/**
* Test delete_choice_responses
*/
public function test_delete_choice_responses(): void {
global $DB;
$this->resetAfterTest(true);
$course = self::getDataGenerator()->create_course();
$params = new \stdClass();
$params->course = $course->id;
$params->option = array('fried rice', 'spring rolls', 'sweet and sour pork', 'satay beef', 'gyouza');
$params->name = 'First Choice Activity';
$params->showresults = CHOICE_SHOWRESULTS_ALWAYS;
$params->allowmultiple = 1;
$params->showunanswered = 1;
$choice = self::getDataGenerator()->create_module('choice', $params);
$cm = get_coursemodule_from_id('choice', $choice->cmid);
$choiceinstance = choice_get_choice($cm->instance);
$options = array_keys($choiceinstance->option);
$student = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
// Enroll student in Course1.
self::getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
$this->setUser($student);
$results = mod_choice_external::submit_choice_response($choice->id, array($options[1], $options[2]));
$results = external_api::clean_returnvalue(mod_choice_external::submit_choice_response_returns(), $results);
$myresponses = array_keys(choice_get_my_response($choice));
// Try to delete responses when allow update is false.
try {
mod_choice_external::delete_choice_responses($choice->id, array($myresponses[0], $myresponses[0]));
$this->fail('Exception expected due to missing permissions.');
} catch (\required_capability_exception $e) {
$this->assertEquals('nopermissions', $e->errorcode);
}
// Set allow update to true, and a passed time close.
$DB->set_field('choice', 'allowupdate', 1, array('id' => $choice->id));
$DB->set_field('choice', 'timeclose', time() - DAYSECS, array('id' => $choice->id));
try {
mod_choice_external::delete_choice_responses($choice->id, array($myresponses[0], $myresponses[1]));
$this->fail('Exception expected due to expired choice.');
} catch (\moodle_exception $e) {
$this->assertEquals('expired', $e->errorcode);
}
// Reset time close. We should be able now to delete all the responses.
$DB->set_field('choice', 'timeclose', 0, array('id' => $choice->id));
$results = mod_choice_external::delete_choice_responses($choice->id, array($myresponses[0], $myresponses[1]));
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertTrue($results['status']);
$this->assertCount(0, $results['warnings']);
// Now, in the DB 0 responses.
$this->assertCount(0, choice_get_my_response($choice));
// Submit again the responses.
$results = mod_choice_external::submit_choice_response($choice->id, array($options[1], $options[2]));
$results = external_api::clean_returnvalue(mod_choice_external::submit_choice_response_returns(), $results);
$myresponses = array_keys(choice_get_my_response($choice));
// Delete only one response.
$results = mod_choice_external::delete_choice_responses($choice->id, array($myresponses[0]));
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertTrue($results['status']);
$this->assertCount(0, $results['warnings']);
// Now, in the DB 1 response still.
$this->assertCount(1, choice_get_my_response($choice));
// Delete the remaining response, passing 2 invalid responses ids.
$results = mod_choice_external::delete_choice_responses($choice->id, array($myresponses[1], $myresponses[0] + 2,
$myresponses[0] + 3));
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertTrue($results['status']);
// 2 warnings, 2 invalid responses.
$this->assertCount(2, $results['warnings']);
// Now, in the DB 0 responses.
$this->assertCount(0, choice_get_my_response($choice));
// Now, as an admin we must be able to delete all the responses under any condition.
$this->setUser($student);
// Submit again the responses.
$results = mod_choice_external::submit_choice_response($choice->id, array($options[1], $options[2]));
$results = external_api::clean_returnvalue(mod_choice_external::submit_choice_response_returns(), $results);
$studentresponses = array_keys(choice_get_my_response($choice));
$this->setAdminUser();
$DB->set_field('choice', 'allowupdate', 0, array('id' => $choice->id));
$DB->set_field('choice', 'timeclose', time() - DAYSECS, array('id' => $choice->id));
$results = mod_choice_external::delete_choice_responses($choice->id, array($studentresponses[0], $studentresponses[1]));
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertTrue($results['status']);
$this->assertCount(0, $results['warnings']);
// Submit again the responses.
$this->setUser($student);
$DB->set_field('choice', 'timeclose', 0, array('id' => $choice->id));
$results = mod_choice_external::submit_choice_response($choice->id, array($options[1], $options[2]));
$results = external_api::clean_returnvalue(mod_choice_external::submit_choice_response_returns(), $results);
// Test admin try to delete his own responses (he didn't respond so nothing should be deleted).
$this->setAdminUser();
$results = mod_choice_external::delete_choice_responses($choice->id);
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertFalse($results['status']);
$this->assertCount(0, $results['warnings']);
$allresponses = choice_get_all_responses($choice);
$this->assertCount(2, $allresponses); // No responses deleted (admin didn't submit any).
// Now admin submit a couple of responses more.
$results = mod_choice_external::submit_choice_response($choice->id, array($options[1], $options[2]));
$results = external_api::clean_returnvalue(mod_choice_external::submit_choice_response_returns(), $results);
$allresponses = choice_get_all_responses($choice);
$this->assertCount(4, $allresponses);
// Admin responses are deleted when passing an empty array.
$results = mod_choice_external::delete_choice_responses($choice->id);
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertTrue($results['status']);
$this->assertCount(0, $results['warnings']);
$allresponses = choice_get_all_responses($choice);
$this->assertCount(2, $allresponses);
// Now admin will delete all the other users responses.
$results = mod_choice_external::delete_choice_responses($choice->id, array_keys($allresponses));
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertTrue($results['status']);
$this->assertCount(0, $results['warnings']);
$allresponses = choice_get_all_responses($choice);
$this->assertCount(0, $allresponses); // Now all the responses were deleted.
// Admin try do delete an invalid response.
$results = mod_choice_external::delete_choice_responses($choice->id, array(-1));
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertFalse($results['status']);
$this->assertCount(1, $results['warnings']);
// Now, in the DB 0 responses.
$this->setUser($student);
// Submit again respones.
$DB->set_field('choice', 'allowupdate', 1, array('id' => $choice->id));
$DB->set_field('choice', 'timeclose', 0, array('id' => $choice->id));
$results = mod_choice_external::submit_choice_response($choice->id, array($options[1], $options[2]));
$results = external_api::clean_returnvalue(mod_choice_external::submit_choice_response_returns(), $results);
// Delete all responses.
$results = mod_choice_external::delete_choice_responses($choice->id);
$results = external_api::clean_returnvalue(mod_choice_external::delete_choice_responses_returns(), $results);
$this->assertTrue($results['status']);
$this->assertCount(0, $results['warnings']);
$this->assertCount(0, choice_get_my_response($choice));
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* mod_choice data generator.
*
* @package mod_choice
* @category test
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* mod_choice data generator class.
*
* @package mod_choice
* @category test
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_choice_generator extends testing_module_generator {
public function create_instance($record = null, array $options = null) {
$record = (object)(array)$record;
if (!isset($record->timemodified)) {
$record->timemodified = time();
}
if (!isset($record->option)) {
$record->option = array();
$record->option[] = 'Soft Drink';
$record->option[] = 'Beer';
$record->option[] = 'Wine';
$record->option[] = 'Spirits';
} else if (!is_array($record->option)) {
$record->option = preg_split('/\s*,\s*/', trim($record->option), -1, PREG_SPLIT_NO_EMPTY);
}
return parent::create_instance($record, (array)$options);
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_choice;
/**
* Generator tests class.
*
* @package mod_choice
* @copyright 2013 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class generator_test extends \advanced_testcase {
public function test_create_instance(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$this->assertFalse($DB->record_exists('choice', array('course' => $course->id)));
$choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
$this->assertEquals(1, $DB->count_records('choice', array('course' => $course->id)));
$this->assertTrue($DB->record_exists('choice', array('course' => $course->id)));
$this->assertTrue($DB->record_exists('choice', array('id' => $choice->id)));
$params = array('course' => $course->id, 'name' => 'One more choice');
$choice = $this->getDataGenerator()->create_module('choice', $params);
$this->assertEquals(2, $DB->count_records('choice', array('course' => $course->id)));
$this->assertEquals('One more choice', $DB->get_field_select('choice', 'name', 'id = :id', array('id' => $choice->id)));
$params = new \stdClass();
$params->course = $course->id;
$params->option = array('fried rice', 'spring rolls', 'sweet and sour pork', 'satay beef', 'gyouza');
$choice = $this->getDataGenerator()->create_module('choice', $params);
$this->assertEquals(5, $DB->count_records('choice_options', array('choiceid' => $choice->id)));
}
}
File diff suppressed because it is too large Load Diff
+316
View File
@@ -0,0 +1,316 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy provider tests.
*
* @package mod_choice
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_choice\privacy;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\deletion_criteria;
use mod_choice\privacy\provider;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy provider tests class.
*
* @package mod_choice
* @copyright 2018 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider_test extends \core_privacy\tests\provider_testcase {
/** @var stdClass The student object. */
protected $student;
/** @var stdClass The choice object. */
protected $choice;
/** @var stdClass The course object. */
protected $course;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
$this->resetAfterTest();
global $DB;
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$options = ['fried rice', 'spring rolls', 'sweet and sour pork', 'satay beef', 'gyouza'];
$params = [
'course' => $course->id,
'option' => $options,
'name' => 'First Choice Activity',
'showpreview' => 0
];
$plugingenerator = $generator->get_plugin_generator('mod_choice');
// The choice activity the user will answer.
$choice = $plugingenerator->create_instance($params);
// Create another choice activity.
$plugingenerator->create_instance($params);
$cm = get_coursemodule_from_instance('choice', $choice->id);
// Create a student which will make a choice.
$student = $generator->create_user();
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$generator->enrol_user($student->id, $course->id, $studentrole->id);
$choicewithoptions = choice_get_choice($choice->id);
$optionids = array_keys($choicewithoptions->option);
choice_user_submit_response($optionids[2], $choice, $student->id, $course, $cm);
$this->student = $student;
$this->choice = $choice;
$this->course = $course;
}
/**
* Test for provider::get_metadata().
*/
public function test_get_metadata(): void {
$collection = new collection('mod_choice');
$newcollection = provider::get_metadata($collection);
$itemcollection = $newcollection->get_collection();
$this->assertCount(1, $itemcollection);
$table = reset($itemcollection);
$this->assertEquals('choice_answers', $table->get_name());
$privacyfields = $table->get_privacy_fields();
$this->assertArrayHasKey('choiceid', $privacyfields);
$this->assertArrayHasKey('optionid', $privacyfields);
$this->assertArrayHasKey('userid', $privacyfields);
$this->assertArrayHasKey('timemodified', $privacyfields);
$this->assertEquals('privacy:metadata:choice_answers', $table->get_summary());
}
/**
* Test for provider::get_contexts_for_userid().
*/
public function test_get_contexts_for_userid(): void {
$cm = get_coursemodule_from_instance('choice', $this->choice->id);
$contextlist = provider::get_contexts_for_userid($this->student->id);
$this->assertCount(1, $contextlist);
$contextforuser = $contextlist->current();
$cmcontext = \context_module::instance($cm->id);
$this->assertEquals($cmcontext->id, $contextforuser->id);
}
/**
* Test for provider::export_user_data().
*/
public function test_export_for_context(): void {
$cm = get_coursemodule_from_instance('choice', $this->choice->id);
$cmcontext = \context_module::instance($cm->id);
// Export all of the data for the context.
$this->export_context_data_for_user($this->student->id, $cmcontext, 'mod_choice');
$writer = \core_privacy\local\request\writer::with_context($cmcontext);
$this->assertTrue($writer->has_any_data());
}
/**
* Test for provider::delete_data_for_all_users_in_context().
*/
public function test_delete_data_for_all_users_in_context(): void {
global $DB;
$choice = $this->choice;
$generator = $this->getDataGenerator();
$cm = get_coursemodule_from_instance('choice', $this->choice->id);
// Create another student who will answer the choice activity.
$student = $generator->create_user();
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$generator->enrol_user($student->id, $this->course->id, $studentrole->id);
$choicewithoptions = choice_get_choice($choice->id);
$optionids = array_keys($choicewithoptions->option);
choice_user_submit_response($optionids[1], $choice, $student->id, $this->course, $cm);
// Before deletion, we should have 2 responses.
$count = $DB->count_records('choice_answers', ['choiceid' => $choice->id]);
$this->assertEquals(2, $count);
// Delete data based on context.
$cmcontext = \context_module::instance($cm->id);
provider::delete_data_for_all_users_in_context($cmcontext);
// After deletion, the choice answers for that choice activity should have been deleted.
$count = $DB->count_records('choice_answers', ['choiceid' => $choice->id]);
$this->assertEquals(0, $count);
}
/**
* Test for provider::delete_data_for_user().
*/
public function test_delete_data_for_user_(): void {
global $DB;
$choice = $this->choice;
$generator = $this->getDataGenerator();
$cm1 = get_coursemodule_from_instance('choice', $this->choice->id);
// Create a second choice activity.
$options = ['Boracay', 'Camiguin', 'Bohol', 'Cebu', 'Coron'];
$params = [
'course' => $this->course->id,
'option' => $options,
'name' => 'Which do you think is the best island in the Philippines?',
'showpreview' => 0
];
$plugingenerator = $generator->get_plugin_generator('mod_choice');
$choice2 = $plugingenerator->create_instance($params);
$plugingenerator->create_instance($params);
$cm2 = get_coursemodule_from_instance('choice', $choice2->id);
// Make a selection for the first student for the 2nd choice activity.
$choicewithoptions = choice_get_choice($choice2->id);
$optionids = array_keys($choicewithoptions->option);
choice_user_submit_response($optionids[2], $choice2, $this->student->id, $this->course, $cm2);
// Create another student who will answer the first choice activity.
$otherstudent = $generator->create_user();
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$generator->enrol_user($otherstudent->id, $this->course->id, $studentrole->id);
$choicewithoptions = choice_get_choice($choice->id);
$optionids = array_keys($choicewithoptions->option);
choice_user_submit_response($optionids[1], $choice, $otherstudent->id, $this->course, $cm1);
// Before deletion, we should have 2 responses.
$count = $DB->count_records('choice_answers', ['choiceid' => $choice->id]);
$this->assertEquals(2, $count);
$context1 = \context_module::instance($cm1->id);
$context2 = \context_module::instance($cm2->id);
$contextlist = new \core_privacy\local\request\approved_contextlist($this->student, 'choice',
[\context_system::instance()->id, $context1->id, $context2->id]);
provider::delete_data_for_user($contextlist);
// After deletion, the choice answers for the first student should have been deleted.
$count = $DB->count_records('choice_answers', ['choiceid' => $choice->id, 'userid' => $this->student->id]);
$this->assertEquals(0, $count);
// Confirm that we only have one choice answer available.
$choiceanswers = $DB->get_records('choice_answers');
$this->assertCount(1, $choiceanswers);
$lastresponse = reset($choiceanswers);
// And that it's the other student's response.
$this->assertEquals($otherstudent->id, $lastresponse->userid);
}
/**
* Test for provider::get_users_in_context().
*/
public function test_get_users_in_context(): void {
$cm = get_coursemodule_from_instance('choice', $this->choice->id);
$cmcontext = \context_module::instance($cm->id);
$userlist = new \core_privacy\local\request\userlist($cmcontext, 'mod_choice');
\mod_choice\privacy\provider::get_users_in_context($userlist);
$this->assertEquals(
[$this->student->id],
$userlist->get_userids()
);
}
/**
* Test for provider::get_users_in_context() with invalid context type.
*/
public function test_get_users_in_context_invalid_context_type(): void {
$systemcontext = \context_system::instance();
$userlist = new \core_privacy\local\request\userlist($systemcontext, 'mod_choice');
\mod_choice\privacy\provider::get_users_in_context($userlist);
$this->assertCount(0, $userlist->get_userids());
}
/**
* Test for provider::delete_data_for_users().
*/
public function test_delete_data_for_users(): void {
global $DB;
$choice = $this->choice;
$generator = $this->getDataGenerator();
$cm1 = get_coursemodule_from_instance('choice', $this->choice->id);
// Create a second choice activity.
$options = ['Boracay', 'Camiguin', 'Bohol', 'Cebu', 'Coron'];
$params = [
'course' => $this->course->id,
'option' => $options,
'name' => 'Which do you think is the best island in the Philippines?',
'showpreview' => 0
];
$plugingenerator = $generator->get_plugin_generator('mod_choice');
$choice2 = $plugingenerator->create_instance($params);
$plugingenerator->create_instance($params);
$cm2 = get_coursemodule_from_instance('choice', $choice2->id);
// Make a selection for the first student for the 2nd choice activity.
$choicewithoptions = choice_get_choice($choice2->id);
$optionids = array_keys($choicewithoptions->option);
choice_user_submit_response($optionids[2], $choice2, $this->student->id, $this->course, $cm2);
// Create 2 other students who will answer the first choice activity.
$otherstudent = $generator->create_and_enrol($this->course, 'student');
$anotherstudent = $generator->create_and_enrol($this->course, 'student');
$choicewithoptions = choice_get_choice($choice->id);
$optionids = array_keys($choicewithoptions->option);
choice_user_submit_response($optionids[1], $choice, $otherstudent->id, $this->course, $cm1);
choice_user_submit_response($optionids[1], $choice, $anotherstudent->id, $this->course, $cm1);
// Before deletion, we should have 3 responses in the first choice activity.
$count = $DB->count_records('choice_answers', ['choiceid' => $choice->id]);
$this->assertEquals(3, $count);
$context1 = \context_module::instance($cm1->id);
$approveduserlist = new \core_privacy\local\request\approved_userlist($context1, 'choice',
[$this->student->id, $otherstudent->id]);
provider::delete_data_for_users($approveduserlist);
// After deletion, the choice answers of the 2 students provided above should have been deleted
// from the first choice activity. So there should only remain 1 answer which is for $anotherstudent.
$choiceanswers = $DB->get_records('choice_answers', ['choiceid' => $choice->id]);
$this->assertCount(1, $choiceanswers);
$lastresponse = reset($choiceanswers);
$this->assertEquals($anotherstudent->id, $lastresponse->userid);
// Confirm that the answer that was submitted in the other choice activity is intact.
$choiceanswers = $DB->get_records_select('choice_answers', 'choiceid <> ?', [$choice->id]);
$this->assertCount(1, $choiceanswers);
$lastresponse = reset($choiceanswers);
// And that it's for the choice2 activity.
$this->assertEquals($choice2->id, $lastresponse->choiceid);
}
}
+27
View File
@@ -0,0 +1,27 @@
This files describes API changes in /mod/choice/*,
information provided here is intended especially for developers.
=== 3.3.2 ===
* choice_refresh_events() Now takes two additional parameters to refine the update to a specific instance. This function
now optionally takes the module instance object or ID, and the course module object or ID. Please try to send the full
objects instead of the ids to save DB calls.
=== 3.2 ===
* Events mod_choice\event\answer_submitted and mod_choice\event\answer_updated
are no longer triggered. Observers listening to these events must instead listen
to mod_choice\event\answer_created and mod_choice\event\answer_deleted that are
triggered for each option that is selected or unselected. User whose choice was
modified can be found in $event->relateduserid (this does not have to be the
user who performs the action).
=== 3.0 ===
* External function mod_choice_external::get_choices_by_courses returned parameter "name" and
mod_choice_external::get_choice_results "text" have been changed to PARAM_RAW.
This is because the new external_format_string function may return raw data if the global moodlewssettingraw parameter is used.
=== 2.9 ===
* custom renderers must be updated to include action=makechoice field in display_options()
+30
View File
@@ -0,0 +1,30 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Version information
*
* @package mod_choice
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'mod_choice'; // Full name of the plugin (used for diagnostics)
$plugin->cron = 0;
+249
View File
@@ -0,0 +1,249 @@
<?php
require_once("../../config.php");
require_once("lib.php");
require_once($CFG->libdir . '/completionlib.php');
$id = required_param('id', PARAM_INT); // Course Module ID
$action = optional_param('action', '', PARAM_ALPHANUMEXT);
$attemptids = optional_param_array('attemptid', array(), PARAM_INT); // Get array of responses to delete or modify.
$userids = optional_param_array('userid', array(), PARAM_INT); // Get array of users whose choices need to be modified.
$notify = optional_param('notify', '', PARAM_ALPHA);
$url = new moodle_url('/mod/choice/view.php', array('id'=>$id));
if ($action !== '') {
$url->param('action', $action);
}
$PAGE->set_url($url);
if (! $cm = get_coursemodule_from_id('choice', $id)) {
throw new \moodle_exception('invalidcoursemodule');
}
$cm = cm_info::create($cm);
if (! $course = $DB->get_record("course", array("id" => $cm->course))) {
throw new \moodle_exception('coursemisconf');
}
require_course_login($course, false, $cm);
if (!$choice = choice_get_choice($cm->instance)) {
throw new \moodle_exception('invalidcoursemodule');
}
$strchoice = get_string('modulename', 'choice');
$strchoices = get_string('modulenameplural', 'choice');
$context = context_module::instance($cm->id);
list($choiceavailable, $warnings) = choice_get_availability_status($choice);
if ($action == 'delchoice' and confirm_sesskey() and is_enrolled($context, NULL, 'mod/choice:choose') and $choice->allowupdate
and $choiceavailable) {
$answercount = $DB->count_records('choice_answers', array('choiceid' => $choice->id, 'userid' => $USER->id));
if ($answercount > 0) {
$choiceanswers = $DB->get_records('choice_answers', array('choiceid' => $choice->id, 'userid' => $USER->id),
'', 'id');
$todelete = array_keys($choiceanswers);
choice_delete_responses($todelete, $choice, $cm, $course);
redirect("view.php?id=$cm->id");
}
}
$PAGE->set_title($choice->name);
$PAGE->set_heading($course->fullname);
/// Submit any new data if there is any
if (data_submitted() && !empty($action) && confirm_sesskey()) {
$timenow = time();
if (has_capability('mod/choice:deleteresponses', $context)) {
if ($action === 'delete') {
// Some responses need to be deleted.
choice_delete_responses($attemptids, $choice, $cm, $course);
redirect("view.php?id=$cm->id");
}
if (preg_match('/^choose_(\d+)$/', $action, $actionmatch)) {
// Modify responses of other users.
$newoptionid = (int)$actionmatch[1];
choice_modify_responses($userids, $attemptids, $newoptionid, $choice, $cm, $course);
redirect("view.php?id=$cm->id");
}
}
// Redirection after all POSTs breaks block editing, we need to be more specific!
if ($choice->allowmultiple) {
$answer = optional_param_array('answer', array(), PARAM_INT);
} else {
$answer = optional_param('answer', '', PARAM_INT);
}
if (!$choiceavailable) {
$reason = current(array_keys($warnings));
throw new moodle_exception($reason, 'choice', '', $warnings[$reason]);
}
if ($answer && is_enrolled($context, null, 'mod/choice:choose')) {
choice_user_submit_response($answer, $choice, $USER->id, $course, $cm);
redirect(new moodle_url('/mod/choice/view.php',
array('id' => $cm->id, 'notify' => 'choicesaved', 'sesskey' => sesskey())));
} else if (empty($answer) and $action === 'makechoice') {
// We cannot use the 'makechoice' alone because there might be some legacy renderers without it,
// outdated renderers will not get the 'mustchoose' message - bad luck.
redirect(new moodle_url('/mod/choice/view.php',
array('id' => $cm->id, 'notify' => 'mustchooseone', 'sesskey' => sesskey())));
}
}
// Completion and trigger events.
choice_view($choice, $course, $cm, $context);
$PAGE->add_body_class('limitedwidth');
echo $OUTPUT->header();
if ($notify and confirm_sesskey()) {
if ($notify === 'choicesaved') {
echo $OUTPUT->notification(get_string('choicesaved', 'choice'), 'notifysuccess');
} else if ($notify === 'mustchooseone') {
echo $OUTPUT->notification(get_string('mustchooseone', 'choice'), 'notifyproblem');
}
}
/// Display the choice and possibly results
$eventdata = array();
$eventdata['objectid'] = $choice->id;
$eventdata['context'] = $context;
/// Check to see if groups are being used in this choice
$groupmode = groups_get_activity_groupmode($cm);
// Check if we want to include responses from inactive users.
$onlyactive = $choice->includeinactive ? false : true;
$allresponses = choice_get_response_data($choice, $cm, $groupmode, $onlyactive); // Big function, approx 6 SQL calls per user.
if (has_capability('mod/choice:readresponses', $context) && !$PAGE->has_secondary_navigation()) {
choice_show_reportlink($allresponses, $cm);
}
echo '<div class="clearer"></div>';
$timenow = time();
$current = choice_get_my_response($choice);
//if user has already made a selection, and they are not allowed to update it or if choice is not open, show their selected answer.
if (isloggedin() && (!empty($current)) &&
(empty($choice->allowupdate) || ($timenow > $choice->timeclose)) ) {
$choicetexts = array();
foreach ($current as $c) {
$choicetexts[] = format_string(choice_get_option_text($choice, $c->optionid));
}
echo $OUTPUT->box(get_string("yourselection", "choice") . ": " . implode('; ', $choicetexts), 'generalbox', 'yourselection');
}
/// Print the form
$choiceopen = true;
if ((!empty($choice->timeopen)) && ($choice->timeopen > $timenow)) {
if ($choice->showpreview) {
echo $OUTPUT->box(get_string('previewing', 'choice'), 'generalbox alert');
} else {
echo $OUTPUT->footer();
exit;
}
} else if ((!empty($choice->timeclose)) && ($timenow > $choice->timeclose)) {
$choiceopen = false;
}
if ( (!$current or $choice->allowupdate) and $choiceopen and is_enrolled($context, NULL, 'mod/choice:choose')) {
// Show information on how the results will be published to students.
$publishinfo = null;
switch ($choice->showresults) {
case CHOICE_SHOWRESULTS_NOT:
$publishinfo = get_string('publishinfonever', 'choice');
break;
case CHOICE_SHOWRESULTS_AFTER_ANSWER:
if ($choice->publish == CHOICE_PUBLISH_ANONYMOUS) {
$publishinfo = get_string('publishinfoanonafter', 'choice');
} else {
$publishinfo = get_string('publishinfofullafter', 'choice');
}
break;
case CHOICE_SHOWRESULTS_AFTER_CLOSE:
if ($choice->publish == CHOICE_PUBLISH_ANONYMOUS) {
$publishinfo = get_string('publishinfoanonclose', 'choice');
} else {
$publishinfo = get_string('publishinfofullclose', 'choice');
}
break;
default:
// No need to inform the user in the case of CHOICE_SHOWRESULTS_ALWAYS since it's already obvious that the results are
// being published.
break;
}
// Show info if necessary.
if (!empty($publishinfo)) {
echo $OUTPUT->notification($publishinfo, 'info');
}
// They haven't made their choice yet or updates allowed and choice is open.
$options = choice_prepare_options($choice, $USER, $cm, $allresponses);
$renderer = $PAGE->get_renderer('mod_choice');
echo $renderer->display_options($options, $cm->id, $choice->display, $choice->allowmultiple);
$choiceformshown = true;
} else {
$choiceformshown = false;
}
if (!$choiceformshown) {
$sitecontext = context_system::instance();
if (isguestuser()) {
// Guest account
echo $OUTPUT->confirm(get_string('noguestchoose', 'choice').'<br /><br />'.get_string('liketologin'),
get_login_url(), new moodle_url('/course/view.php', array('id'=>$course->id)));
} else if (!is_enrolled($context)) {
// Only people enrolled can make a choice
$SESSION->wantsurl = qualified_me();
$SESSION->enrolcancel = get_local_referer(false);
$coursecontext = context_course::instance($course->id);
$courseshortname = format_string($course->shortname, true, array('context' => $coursecontext));
echo $OUTPUT->box_start('generalbox', 'notice');
echo '<p align="center">'. get_string('notenrolledchoose', 'choice') .'</p>';
echo $OUTPUT->container_start('continuebutton');
echo $OUTPUT->single_button(new moodle_url('/enrol/index.php?', array('id'=>$course->id)), get_string('enrolme', 'core_enrol', $courseshortname));
echo $OUTPUT->container_end();
echo $OUTPUT->box_end();
}
}
// print the results at the bottom of the screen
if (choice_can_view_results($choice, $current, $choiceopen)) {
$results = prepare_choice_show_results($choice, $course, $cm, $allresponses);
$renderer = $PAGE->get_renderer('mod_choice');
if ($results->publish) { // If set to publish full results, display a heading for the responses section.
echo html_writer::tag('h3', format_string(get_string("responses", "choice")), ['class' => 'mt-4']);
}
if ($groupmode) { // If group mode is enabled, display the groups selector.
groups_get_activity_group($cm, true);
$groupsactivitymenu = groups_print_activity_menu($cm, new moodle_url('/mod/choice/view.php', ['id' => $id]),
true);
echo html_writer::div($groupsactivitymenu, 'mt-3 mb-1');
}
$resultstable = $renderer->display_result($results);
echo $OUTPUT->box($resultstable);
} else if (!$choiceformshown) {
echo $OUTPUT->box(get_string('noresultsviewable', 'choice'));
}
echo $OUTPUT->footer();