Browse Source

first commit

master
Michael de Raadt 9 years ago
commit
2b103166ca
  1. 23
      README.txt
  2. 114
      backup/moodle2/restore_completion_progress_block_task.class.php
  3. 277
      block_completion_progress.php
  4. 73
      db/access.php
  5. 48
      db/upgrade.php
  6. 156
      edit_form.php
  7. 150
      lang/en/block_completion_progress.php
  8. 581
      lib.php
  9. 101
      module.js
  10. 415
      overview.php
  11. BIN
      pix/blank.gif
  12. BIN
      pix/cross.gif
  13. BIN
      pix/left.gif
  14. BIN
      pix/right.gif
  15. BIN
      pix/tick.gif
  16. 95
      settings.php
  17. 186
      styles.css
  18. 32
      version.php

23
README.txt

@ -0,0 +1,23 @@
The Completion Progress block is a time-management tool for students.
It visually shows what activities/resources a student is supposed to interact with in a course.
It is colour-coded so students can quickly see what they have and have not completed/viewed.
The block shows activities with activity completion settings.
To Install
1. Copy the completion_progress directory to the blocks/ directory of your Moodle instance
2. Visit the notifications page
For more information, visit...
http://docs.moodle.org/en/Installing_contributed_modules_or_plugins
Once the Completion Progress block is installed, you can use it in a course as follows.
1. Turn editing on
2. Create your activities/resources as normal
3. Set completion settings for each activity you want to appear in the bar, including an expected by date
4. Add the Completion Progress block to your page
5. Move your block into a prominent position
6. (Optional) Configure how the block should appear
Hidden items will not appear in the Completion Progress block until they are visible to students.
This is useful for a scheduled release of activities.

114
backup/moodle2/restore_completion_progress_block_task.class.php

@ -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/>.
/**
* Backup task for the Completion Progress block
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_completion_progress_block_task extends restore_block_task {
/**
* Translates the backed up configuration data for the target course modules.
*
* @global type $DB
*/
public function after_restore() {
global $DB;
// Get the blockid.
$id = $this->get_blockid();
// Get restored course id.
$courseid = $this->get_courseid();
if ($configdata = $DB->get_field('block_instances', 'configdata', array('id' => $id))) {
$config = (array)unserialize(base64_decode($configdata));
$newactivities = array();
// Translate the old config information to the target course values.
foreach ($config['selectactivities'] as $index => $value) {
$matches = array();
preg_match('/(.+)-(\d+)/', $value, $matches);
if (!empty($matches)) {
$module = $matches[1];
$instance = $matches[2];
// Find the mapped instance ID.
if ($newinstance = restore_dbops::get_backup_ids_record($this->get_restoreid(), $module, $instance)) {
$newinstanceid = $newinstance->newitemid;
$newactivities[] = "$module-$newinstanceid";
}
}
}
// Save everything back to DB.
$config['selectactivities'] = $newactivities;
$configdata = base64_encode(serialize((object)$config));
$DB->set_field('block_instances', 'configdata', $configdata, array('id' => $id));
}
}
/**
* There are no unusual settings for this restore
*/
protected function define_my_settings() {
}
/**
* There are no unusual steps for this restore
*/
protected function define_my_steps() {
}
/**
* There are no files associated with this block
*
* @return array An empty array
*/
public function get_fileareas() {
return array();
}
/**
* There are no specially encoded attributes
*
* @return array An empty array
*/
public function get_configdata_encoded_attributes() {
return array();
}
/**
* There is no coded content in the backup
*
* @return array An empty array
*/
static public function define_decode_contents() {
return array();
}
/**
* There are no coded links in the backup
*
* @return array An empty array
*/
static public function define_decode_rules() {
return array();
}
}

277
block_completion_progress.php

@ -0,0 +1,277 @@
<?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/>.
/**
* Completion Progress block definition
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(dirname(__FILE__) . '/../../config.php');
require_once($CFG->dirroot.'/blocks/completion_progress/lib.php');
defined('MOODLE_INTERNAL') || die;
/**
* Completion Progress block class
*
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_completion_progress extends block_base {
/**
* Sets the block title
*
* @return void
*/
public function init() {
$this->title = get_string('config_default_title', 'block_completion_progress');
}
/**
* we have global config/settings data
*
* @return bool
*/
public function has_config() {
return true;
}
/**
* Controls the block title based on instance configuration
*
* @return bool
*/
public function specialization() {
if (isset($this->config->progressTitle) && trim($this->config->progressTitle) != '') {
$this->title = format_string($this->config->progressTitle);
}
}
/**
* Controls whether multiple instances of the block are allowed on a page
*
* @return bool
*/
public function instance_allow_multiple() {
return !block_completion_progress_on_site_page();
}
/**
* Controls whether the block is configurable
*
* @return bool
*/
public function instance_allow_config() {
return !block_completion_progress_on_site_page();
}
/**
* Defines where the block can be added
*
* @return array
*/
public function applicable_formats() {
return array(
'course-view' => true,
'site' => true,
'mod' => false,
'my' => true
);
}
/**
* Creates the blocks main content
*
* @return string
*/
public function get_content() {
global $USER, $COURSE, $CFG, $OUTPUT, $DB;
// If content has already been generated, don't waste time generating it again.
if ($this->content !== null) {
return $this->content;
}
$this->content = new stdClass;
$this->content->text = '';
$this->content->footer = '';
$blockinstancesonpage = array();
// Guests do not have any progress. Don't show them the block.
if (!isloggedin() or isguestuser()) {
return $this->content;
}
// Draw the multi-bar content for the Dashboard and Front page.
if (block_completion_progress_on_site_page()) {
if (!$CFG->enablecompletion) {
$this->content->text .= get_string('completion_not_enabled', 'block_completion_progress');
return $this->content;
}
$courses = enrol_get_my_courses();
$coursenametoshow = get_config('block_completion_progress', 'coursenametoshow') ?:
DEFAULT_COMPLETIONPROGRESS_COURSENAMETOSHOW;
$sql = "SELECT bi.id,
bp.id AS blockpositionid,
COALESCE(bp.region, bi.defaultregion) AS region,
COALESCE(bp.weight, bi.defaultweight) AS weight,
COALESCE(bp.visible, 1) AS visible,
bi.configdata
FROM {block_instances} bi
LEFT JOIN {block_positions} bp ON bp.blockinstanceid = bi.id
AND ".$DB->sql_like('bp.pagetype', ':pagetype', false)."
WHERE bi.blockname = 'completion_progress'
AND bi.parentcontextid = :contextid
ORDER BY region, weight, bi.id";
foreach ($courses as $courseid => $course) {
// Get specific block config and context.
$completion = new completion_info($course);
if ($course->visible && $completion->is_enabled()) {
$context = CONTEXT_COURSE::instance($course->id);
$params = array('contextid' => $context->id, 'pagetype' => 'course-view-%');
$blockinstances = $DB->get_records_sql($sql, $params);
foreach ($blockinstances as $blockid => $blockinstance) {
$blockinstance->config = unserialize(base64_decode($blockinstance->configdata));
$blockinstance->activities = block_completion_progress_get_activities($course->id, $blockinstance->config);
$blockinstance->activities = block_completion_progress_filter_visibility($blockinstance->activities,
$USER->id, $course->id);
$blockcontext = CONTEXT_BLOCK::instance($blockid);
if (
!has_capability('block/completion_progress:showbar', $blockcontext) ||
$blockinstance->visible == 0 ||
empty($blockinstance->activities) ||
(
!empty($blockinstance->config->group) &&
!has_capability('moodle/site:accessallgroups', $context) &&
!groups_is_member($blockinstance->config->group, $USER->id)
)
) {
unset($blockinstances[$blockid]);
}
}
$blockinstancesonpage = array_merge($blockinstancesonpage, array_keys($blockinstances));
// Output the Progress Bar.
if (!empty($blockinstances)) {
$courselink = new moodle_url('/course/view.php', array('id' => $course->id));
$linktext = HTML_WRITER::tag('h3', s($course->$coursenametoshow));
$this->content->text .= HTML_WRITER::link($courselink, $linktext);
}
foreach ($blockinstances as $blockid => $blockinstance) {
if (
isset($blockinstance->config) &&
isset($blockinstance->config->progressTitle) &&
$blockinstance->config->progressTitle != ''
) {
$this->content->text .= HTML_WRITER::tag('p', s(format_string($blockinstance->config->progressTitle)));
}
$submissions = block_completion_progress_student_submissions($course->id, $USER->id);
$completions = block_completion_progress_completions($blockinstance->activities, $USER->id, $course,
$submissions);
$this->content->text .= block_completion_progress_bar($blockinstance->activities,
$completions,
$blockinstance->config,
$USER->id,
$course->id,
$blockinstance->id);
}
}
}
// Show a message explaining lack of bars, but only while editing is on.
if ($this->page->user_is_editing() && $this->content->text == '') {
$this->content->text = get_string('no_blocks', 'block_completion_progress');
}
}
// Gather content for block on regular course.
else {
// Check if user is in group for block.
if (
!empty($this->config->group) &&
!has_capability('moodle/site:accessallgroups', $this->context) &&
!groups_is_member($this->config->group, $USER->id)
) {
return $this->content;
}
// Check if completion is enabled.
$completion = new completion_info($COURSE);
if (!$CFG->enablecompletion || !$completion->is_enabled()) {
if (has_capability('moodle/block:edit', $this->context)) {
$this->content->text .= get_string('completion_not_enabled', 'block_completion_progress');
}
return $this->content;
}
// Check if any activities/resources have been created.
$activities = block_completion_progress_get_activities($COURSE->id, $this->config);
$activities = block_completion_progress_filter_visibility($activities, $USER->id, $COURSE->id);
if (empty($activities)) {
if (has_capability('moodle/block:edit', $this->context)) {
$this->content->text .= get_string('no_activities_config_message', 'block_completion_progress');
}
return $this->content;
}
// Display progress bar.
if (has_capability('block/completion_progress:showbar', $this->context)) {
$submissions = block_completion_progress_student_submissions($COURSE->id, $USER->id);
$completions = block_completion_progress_completions($activities, $USER->id, $COURSE, $submissions);
$this->content->text .= block_completion_progress_bar(
$activities,
$completions,
$this->config,
$USER->id,
$COURSE->id,
$this->instance->id
);
}
$blockinstancesonpage = array($this->instance->id);
// Allow teachers to access the overview page.
if (has_capability('block/completion_progress:overview', $this->context)) {
$parameters = array('instanceid' => $this->instance->id, 'courseid' => $COURSE->id, 'sesskey' => sesskey());
$url = new moodle_url('/blocks/completion_progress/overview.php', $parameters);
$label = get_string('overview', 'block_completion_progress');
$options = array('class' => 'overviewButton');
$this->content->text .= $OUTPUT->single_button($url, $label, 'post', $options);
}
}
// Organise access to JS.
$jsmodule = array(
'name' => 'block_completion_progress',
'fullpath' => '/blocks/completion_progress/module.js',
'requires' => array(),
'strings' => array(),
);
$arguments = array($blockinstancesonpage, array($USER->id));
$this->page->requires->js_init_call('M.block_completion_progress.setupScrolling', array(), false, $jsmodule);
$this->page->requires->js_init_call('M.block_completion_progress.init', $arguments, false, $jsmodule);
return $this->content;
}
}

