MDL-49399 task: Add admin log viewer
AMOS BEGIN CPY [eventstarttime,core_calendar],[task_starttime,core_admin] CPY [eventduration,core_calendar],[task_duration,core_admin] CPY [result,core_cache],[task_result,core_admin] CPY [database,install],[task_dbstats,core_admin] CPY [fail,install],[task_result:failed,core_admin] AMOS END
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Task log table.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2018 Andrew Nicols <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_admin;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/tablelib.php');
|
||||
|
||||
/**
|
||||
* Table to display list of task logs.
|
||||
*
|
||||
* @copyright 2018 Andrew Nicols <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class task_log_table extends \table_sql {
|
||||
|
||||
/**
|
||||
* Constructor for the task_log table.
|
||||
*
|
||||
* @param string $filter
|
||||
* @param int $resultfilter
|
||||
*/
|
||||
public function __construct(string $filter = '', int $resultfilter = null) {
|
||||
global $DB;
|
||||
|
||||
if (-1 === $resultfilter) {
|
||||
$resultfilter = null;
|
||||
}
|
||||
|
||||
parent::__construct('tasklogs');
|
||||
|
||||
$columnheaders = [
|
||||
'classname' => get_string('name'),
|
||||
'type' => get_string('tasktype', 'admin'),
|
||||
'userid' => get_string('user', 'admin'),
|
||||
'timestart' => get_string('task_starttime', 'admin'),
|
||||
'duration' => get_string('task_duration', 'admin'),
|
||||
'db' => get_string('task_dbstats', 'admin'),
|
||||
'result' => get_string('task_result', 'admin'),
|
||||
'actions' => '',
|
||||
];
|
||||
$this->define_columns(array_keys($columnheaders));
|
||||
$this->define_headers(array_values($columnheaders));
|
||||
|
||||
// The name column is a header.
|
||||
$this->define_header_column('classname');
|
||||
|
||||
// This table is not collapsible.
|
||||
$this->collapsible(false);
|
||||
|
||||
// The actions class should not wrap. Use the BS text utility class.
|
||||
$this->column_class('actions', 'text-nowrap');
|
||||
|
||||
// Allow pagination.
|
||||
$this->pageable(true);
|
||||
|
||||
// Allow sorting. Default to sort by timestarted DESC.
|
||||
$this->sortable(true, 'timestart', SORT_DESC);
|
||||
|
||||
// Add filtering.
|
||||
$where = [];
|
||||
$params = [];
|
||||
if (!empty($filter)) {
|
||||
$where[] = $DB->sql_like('classname', ':filter', false, false);
|
||||
$filter = str_replace('\\', '\\\\', $filter);
|
||||
$params['filter'] = '%' . $DB->sql_like_escape($filter) . '%';
|
||||
}
|
||||
|
||||
if (null !== $resultfilter) {
|
||||
$where[] = 'tl.result = :result';
|
||||
$params['result'] = $resultfilter;
|
||||
}
|
||||
|
||||
$where = implode(' AND ', $where);
|
||||
|
||||
$this->set_sql('', '', $where, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the db. Store results in the table object for use by build_table.
|
||||
*
|
||||
* @param int $pagesize size of page for paginated displayed table.
|
||||
* @param bool $useinitialsbar do you want to use the initials bar. Bar
|
||||
* will only be used if there is a fullname column defined for the table.
|
||||
*/
|
||||
public function query_db($pagesize, $useinitialsbar = true) {
|
||||
global $DB;
|
||||
|
||||
// Fetch the attempts.
|
||||
$sort = $this->get_sql_sort();
|
||||
if ($sort) {
|
||||
$sort = "ORDER BY $sort";
|
||||
}
|
||||
|
||||
$extrafields = get_extra_user_fields(\context_system::instance());
|
||||
$userfields = \user_picture::fields('u', $extrafields, 'userid2', 'user');
|
||||
|
||||
$where = '';
|
||||
if (!empty($this->sql->where)) {
|
||||
$where = "WHERE {$this->sql->where}";
|
||||
}
|
||||
|
||||
$sql = "SELECT
|
||||
tl.*,
|
||||
tl.dbreads + tl.dbwrites AS db,
|
||||
tl.timeend - tl.timestart AS duration,
|
||||
{$userfields}
|
||||
FROM {task_log} tl
|
||||
LEFT JOIN {user} u ON u.id = tl.userid
|
||||
{$where}
|
||||
{$sort}";
|
||||
|
||||
$this->pagesize($pagesize, $DB->count_records_sql("SELECT COUNT('x') FROM {task_log} tl {$where}", $this->sql->params));
|
||||
if (!$this->is_downloading()) {
|
||||
$this->rawdata = $DB->get_records_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
|
||||
} else {
|
||||
$this->rawdata = $DB->get_records_sql($sql, $this->sql->params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the name cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_classname($row) : string {
|
||||
$output = '';
|
||||
if (class_exists($row->classname)) {
|
||||
$task = new $row->classname;
|
||||
if ($task instanceof \core\task\scheduled_task) {
|
||||
$output = $task->get_name();
|
||||
}
|
||||
}
|
||||
|
||||
$output .= \html_writer::tag('div', "\\{$row->classname}", [
|
||||
'class' => 'task-class',
|
||||
]);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the type cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_type($row) : string {
|
||||
if (\core\task\database_logger::TYPE_SCHEDULED == $row->type) {
|
||||
return get_string('task_type:scheduled', 'admin');
|
||||
} else {
|
||||
return get_string('task_type:adhoc', 'admin');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the timestart cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_result($row) : string {
|
||||
if ($row->result) {
|
||||
return get_string('task_result:failed', 'admin');
|
||||
} else {
|
||||
return get_string('success');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the timestart cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_timestart($row) : string {
|
||||
return userdate($row->timestart, get_string('strftimedatetimeshort', 'langconfig'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the duration cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_duration($row) : string {
|
||||
$duration = round($row->timeend - $row->timestart, 2);
|
||||
|
||||
if (empty($duration)) {
|
||||
// The format_time function returns 'now' when the difference is exactly 0.
|
||||
// Note: format_time performs concatenation in exactly this fashion so we should do this for consistency.
|
||||
return '0 ' . get_string('secs', 'moodle');
|
||||
}
|
||||
|
||||
return format_time($duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the DB details cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_db($row) : string {
|
||||
$output = '';
|
||||
|
||||
$output .= \html_writer::div(get_string('task_stats:dbreads', 'admin', $row->dbreads));
|
||||
$output .= \html_writer::div(get_string('task_stats:dbwrites', 'admin', $row->dbwrites));
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the actions cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_actions($row) : string {
|
||||
global $OUTPUT;
|
||||
|
||||
$actions = [];
|
||||
|
||||
$url = new \moodle_url('/admin/tasklogs.php', ['logid' => $row->id]);
|
||||
|
||||
// Quick view.
|
||||
$actions[] = $OUTPUT->action_icon(
|
||||
$url,
|
||||
new \pix_icon('e/search', get_string('view')),
|
||||
new \popup_action('click', $url)
|
||||
);
|
||||
|
||||
// Download.
|
||||
$actions[] = $OUTPUT->action_icon(
|
||||
new \moodle_url($url, ['download' => true]),
|
||||
new \pix_icon('t/download', get_string('download'))
|
||||
);
|
||||
|
||||
return implode(' ', $actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the user cell.
|
||||
*
|
||||
* @param \stdClass $row
|
||||
* @return string
|
||||
*/
|
||||
public function col_userid($row) : string {
|
||||
if (empty($row->userid)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$user = (object) [];
|
||||
username_load_fields_from_object($user, $row, 'user');
|
||||
|
||||
return fullname($user);
|
||||
}
|
||||
}
|
||||
@@ -246,10 +246,17 @@ if (\core\task\logmanager::uses_standard_settings()) {
|
||||
PARAM_INT
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
$ADMIN->add('taskconfig', $temp);
|
||||
|
||||
if (empty($CFG->task_log_class) || '\\core\\task\\database_logger' == $CFG->task_log_class) {
|
||||
$ADMIN->add('taskconfig', new admin_externalpage(
|
||||
'tasklogs',
|
||||
new lang_string('tasklogs','admin'),
|
||||
"{$CFG->wwwroot}/{$CFG->admin}/tasklogs.php"
|
||||
));
|
||||
}
|
||||
|
||||
// E-mail settings.
|
||||
$ADMIN->add('server', new admin_category('email', new lang_string('categoryemail', 'admin')));
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Task log.
|
||||
*
|
||||
* @package admin
|
||||
* @copyright 2018 Andrew Nicols <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
require_once("{$CFG->libdir}/adminlib.php");
|
||||
require_once("{$CFG->libdir}/tablelib.php");
|
||||
require_once("{$CFG->libdir}/filelib.php");
|
||||
|
||||
$filter = optional_param('filter', '', PARAM_ALPHANUMEXT);
|
||||
$result = optional_param('result', null, PARAM_INT);
|
||||
|
||||
$pageurl = new \moodle_url('/admin/tasklogs.php');
|
||||
$pageurl->param('filter', $filter);
|
||||
|
||||
$PAGE->set_url($pageurl);
|
||||
$PAGE->set_context(context_system::instance());
|
||||
$PAGE->set_pagelayout('admin');
|
||||
$strheading = get_string('tasklogs', 'tool_task');
|
||||
$PAGE->set_title($strheading);
|
||||
$PAGE->set_heading($strheading);
|
||||
|
||||
require_login();
|
||||
|
||||
require_capability('moodle/site:config', context_system::instance());
|
||||
admin_externalpage_setup('tasklogs');
|
||||
|
||||
$logid = optional_param('logid', null, PARAM_INT);
|
||||
$download = optional_param('download', false, PARAM_BOOL);
|
||||
|
||||
if (null !== $logid) {
|
||||
$log = $DB->get_record('task_log', ['id' => $logid], '*', MUST_EXIST);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$file = $fs->get_file(\context_system::instance()->id, 'core', 'task_logs', $log->id, '/', 'log.txt');
|
||||
|
||||
$filename = str_replace('\\', '_', $log->classname) . "-{$log->id}.log";
|
||||
send_stored_file($file, null, 0, $download, [
|
||||
'filename' => $filename,
|
||||
]);
|
||||
}
|
||||
|
||||
$renderer = $PAGE->get_renderer('tool_task');
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->render_from_template('core_admin/tasklogs', (object) [
|
||||
'action' => $pageurl->out(),
|
||||
'filter' => $filter,
|
||||
'resultfilteroptions' => [
|
||||
(object) [
|
||||
'value' => -1,
|
||||
'title' => get_string('all'),
|
||||
'selected' => (-1 === $result),
|
||||
],
|
||||
(object) [
|
||||
'value' => 0,
|
||||
'title' => get_string('success'),
|
||||
'selected' => (0 === $result),
|
||||
],
|
||||
(object) [
|
||||
'value' => 1,
|
||||
'title' => get_string('task_result:failed', 'admin'),
|
||||
'selected' => (1 === $result),
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$table = new \core_admin\task_log_table($filter, $result);
|
||||
$table->baseurl = $pageurl;
|
||||
$table->out(100, false);
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
@@ -0,0 +1,34 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_admin/tasklogs
|
||||
|
||||
Task Logs template.
|
||||
}}
|
||||
<form class="form-inline" method="GET" action="{{{action}}}">
|
||||
<label class="sr-only" for="tasklog-filter">{{#str}}filter{{/str}}</label>
|
||||
<input class="form-control" type="text" id="tasklog-filter" name="filter" value="{{{filter}}}">
|
||||
|
||||
<label class="sr-only" for="tasklog-resultfilter">{{#str}}resultfilter, admin{{/str}}</label>
|
||||
<select class="form-control custom-select" name="result" id="tasklog-resultfilter">
|
||||
{{#resultfilteroptions}}
|
||||
<option value="{{{value}}}"{{#selected}} selected="selected"{{/selected}}>{{title}}</option>
|
||||
{{/resultfilteroptions}}
|
||||
</select>
|
||||
|
||||
<input class="btn btn-primary" type="submit" value="{{#str}}filter{{/str}}">
|
||||
</form>
|
||||
@@ -1030,6 +1030,7 @@ $string['requestcategoryselection'] = 'Enable category selection';
|
||||
$string['restorecourse'] = 'Restore course';
|
||||
$string['restorernewroleid'] = 'Restorers\' role in courses';
|
||||
$string['restorernewroleid_help'] = 'If the user does not already have the permission to manage the newly restored course, the user is automatically assigned this role and enrolled if necessary. Select "None" if you do not want restorers to be able to manage every restored course.';
|
||||
$string['resultfilter'] = 'Filter by result';
|
||||
$string['reverseproxy'] = 'Reverse proxy';
|
||||
$string['riskconfig'] = 'Users could change site configuration and behaviour';
|
||||
$string['riskconfigshort'] = 'Configuration risk';
|
||||
@@ -1173,6 +1174,16 @@ $string['task_logretention'] = 'Retention period';
|
||||
$string['task_logretention_desc'] = 'The maximum period that logs should be kept for. This setting interacts with the \'Retain runs\' setting: whichever is reached first will apply';
|
||||
$string['task_logretainruns'] = 'Retain runs';
|
||||
$string['task_logretainruns_desc'] = 'The number of runs of each task to retain. This setting interacts with the \'Retention period\' setting: whichever is reached first will apply.';
|
||||
$string['task_type:adhoc'] = 'Adhoc';
|
||||
$string['task_type:scheduled'] = 'Scheduled';
|
||||
$string['task_result:failed'] = 'Fail';
|
||||
$string['task_stats:dbreads'] = '{$a} reads';
|
||||
$string['task_stats:dbwrites'] = '{$a} writes';
|
||||
$string['task_starttime'] = 'Start time';
|
||||
$string['task_duration'] = 'Duration';
|
||||
$string['task_dbstats'] = 'Database';
|
||||
$string['task_result'] = 'Result';
|
||||
$string['tasktype'] = 'Type';
|
||||
$string['taskadmintitle'] = 'Tasks';
|
||||
$string['taskanalyticscleanup'] = 'Analytics cleanup';
|
||||
$string['taskautomatedbackup'] = 'Automated backups';
|
||||
|
||||
@@ -84,6 +84,29 @@ class database_logger implements task_logger {
|
||||
$logdata->id = $DB->insert_record('task_log', $logdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this task logger has a report available.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_log_report() : bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any URL available for viewing relevant task log reports.
|
||||
*
|
||||
* @param string $classname The task class to fetch for
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public static function get_url_for_task_class(string $classname) : \moodle_url {
|
||||
global $CFG;
|
||||
|
||||
return new \moodle_url("/{$CFG->admin}/tasklogs.php", [
|
||||
'filter' => $classname,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old task logs.
|
||||
*/
|
||||
|
||||
@@ -144,7 +144,12 @@ class logmanager {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !empty(self::get_logger_classname());
|
||||
$loggerclass = self::get_logger_classname();
|
||||
if (empty($loggerclass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $loggerclass::is_configured();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,7 +181,7 @@ class logmanager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use the standard settings fore
|
||||
* Whether to use the standard settings form.
|
||||
*/
|
||||
public static function uses_standard_settings() : bool {
|
||||
$classname = self::get_logger_classname();
|
||||
|
||||
@@ -592,7 +592,7 @@ class manager {
|
||||
$task->get_lock()->release();
|
||||
|
||||
// Finalise the log output.
|
||||
\core\task\logmanager::finalise_log(true);
|
||||
logmanager::finalise_log(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,7 +604,7 @@ class manager {
|
||||
global $DB;
|
||||
|
||||
// Finalise the log output.
|
||||
\core\task\logmanager::finalise_log();
|
||||
logmanager::finalise_log();
|
||||
|
||||
// Delete the adhoc task record - it is finished.
|
||||
$DB->delete_records('task_adhoc', array('id' => $task->get_id()));
|
||||
@@ -651,7 +651,7 @@ class manager {
|
||||
$task->get_lock()->release();
|
||||
|
||||
// Finalise the log output.
|
||||
\core\task\logmanager::finalise_log(true);
|
||||
logmanager::finalise_log(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -680,7 +680,7 @@ class manager {
|
||||
global $DB;
|
||||
|
||||
// Finalise the log output.
|
||||
\core\task\logmanager::finalise_log();
|
||||
logmanager::finalise_log();
|
||||
|
||||
$classname = self::get_canonical_class_name($task);
|
||||
$record = $DB->get_record('task_scheduled', array('classname' => $classname));
|
||||
|
||||
@@ -54,4 +54,18 @@ interface task_logger {
|
||||
public static function store_log_for_task(task_base $task, string $logpath, bool $failed,
|
||||
int $dbreads, int $dbwrites, float $timestart, float $timeend);
|
||||
|
||||
/**
|
||||
* Whether this task logger has a report available.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_log_report() : bool;
|
||||
|
||||
/**
|
||||
* Get any URL available for viewing relevant task log reports.
|
||||
*
|
||||
* @param string $classname The task class to fetch for
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public static function get_url_for_task_class(string $classname) : \moodle_url;
|
||||
}
|
||||
|
||||
@@ -521,6 +521,11 @@ class task_logging_test_mocked_logger implements \core\task\task_logger {
|
||||
*/
|
||||
public static $storelogfortask = [];
|
||||
|
||||
/**
|
||||
* @var bool Whether this logger has a report.
|
||||
*/
|
||||
public static $haslogreport = true;
|
||||
|
||||
/**
|
||||
* Reset the test class.
|
||||
*/
|
||||
@@ -555,4 +560,23 @@ class task_logging_test_mocked_logger implements \core\task\task_logger {
|
||||
self::$storelogfortask[] = func_get_args();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this task logger has a report available.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_log_report() : bool {
|
||||
return self::$haslogreport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any URL available for viewing relevant task log reports.
|
||||
*
|
||||
* @param string $classname The task class to fetch for
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public static function get_url_for_task_class(string $classname) : \moodle_url {
|
||||
return new \moodle_url('');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -827,3 +827,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#page-admin-tasklogs {
|
||||
.task-class {
|
||||
font-size: $font-size-sm;
|
||||
color: $gray-600;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11151,6 +11151,10 @@ div.editor_atto_toolbar button .icon {
|
||||
padding-left: 0.5rem;
|
||||
content: "/"; }
|
||||
|
||||
#page-admin-tasklogs .task-class {
|
||||
font-size: 0.8203125rem;
|
||||
color: #868e96; }
|
||||
|
||||
.blockmovetarget .accesshide {
|
||||
position: relative;
|
||||
left: initial; }
|
||||
|
||||
Reference in New Issue
Block a user