mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 21:49:15 +01:00
Merge branch 'MDL-73645-master-1' of git://github.com/mihailges/moodle
This commit is contained in:
commit
c08caf57c5
@ -69,7 +69,9 @@ $PAGE->set_context($context);
|
||||
|
||||
// Set up navigation and breadcrumbs.
|
||||
$strrecipients = get_string('recipients', 'badges');
|
||||
$PAGE->navbar->add($badge->name, new moodle_url('overview.php', array('id' => $badge->id)))->add($strrecipients);
|
||||
$PAGE->navbar->add($badge->name, new moodle_url('overview.php', array('id' => $badge->id)))
|
||||
->add($strrecipients, new moodle_url('recipients.php', array('id' => $badge->id)))
|
||||
->add(get_string('award', 'badges'));
|
||||
$PAGE->set_title($strrecipients);
|
||||
$PAGE->set_heading($heading);
|
||||
|
||||
|
@ -73,7 +73,9 @@ $PAGE->set_context($context);
|
||||
$PAGE->set_url('/badges/criteria_settings.php', $urlparams);
|
||||
$PAGE->set_heading($heading);
|
||||
$PAGE->set_title($badge->name);
|
||||
$PAGE->navbar->add($badge->name, new moodle_url('overview.php', array('id' => $badge->id)))->add(get_string('criteria_' . $type, 'badges'));
|
||||
$PAGE->navbar->add($badge->name, new moodle_url('overview.php', array('id' => $badge->id)))
|
||||
->add(get_string('bcriteria', 'badges'), new moodle_url('criteria.php', ['id' => $badge->id]))
|
||||
->add(get_string('criteria_' . $type, 'badges'));
|
||||
|
||||
$cparams = array('criteriatype' => $type, 'badgeid' => $badge->id);
|
||||
if ($edit) {
|
||||
|
@ -88,6 +88,9 @@ if ($PAGE->course) {
|
||||
|
||||
$PAGE->set_url(new \moodle_url('/contentbank/edit.php', $values));
|
||||
$PAGE->set_context($context);
|
||||
if ($content) {
|
||||
$PAGE->navbar->add($content->get_name(), new \moodle_url('/contentbank/view.php', ['id' => $id]));
|
||||
}
|
||||
$PAGE->navbar->add(get_string('edit'));
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
|
@ -46,7 +46,6 @@ $strreset = get_string('reset');
|
||||
$strresetcourse = get_string('resetcourse');
|
||||
$strremove = get_string('remove');
|
||||
|
||||
$PAGE->navbar->add($strresetcourse);
|
||||
$PAGE->set_title($course->fullname.': '.$strresetcourse);
|
||||
$PAGE->set_heading($course->fullname.': '.$strresetcourse);
|
||||
|
||||
|
@ -40,7 +40,8 @@ if (!$plugin) {
|
||||
require_login($course);
|
||||
require_capability('enrol/' . $type . ':config', $context);
|
||||
|
||||
$PAGE->set_url('/enrol/editinstance.php', array('courseid' => $course->id, 'id' => $instanceid, 'type' => $type));
|
||||
$url = new moodle_url('/enrol/editinstance.php', ['courseid' => $course->id, 'id' => $instanceid, 'type' => $type]);
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_pagelayout('admin');
|
||||
$PAGE->set_docs_path('enrol/' . $type . '/edit');
|
||||
|
||||
@ -54,6 +55,11 @@ if (!enrol_is_enabled($type)) {
|
||||
|
||||
if ($instanceid) {
|
||||
$instance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => $type, 'id' => $instanceid), '*', MUST_EXIST);
|
||||
if ($instance->status == ENROL_INSTANCE_DISABLED) { // The instance is currently disabled.
|
||||
navigation_node::override_active_url(new moodle_url('/enrol/instances.php', ['id' => $course->id]));
|
||||
$name = $instance->name ?: get_string('pluginname', 'enrol_' . $type);
|
||||
$PAGE->navbar->add($name, $url);
|
||||
}
|
||||
|
||||
} else {
|
||||
require_capability('moodle/course:enrolconfig', $context);
|
||||
@ -64,6 +70,7 @@ if ($instanceid) {
|
||||
$instance->id = null;
|
||||
$instance->courseid = $course->id;
|
||||
$instance->status = ENROL_INSTANCE_ENABLED; // Do not use default for automatically created instances here.
|
||||
$PAGE->navbar->add(get_string('pluginname', 'enrol_' . $type), $url);
|
||||
}
|
||||
|
||||
$mform = new enrol_instance_edit_form(null, array($instance, $plugin, $context, $type, $return));
|
||||
|
@ -30,7 +30,7 @@ $contextid = optional_param('id', SYSCONTEXTID, PARAM_INT);
|
||||
$action = optional_param('action', '', PARAM_ALPHA);
|
||||
$edit = optional_param('edit', false, PARAM_BOOL); //are we editing?
|
||||
|
||||
$PAGE->set_url('/grade/edit/letter/index.php', array('id' => $contextid));
|
||||
$url = new moodle_url('/grade/edit/letter/index.php', array('id' => $contextid));
|
||||
|
||||
list($context, $course, $cm) = get_context_info_array($contextid);
|
||||
$contextid = null;//now we have a context object throw away the $contextid from the params
|
||||
@ -42,7 +42,11 @@ if (!$edit) {
|
||||
}
|
||||
} else {//else we're editing
|
||||
require_capability('moodle/grade:manageletters', $context);
|
||||
navigation_node::override_active_url($url);
|
||||
$url->param('edit', 1);
|
||||
$PAGE->navbar->add(get_string('editgradeletters', 'grades'), $url);
|
||||
}
|
||||
$PAGE->set_url($url);
|
||||
|
||||
$returnurl = null;
|
||||
$editparam = null;
|
||||
|
@ -79,7 +79,6 @@ if ($id) {
|
||||
require_login($course);
|
||||
$context = context_course::instance($course->id);
|
||||
require_capability('moodle/grade:manage', $context);
|
||||
navigation_node::override_active_url(new moodle_url('/grade/edit/outcome/course.php', array('id'=>$courseid)));
|
||||
|
||||
$outcome_rec = new stdClass();
|
||||
$outcome_rec->standard = 0;
|
||||
@ -98,6 +97,10 @@ if ($id) {
|
||||
if (!$courseid) {
|
||||
require_once $CFG->libdir.'/adminlib.php';
|
||||
admin_externalpage_setup('outcomes');
|
||||
} else {
|
||||
navigation_node::override_active_url(new moodle_url('/grade/edit/outcome/course.php', ['id' => $courseid]));
|
||||
$PAGE->navbar->add(get_string('manageoutcomes', 'grades'),
|
||||
new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid]));
|
||||
}
|
||||
|
||||
// default return url
|
||||
@ -158,6 +161,8 @@ if ($mform->is_cancelled()) {
|
||||
redirect($returnurl);
|
||||
}
|
||||
|
||||
$PAGE->navbar->add($heading, $url);
|
||||
|
||||
print_grade_page_head($courseid ?: SITEID, 'outcome', 'edit', $heading, false, false, false);
|
||||
|
||||
if (!grade_scale::fetch_all_local($courseid) && !grade_scale::fetch_all_global()) {
|
||||
|
@ -32,7 +32,8 @@ $courseid = optional_param('courseid', 0, PARAM_INT);
|
||||
$action = optional_param('action', '', PARAM_ALPHA);
|
||||
$scope = optional_param('scope', 'custom', PARAM_ALPHA);
|
||||
|
||||
$PAGE->set_url('/grade/edit/outcome/import.php', array('courseid' => $courseid));
|
||||
$url = new moodle_url('/grade/edit/outcome/import.php', array('courseid' => $courseid));
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_pagelayout('admin');
|
||||
|
||||
/// Make sure they can even access this course
|
||||
@ -46,6 +47,11 @@ if ($courseid) {
|
||||
if (empty($CFG->enableoutcomes)) {
|
||||
redirect('../../index.php?id='.$courseid);
|
||||
}
|
||||
navigation_node::override_active_url(new moodle_url('/grade/edit/outcome/course.php', ['id' => $courseid]));
|
||||
$PAGE->navbar->add(get_string('manageoutcomes', 'grades'),
|
||||
new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid]));
|
||||
$PAGE->navbar->add(get_string('importoutcomes', 'grades'),
|
||||
new moodle_url('/grade/edit/outcome/import.php', ['courseid' => $courseid]));
|
||||
|
||||
} else {
|
||||
require_once $CFG->libdir.'/adminlib.php';
|
||||
@ -55,8 +61,6 @@ if ($courseid) {
|
||||
|
||||
require_capability('moodle/grade:manageoutcomes', $context);
|
||||
|
||||
$navigation = grade_build_nav(__FILE__, get_string('outcomes', 'grades'), $courseid);
|
||||
|
||||
$upload_form = new import_outcomes_form();
|
||||
|
||||
if ($upload_form->is_cancelled()) {
|
||||
|
@ -29,7 +29,8 @@ require_once($CFG->libdir.'/gradelib.php');
|
||||
$courseid = optional_param('id', 0, PARAM_INT);
|
||||
$action = optional_param('action', '', PARAM_ALPHA);
|
||||
|
||||
$PAGE->set_url('/grade/edit/outcome/index.php', array('id' => $courseid));
|
||||
$url = new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid]);
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_pagelayout('admin');
|
||||
|
||||
/// Make sure they can even access this course
|
||||
@ -44,6 +45,7 @@ if ($courseid) {
|
||||
}
|
||||
// This page doesn't exist on the navigation so map it to another
|
||||
navigation_node::override_active_url(new moodle_url('/grade/edit/outcome/course.php', array('id'=>$courseid)));
|
||||
$PAGE->navbar->add(get_string('manageoutcomes', 'grades'), $url);
|
||||
} else {
|
||||
if (empty($CFG->enableoutcomes)) {
|
||||
redirect('../../../');
|
||||
@ -90,6 +92,7 @@ switch ($action) {
|
||||
|
||||
if(!$deleteconfirmed){
|
||||
$PAGE->set_title(get_string('outcomedelete', 'grades'));
|
||||
$PAGE->navbar->add(get_string('outcomedelete', 'grades'));
|
||||
echo $OUTPUT->header();
|
||||
$confirmurl = new moodle_url('index.php', array(
|
||||
'id' => $courseid, 'outcomeid' => $outcome->id,
|
||||
|
@ -144,6 +144,7 @@ if ($mform->is_cancelled()) {
|
||||
}
|
||||
|
||||
$heading = $id ? get_string('editscale', 'grades') : get_string('addscale', 'grades');
|
||||
$PAGE->navbar->add($heading);
|
||||
print_grade_page_head($COURSE->id, 'scale', null, $heading, false, false, false);
|
||||
|
||||
$mform->display();
|
||||
|
@ -81,7 +81,11 @@ switch ($action) {
|
||||
$deleteconfirmed = optional_param('deleteconfirmed', 0, PARAM_BOOL);
|
||||
|
||||
if (!$deleteconfirmed) {
|
||||
$strdeletescale = get_string('delete'). ' '. get_string('scale');
|
||||
if ($courseid) {
|
||||
$PAGE->navbar->add(get_string('scales'), new moodle_url('/grade/edit/scale/index.php',
|
||||
['id' => $courseid]));
|
||||
}
|
||||
$strdeletescale = get_string('deletescale', 'grades');
|
||||
$PAGE->navbar->add($strdeletescale);
|
||||
$PAGE->set_title($strdeletescale);
|
||||
$PAGE->set_heading($COURSE->fullname);
|
||||
|
@ -76,11 +76,18 @@ if (!empty($key->userid) and $USER->id != $key->userid) {
|
||||
|
||||
$returnurl = $CFG->wwwroot.'/grade/export/keymanager.php?id='.$course->id;
|
||||
|
||||
$strkeys = get_string('keymanager', 'userkey');
|
||||
$strexportgrades = get_string('export', 'grades');
|
||||
$PAGE->navbar->add($strexportgrades, new moodle_url('/grade/export/index.php', ['id' => $courseid]));
|
||||
$PAGE->navbar->add($strkeys, new moodle_url('/grade/export/keymanager.php', ['id' => $courseid]));
|
||||
|
||||
if ($id and $delete) {
|
||||
if (!$confirm) {
|
||||
$PAGE->set_title(get_string('deleteselectedkey'));
|
||||
$PAGE->set_heading($course->fullname);
|
||||
$PAGE->set_secondary_active_tab('grades');
|
||||
$PAGE->navbar->add(get_string('deleteuserkey', 'userkey'));
|
||||
|
||||
echo $OUTPUT->header();
|
||||
$optionsyes = array('id'=>$id, 'delete'=>1, 'courseid'=>$courseid, 'sesskey'=>sesskey(), 'confirm'=>1);
|
||||
$optionsno = array('id'=>$courseid);
|
||||
@ -118,17 +125,12 @@ if ($editform->is_cancelled()) {
|
||||
redirect($returnurl);
|
||||
}
|
||||
|
||||
$strkeys = get_string('userkeys', 'userkey');
|
||||
$strgrades = get_string('grades');
|
||||
|
||||
if ($id) {
|
||||
$strheading = get_string('edituserkey', 'userkey');
|
||||
} else {
|
||||
$strheading = get_string('createuserkey', 'userkey');
|
||||
}
|
||||
|
||||
$PAGE->navbar->add($strgrades, new moodle_url('/grade/index.php', array('id'=>$courseid)));
|
||||
$PAGE->navbar->add($strkeys, new moodle_url('/grade/export/keymanager.php', array('id'=>$courseid)));
|
||||
$PAGE->navbar->add($strheading);
|
||||
|
||||
/// Print header
|
||||
|
@ -33,7 +33,8 @@ $id = optional_param('id', 0, PARAM_INT);
|
||||
$delete = optional_param('delete', 0, PARAM_BOOL);
|
||||
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
||||
|
||||
$PAGE->set_url('/grade/import/key.php', array('courseid' => $courseid, 'id' => $id));
|
||||
$url = new moodle_url('/grade/import/key.php', ['courseid' => $courseid, 'id' => $id]);
|
||||
$PAGE->set_url($url);
|
||||
|
||||
if ($id) {
|
||||
if (!$key = $DB->get_record('user_private_key', array('id' => $id))) {
|
||||
@ -76,11 +77,18 @@ if (!empty($key->userid) and $USER->id != $key->userid) {
|
||||
|
||||
$returnurl = $CFG->wwwroot.'/grade/import/keymanager.php?id='.$course->id;
|
||||
|
||||
$strkeys = get_string('keymanager', 'userkey');
|
||||
$strimportgrades = get_string('import', 'grades');
|
||||
$PAGE->navbar->add($strimportgrades, new moodle_url(new moodle_url('/grade/import/index.php', ['id' => $courseid])));
|
||||
$PAGE->navbar->add($strkeys, new moodle_url('/grade/import/keymanager.php', ['id' => $courseid]));
|
||||
|
||||
if ($id and $delete) {
|
||||
if (!$confirm) {
|
||||
$PAGE->set_title(get_string('deleteselectedkey'));
|
||||
$PAGE->set_heading($course->fullname);
|
||||
$PAGE->set_secondary_active_tab('grades');
|
||||
$PAGE->navbar->add(get_string('deleteuserkey', 'userkey'));
|
||||
|
||||
echo $OUTPUT->header();
|
||||
$optionsyes = array('id'=>$id, 'delete'=>1, 'courseid'=>$courseid, 'sesskey'=>sesskey(), 'confirm'=>1);
|
||||
$optionsno = array('id'=>$courseid);
|
||||
@ -118,17 +126,12 @@ if ($editform->is_cancelled()) {
|
||||
redirect($returnurl);
|
||||
}
|
||||
|
||||
$strkeys = get_string('userkeys', 'userkey');
|
||||
$strgrades = get_string('grades');
|
||||
|
||||
if ($id) {
|
||||
$strheading = get_string('edituserkey', 'userkey');
|
||||
} else {
|
||||
$strheading = get_string('createuserkey', 'userkey');
|
||||
}
|
||||
|
||||
$PAGE->navbar->add($strgrades, new moodle_url('/grade/index.php', array('id'=>$courseid)));
|
||||
$PAGE->navbar->add($strkeys, new moodle_url('/grade/import/keymanager.php', array('id'=>$courseid)));
|
||||
$PAGE->navbar->add($strheading);
|
||||
|
||||
/// Print header
|
||||
|
@ -132,7 +132,8 @@ navigation_node::override_active_url(new moodle_url('/group/index.php', array('i
|
||||
$PAGE->set_pagelayout('admin');
|
||||
|
||||
$PAGE->navbar->add($strparticipants, new moodle_url('/user/index.php', array('id'=>$courseid)));
|
||||
$PAGE->navbar->add($strgroups, new moodle_url('/group/index.php', array('id'=>$courseid)));
|
||||
$PAGE->navbar->add(get_string('groupings', 'group'),
|
||||
new moodle_url('/group/groupings.php', ['id' => $courseid]));
|
||||
$PAGE->navbar->add($straddgroupstogroupings);
|
||||
|
||||
/// Print header
|
||||
|
@ -148,6 +148,7 @@ $string['decimalpoints_help'] = 'This setting determines the number of decimal p
|
||||
$string['default'] = 'Default';
|
||||
$string['defaultprev'] = 'Default ({$a})';
|
||||
$string['deletecategory'] = 'Delete category';
|
||||
$string['deletescale'] = 'Delete scale';
|
||||
$string['disablegradehistory'] = 'Disable grade history';
|
||||
$string['disablegradehistory_help'] = 'Disable history tracking of changes in grades related tables. This may speed up the server a little and conserve space in database.';
|
||||
$string['displaylettergrade'] = 'Display letter grades';
|
||||
|
@ -26,6 +26,7 @@ $string['adduserkey'] = 'Add user key';
|
||||
$string['createnewkey'] = 'Create a new user key';
|
||||
$string['createuserkey'] = 'Create user key';
|
||||
$string['deletekeyconfirm'] = 'Do you really want to delete this user key?';
|
||||
$string['deleteuserkey'] = 'Delete user key';
|
||||
$string['edituserkey'] = 'Edit user key';
|
||||
$string['keyiprestriction'] = 'Key IP restriction';
|
||||
$string['keyiprestriction_help'] = 'Enter a specific IP address, or a range of IP addresses that will be the only IP addresses allowed to access this data. Leave empty to disable IP restriction (not recommended).';
|
||||
|
@ -16,6 +16,12 @@
|
||||
|
||||
namespace theme_boost;
|
||||
|
||||
use core\navigation\views\view;
|
||||
use navigation_node;
|
||||
use moodle_url;
|
||||
use action_link;
|
||||
use lang_string;
|
||||
|
||||
/**
|
||||
* Creates a navbar for boost that allows easy control of the navbar items.
|
||||
*
|
||||
@ -49,10 +55,36 @@ class boostnavbar implements \renderable {
|
||||
protected function prepare_nodes_for_boost(): void {
|
||||
global $PAGE;
|
||||
|
||||
// Don't show the navigation if we are in the course context.
|
||||
// Defines whether section items with an action should be removed by default.
|
||||
$removesections = true;
|
||||
|
||||
if ($this->page->context->contextlevel == CONTEXT_COURSE) {
|
||||
$this->clear_items();
|
||||
return;
|
||||
// Remove any duplicate navbar nodes.
|
||||
$this->remove_duplicate_items();
|
||||
// Remove 'My courses' and 'Courses' if we are in the course context.
|
||||
$this->remove('mycourses');
|
||||
$this->remove('courses');
|
||||
// Remove the course category breadcrumb node.
|
||||
$this->remove($this->page->course->category);
|
||||
// Remove the course breadcrumb node.
|
||||
$this->remove($this->page->course->id);
|
||||
// Remove the navbar nodes that already exist in the secondary navigation menu.
|
||||
$this->remove_items_that_exist_in_navigation($PAGE->secondarynav);
|
||||
|
||||
switch ($this->page->pagetype) {
|
||||
case 'group-groupings':
|
||||
case 'group-grouping':
|
||||
case 'group-overview':
|
||||
case 'group-assign':
|
||||
// Remove the 'Groups' navbar node in the Groupings, Grouping, group Overview and Assign pages.
|
||||
$this->remove('groups');
|
||||
case 'backup-backup':
|
||||
case 'backup-restorefile':
|
||||
case 'backup-copy':
|
||||
case 'course-reset':
|
||||
// Remove the 'Import' navbar node in the Backup, Restore, Copy course and Reset pages.
|
||||
$this->remove('import');
|
||||
}
|
||||
}
|
||||
|
||||
$this->remove('myhome'); // Dashboard.
|
||||
@ -61,18 +93,21 @@ class boostnavbar implements \renderable {
|
||||
// Remove 'My courses' if we are in the module context.
|
||||
if ($this->page->context->contextlevel == CONTEXT_MODULE) {
|
||||
$this->remove('mycourses');
|
||||
$this->remove('courses');
|
||||
// Remove the course category breadcrumb node.
|
||||
$this->remove($this->page->course->category);
|
||||
$courseformat = course_get_format($this->page->course)->get_course();
|
||||
// Section items can be only removed if a course layout (coursedisplay) is not explicitly set in the
|
||||
// given course format or the set course layout is not 'One section per page'.
|
||||
$removesections = !isset($courseformat->coursedisplay) ||
|
||||
$courseformat->coursedisplay != COURSE_DISPLAY_MULTIPAGE;
|
||||
}
|
||||
|
||||
if (!is_null($this->get_item('root'))) { // We are in site administration.
|
||||
// Remove the 'Site administration' navbar node as it already exists in the primary navigation menu.
|
||||
$this->remove('root');
|
||||
// Loop through the remaining navbar nodes and remove the ones that already exist in the secondary
|
||||
// navigation menu.
|
||||
foreach ($this->items as $item) {
|
||||
if ($PAGE->secondarynav->get($item->key)) {
|
||||
$this->remove($item->key);
|
||||
}
|
||||
}
|
||||
// Remove the navbar nodes that already exist in the secondary navigation menu.
|
||||
$this->remove_items_that_exist_in_navigation($PAGE->secondarynav);
|
||||
}
|
||||
|
||||
// Set the designated one path for courses.
|
||||
@ -83,7 +118,7 @@ class boostnavbar implements \renderable {
|
||||
$mycoursesnode->text = get_string('mycourses');
|
||||
}
|
||||
|
||||
$this->remove_no_link_items();
|
||||
$this->remove_no_link_items($removesections);
|
||||
|
||||
// Don't display the navbar if there is only one item. Apparently this is bad UX design.
|
||||
if ($this->item_count() <= 1) {
|
||||
@ -188,16 +223,94 @@ class boostnavbar implements \renderable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove items that are categories or have no actions associated with them.
|
||||
* Remove items that have no actions associated with them and optionally remove items that are sections.
|
||||
*
|
||||
* The only exception is the last item in the list which may not have a link but needs to be displayed.
|
||||
*
|
||||
* @param bool $removesections Whether section items should be also removed (only applies when they have an action)
|
||||
*/
|
||||
protected function remove_no_link_items(): void {
|
||||
protected function remove_no_link_items(bool $removesections = true): void {
|
||||
foreach ($this->items as $key => $value) {
|
||||
if (!$value->is_last() && (!$value->has_action() || $value->type == \navigation_node::TYPE_SECTION)) {
|
||||
if (!$value->is_last() &&
|
||||
(!$value->has_action() || ($value->type == \navigation_node::TYPE_SECTION && $removesections))) {
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
}
|
||||
$this->items = array_values($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove breadcrumb items that already exist in a given navigation view.
|
||||
*
|
||||
* This method removes the breadcrumb items that have a text => action match in a given navigation view
|
||||
* (primary or secondary).
|
||||
*
|
||||
* @param view $navigationview The navigation view object.
|
||||
*/
|
||||
protected function remove_items_that_exist_in_navigation(view $navigationview): void {
|
||||
// Loop through the navigation view items and create a 'text' => 'action' array which will be later used
|
||||
// to compare whether any of the breadcrumb items matches these pairs.
|
||||
$navigationviewitems = [];
|
||||
foreach ($navigationview->children as $child) {
|
||||
list($childtext, $childaction) = $this->get_node_text_and_action($child);
|
||||
if ($childaction) {
|
||||
$navigationviewitems[$childtext] = $childaction;
|
||||
}
|
||||
}
|
||||
// Loop through the breadcrumb items and if the item's 'text' and 'action' values matches with any of the
|
||||
// existing navigation view items, remove it from the breadcrumbs.
|
||||
foreach ($this->items as $item) {
|
||||
list($itemtext, $itemaction) = $this->get_node_text_and_action($item);
|
||||
if ($itemaction) {
|
||||
if (array_key_exists($itemtext, $navigationviewitems) &&
|
||||
$navigationviewitems[$itemtext] === $itemaction) {
|
||||
$this->remove($item->key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove duplicate breadcrumb items.
|
||||
*
|
||||
* This method looks for breadcrumb items that have identical text and action values and removes the first item.
|
||||
*/
|
||||
protected function remove_duplicate_items(): void {
|
||||
$taken = [];
|
||||
// Reverse the order of the items before filtering so that the first occurrence is removed instead of the last.
|
||||
$filtereditems = array_values(array_filter(array_reverse($this->items), function($item) use (&$taken) {
|
||||
list($itemtext, $itemaction) = $this->get_node_text_and_action($item);
|
||||
if ($itemaction) {
|
||||
if (array_key_exists($itemtext, $taken) && $taken[$itemtext] === $itemaction) {
|
||||
return false;
|
||||
}
|
||||
$taken[$itemtext] = $itemaction;
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
// Reverse back the order.
|
||||
$this->items = array_reverse($filtereditems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that returns an array of the text and the outputted action url (if exists) for a given
|
||||
* navigation node.
|
||||
*
|
||||
* @param navigation_node $node The navigation node object.
|
||||
* @return array
|
||||
*/
|
||||
protected function get_node_text_and_action(navigation_node $node): array {
|
||||
$text = $node->text instanceof lang_string ? $node->text->out() : $node->text;
|
||||
$action = null;
|
||||
if ($node->has_action()) {
|
||||
if ($node->action instanceof moodle_url) {
|
||||
$action = $node->action->out();
|
||||
} else if ($node->action instanceof action_link) {
|
||||
$action = $node->action->url->out();
|
||||
} else {
|
||||
$action = $node->action;
|
||||
}
|
||||
}
|
||||
return [$text, $action];
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ namespace theme_boost;
|
||||
* Test the boostnavbar file
|
||||
*
|
||||
* @package theme_boost
|
||||
* @covers \theme_boost\boostnavbar
|
||||
* @copyright 2021 Peter Dias
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@ -32,12 +33,13 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
*/
|
||||
public function remove_no_link_items_provider(): array {
|
||||
return [
|
||||
'All nodes have links links including leaf node' => [
|
||||
'All nodes have links links including leaf node. Set to remove section nodes.' => [
|
||||
[
|
||||
'node1' => true,
|
||||
'node2' => true,
|
||||
'node3' => true,
|
||||
'node1' => ['hasaction' => true, 'issection' => false],
|
||||
'node2' => ['hasaction' => true, 'issection' => false],
|
||||
'node3' => ['hasaction' => true, 'issection' => false],
|
||||
],
|
||||
true,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
@ -47,12 +49,13 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
'node3' => true,
|
||||
]
|
||||
],
|
||||
'Only some parent nodes have links. Leaf node has a link.' => [
|
||||
'Only some parent nodes have links. Leaf node has a link. Set to remove section nodes.' => [
|
||||
[
|
||||
'node1' => false,
|
||||
'node2' => true,
|
||||
'node3' => true,
|
||||
'node1' => ['hasaction' => false, 'issection' => false],
|
||||
'node2' => ['hasaction' => true, 'issection' => false],
|
||||
'node3' => ['hasaction' => true, 'issection' => false],
|
||||
],
|
||||
true,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
@ -61,12 +64,13 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
'node3' => true,
|
||||
]
|
||||
],
|
||||
'All parent nodes do not have links. Leaf node has a link.' => [
|
||||
'All parent nodes do not have links. Leaf node has a link. Set to remove section nodes.' => [
|
||||
[
|
||||
'node1' => false,
|
||||
'node2' => false,
|
||||
'node3' => true,
|
||||
'node1' => ['hasaction' => false, 'issection' => false],
|
||||
'node2' => ['hasaction' => false, 'issection' => false],
|
||||
'node3' => ['hasaction' => true, 'issection' => false],
|
||||
],
|
||||
true,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
@ -74,12 +78,13 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
'node3' => true,
|
||||
]
|
||||
],
|
||||
'All parent nodes have links. Leaf node does not has a link.' => [
|
||||
'All parent nodes have links. Leaf node does not has a link. Set to remove section nodes.' => [
|
||||
[
|
||||
'node1' => true,
|
||||
'node2' => true,
|
||||
'node3' => false,
|
||||
'node1' => ['hasaction' => true, 'issection' => false],
|
||||
'node2' => ['hasaction' => true, 'issection' => false],
|
||||
'node3' => ['hasaction' => false, 'issection' => false],
|
||||
],
|
||||
true,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
@ -89,12 +94,13 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
'node3' => false,
|
||||
]
|
||||
],
|
||||
'All parent nodes do not have links. Leaf node does not has a link.' => [
|
||||
'All parent nodes do not have links. Leaf node does not has a link. Set to remove section nodes.' => [
|
||||
[
|
||||
'node1' => false,
|
||||
'node2' => false,
|
||||
'node3' => false,
|
||||
'node1' => ['hasaction' => false, 'issection' => false],
|
||||
'node2' => ['hasaction' => false, 'issection' => false],
|
||||
'node3' => ['hasaction' => false, 'issection' => false],
|
||||
],
|
||||
true,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
@ -102,12 +108,13 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
'node3' => false,
|
||||
]
|
||||
],
|
||||
'Some parent nodes do not have links. Leaf node does not has a link.' => [
|
||||
'Some parent nodes do not have links. Leaf node does not has a link. Set to remove section nodes.' => [
|
||||
[
|
||||
'node1' => true,
|
||||
'node2' => false,
|
||||
'node3' => false,
|
||||
'node1' => ['hasaction' => true, 'issection' => false],
|
||||
'node2' => ['hasaction' => false, 'issection' => false],
|
||||
'node3' => ['hasaction' => false, 'issection' => false],
|
||||
],
|
||||
true,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
@ -115,6 +122,57 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
'node1' => true,
|
||||
'node3' => false,
|
||||
]
|
||||
],
|
||||
'All nodes have links links including leaf node and section nodes. Set to remove section nodes.' => [
|
||||
[
|
||||
'node1' => ['hasaction' => true, 'issection' => false],
|
||||
'node2' => ['hasaction' => true, 'issection' => false],
|
||||
'sectionnode1' => ['hasaction' => true, 'issection' => true],
|
||||
'node3' => ['hasaction' => true, 'issection' => false],
|
||||
],
|
||||
true,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
'tc_1' => true,
|
||||
'node1' => true,
|
||||
'node2' => true,
|
||||
'node3' => true,
|
||||
]
|
||||
],
|
||||
'All nodes have links links including leaf node and section nodes. Set to not remove section nodes.' => [
|
||||
[
|
||||
'node1' => ['hasaction' => true, 'issection' => false],
|
||||
'node2' => ['hasaction' => true, 'issection' => false],
|
||||
'sectionnode1' => ['hasaction' => true, 'issection' => true],
|
||||
'node3' => ['hasaction' => true, 'issection' => false],
|
||||
],
|
||||
false,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
'tc_1' => true,
|
||||
'node1' => true,
|
||||
'node2' => true,
|
||||
'sectionnode1' => true,
|
||||
'node3' => true,
|
||||
]
|
||||
],
|
||||
'Only some parent nodes have links. Section node does not have a link. Set to not remove section nodes.' => [
|
||||
[
|
||||
'node1' => ['hasaction' => false, 'issection' => false],
|
||||
'node2' => ['hasaction' => true, 'issection' => false],
|
||||
'sectionnode1' => ['hasaction' => false, 'issection' => true],
|
||||
'node3' => ['hasaction' => true, 'issection' => false],
|
||||
],
|
||||
true,
|
||||
[
|
||||
'Home' => true,
|
||||
'Courses' => true,
|
||||
'tc_1' => true,
|
||||
'node2' => true,
|
||||
'node3' => true,
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
@ -123,10 +181,11 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
*
|
||||
* @dataProvider remove_no_link_items_provider
|
||||
* @param array $setup
|
||||
* @param bool $removesectionnodes Whether to remove the section nodes with an associated action.
|
||||
* @param array $expected
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function test_remove_no_link_items(array $setup, array $expected) {
|
||||
public function test_remove_no_link_items(array $setup, bool $removesectionnodes, array $expected) {
|
||||
global $PAGE;
|
||||
|
||||
$this->resetAfterTest();
|
||||
@ -138,8 +197,9 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
$page->set_url(new \moodle_url('/course/view.php', array('id' => $course->id)));
|
||||
// A dummy url to use. We don't care where it's pointing to.
|
||||
$url = new \moodle_url('/');
|
||||
foreach ($setup as $node => $hasaction) {
|
||||
$page->navbar->add($node, $hasaction ? $url : null);
|
||||
foreach ($setup as $key => $value) {
|
||||
$page->navbar->add($key, $value['hasaction'] ? $url : null,
|
||||
$value['issection'] ? \navigation_node::TYPE_SECTION : null);
|
||||
}
|
||||
|
||||
$boostnavbar = $this->getMockBuilder(boostnavbar::class)
|
||||
@ -155,7 +215,7 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
// Make the call to the function.
|
||||
$rcm = $rc->getMethod('remove_no_link_items');
|
||||
$rcm->setAccessible(true);
|
||||
$rcm->invoke($boostnavbar);
|
||||
$rcm->invoke($boostnavbar, $removesectionnodes);
|
||||
|
||||
// Get the value for the class variable that the function modifies.
|
||||
$values = $rcp->getValue($boostnavbar);
|
||||
@ -165,4 +225,322 @@ class boostnavbar_test extends \advanced_testcase {
|
||||
}
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for test_remove_duplicate_items.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function remove_duplicate_items_provider(): array {
|
||||
global $CFG;
|
||||
|
||||
return [
|
||||
'Breadcrumb items with identical text and action url (actions of same type moodle_url).' => [
|
||||
[
|
||||
[
|
||||
'text' => 'Node 1',
|
||||
'action' => new \moodle_url('/page1.php')
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 4',
|
||||
'action' => new \moodle_url('/page4.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 1])
|
||||
],
|
||||
],
|
||||
['Home', 'Node 1', 'Node 4', 'Node 2']
|
||||
],
|
||||
'Breadcrumb items with identical text and action url (actions of different type moodle_url/text).' => [
|
||||
[
|
||||
[
|
||||
'text' => 'Node 1',
|
||||
'action' => new \moodle_url('/page1.php')
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 4',
|
||||
'action' => new \moodle_url('/page4.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => "{$CFG->wwwroot}/page2.php?id=1"
|
||||
],
|
||||
],
|
||||
['Home', 'Node 1', 'Node 4', 'Node 2']
|
||||
],
|
||||
'Breadcrumb items with identical text and action url (actions of different type moodle_url/action_link).' => [
|
||||
[
|
||||
[
|
||||
'text' => 'Node 1',
|
||||
'action' => new \moodle_url('/page1.php')
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 4',
|
||||
'action' => new \moodle_url('/page4.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \action_link(new \moodle_url('/page2.php', ['id' => 1]), 'Action link')
|
||||
],
|
||||
],
|
||||
['Home', 'Node 1', 'Node 4', 'Node 2']
|
||||
],
|
||||
'Breadcrumbs items with identical text but not identical action url.' => [
|
||||
[
|
||||
[
|
||||
'text' => 'Node 1',
|
||||
'action' => new \moodle_url('/page1.php')
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 2])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 4',
|
||||
'action' => new \moodle_url('/page4.php', ['id' => 1])
|
||||
],
|
||||
],
|
||||
['Home', 'Node 1', 'Node 2', 'Node 2', 'Node 4']
|
||||
],
|
||||
'Breadcrumb items with identical action url but not identical text.' => [
|
||||
[
|
||||
[
|
||||
'text' => 'Node 1',
|
||||
'action' => new \moodle_url('/page1.php')
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 3',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 4',
|
||||
'action' => new \moodle_url('/page4.php', ['id' => 1])
|
||||
],
|
||||
],
|
||||
['Home', 'Node 1', 'Node 2', 'Node 3', 'Node 4']
|
||||
],
|
||||
'Breadcrumb items without any identical action url or text.' => [
|
||||
[
|
||||
[
|
||||
'text' => 'Node 1',
|
||||
'action' => new \moodle_url('/page1.php')
|
||||
],
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 3',
|
||||
'action' => new \moodle_url('/page3.php', ['id' => 1])
|
||||
],
|
||||
[
|
||||
'text' => 'Node 4',
|
||||
'action' => new \moodle_url('/page4.php', ['id' => 1])
|
||||
],
|
||||
],
|
||||
['Home', 'Node 1', 'Node 2', 'Node 3', 'Node 4']
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the remove_duplicate_items function.
|
||||
*
|
||||
* @dataProvider remove_duplicate_items_provider
|
||||
* @param array $navbarnodes The array containing the text and action of the nodes to be added to the navbar
|
||||
* @param array $expected The array containing the text of the expected navbar nodes
|
||||
*/
|
||||
public function test_remove_duplicate_items(array $navbarnodes, array $expected) {
|
||||
$this->resetAfterTest();
|
||||
$page = new \moodle_page();
|
||||
$page->set_url('/');
|
||||
|
||||
// Add the navbar nodes.
|
||||
foreach ($navbarnodes as $node) {
|
||||
$page->navbar->add($node['text'], $node['action'], \navigation_node::TYPE_CUSTOM);
|
||||
}
|
||||
|
||||
$boostnavbar = $this->getMockBuilder(boostnavbar::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([])
|
||||
->getMock();
|
||||
|
||||
$rc = new \ReflectionClass(boostnavbar::class);
|
||||
$rcp = $rc->getProperty('items');
|
||||
$rcp->setAccessible(true);
|
||||
$rcp->setValue($boostnavbar, $page->navbar->get_items());
|
||||
|
||||
// Make the call to the function.
|
||||
$rcm = $rc->getMethod('remove_duplicate_items');
|
||||
$rcm->setAccessible(true);
|
||||
$rcm->invoke($boostnavbar);
|
||||
|
||||
// Get the value for the class variable that the function modifies.
|
||||
$values = $rcp->getValue($boostnavbar);
|
||||
$actual = [];
|
||||
foreach ($values as $value) {
|
||||
$actual[] = $value->text;
|
||||
}
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provider for test_remove_items_that_exist_in_navigation.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function remove_items_that_exist_in_navigation_provider(): array {
|
||||
global $CFG;
|
||||
|
||||
return [
|
||||
'Item with identical action url and text exists in the primary navigation menu.' => [
|
||||
'primary',
|
||||
[
|
||||
[
|
||||
'text' => 'Node 1',
|
||||
'action' => new \moodle_url('/page1.php')
|
||||
],
|
||||
],
|
||||
[
|
||||
'Node 1' => new \moodle_url('/page1.php'),
|
||||
'Node 2' => new \moodle_url('/page2.php'),
|
||||
'Node 3' => new \moodle_url('/page1.php'),
|
||||
],
|
||||
['Node 2', 'Node 3']
|
||||
],
|
||||
'Item with identical action url and text exists in the secondary navigation menu.' => [
|
||||
'secondary',
|
||||
[
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php')
|
||||
],
|
||||
],
|
||||
[
|
||||
'Node 1' => new \moodle_url('/page1.php'),
|
||||
'Node 2' => new \moodle_url('/page2.php'),
|
||||
'Node 3' => new \moodle_url('/page1.php'),
|
||||
],
|
||||
['Home', 'Node 1', 'Node 3']
|
||||
],
|
||||
'Multiple items with identical action url and text exist in the secondary navigation menu.' => [
|
||||
'secondary',
|
||||
[
|
||||
[
|
||||
'text' => 'Node 2',
|
||||
'action' => new \moodle_url('/page2.php')
|
||||
],
|
||||
[
|
||||
'text' => 'Node 3',
|
||||
'action' => new \moodle_url('/page3.php')
|
||||
],
|
||||
],
|
||||
[
|
||||
'Node 1' => new \moodle_url('/page1.php'),
|
||||
'Node 2' => "{$CFG->wwwroot}/page2.php",
|
||||
'Node 3' => new \action_link(new \moodle_url('/page3.php'), 'Action link'),
|
||||
],
|
||||
['Home', 'Node 1']
|
||||
],
|
||||
'No items with identical action url and text in the secondary navigation menu.' => [
|
||||
'secondary',
|
||||
[
|
||||
[
|
||||
'text' => 'Node 4',
|
||||
'action' => new \moodle_url('/page4.php')
|
||||
],
|
||||
],
|
||||
[
|
||||
'Node 1' => new \moodle_url('/page1.php'),
|
||||
'Node 2' => new \moodle_url('/page2.php'),
|
||||
'Node 3' => new \moodle_url('/page1.php'),
|
||||
],
|
||||
['Home', 'Node 1', 'Node 2', 'Node 3']
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the remove_items_that_exist_in_navigation function.
|
||||
*
|
||||
* @dataProvider remove_items_that_exist_in_navigation_provider
|
||||
* @param string $navmenu The name of the navigation menu we would like to use (primary or secondary)
|
||||
* @param array $navmenunodes The array containing the text and action of the nodes to be added to the navigation menu
|
||||
* @param array $navbarnodes Array containing the text => action of the nodes to be added to the navbar
|
||||
* @param array $expected Array containing the text of the expected navbar nodes after the filtering
|
||||
*/
|
||||
public function test_remove_items_that_exist_in_navigation(string $navmenu, array $navmenunodes, array $navbarnodes,
|
||||
array $expected) {
|
||||
global $PAGE;
|
||||
|
||||
// Unfortunate hack needed because people use global $PAGE around the place.
|
||||
$PAGE->set_url('/');
|
||||
$this->resetAfterTest();
|
||||
$page = new \moodle_page();
|
||||
$page->set_url('/');
|
||||
|
||||
switch ($navmenu) {
|
||||
case 'primary':
|
||||
$navigationmenu = new \core\navigation\views\primary($page);
|
||||
break;
|
||||
case 'secondary':
|
||||
$navigationmenu = new \core\navigation\views\secondary($page);
|
||||
}
|
||||
|
||||
$navigationmenu->initialise();
|
||||
// Add the additional nodes to the navigation menu.
|
||||
foreach ($navmenunodes as $navmenunode) {
|
||||
$navigationmenu->add($navmenunode['text'], $navmenunode['action'], \navigation_node::TYPE_CUSTOM);
|
||||
}
|
||||
|
||||
// Add the additional navbar nodes.
|
||||
foreach ($navbarnodes as $text => $action) {
|
||||
$page->navbar->add($text, $action, \navigation_node::TYPE_CUSTOM);
|
||||
}
|
||||
|
||||
$boostnavbar = $this->getMockBuilder(boostnavbar::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods([])
|
||||
->getMock();
|
||||
|
||||
$rc = new \ReflectionClass(boostnavbar::class);
|
||||
$rcp = $rc->getProperty('items');
|
||||
$rcp->setAccessible(true);
|
||||
$rcp->setValue($boostnavbar, $page->navbar->get_items());
|
||||
|
||||
// Make the call to the function.
|
||||
$rcm = $rc->getMethod('remove_items_that_exist_in_navigation');
|
||||
$rcm->setAccessible(true);
|
||||
$rcm->invoke($boostnavbar, $navigationmenu);
|
||||
|
||||
// Get the value for the class variable that the function modifies.
|
||||
$values = $rcp->getValue($boostnavbar);
|
||||
$actual = [];
|
||||
foreach ($values as $value) {
|
||||
$actual[] = $value->text;
|
||||
}
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user