73
db/access.php

@ -0,0 +1,73 @@
<?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/>.
/**
* Completion Progress block capability setup
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') or die();
$capabilities = array (
'block/completion_progress:overview' => array (
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_BLOCK,
'archetypes' => array (
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW,
'coursecreator' => CAP_ALLOW
)
),
'block/completion_progress:showbar' => array (
'captype' => 'read',
'contextlevel' => CONTEXT_BLOCK,
'archetypes' => array (
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'student' => CAP_ALLOW,
)
),
'block/completion_progress:addinstance' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'write',
'contextlevel' => CONTEXT_BLOCK,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW,
'coursecreator' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/site:manageblocks'
),
'block/completion_progress:myaddinstance' => array(
'riskbitmask' => RISK_PERSONAL,
'captype' => 'read',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => array(
'user' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/my:manageblocks'
),
);

48
db/upgrade.php

@ -0,0 +1,48 @@
<?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 settings block
*
* 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 block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') or die();
/**
* @global moodle_database $DB
* @param int $oldversion
* @param object $block
*/
function xmldb_block_completion_progress_upgrade($oldversion, $block) {
return true;
}

156
edit_form.php

@ -0,0 +1,156 @@
<?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/>.
/**
* Completion Progress block configuration form definition
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(dirname(__FILE__) . '/../../config.php');
require_once($CFG->dirroot.'/blocks/completion_progress/lib.php');
defined('MOODLE_INTERNAL') || die;
/**
* Completion Progress block config form class
*
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_completion_progress_edit_form extends block_edit_form {
protected function specific_definition($mform) {
global $COURSE, $OUTPUT;
$activities = block_completion_progress_get_activities($COURSE->id, null, 'orderbycourse');
$numactivies = count($activities);
// The My home version is not configurable.
if (block_completion_progress_on_site_page()) {
return;
}
// Start block specific section in config form.
$mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
// Control order of items in Progress Bar.
$expectedbystring = get_string('completionexpected', 'completion');
$orderingoptions = array(
'orderbytime' => get_string('config_orderby_due_time', 'block_completion_progress', $expectedbystring),
'orderbycourse' => get_string('config_orderby_course_order', 'block_completion_progress'),
);
$orderbylabel = get_string('config_orderby', 'block_completion_progress');
$mform->addElement('select', 'config_orderby', $orderbylabel, $orderingoptions);
$mform->setDefault('config_orderby', DEFAULT_ORDERBY);
$mform->addHelpButton('config_orderby', 'how_ordering_works', 'block_completion_progress');
// Check if all elements have an expect completion by time set.
$allwithexpected = true;
$i = 0;
while ($i < $numactivies && $allwithexpected) {
$allwithexpected = $activities[$i]['expected'] != 0;
$i++;
}
if (!$allwithexpected) {
$warningstring = get_string('not_all_expected_set', 'block_completion_progress', $expectedbystring);
$expectedwarning = HTML_WRITER::tag('div', $warningstring, array('class' => 'error'));
$mform->addElement('static', $expectedwarning, '', $expectedwarning);
$mform->disabledif($expectedwarning, 'config_orderby', 'neq', 'orderbytime');
}
// Control how long bars wrap/scroll.
$longbaroptions = array(
'squeeze' => get_string('config_squeeze', 'block_completion_progress'),
'scroll' => get_string('config_scroll', 'block_completion_progress'),
'wrap' => get_string('config_wrap', 'block_completion_progress'),
);
$longbarslabel = get_string('config_longbars', 'block_completion_progress');
$mform->addElement('select', 'config_longbars', $longbarslabel, $longbaroptions);
$defaultlongbars = get_config('block_completion_progress', 'defaultlongbars') ?: DEFAULT_LONGBARS;
$mform->setDefault('config_longbars', $defaultlongbars);
$mform->addHelpButton('config_longbars', 'how_longbars_works', 'block_completion_progress');
// Allow icons to be turned on/off on the block.
$mform->addElement('selectyesno', 'config_progressBarIcons',
get_string('config_icons', 'block_completion_progress').' '.
$OUTPUT->pix_icon('tick', '', 'block_completion_progress', array('class' => 'iconOnConfig')).'&nbsp;'.
$OUTPUT->pix_icon('cross', '', 'block_completion_progress', array('class' => 'iconOnConfig')));
$mform->setDefault('config_progressBarIcons', DEFAULT_PROGRESSBARICONS);
$mform->addHelpButton('config_progressBarIcons', 'why_use_icons', 'block_completion_progress');
// Allow progress percentage to be turned on for students.
$mform->addElement('selectyesno', 'config_showpercentage',
get_string('config_percentage', 'block_completion_progress'));
$mform->setDefault('config_showpercentage', DEFAULT_SHOWPERCENTAGE);
$mform->addHelpButton('config_showpercentage', 'why_show_precentage', 'block_completion_progress');
// Allow the block to be visible to a single group.
$groups = groups_get_all_groups($COURSE->id);
if (!empty($groups)) {
$groupsmenu = array();
$groupsmenu[0] = get_string('allparticipants');
foreach ($groups as $group) {
$groupsmenu[$group->id] = format_string($group->name);
}
$grouplabel = get_string('config_group', 'block_completion_progress');
$mform->addElement('select', 'config_group', $grouplabel, $groupsmenu);
$mform->setDefault('config_group', '0');
$mform->addHelpButton('config_group', 'how_group_works', 'block_completion_progress');
$mform->setAdvanced('config_group', true);
}
// Set block instance title.
$mform->addElement('text', 'config_progressTitle',
get_string('config_title', 'block_completion_progress'));
$mform->setDefault('config_progressTitle', '');
$mform->setType('config_progressTitle', PARAM_MULTILANG);
$mform->addHelpButton('config_progressTitle', 'why_set_the_title', 'block_completion_progress');
$mform->setAdvanced('config_progressTitle', true);
// Control which activities are included in the bar.
$activitiesincludedoptions = array(
'activitycompletion' => get_string('config_activitycompletion', 'block_completion_progress'),
'selectedactivities' => get_string('config_selectedactivities', 'block_completion_progress'),
);
$activitieslabel = get_string('config_activitiesincluded', 'block_completion_progress');
$mform->addElement('select', 'config_activitiesincluded', $activitieslabel, $activitiesincludedoptions);
$mform->setDefault('config_activitiesincluded', DEFAULT_COMPLETIONPROGRESS_ACTIVITIESINCLUDED);
$mform->addHelpButton('config_activitiesincluded', 'how_activitiesincluded_works', 'block_completion_progress');
$mform->setAdvanced('config_activitiesincluded', true);
// Check that there are activities to monitor.
if (empty($activities)) {
$warningstring = get_string('no_activities_config_message', 'block_completion_progress');
$activitieswarning = HTML_WRITER::tag('div', $warningstring, array('class' => 'warning'));
$mform->addElement('static', '', '', $activitieswarning);
} else {
$activitiestoinclude = array();
foreach ($activities as $index => $activity) {
$activitiestoinclude[$activity['type'].'-'.$activity['instance']] = $activity['name'];
}
$selectactivitieslabel = get_string('config_selectactivities', 'block_completion_progress');
$mform->addElement('select', 'config_selectactivities', $selectactivitieslabel, $activitiestoinclude);
$mform->getElement('config_selectactivities')->setMultiple(true);
$mform->getElement('config_selectactivities')->setSize($numactivies);
$mform->setAdvanced('config_selectactivities', true);
$mform->disabledif('config_selectactivities', 'config_activitiesincluded', 'neq', 'selectedactivities');
$mform->addHelpButton('config_selectactivities', 'how_selectactivities_works', 'block_completion_progress');
}
}
}

150
lang/en/block_completion_progress.php

@ -0,0 +1,150 @@
<?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/>.
/**
* Completion Progress block English language translation
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Stings for the Config page.
$string['config_default_title'] = 'Completion Progress';
$string['config_group'] = 'Visible only to group';
$string['config_header_monitored'] = 'Monitored';
$string['config_icons'] = 'Use icons in bar';
$string['config_longbars'] = 'How to present long bars';
$string['config_percentage'] = 'Show percentage to students';
$string['config_scroll'] = 'Scroll';
$string['config_squeeze'] = 'Squeeze';
$string['config_title'] = 'Alternate title';
$string['config_orderby'] = 'Order bar by';
$string['config_orderby_due_time'] = '"{$a}" date';
$string['config_orderby_course_order'] = 'Ordering in course';
$string['config_wrap'] = 'Wrap';
$string['config_activitycompletion'] = 'All activities with completion set';
$string['config_selectedactivities'] = 'Selected activities';
$string['config_activitiesincluded'] = 'Activities included';
$string['config_selectactivities'] = 'Select activities';
// Help strings.
$string['why_set_the_title'] = 'Why you might want to set the block instance title?';
$string['why_set_the_title_help'] = '
<p>There can be multiple instances of the Completion Progress block. You may use different Completion Progress blocks to monitor different sets of activities or resources. For instance you could track progress in assignments in one block and quizzes in another. For this reason you can override the default title and set a more appropriate block title for each instance.</p>
';
$string['why_use_icons'] = 'Why you might want to use icons?';
$string['why_use_icons_help'] = '
<p>You may wish to add tick and cross icons in the Completion Progress to make this block more visually accessible for students with colour-blindness.</p>
<p>It may also make the meaning of the block clearer if you believe colours are not intuitive, either for cultural or personal reasons.</p>
';
$string['why_show_precentage'] = 'Why show a progress percentage to students?';
$string['why_show_precentage_help'] = '
<p>It is possible to show an overall percentage of progress to students.</p>
<p>This is calculated as the number of items complete divided by the total number of items in the bar.</p>
<p>The progress percentage appears until the student mouses over an item in the bar.</p>
';
$string['how_ordering_works'] = 'How ordering works';
$string['how_ordering_works_help'] = '
<p>There are two ways items in the Completion Progress can be ordered.</p>
<ul>
<li><em>"Expect completion on" date</em> (default)<br />
The expected completion dates of activities/resources are used to order the bar. Where activities/resources don\'t have an expected completion date, ordering in the course is used instead.
</li>
<li><em>Ordering in course</em><br />
Activities/resources are presented in the same order as they are on the course page. When this option is used, expected completion dates are ignored.
</li>
</ul>
';
$string['how_group_works'] = 'How visible group works';
$string['how_group_works_help'] = '
<p>Selecting a group will limit the display of the this block to that group only.</p>
';
$string['how_longbars_works'] = 'How long bars are presented';
$string['how_longbars_works_help'] = '
<p>When bars exceed a set length, how they can be presented in one of the following ways.</p>
<ul>
<li>Squeezed into one horizontal bar</li>
<li>Scrolling sideways to show overflowing bar segments</li>
<li>Wrapping to show all bar segments on multiple lines</li>
</ul>
<p>Note that when the bar is wrapped, the NOW indicator will not be shown.</p>
';
$string['how_activitiesincluded_works'] = 'How including activities works';
$string['how_activitiesincluded_works_help'] = '
<p>By default, all activities with Activity completion settings set are included in the bar.</p>
<p>You can also manually select activities to be included.</p>
';
$string['how_selectactivities_works'] = 'How including activities works';
$string['how_selectactivities_works_help'] = '
<p>To manually select activities to be include in the bar, ensure that "'.$string['config_activitiesincluded'].'" is set to "'.$string['config_selectedactivities'].'".</p>
<p>Only activities with activity completion settings set can be included.</p>
<p>Hold the CTRL key to select multiple items.</p>
';
// Other terms.
$string['completion_not_enabled'] = 'Completion tracking is not enabled.';
$string['mouse_over_prompt'] = 'Mouse over or touch bar for info.';
$string['no_activities_config_message'] = '
There are no activities or resources with activity completion set or no activities or resources have been selected.
Set activity completion on activities and resources then configure this block.
';
$string['no_activities_message'] = 'No activities or resources are being monitored. Use config to set up monitoring.';
$string['no_visible_activities_message'] = 'None of the monitored events are currently visible.';
$string['now_indicator'] = 'NOW';
$string['pluginname'] = 'Completion Progress';
$string['showallinfo'] = 'Show all info';
$string['time_expected'] = 'Expected';
$string['not_all_expected_set'] = 'Not all activities with completion have an "{$a}" date set.';
$string['submitted'] = 'Submitted';
// Global setting strings
// Default colours that may have different cultural meanings.
// Note that these defaults can be overridden by the block's global settings.
$string['attempted_colour'] = '#73A839';
$string['submittednotcomplete_colour'] = '#FFCC00';
$string['notAttempted_colour'] = '#C71C22';
$string['futureNotAttempted_colour'] = '#025187';
$string['attempted_colour_title'] = 'Attempted colour';
$string['attempted_colour_descr'] = 'HTML Colour code for elements that have been attempted';
$string['submittednotcomplete_colour_title'] = 'Submitted but not complete colour';
$string['submittednotcomplete_colour_descr'] = 'HTML colour code for elements that have been submitted, but are not yet complete';
$string['notattempted_colour_title'] = 'Not-attempted colour';
$string['notattempted_colour_descr'] = 'HTML colour code for current elements that have not yet been attempted';
$string['futurenotattempted_colour_title'] = 'Future Not-attempted colour';
$string['futurenotattempted_colour_descr'] = 'HTML colour code for future elements that have not yet been attemted';
$string['coursenametoshow'] = 'Course name to show on Dashboard';
$string['shortname'] = 'Short course name';
$string['fullname'] = 'Full course name';
$string['showinactive'] = 'Show inactive students in Overview';
$string['wrapafter'] = 'When wrapping, limit rows to';
$string['defaultlongbars'] = 'Default presentation for long bars';
// Overview page strings.
$string['lastonline'] = 'Last in course';
$string['overview'] = 'Overview of students';
$string['progress'] = 'Progress';
$string['progressbar'] = 'Completion Progress';
// For capabilities.
$string['completion_progress:overview'] = 'View course overview of Completion Progresss for all students';
$string['completion_progress:addinstance'] = 'Add a new Completion Progress block';
$string['completion_progress:myaddinstance'] = 'Add a Completion Progress block to My home page';
$string['completion_progress:showbar'] = 'Show the bar in the Completion Progress block';
// For My home page.
$string['no_blocks'] = "No Completion Progress blocks are set up for your courses.";

581
lib.php

@ -0,0 +1,581 @@
<?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/>.
/**
* Completion Progress block common configuration and helper functions
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
require_once($CFG->libdir.'/completionlib.php');
// Global defaults.
const DEFAULT_COMPLETIONPROGRESS_WRAPAFTER = 16;
const DEFAULT_COMPLETIONPROGRESS_LONGBARS = 'squeeze';
const DEFAULT_COMPLETIONPROGRESS_SCROLLCELLWIDTH = 25;
const DEFAULT_COMPLETIONPROGRESS_COURSENAMETOSHOW = 'shortname';
const DEFAULT_COMPLETIONPROGRESS_SHOWINACTIVE = 0;
const DEFAULT_COMPLETIONPROGRESS_PROGRESSBARICONS = 0;
const DEFAULT_COMPLETIONPROGRESS_ORDERBY = 'orderbytime';
const DEFAULT_COMPLETIONPROGRESS_SHOWPERCENTAGE = 0;
const DEFAULT_COMPLETIONPROGRESS_ACTIVITIESINCLUDED = 'activitycompletion';
/**
* Finds submissions for a user in a course
*
* @param int courseid ID of the course
* @param int userid ID of user in the course
* @return array Course module IDS submissions
*/
function block_completion_progress_student_submissions($courseid, $userid) {
global $DB;
$submissions = array();
$params = array('courseid' => $courseid, 'userid' => $userid);
// Queries to deliver instance IDs of activities with submissions by user.
$queries = array (
'assign' => "SELECT c.id
FROM {assign_submission} s, {assign} a, {modules} m, {course_modules} c
WHERE s.userid = :userid
AND s.latest = 1
AND s.status = 'submitted'
AND s.assignment = a.id
AND a.course = :courseid
AND m.name = 'assign'
AND m.id = c.module
AND c.instance = a.id",
'workshop' => "SELECT c.id
FROM {workshop_submissions} s, {workshop} w, {modules} m, {course_modules} c
WHERE s.authorid = :userid
AND s.workshopid = w.id
AND w.course = :courseid
AND m.name = 'workshop'
AND m.id = c.module
AND c.instance = w.id",
);
foreach ($queries as $moduletype => $query) {
$results = $DB->get_records_sql($query, $params);
foreach ($results as $cmid => $obj) {
$submissions[] = $cmid;
}
}
return $submissions;
}
/**
* Finds submissions for users in a course
*
* @param int courseid ID of the course
* @return array Mapping of userid-cmid pairs for submissions
*/
function block_completion_progress_course_submissions($courseid) {
global $DB;
$submissions = array();
$params = array('courseid' => $courseid);
// Queries to deliver instance IDs of activities with submissions by user.
$queries = array (
'assign' => "SELECT CONCAT(s.userid, '-', c.id)
FROM {assign_submission} s, {assign} a, {modules} m, {course_modules} c
WHERE s.latest = 1
AND s.status = 'submitted'
AND s.assignment = a.id
AND a.course = :courseid
AND m.name = 'assign'
AND m.id = c.module
AND c.instance = a.id",
'workshop' => "SELECT CONCAT(s.authorid, '-', c.id)
FROM {workshop_submissions} s, {workshop} w, {modules} m, {course_modules} c
WHERE s.workshopid = w.id
AND w.course = :courseid
AND m.name = 'workshop'
AND m.id = c.module
AND c.instance = w.id",
);
foreach ($queries as $moduletype => $query) {
$results = $DB->get_records_sql($query, $params);
foreach ($results as $mapping => $obj) {
$submissions[] = $mapping;
}
}
return $submissions;
}
/**
* Returns the alternate links for teachers
*
* @return array URLs and associated capabilities, per activity
*/
function block_completion_progress_modules_with_alternate_links() {
return array(
'assign' => array(
'url' => '/mod/assign/view.php?id=:cmid&action=grading',
'capability' => 'mod/assign:grade',
),
'feedback' => array(
// Breaks if anonymous feedback is collected.
'url' => '/mod/feedback/show_entries.php?id=:cmid&do_show=showoneentry&userid=:userid',
'capability' => 'mod/feedback:viewreports',
),
'lesson' => array(
'url' => '/mod/lesson/report.php?id=:cmid&action=reportdetail&userid=:userid',
'capability' => 'mod/lesson:viewreports',
),
'quiz' => array(
'url' => '/mod/quiz/report.php?id=:cmid&mode=overview',
'capability' => 'mod/quiz:viewreports',
),
);
}
/**
* Returns the activities with completion set in current course
*
* @param int courseid ID of the course
* @param int config The block instance configuration
* @param string forceorder An override for the course order setting
* @return array Activities with completion settings in the course
*/
function block_completion_progress_get_activities($courseid, $config = null, $forceorder = null) {
$modinfo = get_fast_modinfo($courseid, -1);
$sections = $modinfo->get_sections();
$activities = array();
foreach ($modinfo->instances as $module => $instances) {
$modulename = get_string('pluginname', $module);
foreach ($instances as $index => $cm) {
if (
$cm->completion != COMPLETION_TRACKING_NONE && (
$config == null || (
!isset($config->activitiesincluded) || (
$config->activitiesincluded != 'selectedactivities' ||
in_array($module.'-'.$cm->instance, $config->selectactivities))))
) {
$activities[] = array (
'type' => $module,
'modulename' => $modulename,
'id' => $cm->id,
'instance' => $cm->instance,
'name' => $cm->name,
'expected' => $cm->completionexpected,
'section' => $cm->sectionnum,
'position' => array_search($cm->id, $sections[$cm->sectionnum]),
'url' => $cm->url->out(),
'context' => $cm->context,
'icon' => $cm->get_icon_url(),
'available' => $cm->available,
);
}
}
}
// Sort by first value in each element, which is time due.
if ($forceorder == 'orderbycourse' || ($config && $config->orderby == 'orderbycourse')) {
usort($activities, 'block_completion_progress_compare_events');
} else {
usort($activities, 'block_completion_progress_compare_times');
}
return $activities;
}
/**
* Used to compare two activities/resources based on order on course page
*
* @param array $a array of event information
* @param array $b array of event information
* @return <0, 0 or >0 depending on order of activities/resources on course page
*/
function block_completion_progress_compare_events($a, $b) {
if ($a['section'] != $b['section']) {
return $a['section'] - $b['section'];
} else {
return $a['position'] - $b['position'];
}
}
/**
* Used to compare two activities/resources based their expected completion times
*
* @param array $a array of event information
* @param array $b array of event information
* @return <0, 0 or >0 depending on time then order of activities/resources
*/
function block_completion_progress_compare_times($a, $b) {
if (
$a['expected'] != 0 &&
$b['expected'] != 0 &&
$a['expected'] != $b['expected']
) {
return $a['expected'] - $b['expected'];
} else if ($a['expected'] != 0 && $b['expected'] == 0) {
return -1;
} else if ($a['expected'] == 0 && $b['expected'] != 0) {
return 1;
} else {
return block_completion_progress_compare_events($a, $b);
}
}
/**
* Filters activities that a user cannot see due to grouping constraints
*
* @param array $activities The possible activities that can occur for modules
* @param array $userid The user's id
* @param string $courseid the course for filtering visibility
* @return array The array with restricted activities removed
*/
function block_completion_progress_filter_visibility($activities, $userid, $courseid) {
global $CFG, $USER;
$filteredactivities = array();
$modinfo = get_fast_modinfo($courseid, $userid);
$coursecontext = CONTEXT_COURSE::instance($courseid);
// Keep only activities that are visible.
foreach ($activities as $index => $activity) {
$coursemodule = $modinfo->cms[$activity['id']];
// Check visibility in course.
if (!$coursemodule->visible && !has_capability('moodle/course:viewhiddenactivities', $coursecontext, $userid)) {
continue;
}
// Check availability, allowing for visible, but not accessible items.
if (!empty($CFG->enableavailability)) {
if (has_capability('moodle/course:viewhiddenactivities', $coursecontext, $USER->id)) {
$activity['available'] = true;
} else {
if (isset($coursemodule->available) && !$coursemodule->available && empty($coursemodule->availableinfo)) {
continue;
}
$activity['available'] = $coursemodule->available;
}
}
// Check visibility by grouping constraints (includes capability check).
if (!empty($CFG->enablegroupmembersonly)) {
if (isset($coursemodule->uservisible)) {
if ($coursemodule->uservisible != 1 && empty($coursemodule->availableinfo)) {
continue;
}
} else if (!groups_course_module_visible($coursemodule, $userid)) {
continue;
}
}
// Save the visible event.
$filteredactivities[] = $activity;
}
return $filteredactivities;
}
/**
* Checked if a user has completed an activity/resource
*
* @param array $activities The activities with completion in the course
* @param int $userid The user's id
* @param int $course The course instance
* @param array $submissions Submissions by the user
* @return array an describing the user's attempts based on module+instance identifiers
*/
function block_completion_progress_completions($activities, $userid, $course, $submissions) {
$completions = array();
$completion = new completion_info($course);
$cm = new stdClass();
foreach ($activities as $activity) {
$cm->id = $activity['id'];
$activitycompletion = $completion->get_data($cm, true, $userid);
$completions[$activity['id']] = $activitycompletion->completionstate >= 1;
if (!$completions[$activity['id']] && in_array($activity['id'], $submissions)) {
$completions[$activity['id']] = 'submitted';
}
}
return $completions;
}
/**
* Draws a progress bar
*
* @param array $activities The activities with completion in the course
* @param array $completions The user's completion of course activities
* @param stdClass $config The blocks instance configuration settings
* @param int $userid The user's id
* @param int $courseid The course id
* @param int instance The block instance (to identify it on page)
* @param bool $simple Controls whether instructions are shown below a progress bar
* @return string Progress Bar HTML content
*/
function block_completion_progress_bar($activities, $completions, $config, $userid, $courseid, $instance, $simple = false) {
global $OUTPUT, $CFG, $USER;
$content = '';
$now = time();
$numactivities = count($activities);
$dateformat = get_string('strftimedate', 'langconfig');
$alternatelinks = block_completion_progress_modules_with_alternate_links();
// Get colours and use defaults if they are not set in global settings.
$colournames = array(
'attempted_colour' => 'attempted_colour',
'submittednotcomplete_colour' => 'submittednotcomplete_colour',
'notattempted_colour' => 'notAttempted_colour',
'futurenotattempted_colour' => 'futureNotAttempted_colour'
);
$colours = array();
foreach ($colournames as $name => $stringkey) {
$colours[$name] = get_config('block_completion_progress', $name) ?: get_string('block_completion_progress', $stringkey);
}
// Get relevant block instance settings or use defaults.
$useicons = isset($config->progressBarIcons) ? $config->progressBarIcons : DEFAULT_COMPLETIONPROGRESS_PROGRESSBARICONS;
$orderby = isset($config->orderby) ? $config->orderby : DEFAULT_COMPLETIONPROGRESS_ORDERBY;
$defaultlongbars = get_config('block_completion_progress', 'defaultlongbars') ?: DEFAULT_COMPLETIONPROGRESS_LONGBARS;
$longbars = isset($config->longbars) ? $config->longbars : $defaultlongbars;
$displaynow = $orderby == 'orderbytime';
$showpercentage = isset($config->showpercentage) ? $config->showpercentage : DEFAULT_COMPLETIONPROGRESS_SHOWPERCENTAGE;
$rowoptions = array();
$content .= HTML_WRITER::start_div('barContainer');
// Determine the segment width.
$wrapafter = get_config('block_completion_progress', 'wrapafter') ?: DEFAULT_COMPLETIONPROGRESS_WRAPAFTER;
if ($numactivities <= $wrapafter) {
$longbars = 'squeeze';
}
if ($longbars == 'wrap') {
$rows = ceil($numactivities / $wrapafter);
$cellwidth = floor(100 / ceil($numactivities / $rows));
$cellunit = '%';
$celldisplay = 'inline-block';
$displaynow = false;
}
if ($longbars == 'scroll') {
$cellwidth = DEFAULT_COMPLETIONPROGRESS_SCROLLCELLWIDTH;
$cellunit = 'px';
$celldisplay = 'inline-block';
$rowoptions['style'] = 'white-space: nowrap;';
$leftpoly = HTML_WRITER::tag('polygon', '', array('points' => '30,0 0,15 30,30', 'class' => 'triangle-polygon'));
$rightpoly = HTML_WRITER::tag('polygon', '', array('points' => '0,0 30,15 0,30', 'class' => 'triangle-polygon'));
$content .= HTML_WRITER::tag('svg', $leftpoly, array('class' => 'left-arrow-svg', 'height' => '30', 'width' => '30'));
$content .= HTML_WRITER::tag('svg', $rightpoly, array('class' => 'right-arrow-svg', 'height' => '30', 'width' => '30'));
}
if ($longbars == 'squeeze') {
$cellwidth = floor(100 / $numactivities);
$cellunit = '%';
$celldisplay = 'table-cell';
}
// Place now arrow.
if ($orderby == 'orderbytime' && $longbars != 'wrap' && $displaynow == 1 && !$simple) {
// Find where to put now arrow.
$nowpos = 0;
while ($nowpos < $numactivities && $now > $activities[$nowpos]['expected'] && $activities[$nowpos]['expected'] != 0) {
$nowpos++;
}
$content .= HTML_WRITER::start_div('nowRow', $rowoptions);
$nowstring = get_string('now_indicator', 'block_completion_progress');
if ($nowpos < $numactivities / 2) {
for ($i = 0; $i < $nowpos; $i++) {
$content .= HTML_WRITER::div(null, 'blankDiv', array('style' => "width:$cellwidth$cellunit;"));
}
$celloptions = array('style' => "text-align:left; width:$cellwidth;");
$content .= HTML_WRITER::start_div('nowDiv', $celloptions);
$content .= $OUTPUT->pix_icon('left', $nowstring, 'block_completion_progress', array('class' => 'nowicon'));
$content .= $nowstring;
$content .= HTML_WRITER::end_div();
} else {
$celloptions = array('style' => 'text-align:right; width:'. ($cellwidth * $nowpos) . $cellunit .';');
$content .= HTML_WRITER::start_div('nowdiv', $celloptions);
$content .= $nowstring;
$content .= $OUTPUT->pix_icon('right', $nowstring, 'block_completion_progress', array('class' => 'nowicon'));
$content .= HTML_WRITER::end_div();
}
$content .= HTML_WRITER::end_div();
}
// Determine links to activities.
for ($i = 0; $i < $numactivities; $i++) {
if ($userid != $USER->id &&
array_key_exists($activities[$i]['type'], $alternatelinks) &&
has_capability($alternatelinks[$activities[$i]['type']]['capability'], $activities[$i]['context'])
) {
$substitutions = array(
'/:courseid/' => $courseid,
'/:eventid/' => $activities[$i]['instance'],
'/:cmid/' => $activities[$i]['id'],
'/:userid/' => $userid,
);
$link = $alternatelinks[$activities[$i]['type']]['url'];
$link = preg_replace(array_keys($substitutions), array_values($substitutions), $link);
$activities[$i]['link'] = $CFG->wwwroot.$link;
} else {
$activities[$i]['link'] = $activities[$i]['url'];
}
}
// Start progress bar.
$content .= HTML_WRITER::start_div('barRow', $rowoptions);
$counter = 1;
foreach ($activities as $activity) {
$complete = $completions[$activity['id']];
// A cell in the progress bar.
$showinfojs = 'M.block_completion_progress.showInfo('.$instance.','.$userid.','.$activity['id'].');';
$celloptions = array(
'class' => 'progressBarCell',
'ontouchstart' => $showinfojs . ' return false;',
'onmouseover' => $showinfojs,
'style' => 'display:' . $celldisplay .'; width:' . $cellwidth . $cellunit . ';background-color:');
if ($complete === 'submitted') {
$celloptions['style'] .= $colours['submittednotcomplete_colour'].';';
$cellcontent = $OUTPUT->pix_icon('blank', '', 'block_completion_progress');
} else if ($complete === true) {
$celloptions['style'] .= $colours['attempted_colour'].';';
$cellcontent = $OUTPUT->pix_icon($useicons == 1 ? 'tick' : 'blank', '', 'block_completion_progress');
} else if ((!isset($config->orderby) || $config->orderby == 'orderbytime') && $activity['expected'] < $now) {
$celloptions['style'] .= $colours['notattempted_colour'].';';
$cellcontent = $OUTPUT->pix_icon($useicons == 1 ? 'cross' : 'blank', '', 'block_completion_progress');
} else {
$celloptions['style'] .= $colours['futurenotattempted_colour'].';';
$cellcontent = $OUTPUT->pix_icon('blank', '', 'block_completion_progress');
}
if (!empty($activity['available']) || $simple) {
$celloptions['onclick'] = 'document.location=\''.$activity['link'].'\';';
} else {
$celloptions['style'] .= 'cursor: not-allowed;';
}
if ($longbars != 'wrap' && $counter == 1) {
$celloptions['class'] .= ' firstProgressBarCell';
}
if ($longbars != 'wrap' && $counter == $numactivities) {
$celloptions['class'] .= ' lastProgressBarCell';
}
$counter++;
$content .= HTML_WRITER::div($cellcontent, null, $celloptions);
}
$content .= HTML_WRITER::end_div();
$content .= HTML_WRITER::end_div();
// Add the percentage below the progress bar.
if ($showpercentage == 1 && !$simple) {
$progress = block_completion_progress_percentage($activities, $completions);
$percentagecontent = get_string('progress', 'block_completion_progress').': '.$progress.'%';
$percentageoptions = array('class' => 'progressPercentage');
$content .= HTML_WRITER::tag('div', $percentagecontent, $percentageoptions);
}
// Add the info box below the table.
$divoptions = array('class' => 'progressEventInfo',
'id' => 'progressBarInfo'.$instance.'-'.$userid.'-info');
$content .= HTML_WRITER::start_tag('div', $divoptions);
if (!$simple) {
$content .= get_string('mouse_over_prompt', 'block_completion_progress');
$content .= ' ';
$attributes = array (
'class' => 'accesshide',
'onclick' => 'M.block_completion_progress.showAll('.$instance.','.$userid.')'
);
$content .= HTML_WRITER::link('#', get_string('showallinfo', 'block_completion_progress'), $attributes);
}
$content .= HTML_WRITER::end_tag('div');
// Add hidden divs for activity information.
foreach ($activities as $activity) {
$completed = $completions[$activity['id']];
$divoptions = array('class' => 'progressEventInfo',
'id' => 'progressBarInfo'.$instance.'-'.$userid.'-'.$activity['id'],
'style' => 'display: none;');
$content .= HTML_WRITER::start_tag('div', $divoptions);
$text = '';
$text .= html_writer::empty_tag('img',
array('src' => $activity['icon'], 'class' => 'moduleIcon', 'alt' => '', 'role' => 'presentation'));
$text .= s($activity['name']);
if (!empty($activity['available']) || $simple) {
$content .= $OUTPUT->action_link($activity['link'], $text);
} else {
$content .= $text;
}
$content .= HTML_WRITER::empty_tag('br');
if ($completed && $completed !== 'failed' && $completed !== 'submitted') {
$content .= get_string('completed', 'completion').'&nbsp;';
$icon = 'tick';
} else {
$content .= get_string('completion-n', 'completion').'&nbsp;';
$icon = 'cross';
}
$content .= $OUTPUT->pix_icon($icon, '', 'block_completion_progress', array('class' => 'iconInInfo'));
if ($completed === 'submitted') {
$content .= ' (' . get_string('submitted', 'block_completion_progress') . ')';
}
$content .= HTML_WRITER::empty_tag('br');
if ($activity['expected'] != 0) {
$content .= HTML_WRITER::start_tag('div', array('class' => 'expectedBy'));
$content .= get_string('time_expected', 'block_completion_progress').': ';
$content .= userdate($activity['expected'], $dateformat, $CFG->timezone);
$content .= HTML_WRITER::end_tag('div');
}
$content .= HTML_WRITER::end_tag('div');
}
return $content;
}
/**
* Calculates an overall percentage of progress
*
* @param array $activities The possible events that can occur for modules
* @param array $completions The user's attempts on course activities
* @return int Progress value as a percentage
*/
function block_completion_progress_percentage($activities, $completions) {
$completecount = 0;
foreach ($activities as $activity) {
if ($completions[$activity['id']] == 1) {
$completecount++;
}
}
$progressvalue = $completecount == 0 ? 0 : $completecount / count($activities);
return (int)round($progressvalue * 100);
}
/**
* Checks whether the current page is the My home page.
*
* @return bool True when on the My home page.
*/
function block_completion_progress_on_site_page() {
global $SCRIPT, $COURSE;
return $SCRIPT === '/my/index.php' || $COURSE->id == 1;
}

