aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--inc/Logger.php31
-rw-r--r--inc/Ui/Admin.php2
-rw-r--r--lib/plugins/extension/helper/extension.php2
-rw-r--r--lib/plugins/logviewer/admin.php152
-rw-r--r--lib/plugins/logviewer/admin.svg1
-rw-r--r--lib/plugins/logviewer/lang/en/intro.txt8
-rw-r--r--lib/plugins/logviewer/lang/en/lang.php9
-rw-r--r--lib/plugins/logviewer/lang/en/nolog.txt1
-rw-r--r--lib/plugins/logviewer/plugin.info.txt7
-rw-r--r--lib/plugins/logviewer/script.js8
-rw-r--r--lib/plugins/logviewer/style.less41
-rw-r--r--lib/scripts/behaviour.js9
13 files changed, 261 insertions, 11 deletions
diff --git a/.gitignore b/.gitignore
index a2e2107b1..de576886a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,6 +56,7 @@
!/lib/plugins/styling
!/lib/plugins/testing
!/lib/plugins/usermanager
+!/lib/plugins/logviewer
!/lib/plugins/action.php
!/lib/plugins/admin.php
!/lib/plugins/auth.php
diff --git a/inc/Logger.php b/inc/Logger.php
index 3ff1d59b9..0ada6d63e 100644
--- a/inc/Logger.php
+++ b/inc/Logger.php
@@ -50,8 +50,10 @@ class Logger
public function log($message, $details = null, $file = '', $line = 0)
{
// details are logged indented
- if ($details && !is_string($details)) {
- $details = json_encode($details, JSON_PRETTY_PRINT);
+ if ($details) {
+ if (!is_string($details)) {
+ $details = json_encode($details, JSON_PRETTY_PRINT);
+ }
$details = explode("\n", $details);
$loglines = array_map(function ($line) {
return ' ' . $line;
@@ -62,17 +64,35 @@ class Logger
$loglines = [];
}
- $logline = gmdate('c') . "\t" . $message;
+ // datetime, fileline, message
+ $logline = gmdate('Y-m-d H:i:s') . "\t";
if ($file) {
- $logline .= "\t$file";
+ $logline .= $file;
if ($line) $logline .= "($line)";
}
+ $logline .= "\t" . $message;
array_unshift($loglines, $logline);
return $this->writeLogLines($loglines);
}
/**
+ * Construct the log file for the given day
+ *
+ * @param false|string|int $date Date to access, false for today
+ * @return string
+ */
+ public function getLogfile($date = false)
+ {
+ global $conf;
+
+ if ($date !== null) $date = strtotime($date);
+ if (!$date) $date = time();
+
+ return $conf['logdir'] . '/' . $this->facility . '/' . date('Y-m-d', $date) . '.log';
+ }
+
+ /**
* Write the given lines to today's facility log
*
* @param string[] $lines the raw lines to append to the log
@@ -80,8 +100,7 @@ class Logger
*/
protected function writeLogLines($lines)
{
- global $conf;
- $logfile = $conf['logdir'] . '/' . $this->facility . '/' . gmdate('Y-m-d') . '.log';
+ $logfile = $this->getLogfile();
return io_saveFile($logfile, join("\n", $lines) . "\n", true);
}
}
diff --git a/inc/Ui/Admin.php b/inc/Ui/Admin.php
index fe319d414..04d8da5ec 100644
--- a/inc/Ui/Admin.php
+++ b/inc/Ui/Admin.php
@@ -12,7 +12,7 @@ namespace dokuwiki\Ui;
*/
class Admin extends Ui {
- protected $forAdmins = array('usermanager', 'acl', 'extension', 'config', 'styling');
+ protected $forAdmins = array('usermanager', 'acl', 'extension', 'config', 'logviewer', 'styling');
protected $forManagers = array('revert', 'popularity');
/** @var array[] */
protected $menu;
diff --git a/lib/plugins/extension/helper/extension.php b/lib/plugins/extension/helper/extension.php
index 5ddf3323e..eabcd3ac1 100644
--- a/lib/plugins/extension/helper/extension.php
+++ b/lib/plugins/extension/helper/extension.php
@@ -128,7 +128,7 @@ class helper_plugin_extension_extension extends DokuWiki_Plugin
array(
'authad', 'authldap', 'authpdo', 'authplain',
'acl', 'config', 'extension', 'info', 'popularity', 'revert',
- 'safefnrecode', 'styling', 'testing', 'usermanager',
+ 'safefnrecode', 'styling', 'testing', 'usermanager', 'logviewer',
'template:dokuwiki',
)
);
diff --git a/lib/plugins/logviewer/admin.php b/lib/plugins/logviewer/admin.php
new file mode 100644
index 000000000..7a28e208b
--- /dev/null
+++ b/lib/plugins/logviewer/admin.php
@@ -0,0 +1,152 @@
+<?php
+
+use dokuwiki\Logger;
+
+/**
+ * DokuWiki Plugin logviewer (Admin Component)
+ *
+ * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+class admin_plugin_logviewer extends DokuWiki_Admin_Plugin
+{
+
+ protected $facilities;
+ protected $facility;
+ protected $date;
+
+ /** @inheritDoc */
+ public function forAdminOnly()
+ {
+ return true;
+ }
+
+ /** @inheritDoc */
+ public function handle()
+ {
+ global $INPUT;
+
+ $this->facilities = $this->getFacilities();
+ $this->facility = $INPUT->str('facility');
+ if (!in_array($this->facility, $this->facilities)) {
+ $this->facility = $this->facilities[0];
+ }
+
+ $this->date = $INPUT->str('date');
+ if (!preg_match('/^\d\d\d\d-\d\d-\d\d$/', $this->date)) {
+ $this->date = gmdate('Y-m-d');
+ }
+ }
+
+ /** @inheritDoc */
+ public function html()
+ {
+ echo '<div id="plugin__logviewer">';
+ echo $this->locale_xhtml('intro');
+ $this->displayTabs();
+ $this->displayLog();
+ echo '</div>';
+ }
+
+ /**
+ * Show the navigational tabs and date picker
+ */
+ protected function displayTabs()
+ {
+ global $ID;
+
+ $form = new dokuwiki\Form\Form(['method'=>'GET']);
+ $form->setHiddenField('do', 'admin');
+ $form->setHiddenField('page', 'logviewer');
+ $form->setHiddenField('facility', $this->facility);
+ $form->addTextInput('date','Date*')->attr('type','date')->val($this->date)->addClass('quickselect');
+ $form->addButton('submit','>')->attr('type','submit');
+ echo $form->toHTML();
+
+ echo '<ul class="tabs">';
+ foreach ($this->facilities as $facility) {
+ echo '<li>';
+ if ($facility == $this->facility) {
+ echo '<strong>' . hsc($facility) . '</strong>';
+ } else {
+ $link = wl($ID,
+ ['do' => 'admin', 'page' => 'logviewer', 'date' => $this->date, 'facility' => $facility]);
+ echo '<a href="' . $link . '">' . hsc($facility) . '</a>';
+ }
+ echo '</li>';
+ }
+ echo '</ul>';
+
+ }
+
+ /**
+ * Output the logfile contents
+ */
+ protected function displayLog()
+ {
+ $logfile = Logger::getInstance($this->facility)->getLogfile($this->date);
+ if (!file_exists($logfile)) {
+ echo $this->locale_xhtml('nolog');
+ return;
+ }
+
+ // loop through the file an print it
+ echo '<dl>';
+ $lines = file($logfile);
+ $cnt = count($lines);
+ for ($i = 0; $i < $cnt; $i++) {
+ $line = $lines[$i];
+
+ if ($line[0] === ' ' && $line[1] === ' ') {
+ // lines indented by two spaces are details, aggregate them
+ echo '<dd>';
+ while ($line[0] === ' ' && $line[1] === ' ') {
+ echo hsc(substr($line, 2)) . '<br />';
+ $line = $lines[$i++];
+ }
+ echo '</dd>';
+ $i -= 2; // rewind the counter
+ } else {
+ // other lines are actual log lines in three parts
+ list($dt, $file, $msg) = explode("\t", $line, 3);
+ echo '<dt>';
+ echo '<span class="datetime">' . hsc($dt) . '</span>';
+ echo '<span class="log">';
+ echo '<span class="msg">' . hsc($msg) . '</span>';
+ echo '<span class="file">' . hsc($file) . '</span>';
+ echo '</span>';
+ echo '</dt>';
+ }
+ }
+ echo '</dl>';
+ }
+
+ /**
+ * Get the available logging facilities
+ *
+ * @return array
+ */
+ protected function getFacilities()
+ {
+ global $conf;
+ $conf['logdir'];
+
+ // default facilities first
+ $facilities = [
+ Logger::LOG_ERROR,
+ Logger::LOG_DEPRECATED,
+ Logger::LOG_DEBUG,
+ ];
+
+ // add all other dirs
+ $dirs = glob($conf['logdir'] . '/*', GLOB_ONLYDIR);
+ foreach ($dirs as $dir) {
+ $facilities[] = basename($dir);
+ }
+ $facilities = array_unique($facilities);
+
+ return $facilities;
+ }
+
+}
+
diff --git a/lib/plugins/logviewer/admin.svg b/lib/plugins/logviewer/admin.svg
new file mode 100644
index 000000000..3eb481759
--- /dev/null
+++ b/lib/plugins/logviewer/admin.svg
@@ -0,0 +1 @@
+<svg viewBox="0 0 24 24"><path d="M15 20a1 1 0 0 0 1-1V4H8a1 1 0 0 0-1 1v11H5V5a3 3 0 0 1 3-3h11a3 3 0 0 1 3 3v1h-2V5a1 1 0 0 0-1-1 1 1 0 0 0-1 1v14a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3v-1h11a2 2 0 0 0 2 2M9 6h5v2H9V6m0 4h5v2H9v-2m0 4h5v2H9v-2z"/></svg> \ No newline at end of file
diff --git a/lib/plugins/logviewer/lang/en/intro.txt b/lib/plugins/logviewer/lang/en/intro.txt
new file mode 100644
index 000000000..f3b488ec4
--- /dev/null
+++ b/lib/plugins/logviewer/lang/en/intro.txt
@@ -0,0 +1,8 @@
+====== View Logs ======
+
+This interface allows you to view the various logs that are written by DokuWiki. By default, there shouldn't be logged
+much here (it depends on your log settings). However if something goes wrong, chances are high, you'll find useful info
+here. All times are UTC!
+
+Please be aware **log files can contain sensitive information** like passwords, paths or other secrets.
+Be sure to redact the logs appropriately when posting them on the forum or in bug reports!
diff --git a/lib/plugins/logviewer/lang/en/lang.php b/lib/plugins/logviewer/lang/en/lang.php
new file mode 100644
index 000000000..3f32e0bed
--- /dev/null
+++ b/lib/plugins/logviewer/lang/en/lang.php
@@ -0,0 +1,9 @@
+<?php
+/**
+ * English language file for logviewer plugin
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+$lang['menu'] = 'View Logs';
+
diff --git a/lib/plugins/logviewer/lang/en/nolog.txt b/lib/plugins/logviewer/lang/en/nolog.txt
new file mode 100644
index 000000000..2bdefff0c
--- /dev/null
+++ b/lib/plugins/logviewer/lang/en/nolog.txt
@@ -0,0 +1 @@
+There are no log entries for the selected day and log facility.
diff --git a/lib/plugins/logviewer/plugin.info.txt b/lib/plugins/logviewer/plugin.info.txt
new file mode 100644
index 000000000..d18a82c21
--- /dev/null
+++ b/lib/plugins/logviewer/plugin.info.txt
@@ -0,0 +1,7 @@
+base logviewer
+author Andreas Gohr
+email andi@splitbrain.org
+date 2020-08-13
+name logviewer plugin
+desc View DokuWiki logs
+url https://www.dokuwiki.org/plugin:logviewer
diff --git a/lib/plugins/logviewer/script.js b/lib/plugins/logviewer/script.js
new file mode 100644
index 000000000..5a5303c84
--- /dev/null
+++ b/lib/plugins/logviewer/script.js
@@ -0,0 +1,8 @@
+/**
+ * Scroll to the end of the log on load
+ */
+jQuery(function () {
+ var $dl = jQuery('#plugin__logviewer').find('dl');
+ if(!$dl.length) return;
+ $dl.animate({ scrollTop: $dl.prop("scrollHeight")}, 500);
+});
diff --git a/lib/plugins/logviewer/style.less b/lib/plugins/logviewer/style.less
new file mode 100644
index 000000000..f9c8b1d89
--- /dev/null
+++ b/lib/plugins/logviewer/style.less
@@ -0,0 +1,41 @@
+#plugin__logviewer {
+ form {
+ float: right;
+ }
+
+ .tabs {
+ margin-bottom: 2em;
+ }
+
+ dl {
+ max-height: 80vh;
+ overflow: auto;
+
+ dt {
+ display: flex;
+
+ .datetime {
+ flex: 0 0 auto;
+ margin-right: 1em;
+ }
+
+ .log {
+ flex: 1 1 auto;
+
+ span {
+ display: block;
+ }
+
+ span.file {
+ font-family: monospace;
+ }
+ }
+ }
+
+ dd {
+ font-size: 80%;
+ white-space: nowrap;
+ font-family: monospace;
+ }
+ }
+}
diff --git a/lib/scripts/behaviour.js b/lib/scripts/behaviour.js
index f9aad3d02..968d2dd5f 100644
--- a/lib/scripts/behaviour.js
+++ b/lib/scripts/behaviour.js
@@ -102,15 +102,18 @@ var dw_behaviour = {
/**
* Autosubmit quick select forms
*
- * When a <select> tag has the class "quickselect", this script will
+ * When a <select> or <input> tag has the class "quickselect", this script will
* automatically submit its parent form when the select value changes.
* It also hides the submit button of the form.
*
+ * This includes a workaround a weird behaviour when the submit button has a name
+ *
+ * @link https://trackjs.com/blog/when-form-submit-is-not-a-function/
* @author Andreas Gohr <andi@splitbrain.org>
*/
quickSelect: function(){
- jQuery('select.quickselect')
- .on('change', function(e){ e.target.form.submit(); })
+ jQuery('.quickselect')
+ .change(function(e){ HTMLFormElement.prototype.submit.call(e.target.form); })
.closest('form').find(':button').not('.show').hide();
},