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
+199
View File
@@ -0,0 +1,199 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines a category in my profile page navigation.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die();
/**
* Defines a category in my profile page navigation.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class category implements \renderable {
/**
* @var string Name of the category after which this category should appear.
*/
private $after;
/**
* @var string Name of the category.
*/
private $name;
/**
* @var string Title of the category.
*/
private $title;
/**
* @var node[] Array of nodes associated with this category.
*/
private $nodes = array();
/**
* @var string HTML class attribute for this category. Classes should be separated by a space, e.g. 'class1 class2'
*/
private $classes;
/**
* @var array list of properties publicly accessible via __get.
*/
private $properties = array('after', 'name', 'title', 'nodes', 'classes');
/**
* Constructor for category class.
*
* @param string $name Category name.
* @param string $title category title.
* @param null|string $after Name of category after which this category should appear.
* @param null|string $classes a list of css classes.
*/
public function __construct($name, $title, $after = null, $classes = null) {
$this->after = $after;
$this->name = $name;
$this->title = $title;
$this->classes = $classes;
}
/**
* Add a node to this category.
*
* @param node $node node object.
* @see \core_user\output\myprofile\tree::add_node()
*
* @throws \coding_exception
*/
public function add_node(node $node) {
$name = $node->name;
if (isset($this->nodes[$name])) {
throw new \coding_exception("Node with name $name already exists");
}
if ($node->parentcat !== $this->name) {
throw new \coding_exception("Node parent must match with the category it is added to");
}
$this->nodes[$node->name] = $node;
}
/**
* Sort nodes of the category in the order in which they should be displayed.
*
* @see \core_user\output\myprofile\tree::sort_categories()
* @throws \coding_exception
*/
public function sort_nodes() {
$tempnodes = array();
$this->validate_after_order();
// First content noes.
foreach ($this->nodes as $node) {
$after = $node->after;
$content = $node->content;
if (($after == null && !empty($content)) || $node->name === 'editprofile') {
// Can go anywhere in the cat. Also show content nodes first.
$tempnodes = array_merge($tempnodes, array($node->name => $node), $this->find_nodes_after($node));
}
}
// Now nodes with no content.
foreach ($this->nodes as $node) {
$after = $node->after;
$content = $node->content;
if ($after == null && empty($content)) {
// Can go anywhere in the cat. Also show content nodes first.
$tempnodes = array_merge($tempnodes, array($node->name => $node), $this->find_nodes_after($node));
}
}
if (count($tempnodes) !== count($this->nodes)) {
// Orphan nodes found.
throw new \coding_exception('Some of the nodes specified contains invalid \'after\' property');
}
$this->nodes = $tempnodes;
}
/**
* Verifies that node with content can come after node with content only . Also verifies the same thing for nodes without
* content.
* @throws \coding_exception
*/
protected function validate_after_order() {
$nodearray = $this->nodes;
foreach ($this->nodes as $node) {
$after = $node->after;
if (!empty($after)) {
if (empty($nodearray[$after])) {
throw new \coding_exception('node {$node->name} specified contains invalid \'after\' property');
} else {
// Valid node found.
$afternode = $nodearray[$after];
$beforecontent = $node->content;
$aftercontent = $afternode->content;
if ((empty($beforecontent) && !empty($aftercontent)) || (!empty($beforecontent) && empty($aftercontent))) {
// Only node with content are allowed after content nodes. Same goes for no content nodes.
throw new \coding_exception('node {$node->name} specified contains invalid \'after\' property');
}
}
}
}
}
/**
* Given a node object find all node objects that should appear after it.
*
* @param node $node node object
*
* @return array
*/
protected function find_nodes_after($node) {
$return = array();
$nodearray = $this->nodes;
foreach ($nodearray as $nodeelement) {
if ($nodeelement->after === $node->name) {
// Find all nodes that comes after this node as well.
$return = array_merge($return, array($nodeelement->name => $nodeelement), $this->find_nodes_after($nodeelement));
}
}
return $return;
}
/**
* Magic get method.
*
* @param string $prop property to get.
*
* @return mixed
* @throws \coding_exception
*/
public function __get($prop) {
if (in_array($prop, $this->properties)) {
return $this->$prop;
}
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines Manager class for my profile navigation tree.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die();
/**
* Defines MAnager class for myprofile navigation tree.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class manager {
/**
* Parse all callbacks and builds the tree.
*
* @param \stdClass $user user for which the profile is displayed.
* @param bool $iscurrentuser true if the profile being viewed is of current user, else false.
* @param \stdClass $course Course object
*
* @return tree Fully build tree to be rendered on my profile page.
*/
public static function build_tree($user, $iscurrentuser, $course = null) {
global $CFG;
$tree = new tree();
// Add core nodes.
require_once($CFG->libdir . "/myprofilelib.php");
core_myprofile_navigation($tree, $user, $iscurrentuser, $course);
// Core components.
$components = \core_component::get_core_subsystems();
foreach ($components as $component => $directory) {
if (empty($directory)) {
continue;
}
$file = $directory . "/lib.php";
if (is_readable($file)) {
require_once($file);
$function = "core_" . $component . "_myprofile_navigation";
if (function_exists($function)) {
$function($tree, $user, $iscurrentuser, $course);
}
}
}
// Plugins.
$pluginswithfunction = get_plugins_with_function('myprofile_navigation', 'lib.php');
foreach ($pluginswithfunction as $plugins) {
foreach ($plugins as $function) {
$function($tree, $user, $iscurrentuser, $course);
}
}
$tree->sort_categories();
return $tree;
}
}
+120
View File
@@ -0,0 +1,120 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines a node in my profile page navigation.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die();
/**
* Defines a node in my profile page navigation.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class node implements \renderable {
/**
* @var string Name of parent category.
*/
private $parentcat;
/**
* @var string Name of this node.
*/
private $name;
/**
* @var string Name of the node after which this node should appear.
*/
private $after;
/**
* @var string Title of this node.
*/
private $title;
/**
* @var string|\moodle_url Url that this node should link to.
*/
private $url;
/**
* @var string Content to display under this node.
*/
private $content;
/**
* @var string|\pix_icon Icon for this node.
*/
private $icon;
/**
* @var string HTML class attribute for this node. Classes should be separated by a space, e.g. 'class1 class2'
*/
private $classes;
/**
* @var array list of properties accessible via __get.
*/
private $properties = array('parentcat', 'after', 'name', 'title', 'url', 'content', 'icon', 'classes');
/**
* Constructor for the node.
*
* @param string $parentcat Name of parent category.
* @param string $name Name of this node.
* @param string $title Title of this node.
* @param null|string $after Name of the node after which this node should appear.
* @param null|string|\moodle_url $url Url that this node should link to.
* @param null|string $content Content to display under this node.
* @param null|string|\pix_icon $icon Icon for this node.
* @param null|string $classes a list of css classes.
*/
public function __construct($parentcat, $name, $title, $after = null, $url = null, $content = null, $icon = null,
$classes = null) {
$this->parentcat = $parentcat;
$this->after = $after;
$this->name = $name;
$this->title = $title;
$this->url = is_null($url) ? null : new \moodle_url($url);
$this->content = $content;
$this->icon = $icon;
$this->classes = $classes;
}
/**
* Magic get method.
*
* @param string $prop property to get.
*
* @return mixed
* @throws \coding_exception
*/
public function __get($prop) {
if (in_array($prop, $this->properties)) {
return $this->$prop;
}
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
}
}
+129
View File
@@ -0,0 +1,129 @@
<?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/>.
/**
* myprofile renderer.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die;
/**
* Report log renderer's for printing reports.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends \plugin_renderer_base {
/**
* Render the whole tree.
*
* @param tree $tree
*
* @return string
*/
public function render_tree(tree $tree) {
$return = \html_writer::start_tag('div', array('class' => 'profile_tree'));
$categories = $tree->categories;
foreach ($categories as $category) {
$return .= $this->render($category);
}
$return .= \html_writer::end_tag('div');
return $return;
}
/**
* Render a category.
*
* @param category $category
*
* @return string
*/
public function render_category(category $category) {
$classes = $category->classes;
if (empty($classes)) {
$return = \html_writer::start_tag('section',
array('class' => 'node_category card d-inline-block w-100 mb-3'));
$return .= \html_writer::start_tag('div', array('class' => 'card-body'));
} else {
$return = \html_writer::start_tag('section',
array('class' => 'node_category card d-inline-block w-100 mb-3' . $classes));
$return .= \html_writer::start_tag('div', array('class' => 'card-body'));
}
$return .= \html_writer::tag('h3', $category->title, array('class' => 'lead'));
$nodes = $category->nodes;
if (empty($nodes)) {
// No nodes, nothing to render.
return '';
}
$return .= \html_writer::start_tag('ul');
foreach ($nodes as $node) {
$return .= $this->render($node);
}
$return .= \html_writer::end_tag('ul');
$return .= \html_writer::end_tag('div');
$return .= \html_writer::end_tag('section');
return $return;
}
/**
* Render a node.
*
* @param node $node
*
* @return string
*/
public function render_node(node $node) {
$return = '';
if (is_object($node->url)) {
$header = \html_writer::link($node->url, $node->title);
} else {
$header = $node->title;
}
$icon = $node->icon;
if (!empty($icon)) {
$header .= $this->render($icon);
}
$content = $node->content;
$classes = $node->classes;
if (!empty($content)) {
if ($header) {
// There is some content to display below this make this a header.
$return = \html_writer::tag('dt', $header);
$return .= \html_writer::tag('dd', $content);
$return = \html_writer::tag('dl', $return);
} else {
$return = \html_writer::span($content);
}
if ($classes) {
$return = \html_writer::tag('li', $return, array('class' => 'contentnode ' . $classes));
} else {
$return = \html_writer::tag('li', $return, array('class' => 'contentnode'));
}
} else {
$return = \html_writer::span($header);
$return = \html_writer::tag('li', $return, array('class' => $classes));
}
return $return;
}
}
+158
View File
@@ -0,0 +1,158 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines profile page navigation tree.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die();
/**
* Defines my profile page navigation tree.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tree implements \renderable {
/**
* @var category[] Array of categories in the tree.
*/
private $categories = array();
/**
* @var node[] Array of nodes in the tree that were directly added to the tree.
*/
private $nodes = array();
/**
* @var array List of properties accessible via __get.
*/
private $properties = array('categories', 'nodes');
/**
* Add a node to the tree.
*
* @param node $node node object.
*
* @throws \coding_exception
*/
public function add_node(node $node) {
$name = $node->name;
if (isset($this->nodes[$name])) {
throw new \coding_exception("Node name $name already used");
}
$this->nodes[$node->name] = $node;
}
/**
* Add a category to the tree.
*
* @param category $cat category object.
*
* @throws \coding_exception
*/
public function add_category(category $cat) {
$name = $cat->name;
if (isset($this->categories[$name])) {
throw new \coding_exception("Category name $name already used");
}
$this->categories[$cat->name] = $cat;
}
/**
* Sort categories and nodes. Builds the tree structure that would be displayed to the user.
*
* @throws \coding_exception
*/
public function sort_categories() {
$this->attach_nodes_to_categories();
$tempcategories = array();
foreach ($this->categories as $category) {
$after = $category->after;
if ($after == null) {
// Can go anywhere in the tree.
$category->sort_nodes();
$tempcategories = array_merge($tempcategories, array($category->name => $category),
$this->find_categories_after($category));
}
}
if (count($tempcategories) !== count($this->categories)) {
// Orphan categories found.
throw new \coding_exception('Some of the categories specified contains invalid \'after\' property');
}
$this->categories = $tempcategories;
}
/**
* Attach various nodes to their respective categories.
*
* @throws \coding_exception
*/
protected function attach_nodes_to_categories() {
foreach ($this->nodes as $node) {
$parentcat = $node->parentcat;
if (!isset($this->categories[$parentcat])) {
throw new \coding_exception("Category $parentcat doesn't exist");
} else {
$this->categories[$parentcat]->add_node($node);
}
}
}
/**
* Find all category nodes that should be displayed after a given a category node.
*
* @param category $category category object
*
* @return category[] array of category objects
* @throws \coding_exception
*/
protected function find_categories_after($category) {
$return = array();
$categoryarray = $this->categories;
foreach ($categoryarray as $categoryelement) {
if ($categoryelement->after == $category->name) {
// Find all categories that comes after this category as well.
$categoryelement->sort_nodes();
$return = array_merge($return, array($categoryelement->name => $categoryelement),
$this->find_categories_after($categoryelement));
}
}
return $return;
}
/**
* Magic get method.
*
* @param string $prop property to get.
*
* @return mixed
* @throws \coding_exception
*/
public function __get($prop) {
if (in_array($prop, $this->properties)) {
return $this->$prop;
}
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
}
}
+374
View File
@@ -0,0 +1,374 @@
<?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/>.
/**
* Class for rendering user filters on the course participants page.
*
* @package core_user
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output;
use core_user\fields;
use renderer_base;
use stdClass;
/**
* Class for rendering user filters on the course participants page.
*
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class participants_filter extends \core\output\datafilter {
/**
* Get data for all filter types.
*
* @return array
*/
protected function get_filtertypes(): array {
$filtertypes = [];
$filtertypes[] = $this->get_keyword_filter();
if ($filtertype = $this->get_enrolmentstatus_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_roles_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_enrolments_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_groups_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_accesssince_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_country_filter()) {
$filtertypes[] = $filtertype;
}
return $filtertypes;
}
/**
* Get data for the enrolment status filter.
*
* @return stdClass|null
*/
protected function get_enrolmentstatus_filter(): ?stdClass {
if (!has_capability('moodle/course:enrolreview', $this->context)) {
return null;
}
return $this->get_filter_object(
'status',
get_string('participationstatus', 'core_enrol'),
false,
true,
null,
[
(object) [
'value' => ENROL_USER_ACTIVE,
'title' => get_string('active'),
],
(object) [
'value' => ENROL_USER_SUSPENDED,
'title' => get_string('inactive'),
],
]
);
}
/**
* Get data for the roles filter.
*
* @return stdClass|null
*/
protected function get_roles_filter(): ?stdClass {
$roles = [];
$roles += [-1 => get_string('noroles', 'role')];
$roles += get_viewable_roles($this->context, null, ROLENAME_BOTH);
if (has_capability('moodle/role:assign', $this->context)) {
$roles += get_assignable_roles($this->context, ROLENAME_BOTH);
}
return $this->get_filter_object(
'roles',
get_string('roles', 'core_role'),
false,
true,
null,
array_map(function($id, $title) {
return (object) [
'value' => $id,
'title' => $title,
];
}, array_keys($roles), array_values($roles))
);
}
/**
* Get data for the roles filter.
*
* @return stdClass|null
*/
protected function get_enrolments_filter(): ?stdClass {
if (!has_capability('moodle/course:enrolreview', $this->context)) {
return null;
}
if ($this->course->id == SITEID) {
// No enrolment methods for the site.
return null;
}
$instances = enrol_get_instances($this->course->id, true);
$plugins = enrol_get_plugins(false);
return $this->get_filter_object(
'enrolments',
get_string('enrolmentinstances', 'core_enrol'),
false,
true,
null,
array_filter(array_map(function($instance) use ($plugins): ?stdClass {
if (!array_key_exists($instance->enrol, $plugins)) {
return null;
}
return (object) [
'value' => $instance->id,
'title' => $plugins[$instance->enrol]->get_instance_name($instance),
];
}, array_values($instances)))
);
}
/**
* Get data for the groups filter.
*
* @return stdClass|null
*/
protected function get_groups_filter(): ?stdClass {
global $USER;
// Filter options for groups, if available.
$seeallgroups = has_capability('moodle/site:accessallgroups', $this->context);
$seeallgroups = $seeallgroups || ($this->course->groupmode != SEPARATEGROUPS);
if ($seeallgroups) {
$groups = [];
$groups += [USERSWITHOUTGROUP => (object) [
'id' => USERSWITHOUTGROUP,
'name' => get_string('nogroup', 'group'),
]];
$groups += groups_get_all_groups($this->course->id);
} else {
// Otherwise, just list the groups the user belongs to.
$groups = groups_get_all_groups($this->course->id, $USER->id);
}
// Return no data if no groups found (which includes if the only value is 'No group').
if (empty($groups) || (count($groups) === 1 && array_key_exists(-1, $groups))) {
return null;
}
return $this->get_filter_object(
'groups',
get_string('groups', 'core_group'),
false,
true,
null,
array_map(function($group) {
return (object) [
'value' => $group->id,
'title' => format_string($group->name, true, ['context' => $this->context]),
];
}, array_values($groups))
);
}
/**
* Get data for the accesssince filter.
*
* @return stdClass|null
*/
protected function get_accesssince_filter(): ?stdClass {
global $CFG, $DB;
$hiddenfields = [];
if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) {
$hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
}
if (array_key_exists('lastaccess', $hiddenfields)) {
return null;
}
// Get minimum lastaccess for this course and display a dropbox to filter by lastaccess going back this far.
// We need to make it diferently for normal courses and site course.
if (!($this->course->id == SITEID)) {
// Regular course.
$params = [
'courseid' => $this->course->id,
'timeaccess' => 0,
];
$select = 'courseid = :courseid AND timeaccess != :timeaccess';
$minlastaccess = $DB->get_field_select('user_lastaccess', 'MIN(timeaccess)', $select, $params);
// Determine enrolled users, who do not have accompanying lastaccess to the course.
[$enrolledsql, $enrolledparams] = get_enrolled_sql($this->context);
$sql = "SELECT 'x'
FROM {user} u
JOIN ({$enrolledsql}) je ON je.id = u.id
LEFT JOIN {user_lastaccess} ula ON ula.userid = je.id AND ula.courseid = :courseid
WHERE COALESCE(ula.timeaccess, 0) = :timeaccess";
$lastaccess0exists = $DB->record_exists_sql($sql, array_merge($params, $enrolledparams));
} else {
// Front page.
$params = ['lastaccess' => 0];
$select = 'lastaccess != :lastaccess';
$minlastaccess = $DB->get_field_select('user', 'MIN(lastaccess)', $select, $params);
$lastaccess0exists = $DB->record_exists('user', $params);
}
$now = usergetmidnight(time());
$getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array {
$values = [];
for ($i = 1; $i <= $count; $i++) {
$timestamp = strtotime("-{$i} {$type}", $now);
if ($timestamp < $minlastaccess) {
break;
}
if ($i === 1) {
$title = get_string("num{$singletype}", 'moodle', $i);
} else {
$title = get_string("num{$type}", 'moodle', $i);
}
$values[] = [
'value' => $timestamp,
'title' => $title,
];
}
return $values;
};
$values = array_merge(
$getoptions(6, 'day', 'days'),
$getoptions(10, 'week', 'weeks'),
$getoptions(11, 'month', 'months'),
$getoptions(1, 'year', 'years')
);
if ($lastaccess0exists) {
$values[] = [
'value' => -1,
'title' => get_string('never', 'moodle'),
];
}
if (count($values) <= 1) {
// Nothing to show.
return null;
}
return $this->get_filter_object(
'accesssince',
get_string('usersnoaccesssince'),
false,
false,
null,
$values
);
}
/**
* Get data for the country filter
*
* @return stdClass|null
*/
protected function get_country_filter(): ?stdClass {
$extrauserfields = fields::get_identity_fields($this->context, false);
if (array_search('country', $extrauserfields) === false) {
return null;
}
$countries = get_string_manager()->get_list_of_countries(true);
return $this->get_filter_object(
'country',
get_string('country'),
false,
true,
'core/datafilter/filtertypes/country',
array_map(function(string $code, string $name): stdClass {
return (object) [
'value' => $code,
'title' => $name,
];
}, array_keys($countries), array_values($countries))
);
}
/**
* Get data for the keywords filter.
*
* @return stdClass|null
*/
protected function get_keyword_filter(): ?stdClass {
return $this->get_filter_object(
'keywords',
get_string('filterbykeyword', 'core_user'),
true,
true,
'core/datafilter/filtertypes/keyword',
[],
true
);
}
/**
* Export the renderer data in a mustache template friendly format.
*
* @param renderer_base $output Unused.
* @return stdClass Data in a format compatible with a mustache template.
*/
public function export_for_template(renderer_base $output): stdClass {
return (object) [
'tableregionid' => $this->tableregionid,
'courseid' => $this->context->instanceid,
'filtertypes' => $this->get_filtertypes(),
'rownumber' => 1,
];
return $data;
}
}
+171
View File
@@ -0,0 +1,171 @@
<?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/>.
/**
* Class containing the data necessary for rendering the status field in the course participants page.
*
* @package core_user
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use renderer_base;
use stdClass;
use templatable;
use user_enrolment_action;
/**
* Class containing the data for the status field.
*
* @package core_user
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class status_field implements renderable, templatable {
/** Active user enrolment status constant. */
const STATUS_ACTIVE = 0;
/** Suspended user enrolment status constant. */
const STATUS_SUSPENDED = 1;
/** Not current user enrolment status constant. */
const STATUS_NOT_CURRENT = 2;
/** @var string $enrolinstancename The enrolment instance name. */
protected $enrolinstancename;
/** @var string $coursename The course's full name. */
protected $coursename;
/** @var string $fullname The user's full name. */
protected $fullname;
/** @var string $status The user enrolment status. */
protected $status;
/** @var int $timestart The timestamp when the user's enrolment starts. */
protected $timestart;
/** @var int $timeend The timestamp when the user's enrolment ends. */
protected $timeend;
/** @var int $timeenrolled The timestamp when the user was enrolled. */
protected $timeenrolled;
/** @var user_enrolment_action[] $enrolactions Array of enrol action objects for the given enrolment method. */
protected $enrolactions;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as active. */
protected $statusactive = false;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as suspended. */
protected $statussuspended = false;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as not current. */
protected $statusnotcurrent = false;
/**
* status_field constructor.
*
* @param string $enrolinstancename The enrolment instance name.
* @param string $coursename The course's full name.
* @param string $fullname The user's full name.
* @param string $status The user enrolment status.
* @param int|null $timestart The timestamp when the user's enrolment starts.
* @param int|null $timeend The timestamp when the user's enrolment ends.
* @param user_enrolment_action[] $enrolactions Array of enrol action objects for the given enrolment method.
* @param int|null $timeenrolled The timestamp when the user was enrolled.
*/
public function __construct($enrolinstancename, $coursename, $fullname, $status, $timestart = null, $timeend = null,
$enrolactions = [], $timeenrolled = null) {
$this->enrolinstancename = $enrolinstancename;
$this->coursename = $coursename;
$this->fullname = $fullname;
$this->status = $status;
$this->timestart = $timestart;
$this->timeend = $timeend;
$this->enrolactions = $enrolactions;
$this->timeenrolled = $timeenrolled;
}
/**
* Function to export the renderer data in a format that is suitable for a
* mustache template. This means:
* 1. No complex types - only stdClass, array, int, string, float, bool
* 2. Any additional info that is required for the template is pre-calculated (e.g. capability checks).
*
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
* @return stdClass|array
*/
public function export_for_template(renderer_base $output) {
$data = new stdClass();
$data->enrolinstancename = $this->enrolinstancename;
$data->coursename = $this->coursename;
$data->fullname = $this->fullname;
$data->status = $this->status;
$data->active = $this->statusactive;
$data->suspended = $this->statussuspended;
$data->notcurrent = $this->statusnotcurrent;
if ($this->timestart) {
$data->timestart = userdate($this->timestart);
}
if ($this->timeend) {
$data->timeend = userdate($this->timeend);
}
if ($this->timeenrolled) {
$data->timeenrolled = userdate($this->timeenrolled);
}
$data->enrolactions = [];
foreach ($this->enrolactions as $enrolaction) {
$action = new stdClass();
$action->url = $enrolaction->get_url()->out(false);
$action->icon = $output->render($enrolaction->get_icon());
$action->attributes = [];
foreach ($enrolaction->get_attributes() as $name => $value) {
$attribute = (object) [
'name' => $name,
'value' => $value
];
$action->attributes[] = $attribute;
}
$data->enrolactions[] = $action;
}
return $data;
}
/**
* Status setter.
*
* @param int $status The user enrolment status representing one of this class' STATUS_* constants.
* @return status_field This class' instance. Useful for chaining.
*/
public function set_status($status = self::STATUS_ACTIVE) {
$this->statusactive = $status == static::STATUS_ACTIVE;
$this->statussuspended = $status == static::STATUS_SUSPENDED;
$this->statusnotcurrent = $status == static::STATUS_NOT_CURRENT;
return $this;
}
}
+236
View File
@@ -0,0 +1,236 @@
<?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 core_user\output;
use context_course;
use core_user;
use core_external\external_api;
use coding_exception;
/**
* Class to display list of user roles.
*
* @package core_user
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_roles_editable extends \core\output\inplace_editable {
/** @var $context */
private $context = null;
/** @var \stdClass[] $courseroles */
private $courseroles;
/** @var \stdClass[] $profileroles */
private $profileroles;
/** @var \stdClass[] $viewableroles */
private $viewableroles;
/** @var \stdClass[] $assignableroles */
private $assignableroles;
/**
* Constructor.
*
* @param \stdClass $course The current course
* @param \context $context The course context
* @param \stdClass $user The current user
* @param \stdClass[] $courseroles The list of course roles.
* @param \stdClass[] $assignableroles The list of assignable roles in this course.
* @param \stdClass[] $profileroles The list of roles that should be visible in a users profile.
* @param \stdClass[] $userroles The list of user roles.
*/
public function __construct($course, $context, $user, $courseroles, $assignableroles, $profileroles, $userroles, $viewableroles = null) {
if ($viewableroles === null) {
debugging('Constructor for user_roles_editable now needs the result of get_viewable_roles passed as viewableroles');
}
// Check capabilities to get editable value.
$editable = has_capability('moodle/role:assign', $context);
// Invent an itemid.
$itemid = $course->id . ':' . $user->id;
$getrole = function($role) {
return $role->roleid;
};
$ids = array_values(array_unique(array_map($getrole, $userroles)));
$value = json_encode($ids);
// Remember these for the display value.
$this->courseroles = $courseroles;
$this->profileroles = $profileroles;
$this->viewableroles = array_keys($viewableroles);
$this->assignableroles = array_keys($assignableroles);
$this->context = $context;
parent::__construct('core_user', 'user_roles', $itemid, $editable, $value, $value);
// Removed the roles that were assigned to the user at a different context.
$options = $assignableroles;
foreach ($userroles as $role) {
if (isset($assignableroles[$role->roleid])) {
if ($role->contextid != $context->id) {
unset($options[$role->roleid]);
}
}
}
$fullname = htmlspecialchars(fullname($user), ENT_QUOTES, 'utf-8');
$this->edithint = get_string('xroleassignments', 'role', $fullname);
$this->editlabel = get_string('xroleassignments', 'role', $fullname);
$attributes = ['multiple' => true];
$this->set_type_autocomplete($options, $attributes);
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param \renderer_base $output
* @return array
*/
public function export_for_template(\renderer_base $output) {
$listofroles = [];
$roleids = json_decode($this->value);
$viewableroleids = array_intersect($roleids, array_merge($this->viewableroles, $this->assignableroles));
foreach ($viewableroleids as $id) {
// If this is a student, we only show a subset of the roles.
if ($this->editable || array_key_exists($id, $this->profileroles)) {
$listofroles[] = format_string($this->courseroles[$id]->localname, true, ['context' => $this->context]);
}
}
if (!empty($listofroles)) {
$this->displayvalue = implode(', ', $listofroles);
} else if (!empty($roleids) && empty($viewableroleids)) {
$this->displayvalue = get_string('novisibleroles', 'role');
} else {
$this->displayvalue = get_string('noroles', 'role');
}
return parent::export_for_template($output);
}
/**
* Updates the value in database and returns itself, called from inplace_editable callback
*
* @param int $itemid
* @param mixed $newvalue
* @return \self
*/
public static function update($itemid, $newvalue) {
global $DB;
// Check caps.
// Do the thing.
// Return one of me.
// Validate the inputs.
list($courseid, $userid) = explode(':', $itemid, 2);
$courseid = clean_param($courseid, PARAM_INT);
$userid = clean_param($userid, PARAM_INT);
$roleids = json_decode($newvalue);
foreach ($roleids as $index => $roleid) {
$roleids[$index] = clean_param($roleid, PARAM_INT);
}
// Check user is enrolled in the course.
$context = context_course::instance($courseid);
external_api::validate_context($context);
// Check permissions.
require_capability('moodle/role:assign', $context);
if (!is_enrolled($context, $userid)) {
throw new coding_exception('User does not belong to the course');
}
// Check that all the groups belong to the course.
$allroles = role_fix_names(get_all_roles($context), $context, ROLENAME_BOTH);
$assignableroles = get_assignable_roles($context, ROLENAME_BOTH, false);
$viewableroles = get_viewable_roles($context);
$userrolesbyid = get_user_roles($context, $userid, true, 'c.contextlevel DESC, r.sortorder ASC');
$profileroles = get_profile_roles($context);
// Set an array where the index is the roleid.
$userroles = array();
foreach ($userrolesbyid as $id => $role) {
$userroles[$role->roleid] = $role;
}
$rolestoprocess = [];
foreach ($roleids as $roleid) {
if (!isset($assignableroles[$roleid])) {
throw new coding_exception('Role cannot be assigned in this course.');
}
$rolestoprocess[$roleid] = $roleid;
}
// Process adds.
foreach ($rolestoprocess as $roleid) {
if (!isset($userroles[$roleid])) {
// Add them.
$id = role_assign($roleid, $userid, $context);
// Keep this variable in sync.
$role = new \stdClass();
$role->id = $id;
$role->roleid = $roleid;
$role->contextid = $context->id;
$userroles[$role->roleid] = $role;
}
}
// Process removals.
foreach ($assignableroles as $roleid => $rolename) {
if (isset($userroles[$roleid]) && !isset($rolestoprocess[$roleid])) {
// Do not remove the role if we are not in the same context.
if ($userroles[$roleid]->contextid != $context->id) {
continue;
}
$ras = $DB->get_records('role_assignments', ['contextid' => $context->id, 'userid' => $userid,
'roleid' => $roleid]);
$allremoved = true;
foreach ($ras as $ra) {
if ($ra->component) {
if (strpos($ra->component, 'enrol_') !== 0) {
continue;
}
if (!$plugin = enrol_get_plugin(substr($ra->component, 6))) {
continue;
}
if ($plugin->roles_protected()) {
$allremoved = false;
continue;
}
}
role_unassign($ra->roleid, $ra->userid, $ra->contextid, $ra->component, $ra->itemid);
}
if ($allremoved) {
unset($userroles[$roleid]);
}
}
}
$course = get_course($courseid);
$user = core_user::get_user($userid);
return new self($course, $context, $user, $allroles, $assignableroles, $profileroles, $userroles, $viewableroles);
}
}