101
module.js

@ -0,0 +1,101 @@
M.block_completion_progress = {
progressBarLast: new Array(),
locked: false,
init: function (YUIObject, instances, users) {
var instance;
var user;
for (instance = 0; instance < instances.length; instance++) {
for (user = 0; user < users.length; user++) {
this.progressBarLast[instances[instance] + '-' + users[user]] = 'info';
}
}
this.addEvent(window, 'resize', function(e) {M.block_completion_progress.setupScrolling(YUIObject);});
},
showInfo: function (instance, user, id) {
if (this.locked) {
return;
}
var last = this.progressBarLast[instance + '-' + user];
document.getElementById('progressBarInfo' + instance + '-' + user + '-' + last).style.display = 'none';
document.getElementById('progressBarInfo' + instance + '-' + user + '-' + id).style.display = 'block';
this.progressBarLast[instance + '-' + user] = id;
},
showAll: function (instance, user) {
var infoBlocks = document.getElementsByClassName('progressEventInfo');
var i;
var searchString = 'progressBarInfo' + instance + '-' + user + '-';
var searchStringLength = searchString.length;
for (i = 0; i < infoBlocks.length; i++) {
if (infoBlocks[i].id.substring(0,searchStringLength) == searchString) {
infoBlocks[i].style.display = 'block';
}
}
document.getElementById(searchString + 'info').style.display = 'none';
this.locked = true;
},
setupScrolling: function(YUIObject) {
var barContainers = document.getElementsByClassName('barContainer');
var i;
var leftArrows;
var rightArrows;
var nowIcons;
for (i = 0; i < barContainers.length; i++) {
nowIcons = barContainers[i].getElementsByClassName('nowicon');
if(nowIcons.length > 0) {
barContainers[i].scrollLeft = nowIcons[0].offsetLeft - (barContainers[i].offsetWidth / 2);
}
leftArrows = barContainers[i].getElementsByClassName('left-arrow-svg');
rightArrows = barContainers[i].getElementsByClassName('right-arrow-svg');
if(leftArrows.length > 0) {
this.checkArrows(barContainers[i]);
this.addEvent(leftArrows[0].firstChild, 'click', function(e) {M.block_completion_progress.scrollContainer(e.target.parentNode, -1); e.target.stopPagination();})
this.addEvent(rightArrows[0].firstChild, 'click', function(e) {M.block_completion_progress.scrollContainer(e.target.parentNode, 1); e.target.stopPagination();})
}
}
},
checkArrows: function(container) {
var leftArrow = container.getElementsByClassName('left-arrow-svg')[0];
var rightArrow = container.getElementsByClassName('right-arrow-svg')[0];
var scrolled = container.scrollLeft;
var scrollWidth = container.scrollWidth - container.offsetWidth;
var threshold = 10;
var buffer = 5;
if (scrolled > threshold) {
leftArrow.style.display = 'block';
leftArrow.style.left = (scrolled + buffer) + 'px';
} else {
leftArrow.style.display = 'none';
}
if (scrollWidth > threshold && scrolled < scrollWidth - threshold) {
rightArrow.style.display = 'block';
rightArrow.style.right = (buffer - scrolled) + 'px';
} else {
rightArrow.style.display = 'none';
}
},
scrollContainer: function(arrow, direction) {
var container = arrow.parentNode;
var amount = direction * container.scrollWidth * 0.15;
container.scrollLeft += amount;
M.block_completion_progress.checkArrows(container);
},
addEvent: function(target, evt, func) {
if (target.addEventListener) {
target.removeEventListener(evt, func);
target.addEventListener(evt, func);
}
else if (target.attachEvent) {
target.detachEvent('on' + evt, func);
target.attachEvent('on' + evt, func);
}
}
};

