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
+11
View File
@@ -0,0 +1,11 @@
/**
* AMD module to handle overriding activity completion status.
*
* @module report_progress/completion_override
* @copyright 2016 onwards Eiz Eddin Al Katrib <eiz@barasoft.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.1
*/
define("report_progress/completion_override",["jquery","core/ajax","core/str","core/modal_save_cancel","core/modal_events","core/notification","core/custom_interaction_events","core/templates","core/pending"],(function($,Ajax,Str,ModalSaveCancel,ModalEvents,Notification,CustomEvents,Templates,Pending){var userFullName,triggerElement,userConfirm=function(e,data){data.originalEvent.preventDefault(),data.originalEvent.stopPropagation(),e.preventDefault(),e.stopPropagation();var elemData=(triggerElement=$(e.currentTarget)).data("changecompl").split("-"),override={userid:elemData[0],cmid:elemData[1],newstate:elemData[2]},newStateStr=1==override.newstate?"completion-y":"completion-n";Str.get_strings([{key:newStateStr,component:"completion"}]).then((function(strings){return Str.get_strings([{key:"confirm",component:"moodle"},{key:"areyousureoverridecompletion",component:"completion",param:strings[0]}])})).then((function(strings){return ModalSaveCancel.create({title:strings[0],body:strings[1],show:!0,removeOnClose:!0})})).then((function(modal){return modal.getRoot().on(ModalEvents.save,(function(){!function(override){const pendingPromise=new Pending("report_progress/compeletion_override/setOverride");Templates.render("core/loading",{}).then((function(html){return triggerElement.append(html),Ajax.call([{methodname:"core_completion_override_activity_completion_status",args:override}])[0]})).then((function(results){var completionState=results.state>0?1:0,tooltipKey=completionState?"completion-y-override":"completion-n-override";Str.get_string(tooltipKey,"completion",userFullName).then((function(stateString){var params={state:stateString,date:"",user:triggerElement.attr("data-userfullname"),activity:triggerElement.attr("data-activityname")};return Str.get_string("progress-title","completion",params)})).then((function(titleString){var tracking,completionTracking=triggerElement.attr("data-completiontracking");return Templates.renderPix((tracking=completionTracking,completionState>0?"i/completion-"+tracking+"-y-override":"i/completion-"+tracking+"-n-override"),"core",titleString)})).then((function(html){var oppositeState=completionState>0?0:1;triggerElement.find(".loading-icon").remove(),triggerElement.data("changecompl",override.userid+"-"+override.cmid+"-"+oppositeState),triggerElement.attr("data-changecompl",override.userid+"-"+override.cmid+"-"+oppositeState),triggerElement.children("img").replaceWith(html)})).catch(Notification.exception)})).then((()=>{pendingPromise.resolve()})).catch(Notification.exception)}(override)})),modal})).catch(Notification.exception)};return{init:function(fullName){userFullName=fullName,$("#completion-progress a.changecompl").each((function(index,element){CustomEvents.define(element,[CustomEvents.events.activate])})),$("#completion-progress").on(CustomEvents.events.activate,"a.changecompl",(function(e,data){userConfirm(e,data)}))}}}));
//# sourceMappingURL=completion_override.min.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,176 @@
// 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/>.
/**
* AMD module to handle overriding activity completion status.
*
* @module report_progress/completion_override
* @copyright 2016 onwards Eiz Eddin Al Katrib <eiz@barasoft.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.1
*/
define(['jquery', 'core/ajax', 'core/str', 'core/modal_save_cancel', 'core/modal_events', 'core/notification',
'core/custom_interaction_events', 'core/templates', 'core/pending'],
function($, Ajax, Str, ModalSaveCancel, ModalEvents, Notification, CustomEvents, Templates, Pending) {
/**
* @var {String} the full name of the current user.
* @private
*/
var userFullName;
/**
* @var {JQuery} JQuery object containing the element (completion link) that was most recently activated.
* @private
*/
var triggerElement;
/**
* Helper function to get the pix icon key based on the completion state.
* @method getIconDescriptorFromState
* @param {number} state The current completion state.
* @param {string} tracking The completion tracking type, either 'manual' or 'auto'.
* @return {string} the key for the respective icon.
* @private
*/
var getIconKeyFromState = function(state, tracking) {
return state > 0 ? 'i/completion-' + tracking + '-y-override' : 'i/completion-' + tracking + '-n-override';
};
/**
* Handles the confirmation of an override change, calling the web service to update it.
* @method setOverride
* @param {Object} override the override data
* @private
*/
var setOverride = function(override) {
const pendingPromise = new Pending('report_progress/compeletion_override/setOverride');
// Generate a loading spinner while we're working.
Templates.render('core/loading', {}).then(function(html) {
// Append the loading spinner to the trigger element.
triggerElement.append(html);
// Update the completion status override.
return Ajax.call([{
methodname: 'core_completion_override_activity_completion_status',
args: override
}])[0];
}).then(function(results) {
var completionState = (results.state > 0) ? 1 : 0;
// Now, build the new title string, get the new icon, and update the DOM.
var tooltipKey = completionState ? 'completion-y-override' : 'completion-n-override';
Str.get_string(tooltipKey, 'completion', userFullName).then(function(stateString) {
var params = {
state: stateString,
date: '',
user: triggerElement.attr('data-userfullname'),
activity: triggerElement.attr('data-activityname')
};
return Str.get_string('progress-title', 'completion', params);
}).then(function(titleString) {
var completionTracking = triggerElement.attr('data-completiontracking');
return Templates.renderPix(getIconKeyFromState(completionState, completionTracking), 'core', titleString);
}).then(function(html) {
var oppositeState = completionState > 0 ? 0 : 1;
triggerElement.find('.loading-icon').remove();
triggerElement.data('changecompl', override.userid + '-' + override.cmid + '-' + oppositeState);
triggerElement.attr('data-changecompl', override.userid + '-' + override.cmid + '-' + oppositeState);
triggerElement.children("img").replaceWith(html);
return;
}).catch(Notification.exception);
return;
})
.then(() => {
pendingPromise.resolve();
return;
}).catch(Notification.exception);
};
/**
* Handler for activation of a completion status button element.
* @method userConfirm
* @param {Event} e the CustomEvents event (CustomEvents.events.activate in this case)
* @param {Object} data an object containing the original event (click, keydown, etc.).
* @private
*/
var userConfirm = function(e, data) {
data.originalEvent.preventDefault();
data.originalEvent.stopPropagation();
e.preventDefault();
e.stopPropagation();
triggerElement = $(e.currentTarget);
var elemData = triggerElement.data('changecompl').split('-');
var override = {
userid: elemData[0],
cmid: elemData[1],
newstate: elemData[2]
};
var newStateStr = (override.newstate == 1) ? 'completion-y' : 'completion-n';
Str.get_strings([
{key: newStateStr, component: 'completion'}
]).then(function(strings) {
return Str.get_strings([
{key: 'confirm', component: 'moodle'},
{key: 'areyousureoverridecompletion', component: 'completion', param: strings[0]}
]);
}).then(function(strings) {
// Create a save/cancel modal.
return ModalSaveCancel.create({
title: strings[0],
body: strings[1],
show: true,
removeOnClose: true,
});
}).then(function(modal) {
// Now set up the handlers for the confirmation or cancellation of the modal, and show it.
// Confirmation only.
modal.getRoot().on(ModalEvents.save, function() {
setOverride(override);
});
return modal;
}).catch(Notification.exception);
};
/**
* Init this module which allows activity completion state to be changed via ajax.
* @method init
* @param {string} fullName The current user's full name.
* @private
*/
var init = function(fullName) {
userFullName = fullName;
// Register the click, space and enter events as activators for the trigger element.
$('#completion-progress a.changecompl').each(function(index, element) {
CustomEvents.define(element, [CustomEvents.events.activate]);
});
// Set the handler on the parent element (the table), but filter so the callback is only called for <a> type children
// having the '.changecompl' class. The <a> element can then be accessed in the callback via e.currentTarget.
$('#completion-progress').on(CustomEvents.events.activate, "a.changecompl", function(e, data) {
userConfirm(e, data);
});
};
return /** @alias module:report_progress/completion_override */ {
init: init
};
});
+80
View File
@@ -0,0 +1,80 @@
<?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 report_progress\local;
/**
* Helper for report progress.
*
* @package report_progress
* @copyright 2021 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL Juv3 or later
*/
class helper {
/** The default number of results to be shown per page. */
const COMPLETION_REPORT_PAGE = 25;
/**
* Get activities information by the activity include and activity order option.
*
* @param \completion_info $completion Completion information of course.
* @param string $activityinclude Activity type for filtering.
* @param string $activityorder Activity sort option.
* @param int $activitysection Section number for filtering.
* @return array The available activity types and activities array after filtering and sorting.
* @throws \coding_exception
*/
public static function get_activities_to_show(\completion_info $completion, string $activityinclude,
string $activityorder, int $activitysection = -1): array {
// Get all activity types.
$activities = $completion->get_activities();
$availableactivitytypes = [];
foreach ($activities as $activity) {
if ($activity->modname == $activityinclude || $activitysection == -1 || $activity->sectionnum == $activitysection) {
$availableactivitytypes[$activity->modname] = $activity->get_module_type_name(true);
}
}
asort($availableactivitytypes);
$availableactivitytypes = ['all' => get_string('allactivitiesandresources', 'report_progress')] +
$availableactivitytypes;
// Filter activities by section.
if ($activitysection > -1) {
$activities = array_filter($activities, function($activity) use ($activitysection) {
return $activity->sectionnum == $activitysection;
});
}
// Filter activities by type.
if (!empty($activityinclude) && $activityinclude !== 'all') {
$activities = array_filter($activities, function($activity) use ($activityinclude) {
return $activity->modname === $activityinclude;
});
}
// The activities are sorted by activity order on course page by default.
if ($activityorder === 'alphabetical') {
usort($activities, function($a, $b) {
return strcmp($a->name, $b->name);
});
}
return [$availableactivitytypes, $activities];
}
}
+136
View File
@@ -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/>.
namespace report_progress\output;
use single_select;
use plugin_renderer_base;
use html_writer;
/**
* Renderer for report progress.
*
* @package report_progress
* @copyright 2021 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL Juv3 or later
*/
class renderer extends plugin_renderer_base {
/**
* Render include activity single select box.
*
* @param \moodle_url $url The base url.
* @param array $activitytypes The activity type options.
* @param string $activityinclude The current selected option.
* @return string HTML
* @throws \coding_exception
*/
public function render_include_activity_select(\moodle_url $url, array $activitytypes,
string $activityinclude): string {
$includeurl = fullclone($url);
$includeurl->remove_params(['page', 'activityinclude']);
$activityincludeselect = new single_select(
$url, 'activityinclude',
$activitytypes, $activityinclude, null, 'include-activity-select-report'
);
$activityincludeselect->set_label(get_string('include', 'report_progress'));
return \html_writer::div($this->output->render($activityincludeselect),
'include-activity-selector d-inline-block mr-3' );
}
/**
* Render activity order single select box.
*
* @param \moodle_url $url The base url.
* @param string $activityorder The current selected option.
* @return string HTML
* @throws \coding_exception
*/
public function render_activity_order_select(\moodle_url $url, string $activityorder): string {
$activityorderurl = fullclone($url);
$activityorderurl->remove_params(['activityorder']);
$options = ['orderincourse' => get_string('orderincourse', 'report_progress'),
'alphabetical' => get_string('alphabetical', 'report_progress')];
$sorttable = new single_select(
$activityorderurl, 'activityorder',
$options, $activityorder, null, 'activity-order-select-report'
);
$sorttable->set_label(get_string('activityorder', 'report_progress'));
return \html_writer::div($this->output->render($sorttable),
'activity-order-selector include-activity-selector d-inline-block');
}
/**
* Render groups single select box.
*
* @param \moodle_url $url The base url.
* @param \stdClass $course Current course.
* @return string HTML
*/
public function render_groups_select(\moodle_url $url, \stdClass $course): string {
$groupurl = fullclone($url);
$groupurl->remove_params(['page', 'group']);
$groupoutput = groups_print_course_menu($course, $groupurl, true);
if (empty($groupoutput)) {
return $groupoutput;
}
return \html_writer::div($groupoutput, 'd-inline-block mr-3');
}
/**
* Render activity section single select box.
*
* @param \moodle_url $url The base url.
* @param string $activitysection The current selected section.
* @param array $sections An array containing all sections of the course
* @return string HTML
* @throws \coding_exception
*/
public function render_activity_section_select(\moodle_url $url, string $activitysection, array $sections): string {
$activitysectionurl = fullclone($url);
$activitysectionurl->remove_params(['activitysection']);
$options = $sections;
$options[-1] = get_string('no_filter_by_section', 'report_progress');
$sorttable = new single_select(
$activitysectionurl, 'activitysection',
$options, $activitysection, null, 'activity-section-select-report'
);
$sorttable->set_label(get_string('activitysection', 'report_progress'));
return \html_writer::div($this->output->render($sorttable),
'activity-section-selector include-activity-selector d-inline-block ml-3');
}
/**
* Render download buttons.
*
* @param \moodle_url $url The base url.
* @return string HTML
* @throws \coding_exception
*/
public function render_download_buttons(\moodle_url $url): string {
$downloadurl = fullclone($url);
$downloadurl->remove_params(['page']);
$downloadurl->param('format', 'csv');
$downloadhtml = html_writer::start_tag('ul', ['class' => 'progress-actions']);
$downloadhtml .= html_writer::tag('li', html_writer::link($downloadurl, get_string('csvdownload', 'completion')));
$downloadurl->param('format', 'excelcsv');
$downloadhtml .= html_writer::tag('li', html_writer::link($downloadurl, get_string('excelcsvdownload', 'completion')));
$downloadhtml .= html_writer::end_tag('ul');
return $downloadhtml;
}
}
@@ -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/>.
/**
* Privacy provider implementation for report_progress.
*
* @package report_progress
* @copyright 2018 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace report_progress\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy provider implementation for report_progress.
*
* @copyright 2018 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
+43
View File
@@ -0,0 +1,43 @@
<?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/>.
/**
* Capabilities
*
* @package report_progress
* @copyright 2008 Sam Marshall
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'report/progress:view' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
),
'clonepermissionsfrom' => 'coursereport/progress:view',
)
);
+32
View File
@@ -0,0 +1,32 @@
<?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/>.
/**
* Post installation and migration code.
*
* @package report
* @subpackage progress
* @copyright 2011 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
function xmldb_report_progress_install() {
global $DB;
}
+461
View File
@@ -0,0 +1,461 @@
<?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 progress reports
*
* @package report
* @subpackage progress
* @copyright 2008 Sam Marshall
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core\report_helper;
use \report_progress\local\helper;
require('../../config.php');
require_once($CFG->libdir . '/completionlib.php');
// Get course
$id = required_param('course',PARAM_INT);
$course = $DB->get_record('course',array('id'=>$id));
if (!$course) {
throw new \moodle_exception('invalidcourseid');
}
$context = context_course::instance($course->id);
// Sort (default lastname, optionally firstname)
$sort = optional_param('sort','',PARAM_ALPHA);
$firstnamesort = $sort == 'firstname';
// CSV format
$format = optional_param('format','',PARAM_ALPHA);
$excel = $format == 'excelcsv';
$csv = $format == 'csv' || $excel;
// Paging, sorting and filtering.
$page = optional_param('page', 0, PARAM_INT);
$sifirst = optional_param('sifirst', 'all', PARAM_NOTAGS);
$silast = optional_param('silast', 'all', PARAM_NOTAGS);
$groupid = optional_param('group', 0, PARAM_INT);
$activityinclude = optional_param('activityinclude', 'all', PARAM_TEXT);
$activityorder = optional_param('activityorder', 'orderincourse', PARAM_TEXT);
$activitysection = optional_param('activitysection', -1, PARAM_INT);
// Whether to show extra user identity information
$userfields = \core_user\fields::for_identity($context);
$extrafields = $userfields->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
$leftcols = 1 + count($extrafields);
function csv_quote($value) {
global $excel;
if ($excel) {
return core_text::convert('"'.str_replace('"',"'",$value).'"','UTF-8','UTF-16LE');
} else {
return '"'.str_replace('"',"'",$value).'"';
}
}
$url = new moodle_url('/report/progress/index.php', array('course'=>$id));
$PAGE->navigation->override_active_url($url);
if ($sort !== '') {
$url->param('sort', $sort);
}
if ($format !== '') {
$url->param('format', $format);
}
if ($page !== 0) {
$url->param('page', $page);
}
if ($sifirst !== 'all') {
$url->param('sifirst', $sifirst);
}
if ($silast !== 'all') {
$url->param('silast', $silast);
}
if ($groupid !== 0) {
$url->param('group', $groupid);
}
if ($activityinclude !== '') {
$url->param('activityinclude', $activityinclude);
}
if ($activityorder !== '') {
$url->param('activityorder', $activityorder);
}
if ($activitysection !== '') {
$url->param('activitysection', $activitysection);
}
$PAGE->set_url($url);
$PAGE->set_pagelayout('report');
require_login($course);
// Check basic permission
require_capability('report/progress:view',$context);
// Get group mode
$group = groups_get_course_group($course,true); // Supposed to verify group
if ($group===0 && $course->groupmode==SEPARATEGROUPS) {
require_capability('moodle/site:accessallgroups',$context);
}
// Get data on activities and progress of all users, and give error if we've
// nothing to display (no users or no activities).
$completion = new completion_info($course);
list($activitytypes, $activities) = helper::get_activities_to_show($completion, $activityinclude, $activityorder, $activitysection);
$output = $PAGE->get_renderer('report_progress');
if ($sifirst !== 'all') {
set_user_preference('ifirst', $sifirst);
}
if ($silast !== 'all') {
set_user_preference('ilast', $silast);
}
if (!empty($USER->preference['ifirst'])) {
$sifirst = $USER->preference['ifirst'];
} else {
$sifirst = 'all';
}
if (!empty($USER->preference['ilast'])) {
$silast = $USER->preference['ilast'];
} else {
$silast = 'all';
}
// Generate where clause
$where = array();
$where_params = array();
if ($sifirst !== 'all') {
$where[] = $DB->sql_like('u.firstname', ':sifirst', false, false);
$where_params['sifirst'] = $sifirst.'%';
}
if ($silast !== 'all') {
$where[] = $DB->sql_like('u.lastname', ':silast', false, false);
$where_params['silast'] = $silast.'%';
}
// Get user match count
$total = $completion->get_num_tracked_users(implode(' AND ', $where), $where_params, $group);
// Total user count
$grandtotal = $completion->get_num_tracked_users('', array(), $group);
// Get user data
$progress = array();
if ($total) {
$progress = $completion->get_progress_all(
implode(' AND ', $where),
$where_params,
$group,
$firstnamesort ? 'u.firstname ASC, u.lastname ASC' : 'u.lastname ASC, u.firstname ASC',
$csv ? 0 : helper::COMPLETION_REPORT_PAGE,
$csv ? 0 : $page * helper::COMPLETION_REPORT_PAGE,
$context
);
}
if ($csv && $grandtotal && count($activities)>0) { // Only show CSV if there are some users/actvs
$shortname = format_string($course->shortname, true, array('context' => $context));
header('Content-Disposition: attachment; filename=progress.'.
preg_replace('/[^a-z0-9-]/','_',core_text::strtolower(strip_tags($shortname))).'.csv');
// Unicode byte-order mark for Excel
if ($excel) {
header('Content-Type: text/csv; charset=UTF-16LE');
print chr(0xFF).chr(0xFE);
$sep="\t".chr(0);
$line="\n".chr(0);
} else {
header('Content-Type: text/csv; charset=UTF-8');
$sep=",";
$line="\n";
}
} else {
// Navigation and header
$strreports = get_string("reports");
$strcompletion = get_string('activitycompletion', 'completion');
$PAGE->set_title($strcompletion);
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
// Print the selected dropdown.
$pluginname = get_string('pluginname', 'report_progress');
report_helper::print_report_selector($pluginname);
$PAGE->requires->js_call_amd('report_progress/completion_override', 'init', [fullname($USER)]);
// Handle groups (if enabled).
echo $output->render_groups_select($url, $course);
// Display include activity filter.
echo $output->render_include_activity_select($url, $activitytypes, $activityinclude);
// Display activity order options.
echo $output->render_activity_order_select($url, $activityorder);
// Display section selector.
$modinfo = get_fast_modinfo($course);
$sections = [];
$cmids = array_keys($completion->get_activities());
foreach ($modinfo->get_sections() as $sectionnum => $section) {
if (empty(array_intersect($section, $cmids))) {
continue;
}
$sectionname = get_section_name($course, $sectionnum);
if (empty($sectionname)) {
$sectionname = get_string('section') . ' ' . $sectionnum;
}
$sections[$sectionnum] = $sectionname;
}
echo $output->render_activity_section_select($url, $activitysection, $sections);
}
if (count($activities)==0) {
echo $OUTPUT->container(get_string('err_noactivities', 'completion'), 'errorbox errorboxcontent');
echo $OUTPUT->footer();
exit;
}
// If no users in this course what-so-ever
if (!$grandtotal) {
echo $OUTPUT->container(get_string('err_nousers', 'completion'), 'errorbox errorboxcontent');
echo $OUTPUT->footer();
exit;
}
// Build link for paging
$link = $CFG->wwwroot.'/report/progress/?course='.$course->id;
if (strlen($sort)) {
$link .= '&amp;sort='.$sort;
}
$link .= '&amp;start=';
$pagingbar = '';
// Initials bar.
$prefixfirst = 'sifirst';
$prefixlast = 'silast';
// The URL used in the initials bar should reset the 'start' parameter.
$initialsbarurl = fullclone($url);
$initialsbarurl->remove_params('page');
$pagingbar .= $OUTPUT->initials_bar($sifirst, 'firstinitial mt-2', get_string('firstname'), $prefixfirst, $initialsbarurl);
$pagingbar .= $OUTPUT->initials_bar($silast, 'lastinitial', get_string('lastname'), $prefixlast, $initialsbarurl);
$pagingbar .= $OUTPUT->paging_bar($total, $page, helper::COMPLETION_REPORT_PAGE, $url);
// Okay, let's draw the table of progress info,
// Start of table
if (!$csv) {
print '<br class="clearer"/>'; // ugh
print $pagingbar;
if (!$total) {
echo $OUTPUT->notification(get_string('nothingtodisplay'), 'info', false);
echo $OUTPUT->footer();
exit;
}
print '<div id="completion-progress-wrapper" class="no-overflow">';
print '<table id="completion-progress" class="generaltable flexible boxaligncenter"><thead><tr style="vertical-align:top">';
// User heading / sort option
print '<th scope="col" class="completion-sortchoice">';
$sorturl = fullclone($url);
if ($firstnamesort) {
$sorturl->param('sort', 'lastname');
$sortlink = html_writer::link($sorturl, get_string('lastname'));
print
get_string('firstname') . " / $sortlink";
} else {
$sorturl->param('sort', 'firstname');
$sortlink = html_writer::link($sorturl, get_string('firstname'));
print "$sortlink / " . get_string('lastname');
}
print '</th>';
// Print user identity columns
foreach ($extrafields as $field) {
echo '<th scope="col" class="completion-identifyfield">' .
\core_user\fields::get_display_name($field) . '</th>';
}
} else {
foreach ($extrafields as $field) {
echo $sep . csv_quote(\core_user\fields::get_display_name($field));
}
}
// Activities
$formattedactivities = array();
foreach($activities as $activity) {
$datepassed = $activity->completionexpected && $activity->completionexpected <= time();
$datepassedclass = $datepassed ? 'completion-expired' : '';
if ($activity->completionexpected) {
if ($csv) {
$datetext = userdate($activity->completionexpected, "%F %T");
} else {
$datetext = userdate($activity->completionexpected, get_string('strftimedate', 'langconfig'));
}
} else {
$datetext='';
}
// Some names (labels) come URL-encoded and can be very long, so shorten them
$displayname = format_string($activity->name, true, array('context' => $activity->context));
if ($csv) {
print $sep.csv_quote($displayname).$sep.csv_quote($datetext);
} else {
$shortenedname = shorten_text($displayname);
print '<th scope="col" class="completion-header '.$datepassedclass.'">'.
'<a href="'.$CFG->wwwroot.'/mod/'.$activity->modname.
'/view.php?id='.$activity->id.'" title="' . s($displayname) . '">'.
'<div class="rotated-text-container"><span class="rotated-text">'.$shortenedname.'</span></div>'.
'<div class="modicon">'.
$OUTPUT->image_icon('monologo', get_string('modulename', $activity->modname), $activity->modname) .
'</div>'.
'</a>';
if ($activity->completionexpected) {
print '<div class="completion-expected"><span>'.$datetext.'</span></div>';
}
print '</th>';
}
$formattedactivities[$activity->id] = (object)array(
'datepassedclass' => $datepassedclass,
'displayname' => $displayname,
);
}
if ($csv) {
print $line;
} else {
print '</tr></thead><tbody>';
}
// Row for each user
foreach($progress as $user) {
// User name
if ($csv) {
print csv_quote(fullname($user, has_capability('moodle/site:viewfullnames', $context)));
foreach ($extrafields as $field) {
echo $sep . csv_quote($user->{$field});
}
} else {
print '<tr><th scope="row"><a href="' . $CFG->wwwroot . '/user/view.php?id=' .
$user->id . '&amp;course=' . $course->id . '">' .
fullname($user, has_capability('moodle/site:viewfullnames', $context)) . '</a></th>';
foreach ($extrafields as $field) {
echo '<td>' . s($user->{$field}) . '</td>';
}
}
// Progress for each activity
foreach($activities as $activity) {
// Get progress information and state
if (array_key_exists($activity->id, $user->progress)) {
$thisprogress = $user->progress[$activity->id];
$state = $thisprogress->completionstate;
$overrideby = $thisprogress->overrideby;
$date = userdate($thisprogress->timemodified);
} else {
$state = COMPLETION_INCOMPLETE;
$overrideby = 0;
$date = '';
}
// Work out how it corresponds to an icon
switch($state) {
case COMPLETION_INCOMPLETE :
$completiontype = 'n'.($overrideby ? '-override' : '');
break;
case COMPLETION_COMPLETE :
$completiontype = 'y'.($overrideby ? '-override' : '');
break;
case COMPLETION_COMPLETE_PASS :
$completiontype = 'pass';
break;
case COMPLETION_COMPLETE_FAIL :
$completiontype = 'fail';
break;
}
$completiontrackingstring = $activity->completion == COMPLETION_TRACKING_AUTOMATIC ? 'auto' : 'manual';
$completionicon = 'completion-' . $completiontrackingstring. '-' . $completiontype;
if ($overrideby) {
$overridebyuser = \core_user::get_user($overrideby, '*', MUST_EXIST);
$describe = get_string('completion-' . $completiontype, 'completion', fullname($overridebyuser));
} else {
$describe = get_string('completion-' . $completiontype, 'completion');
}
$a=new StdClass;
$a->state=$describe;
$a->date=$date;
$a->user = fullname($user, has_capability('moodle/site:viewfullnames', $context));
$a->activity = $formattedactivities[$activity->id]->displayname;
$fulldescribe=get_string('progress-title','completion',$a);
if ($csv) {
if ($date != '') {
$date = userdate($thisprogress->timemodified, "%F %T");
}
print $sep.csv_quote($describe).$sep.csv_quote($date);
} else {
$celltext = $OUTPUT->pix_icon('i/' . $completionicon, s($fulldescribe));
if (has_capability('moodle/course:overridecompletion', $context) &&
$state != COMPLETION_COMPLETE_PASS && $state != COMPLETION_COMPLETE_FAIL) {
$newstate = ($state == COMPLETION_COMPLETE) ? COMPLETION_INCOMPLETE : COMPLETION_COMPLETE;
$changecompl = $user->id . '-' . $activity->id . '-' . $newstate;
$url = new moodle_url($PAGE->url, ['sesskey' => sesskey()]);
$celltext = html_writer::link($url, $celltext, array('class' => 'changecompl', 'data-changecompl' => $changecompl,
'data-activityname' => $a->activity,
'data-userfullname' => $a->user,
'data-completiontracking' => $completiontrackingstring,
'role' => 'button'));
}
print '<td class="completion-progresscell '.$formattedactivities[$activity->id]->datepassedclass.'">'.
$celltext . '</td>';
}
}
if ($csv) {
print $line;
} else {
print '</tr>';
}
}
if ($csv) {
exit;
}
print '</tbody></table>';
print '</div>';
echo $output->render_download_buttons($url);
echo $OUTPUT->footer();
@@ -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/>.
/**
* Lang strings
*
* @package report
* @subpackage progress
* @copyright 2008 Sam Marshall
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Activity completion';
$string['activityorder'] = 'Activity order';
$string['activitysection'] = 'Section';
$string['allactivitiesandresources'] = 'All activities and resources';
$string['alphabetical'] = 'Alphabetical ';
$string['include'] = 'Include';
$string['no_filter_by_section'] = 'Do not filter by section';
$string['orderincourse'] = 'Order in course';
$string['page-report-progress-x'] = 'Any activity completion report';
$string['page-report-progress-index'] = 'Activity completion report';
$string['page-report-progress-user'] = 'User activity completion report';
$string['privacy:metadata'] = 'The Activity completion report only shows data stored in other locations.';
$string['progress:view'] = 'View activity completion reports';
+69
View File
@@ -0,0 +1,69 @@
<?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 contains functions used by the progress report
*
* @package report
* @subpackage progress
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
/**
* This function extends the navigation with the report items
*
* @param navigation_node $navigation The navigation node to extend
* @param stdClass $course The course to object for the report
* @param stdClass $context The context of the course
*/
function report_progress_extend_navigation_course($navigation, $course, $context) {
global $CFG, $OUTPUT;
require_once($CFG->libdir.'/completionlib.php');
$showonnavigation = has_capability('report/progress:view', $context);
$group = groups_get_course_group($course,true); // Supposed to verify group
if($group===0 && $course->groupmode==SEPARATEGROUPS) {
$showonnavigation = ($showonnavigation && has_capability('moodle/site:accessallgroups', $context));
}
$completion = new completion_info($course);
$showonnavigation = ($showonnavigation && $completion->is_enabled() && $completion->has_activities());
if ($showonnavigation) {
$url = new moodle_url('/report/progress/index.php', array('course'=>$course->id));
$navigation->add(get_string('pluginname','report_progress'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
}
}
/**
* Return a list of page types
* @param string $pagetype current page type
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
* @return array
*/
function report_progress_page_type_list($pagetype, $parentcontext, $currentcontext) {
$array = array(
'*' => get_string('page-x', 'pagetype'),
'report-*' => get_string('page-report-x', 'pagetype'),
'report-progress-*' => get_string('page-report-progress-x', 'report_progress'),
'report-progress-index' => get_string('page-report-progress-index', 'report_progress'),
);
return $array;
}
+66
View File
@@ -0,0 +1,66 @@
#page-report-progress-index #completion-progress th,
#page-report-progress-index #completion-progress td {
padding: 2px 2px;
font-weight: normal;
border-right: 1px solid #eee;
}
#page-report-progress-index .progress-actions {
text-align: center;
list-style: none;
}
#page-report-progress-index .completion_pagingbar {
margin: 1em 0;
text-align: center;
}
#page-report-progress-index .completion_prev {
display: inline;
margin-right: 2em;
}
#page-report-progress-index .completion_pagingbar p {
display: inline;
}
#page-report-progress-index .completion_next {
display: inline;
margin-left: 2em;
}
#page-report-progress-index .completion-header,
#page-report-progress-index .completion-progresscell {
text-align: center;
}
/* Custom CSS for rotated header.. */
#page-report-progress-index .rotated-text-container {
display: inline-block;
width: 16px;
}
#page-report-progress-index .rotated-text {
display: inline-block;
white-space: nowrap;
/*rtl:remove*/
transform: translate(0, 100%) rotate(-90deg);
/*rtl:raw:
transform: translate(0, 100%) rotate(90deg);
*/
transform-origin: 0 0;
vertical-align: middle;
}
#page-report-progress-index .rotated-text:after {
content: "";
float: left;
margin-top: 100%;
}
#page-report-progress-index .modicon {
padding-top: 5px;
}
#completion-progress .icon {
margin-left: 0.5rem;
}
@@ -0,0 +1,110 @@
@report @report_progress
Feature: Teacher can view and override users' activity completion data via the progress report.
In order to view and override a student's activity completion status
As a teacher
I need to view the course progress report and click the respective completion status icon
Background:
Given the following "courses" exist:
| fullname | shortname | format | enablecompletion |
| Course 1 | C1 | topics | 1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | section | completion | completionview | completionusegrade | assignsubmission_onlinetext_enabled | submissiondrafts |
| assign | my assignment | A1 desc | C1 | assign1 | 0 | 1 | 0 | | 0 | 0 |
| assign | my assignment 2 | A2 desc | C1 | assign2 | 0 | 2 | 1 | | 0 | 0 |
| assign | my assignment 3 | A3 desc | C1 | assign3 | 0 | 2 | 1 | 1 | 1 | 0 |
And the following "users" exist:
| username | firstname | lastname | email | idnumber | middlename | alternatename | firstnamephonetic | lastnamephonetic |
| teacher1 | Teacher | One | teacher1@example.com | t1 | | fred | | |
| student1 | Grainne | Beauchamp | student1@example.com | s1 | Ann | Jill | Gronya | Beecham |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following config values are set as admin:
| fullnamedisplay | firstname |
| alternativefullnameformat | middlename, alternatename, firstname, lastname |
# Course comprising one activity with auto completion (student must view it) and one with manual completion.
# This confirms that after being completed by the student and overridden by the teacher, that both activities can still be
# completed again via normal mechanisms.
@javascript
Scenario: Given the status has been overridden, when a student tries to complete it again, completion can still occur.
# Student completes the activities, manual and automatic completion.
Given I am on the "Course 1" course page logged in as student1
And the manual completion button of "my assignment" is displayed as "Mark as done"
And I toggle the manual completion state of "my assignment"
And the manual completion button of "my assignment" is displayed as "Done"
And I am on the "my assignment 2" "assign activity" page
And the "View" completion condition of "my assignment 2" is displayed as "done"
And I log out
# Teacher overrides the activity completion statuses to incomplete.
When 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 "Ann, Jill, Grainne, Beauchamp, my assignment: Completed" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And "Ann, Jill, Grainne, Beauchamp, my assignment 2: Completed" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I click on "my assignment" "link" in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I click on "Save changes" "button"
And "Ann, Jill, Grainne, Beauchamp, my assignment: Not completed (set by Teacher)" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I click on "my assignment 2" "link" in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I click on "Save changes" "button"
And "Ann, Jill, Grainne, Beauchamp, my assignment 2: Not completed (set by Teacher)" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I log out
# Student can now complete the activities again, via normal means.
And I am on the "Course 1" course page logged in as student1
Then the manual completion button of "my assignment" overridden by "Teacher" is displayed as "Mark as done"
And the "View" completion condition of "my assignment 2" overridden by "Teacher" is displayed as "todo"
And I toggle the manual completion state of "my assignment"
And the manual completion button of "my assignment" is displayed as "Done"
And I am on the "my assignment 2" "assign activity" page
And I am on "Course 1" course homepage
And the "View" completion condition of "my assignment 2" is displayed as "done"
And I log out
# And the activity completion report should show the same.
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 "Ann, Jill, Grainne, Beauchamp, my assignment: Completed" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And "Ann, Jill, Grainne, Beauchamp, my assignment 2: Completed" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
# Course comprising one activity with auto completion (student must view it and receive a grade) and one with manual completion.
# This confirms that after being overridden to complete by the teacher, that the completion status for activities with automatic
# completion can no longer be affected by any normal completion mechanisms triggered by the student. Manual completion unaffected.
@javascript
Scenario: Given the status has been overridden to complete, when a student triggers completion updates, the status remains fixed.
# When the teacher overrides the activity completion statuses to complete.
When 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 "Ann, Jill, Grainne, Beauchamp, my assignment: Not completed" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And "Ann, Jill, Grainne, Beauchamp, my assignment 3: Not completed" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I click on "my assignment" "link" in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I click on "Save changes" "button"
And "Ann, Jill, Grainne, Beauchamp, my assignment: Completed (set by Teacher)" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I click on "my assignment 3" "link" in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I click on "Save changes" "button"
And "Ann, Jill, Grainne, Beauchamp, my assignment 3: Completed (set by Teacher)" "icon" should exist in the "Ann, Jill, Grainne, Beauchamp" "table_row"
And I log out
# Then as a student, confirm that automatic completion checks are no longer triggered (such as after an assign submission).
And I am on the "Course 1" course page logged in as student1
Then the "Receive a grade" completion condition of "my assignment 3" overridden by "Teacher" is displayed as "done"
And I am on the "my assignment 3" "assign activity" page
And I press "Add submission"
And I set the following fields to these values:
| Online text | I'm the student first submission |
And I press "Save changes"
And I should see "Submitted for grading"
And I am on "Course 1" course homepage
And the "Receive a grade" completion condition of "my assignment 3" overridden by "Teacher" is displayed as "done"
# And Confirm that manual completion changes are still allowed.
And I am on "Course 1" course homepage
And the manual completion button of "my assignment" overridden by "Teacher" is displayed as "Done"
And I toggle the manual completion state of "my assignment"
And the manual completion button of "my assignment" is displayed as "Mark as done"
@@ -0,0 +1,74 @@
@report @report_progress
Feature: Teacher can view and filter activity completion data by group, activity type and section.
Background:
Given the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
| format | topics |
| enablecompletion | 1 |
| groupmode | 1 |
| initsections | 1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | section | completion | completionview |
| quiz | My quiz B | A3 desc | C1 | quizb | 0 | 2 | 1 |
| quiz | My quiz A | A3 desc | C1 | quiza | 1 | 2 | 1 |
| page | My page | A4 desc | C1 | page1 | 2 | 2 | 1 |
| assign | My assignment | A1 desc | C1 | assign1 | 2 | 2 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | One | teacher1@example.com |
| student1 | Student | One | student1@example.com |
| student2 | Student | Two | student2@example.com |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G2 |
| teacher1 | G1 |
| teacher1 | G2 |
@javascript
Scenario: Teacher can view the activity completion report using filtering and sorting options.
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to "Reports" in current page administration
And I click on "Activity completion" "link"
Then "My quiz B" "link" should appear before "My quiz A" "link" in the "completion-progress" "table"
And I should see "My assignment" in the "completion-progress" "table"
And I should see "My page" in the "completion-progress" "table"
And I should see "Student One" in the "completion-progress" "table"
And I should see "Student Two" in the "completion-progress" "table"
And I set the field "Separate groups" to "Group 1"
And I set the field "Include" to "Quizzes"
And I set the field "Activity order" to "Alphabetical"
And "My quiz A" "link" should appear before "My quiz B" "link" in the "completion-progress" "table"
And I should not see "My assignment" in the "completion-progress" "table"
And I should not see "My page" in the "completion-progress" "table"
And I should see "Student One" in the "completion-progress" "table"
And I should not see "Student Two" in the "completion-progress" "table"
And I set the field "Separate groups" to "All participants"
And I set the field "Include" to "All activities and resources"
And I set the field "Section" to "Section 1"
And I should not see "My assignment" in the "completion-progress" "table"
And I should not see "My page" in the "completion-progress" "table"
And I should not see "My assignment" in the "completion-progress" "table"
And I should not see "My quiz B" in the "completion-progress" "table"
And I should see "My quiz A" in the "completion-progress" "table"
And I should see "Quiz" in the "activityinclude" "select"
And I should not see "Page" in the "activityinclude" "select"
And I set the field "Section" to "Section 2"
And I should not see "Quiz" in the "activityinclude" "select"
And I should see "Page" in the "activityinclude" "select"
And I should see "Assignment" in the "activityinclude" "select"
And I set the field "Include" to "Page"
And I set the field "Section" to "Section 1"
And I should see "Page" in the "activityinclude" "select"
@@ -0,0 +1,50 @@
@report @report_progress
Feature: In a course administration page, navigate through report page, test for selector in activity completion
In order to view and override a student's activity completion status
As a teacher
Go to course administration -> Reports -> Activity completion
Background:
Given the following "courses" exist:
| fullname | shortname | format | enablecompletion |
| Course 1 | C1 | topics | 1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | section | completion | completionview | completionusegrade | assignsubmission_onlinetext_enabled | submissiondrafts |
| assign | my assignment | A1 desc | C1 | assign1 | 0 | 1 | 0 | | 0 | 0 |
| assign | my assignment 2 | A2 desc | C1 | assign2 | 0 | 2 | 1 | | 0 | 0 |
| assign | my assignment 3 | A3 desc | C1 | assign3 | 0 | 2 | 1 | 1 | 1 | 0 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | One | teacher1@example.com |
| student1 | Student | One | student1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
@javascript
Scenario: Selector should be available in the course acitivity completion page
Given I log in as "admin"
And I am on "Course 1" course homepage
When I navigate to "Reports" in current page administration
And I click on "Activity completion" "link"
Then "Report" "field" should exist in the "tertiary-navigation" "region"
And I should see "Activity completion" in the "tertiary-navigation" "region"
Scenario: Custom profile fields selected for identity should display on the activity completion report
Given the following "custom profile fields" exist:
| datatype | shortname | name |
| text | frog | Favourite frog |
And the following "users" exist:
| username | firstname | lastname | profile_field_frog |
| student2 | Student | Two | Kermit |
And the following "course enrolments" exist:
| user | course | role |
| student2 | C1 | student |
And the following config values are set as admin:
| showuseridentity | email,profile_field_frog |
When I am on the "C1" "Course" page logged in as "admin"
And I navigate to "Reports" in current page administration
And I click on "Activity completion" "link"
Then I should see "Favourite frog"
Then I should see "Kermit" in the "Student Two" "table_row"
@@ -0,0 +1,125 @@
<?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 report_progress;
use advanced_testcase;
use completion_info;
use testing_data_generator;
/**
* Class for testing report progress helper.
*
* @package report_progress
* @covers \report_progress\local\helper
* @copyright 2021 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
*/
final class report_progress_helper_test extends advanced_testcase {
/** @var testing_data_generator data generator.*/
protected $generator;
/**
* Set up testcase.
*/
public function setUp(): void {
global $CFG;
$CFG->enablecompletion = true;
$this->setAdminUser();
$this->resetAfterTest();
$this->generator = $this->getDataGenerator();
}
/**
* Test process_activities_by_filter_options function.
*/
public function test_sort_activities() {
$expectedactivitytypes = ['all' => 'All activities and resources', 'assign' => 'Assignments', 'quiz' => 'Quizzes'];
// Generate test data.
$course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
$quiz2 = $this->generator->create_module('quiz', ['course' => $course->id, 'name' => 'Quiz 2'],
['completion' => 1]);
$quiz1 = $this->generator->create_module('quiz', ['course' => $course->id, 'name' => 'Quiz 1'],
['completion' => 1]);
$assign1 = $this->generator->create_module('assign', ['course' => $course->id, 'name' => 'Assignment'],
['completion' => 1]);
$completion = new completion_info($course);
// Sort the activities by name.
list($activitytypes, $activities) = \report_progress\local\helper::get_activities_to_show($completion, 'quiz',
'alphabetical');
// Check weather the result is filtered and sorted.
$this->assertEquals(2, count($activities));
$this->assertEquals('Quiz 1', $activities[0]->name);
$this->assertEquals('Quiz 2', $activities[1]->name);
$this->assertEquals($activitytypes, $expectedactivitytypes);
}
/**
* Test filtering by section.
*/
public function test_filter_activities_by_section() {
$course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
$this->generator->create_module('quiz', ['course' => $course->id, 'name' => 'Quiz 2', 'section' => 1],
['completion' => 1]);
$this->generator->create_module('quiz', ['course' => $course->id, 'name' => 'Quiz 1', 'section' => 2],
['completion' => 1]);
$this->generator->create_module('assign', ['course' => $course->id, 'name' => 'Assignment', 'section' => 2],
['completion' => 1]);
$completion = new completion_info($course);
// Get activities of section 0.
list($activitytypes, $activities) = \report_progress\local\helper::get_activities_to_show($completion, '', '', 0);
$this->assertEquals(0, count($activities));
$this->assertEquals($activitytypes, ['all' => 'All activities and resources']);
// Get activities of section 1.
list($activitytypes, $activities) = \report_progress\local\helper::get_activities_to_show($completion, '', '', 1);
$this->assertEquals(1, count($activities));
$this->assertEquals('Quiz 2', array_shift($activities)->name);
$this->assertEquals($activitytypes, ['all' => 'All activities and resources', 'quiz' => 'Quizzes']);
// Get activities of section 2.
list($activitytypes, $activities) = \report_progress\local\helper::get_activities_to_show($completion, '', '', 2);
$this->assertEquals(2, count($activities));
$this->assertEquals('Quiz 1', array_shift($activities)->name);
$this->assertEquals('Assignment', array_shift($activities)->name);
$this->assertEquals(
$activitytypes,
['all' => 'All activities and resources', 'assign' => 'Assignments', 'quiz' => 'Quizzes']
);
// Get assignments of section 2.
list($activitytypes, $activities) = \report_progress\local\helper::get_activities_to_show($completion, 'assign', '', 2);
$this->assertEquals(1, count($activities));
$this->assertEquals('Assignment', array_shift($activities)->name);
$this->assertEquals(
$activitytypes,
['all' => 'All activities and resources', 'assign' => 'Assignments', 'quiz' => 'Quizzes']
);
// Get assignments of section 1.
list($activitytypes, $activities) = \report_progress\local\helper::get_activities_to_show($completion, 'assign', '', 1);
$this->assertEquals(0, count($activities));
$this->assertEquals(
$activitytypes,
['all' => 'All activities and resources', 'assign' => 'Assignments', 'quiz' => 'Quizzes']
);
}
}
+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 details
*
* @package report
* @subpackage progress
* @copyright 2008 Sam Marshall
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'report_progress'; // Full name of the plugin (used for diagnostics)