415
overview.php

@ -0,0 +1,415 @@
<?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/>.
/**
* Completion Progress block overview page
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Include required files.
require_once(dirname(__FILE__) . '/../../config.php');
require_once($CFG->dirroot.'/blocks/completion_progress/lib.php');
require_once($CFG->libdir.'/tablelib.php');
define('USER_SMALL_CLASS', 20); // Below this is considered small.
define('USER_LARGE_CLASS', 200); // Above this is considered large.
define('DEFAULT_PAGE_SIZE', 20);
define('SHOW_ALL_PAGE_SIZE', 5000);
// Gather form data.
$id = required_param('instanceid', PARAM_INT);
$courseid = required_param('courseid', PARAM_INT);
$page = optional_param('page', 0, PARAM_INT); // Which page to show.
$perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT); // How many per page.
$group = optional_param('group', 0, PARAM_INT); // Group selected.
// Determine course and context.
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
$context = CONTEXT_COURSE::instance($courseid);
// Get specific block config and context.
$block = $DB->get_record('block_instances', array('id' => $id), '*', MUST_EXIST);
$config = unserialize(base64_decode($block->configdata));
$blockcontext = CONTEXT_BLOCK::instance($id);
// Set up page parameters.
$PAGE->set_course($course);
$PAGE->requires->css('/blocks/completion_progress/styles.css');
$PAGE->set_url(
'/blocks/completion_progress/overview.php',
array(
'instanceid' => $id,
'courseid' => $courseid,
'page' => $page,
'perpage' => $perpage,
'group' => $group,
'sesskey' => sesskey(),
)
);
$PAGE->set_context($context);
$title = get_string('overview', 'block_completion_progress');
$PAGE->set_title($title);
$PAGE->set_heading($title);
$PAGE->navbar->add($title);
$PAGE->set_pagelayout('report');
// Check user is logged in and capable of accessing the Overview.
require_login($course, false);
require_capability('block/completion_progress:overview', $blockcontext);
confirm_sesskey();
// Start page output.
echo $OUTPUT->header();
echo $OUTPUT->heading($title, 2);
echo $OUTPUT->container_start('block_completion_progress');
// Check if activities/resources have been selected in config.
$activities = block_completion_progress_get_activities($courseid, $config);
if ($activities == null) {
echo get_string('no_events_message', 'block_completion_progress');
echo $OUTPUT->container_end();
echo $OUTPUT->footer();
die();
}
if (empty($activities)) {
echo get_string('no_visible_events_message', 'block_completion_progress');
echo $OUTPUT->container_end();
echo $OUTPUT->footer();
die();
}
$numactivities = count($activities);
// Determine if a role has been selected.
$sql = "SELECT DISTINCT r.id, r.name, r.archetype
FROM {role} r, {role_assignments} a
WHERE a.contextid = :contextid
AND r.id = a.roleid
AND r.archetype = :archetype";
$params = array('contextid' => $context->id, 'archetype' => 'student');
$studentrole = $DB->get_record_sql($sql, $params);
if ($studentrole) {
$studentroleid = $studentrole->id;
} else {
$studentroleid = 0;
}
$roleselected = optional_param('role', $studentroleid, PARAM_INT);
$rolewhere = $roleselected != 0 ? "AND a.roleid = $roleselected" : '';
// Output group selector if there are groups in the course.
echo $OUTPUT->container_start('progressoverviewmenus');
$groupselected = 0;
$groupuserid = $USER->id;
if (has_capability('moodle/site:accessallgroups', $context)) {
$groupuserid = 0;
}
$groupids = array();
$groups = groups_get_all_groups($course->id, $groupuserid);
if (!empty($groups)) {
$groupstodisplay = array(0 => get_string('allparticipants'));
foreach ($groups as $groupid => $groupobject) {
$groupstodisplay[$groupid] = $groupobject->name;
$groupids[] = $groupid;
}
if (!in_array($group, $groupids)) {
$group = 0;
$PAGE->url->param('group', $group);
}
echo get_string('groupsvisible');
echo $OUTPUT->single_select($PAGE->url, 'group', $groupstodisplay, $group);
}
// Output the roles menu.
$sql = "SELECT DISTINCT r.id, r.name, r.shortname
FROM {role} r, {role_assignments} a
WHERE a.contextid = :contextid
AND r.id = a.roleid";
$params = array('contextid' => $context->id);
$roles = role_fix_names($DB->get_records_sql($sql, $params), $context);
$rolestodisplay = array(0 => get_string('allparticipants'));
foreach ($roles as $role) {
$rolestodisplay[$role->id] = $role->localname;
}
echo '&nbsp;'.get_string('role');
echo $OUTPUT->single_select($PAGE->url, 'role', $rolestodisplay, $roleselected);
echo $OUTPUT->container_end();
// Apply group restrictions.
$params = array();
$groupjoin = '';
if ($group && $group != 0) {
$groupjoin = 'JOIN {groups_members} g ON (g.groupid = :groupselected AND g.userid = u.id)';
$params['groupselected'] = $group;
} else if ($groupuserid != 0 && !empty($groupids)) {
$groupjoin = 'JOIN {groups_members} g ON (g.groupid IN ('.implode(',', $groupids).') AND g.userid = u.id)';
}
// Get the list of users enrolled in the course.
$picturefields = user_picture::fields('u');
$sql = "SELECT DISTINCT $picturefields, COALESCE(l.timeaccess, 0) AS lastonlinetime
FROM {user} u
JOIN {role_assignments} a ON (a.contextid = :contextid AND a.userid = u.id $rolewhere)
$groupjoin
LEFT JOIN {user_lastaccess} l ON (l.courseid = :courseid AND l.userid = u.id)";
$params['contextid'] = $context->id;
$params['courseid'] = $course->id;
$userrecords = $DB->get_records_sql($sql, $params);
if (get_config('block_completion_progress', 'showinactive') !== 1) {
extract_suspended_users($context, $userrecords);
}
$userids = array_keys($userrecords);
$users = array_values($userrecords);
$numberofusers = count($users);
for ($i = 0; $i < $numberofusers; $i++) {
$users[$i]->submissions = array();
}
$submissions = block_completion_progress_course_submissions($course->id);
foreach ($submissions as $mapping) {
$mapvalues = explode('-', $mapping);
$index = 0;
while ($users[$index]->id != $mapvalues[0] && $index < $numberofusers) {
$index++;
}
if ($index < $numberofusers) {
$users[$index]->submissions[] = $mapvalues[1];
}
}
$paged = $numberofusers > $perpage;
if (!$paged) {
$page = 0;
}
// Form for messaging selected participants.
$formattributes = array('action' => $CFG->wwwroot.'/user/action_redir.php', 'method' => 'post', 'id' => 'participantsform');
echo html_writer::start_tag('form', $formattributes);
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'returnto', 'value' => s($PAGE->url->out(false))));
// Setup submissions table.
$table = new flexible_table('mod-block-completion-progress-overview');
$table->pagesize($perpage, $numberofusers);
$tablecolumns = array('select', 'picture', 'fullname', 'lastonline', 'progressbar', 'progress');
$table->define_columns($tablecolumns);
$tableheaders = array(
'',
'',
get_string('fullname'),
get_string('lastonline', 'block_completion_progress'),
get_string('progressbar', 'block_completion_progress'),
get_string('progress', 'block_completion_progress')
);
$table->define_headers($tableheaders);
$table->sortable(true);
$table->set_attribute('class', 'overviewTable');
$table->column_style_all('padding', '5px');
$table->column_style_all('text-align', 'left');
$table->column_style_all('vertical-align', 'middle');
$table->column_style('select', 'text-align', 'right');
$table->column_style('select', 'padding', '5px 0 5px 5px');
$table->column_style('select', 'width', '5%');
$table->column_style('picture', 'width', '5%');
$table->column_style('fullname', 'width', '15%');
$table->column_style('lastonline', 'width', '15%');
$table->column_style('progressbar', 'min-width', '200px');
$table->column_style('progressbar', 'width', '*');
$table->column_style('progressbar', 'padding', '0');
$table->column_style('progress', 'text-align', 'center');
$table->column_style('progress', 'width', '8%');
$table->no_sorting('select');
$select = '';
$table->no_sorting('picture');
$table->no_sorting('progressbar');
$table->define_baseurl($PAGE->url);
$table->setup();
// Get range of students for page.
$startdisplay = $page * $perpage;
$enddisplay = ($startdisplay + $perpage > $numberofusers) ? $numberofusers : ($startdisplay + $perpage);
$sort = $table->get_sql_sort();
if (!$sort) {
$sort = 'firstname DESC';
}
$sortbyprogress = strncmp($sort, 'progress', 8) == 0;
if ($sortbyprogress) {
$startuser = 0;
$enduser = $numberofusers;
} else {
usort($users, 'block_completion_progress_compare_rows');
$startuser = $startdisplay;
$enduser = $enddisplay;
}
// Build array of user information.
$rows = array();
for ($i = $startuser; $i < $enduser; $i++) {
if ($CFG->enablenotes || $CFG->messaging) {
$selectattributes = array('type' => 'checkbox', 'class' => 'usercheckbox', 'name' => 'user'.$users[$i]->id);
$select = html_writer::empty_tag('input', $selectattributes);
}
$picture = $OUTPUT->user_picture($users[$i], array('course' => $course->id));
$namelink = html_writer::link($CFG->wwwroot.'/user/view.php?id='.$users[$i]->id.'&course='.$course->id, fullname($users[$i]));
if (empty($users[$i]->lastonlinetime)) {
$lastonline = get_string('never');
} else {
$lastonline = userdate($users[$i]->lastonlinetime);
}
$useractivities = block_completion_progress_filter_visibility($activities, $users[$i]->id, $course->id);
if (!empty($useractivities)) {
$completions = block_completion_progress_completions($useractivities, $users[$i]->id, $course, $users[$i]->submissions);
$progressbar = block_completion_progress_bar($useractivities, $completions, $config, $users[$i]->id, $course->id,
$block->id, true);
$progressvalue = block_completion_progress_percentage($useractivities, $completions);
$progress = $progressvalue.'%';
} else {
$progressbar = get_string('no_visible_events_message', 'block_completion_progress');
$progressvalue = 0;
$progress = '?';
}
$rows[$i] = array(
'firstname' => strtoupper($users[$i]->firstname),
'lastname' => strtoupper($users[$i]->lastname),
'select' => $select,
'picture' => $picture,
'fullname' => $namelink,
'lastonlinetime' => $users[$i]->lastonlinetime,
'lastonline' => $lastonline,
'progressbar' => $progressbar,
'progressvalue' => $progressvalue,
'progress' => $progress
);
}
// Sort the user rows.
if ($sortbyprogress) {
usort($rows, 'block_completion_progress_compare_rows');
}
// Build the table content and output.
if ($numberofusers > 0) {
for ($i = $startdisplay; $i < $enddisplay; $i++) {
$table->add_data(array($rows[$i]['select'], $rows[$i]['picture'],
$rows[$i]['fullname'], $rows[$i]['lastonline'],
$rows[$i]['progressbar'], $rows[$i]['progress']));
}
}
$table->print_html();
// Output paging controls.
$perpageurl = clone($PAGE->url);
if ($paged) {
$perpageurl->param('perpage', SHOW_ALL_PAGE_SIZE);
echo $OUTPUT->container(html_writer::link($perpageurl, get_string('showall', '', $numberofusers)), array(), 'showall');
} else if ($numberofusers > DEFAULT_PAGE_SIZE) {
$perpageurl->param('perpage', DEFAULT_PAGE_SIZE);
echo $OUTPUT->container(html_writer::link($perpageurl, get_string('showperpage', '', DEFAULT_PAGE_SIZE)), array(), 'showall');
}
// Output messaging controls.
if ($CFG->enablenotes || $CFG->messaging) {
echo html_writer::start_tag('div', array('class' => 'buttons'));
echo html_writer::empty_tag('input', array('type' => 'button', 'id' => 'checkall', 'value' => get_string('selectall')));
echo html_writer::empty_tag('input', array('type' => 'button', 'id' => 'checknone', 'value' => get_string('deselectall')));
$displaylist = array();
if (!empty($CFG->messaging) && has_capability('moodle/course:bulkmessaging', $context)) {
$displaylist['messageselect.php'] = get_string('messageselectadd');
}
if (!empty($CFG->enablenotes) && has_capability('moodle/notes:manage', $context)) {
$displaylist['addnote.php'] = get_string('addnewnote', 'notes');
$displaylist['groupaddnote.php'] = get_string('groupaddnewnote', 'notes');
}
echo html_writer::tag('label', get_string("withselectedusers"), array('for' => 'formactionid'));
echo html_writer::select($displaylist, 'formaction', '', array('' => 'choosedots'), array('id' => 'formactionid'));
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'id', 'value' => $course->id));
echo html_writer::start_tag('noscript', array('style' => 'display:inline;'));
echo html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('ok')));
echo html_writer::end_tag('noscript');
echo $OUTPUT->help_icon('withselectedusers');
echo html_writer::end_tag('div');
echo html_writer::end_tag('form');
}
// Organise access to JS for messaging.
$module = array('name' => 'core_user', 'fullpath' => '/user/module.js');
$PAGE->requires->js_init_call('M.core_user.init_participation', null, false, $module);
// Organise access to JS for progress bars.
$jsmodule = array('name' => 'block_completion_progress', 'fullpath' => '/blocks/completion_progress/module.js');
$arguments = array(array($block->id), $userids);
$PAGE->requires->js_init_call('M.block_completion_progress.setupScrolling', array(), false, $jsmodule);
$PAGE->requires->js_init_call('M.block_completion_progress.init', $arguments, false, $jsmodule);
echo $OUTPUT->container_end();
echo $OUTPUT->footer();
/**
* Compares two table row elements for ordering.
*
* @param mixed $a element containing name, online time and progress info
* @param mixed $b element containing name, online time and progress info
* @return order of pair expressed as -1, 0, or 1
*/
function block_completion_progress_compare_rows($a, $b) {
global $sort;
// Process each of the one or two orders.
$orders = explode(',', $sort);
foreach ($orders as $order) {
// Extract the order information.
$orderelements = explode(' ', trim($order));
$aspect = $orderelements[0];
$ascdesc = $orderelements[1];
// Compensate for presented vs actual.
switch ($aspect) {
case 'name':
$aspect = 'lastname';
break;
case 'lastonline':
$aspect = 'lastonlinetime';
break;
case 'progress':
$aspect = 'progressvalue';
break;
}
// Check of order can be established.
// Check of order can be established.
if (is_array($a)) {
$first = $a[$aspect];
$second = $b[$aspect];
} else {
$first = $a->$aspect;
$second = $b->$aspect;
}
if ($first < $second) {
return $ascdesc == 'ASC' ? 1 : -1;
}
if ($first > $second) {
return $ascdesc == 'ASC' ? -1 : 1;
}
}
// If previous ordering fails, consider values equal.
return 0;
}

BIN
pix/blank.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

BIN
pix/cross.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
pix/left.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

BIN
pix/right.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

BIN
pix/tick.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

95
settings.php

@ -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/>.
/**
* Completion Progress block settings
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/blocks/completion_progress/lib.php');
if ($ADMIN->fulltree) {
$options = array(10 => 10, 12 => 12, 14 => 14, 16 => 16, 18 => 18, 20 => 20);
$settings->add(new admin_setting_configselect('block_completion_progress/wrapafter',
get_string('wrapafter', 'block_completion_progress'),
'',
DEFAULT_COMPLETIONPROGRESS_WRAPAFTER,
$options)
);
$options = array(
'squeeze' => get_string('config_squeeze', 'block_completion_progress'),
'scroll' => get_string('config_scroll', 'block_completion_progress'),
'wrap' => get_string('config_wrap', 'block_completion_progress'),
);
$settings->add(new admin_setting_configselect('block_completion_progress/defaultlongbars',
get_string('defaultlongbars', 'block_completion_progress'),
'',
DEFAULT_COMPLETIONPROGRESS_LONGBARS,
$options)
);
$options = array(
'shortname' => get_string('shortname', 'block_completion_progress'),
'fullname' => get_string('fullname', 'block_completion_progress')
);
$settings->add(new admin_setting_configselect('block_completion_progress/coursenametoshow',
get_string('coursenametoshow', 'block_completion_progress'),
'',
DEFAULT_COMPLETIONPROGRESS_COURSENAMETOSHOW,
$options)
);
$settings->add(new admin_setting_configcolourpicker('block_completion_progress/attempted_colour',
get_string('attempted_colour_title', 'block_completion_progress'),
get_string('attempted_colour_descr', 'block_completion_progress'),
get_string('attempted_colour', 'block_completion_progress'),
null )
);
$settings->add(new admin_setting_configcolourpicker('block_completion_progress/submittednotcomplete_colour',
get_string('submittednotcomplete_colour_title', 'block_completion_progress'),
get_string('submittednotcomplete_colour_descr', 'block_completion_progress'),
get_string('submittednotcomplete_colour', 'block_completion_progress'),
null )
);
$settings->add(new admin_setting_configcolourpicker('block_completion_progress/notattempted_colour',
get_string('notattempted_colour_title', 'block_completion_progress'),
get_string('notattempted_colour_descr', 'block_completion_progress'),
get_string('notAttempted_colour', 'block_completion_progress'),
null )
);
$settings->add(new admin_setting_configcolourpicker('block_completion_progress/futurenotattempted_colour',
get_string('futurenotattempted_colour_title', 'block_completion_progress'),
get_string('futurenotattempted_colour_descr', 'block_completion_progress'),
get_string('futureNotAttempted_colour', 'block_completion_progress'),
null )
);
$settings->add(new admin_setting_configcheckbox('block_completion_progress/showinactive',
get_string('showinactive', 'block_completion_progress'),
'',
DEFAULT_COMPLETIONPROGRESS_SHOWINACTIVE)
);
}

186
styles.css

@ -0,0 +1,186 @@
.block_completion_progress .content {
text-align: left;
}
.block_completion_progress .barContainer {
overflow: hidden;
position: relative;
padding: 0;
}
.block_completion_progress .left-arrow-svg,
.block_completion_progress .right-arrow-svg {
position: absolute;
top: calc(50% - 15px);
display: none;
}
.block_completion_progress .triangle-polygon {
fill: rgba(0, 0, 0, 0.3);
stroke: white;
stroke-width: 3px;
}
.block_completion_progress .nowRow,
.block_completion_progress .barRow {
padding: 0;
width: 100%;
margin: 0;
height: 25px;
display: table;
}
.block_completion_progress .nowRow .blankDiv {
display: table-cell;
}
.block_completion_progress .progressBarCell {
height: 25px;
margin: 0 -1px 0 0;
padding: 0;
text-align: center;
vertical-align: middle;
cursor: pointer;
border-left: solid 1px #e3e3e3;
border-top: solid 1px #e3e3e3;
}
.block_completion_progress .progressBarCell.firstProgressBarCell {
border-radius: 4px 0 0 4px;
border-left: none;
}
.dir-rtl .block_progress .progressBarCell.firstProgressBarCell {
border-radius: 0px 4px 4px 0px;
border-left: solid 1px #e3e3e3;
}
.block_completion_progress .progressBarCell.lastProgressBarCell {
border-radius: 0 4px 4px 0;
}
.dir-rtl .block_progress .progressBarCell.lastProgressBarCell {
border-radius: 4px 0px 0px 4px;
border-left: none;
}
.block_completion_progress .progressBarCell img {
height: auto;
margin: 1px 0 0 0;
max-height: 15px;
max-width: 15px;
padding: 0;
vertical-align: middle;
width: 85%;
}
.block_completion_progress .progressEventInfo,
.block_completion_progress .progressPercentage {
font-size: x-small;
text-align: left;
white-space: pre;
overflow: hidden;
padding: 0;
margin: 5px;
}
.block_completion_progress .progressEventInfo .iconInInfo,
#fitem_id_config_progressBarIcons .iconOnConfig {
height: 1.2em;
}
#page-blocks-completion_progress-overview .overviewTable {
margin: 10px 0;
width: 100%;
table-layout: fixed;
}
.block_completion_progress .progressEventInfo img {
vertical-align: middle;
}
.block_completion_progress .moduleIcon {
float: left;
margin-right: 5px;
max-width: 24px;
}
.block_completion_progress .progressBarHeader {
font-size: 90%;
margin: 0;
padding: 0;
}
.block_completion_progress h3 {
margin-bottom: 0;
}
.block_completion_progress .expectedBy {
margin: 5px auto;
}
.progressWarningBox {
border: 2px solid #F00;
padding: 10px;
background-color: #F66;
color: white;
margin: 10px 0;
font-size: large;
font-weight: bold;
}
.progressConfigBox {
border-bottom: 1px dashed #cccccc;
padding: 5px;
margin: 0 0 5px 0 !important;
}
.progressConfigModuleTitle {
font-weight: bold;
}
.progressConfigModuleTitle img {
vertical-align: middle;
}
.block_completion_progress .overviewButton {
margin: 10px;
text-align: center;
}
#page-blocks-completion_progress-overview .buttons {
margin-top: 20px;
text-align: left;
}
#page-blocks-completion_progress-overview .buttons label {
display: inline;
margin: 0 5px 0 10px;
}
#page-blocks-completion_progress-overview .progressoverviewmenus {
text-align: left;
margin-bottom: 5px;
}
#page-blocks-completion_progress-overview .progressoverviewmenus .singleselect {
text-align: left;
margin-right: 10px;
}
#page-blocks-completion_progress-overview .progressoverviewmenus form,
#page-blocks-completion_progress-overview .progressoverviewmenus select,
#page-blocks-completion_progress-overview .progressoverviewmenus div,
#page-blocks-completion_progress-overview .paging,
#page-blocks-completion_progress-overview #showall {
display: inline;
}
#page-blocks-completion_progress-overview #showall {
margin-left: 10px;
}
#page-blocks-completion_progress-overview .progressoverviewmenus select {
margin-left: 2px;
}

32
version.php

@ -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/>.
/**
* Completion Progress block version details
*
* @package block_completion_progress
* @copyright 2016 Michael de Raadt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
$plugin->version = 2016042500;
$plugin->requires = 2014051200; // Moodle 2.7.
$plugin->maturity = MATURITY_BETA;
$plugin->release = 'Version for Moodle 2.7 onwards';
$plugin->component = 'block_completion_progress';
Loading…
Cancel
Save