Compare commits
11 Commits
e82754c523
...
e59970e0b8
| Author | SHA1 | Date | |
|---|---|---|---|
| e59970e0b8 | |||
| b64d4d91ff | |||
| c442c0df1e | |||
| 9a067eca16 | |||
| fe8d0bbffb | |||
| a3558c470e | |||
|
|
83e348f31e | ||
|
|
23c781f855 | ||
|
|
9ad5912307 | ||
|
|
c470c4266e | ||
|
|
202f571cbe |
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\filelist;
|
||||
namespace dokuwiki\plugin\luxtools;
|
||||
|
||||
class Crawler
|
||||
{
|
||||
@@ -58,7 +58,7 @@ class Crawler
|
||||
$path = $root . $local;
|
||||
|
||||
// do not descent into wiki or data directories
|
||||
if(Path::isWikiControlled($path)) return [];
|
||||
if (Path::isWikiControlled($path)) return [];
|
||||
|
||||
if (($dir = opendir($path)) === false) return [];
|
||||
$result = [];
|
||||
|
||||
42
Output.php
42
Output.php
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\filelist;
|
||||
namespace dokuwiki\plugin\luxtools;
|
||||
|
||||
class Output
|
||||
{
|
||||
@@ -28,7 +28,7 @@ class Output
|
||||
public function renderAsList($params)
|
||||
{
|
||||
if ($this->renderer instanceof \Doku_Renderer_xhtml) {
|
||||
$this->renderer->doc .= '<div class="filelist-plugin">';
|
||||
$this->renderer->doc .= '<div class="filetools-plugin">';
|
||||
}
|
||||
|
||||
$this->renderListItems($this->files, $params);
|
||||
@@ -38,6 +38,40 @@ class Output
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a thumbnail gallery (XHTML only).
|
||||
*
|
||||
* Expects a flat list of file items in $this->files.
|
||||
* Clicking a thumbnail opens the original image.
|
||||
*
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function renderAsGallery($params)
|
||||
{
|
||||
if (!($this->renderer instanceof \Doku_Renderer_xhtml)) {
|
||||
$params['style'] = 'list';
|
||||
$this->renderAsList($params);
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \Doku_Renderer_xhtml $renderer */
|
||||
$renderer = $this->renderer;
|
||||
$renderer->doc .= '<div class="filetools-plugin filetools-gallery">';
|
||||
|
||||
foreach ($this->files as $item) {
|
||||
$url = $this->itemWebUrl($item, !empty($params['randlinks']));
|
||||
$safeUrl = hsc($url);
|
||||
$label = hsc($item['name']);
|
||||
|
||||
$renderer->doc .= '<a href="' . $safeUrl . '" class="media" title="' . $label . '">';
|
||||
$renderer->doc .= '<img src="' . $safeUrl . '" alt="' . $label . '" width="150" loading="lazy" />';
|
||||
$renderer->doc .= '</a>';
|
||||
}
|
||||
|
||||
$renderer->doc .= '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the files as a table, including details if configured that way.
|
||||
*
|
||||
@@ -46,7 +80,7 @@ class Output
|
||||
public function renderAsTable($params)
|
||||
{
|
||||
if ($this->renderer instanceof \Doku_Renderer_xhtml) {
|
||||
$this->renderer->doc .= '<div class="filelist-plugin">';
|
||||
$this->renderer->doc .= '<div class="filetools-plugin">';
|
||||
}
|
||||
|
||||
$items = $this->flattenResultTree($this->files);
|
||||
@@ -277,7 +311,7 @@ class Output
|
||||
|
||||
protected function getLang($key)
|
||||
{
|
||||
$syntax = plugin_load('syntax', 'filelist');
|
||||
$syntax = plugin_load('syntax', 'luxtools');
|
||||
return $syntax->getLang($key);
|
||||
}
|
||||
}
|
||||
|
||||
8
Path.php
8
Path.php
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\filelist;
|
||||
namespace dokuwiki\plugin\luxtools;
|
||||
|
||||
class Path
|
||||
{
|
||||
@@ -60,7 +60,7 @@ class Path
|
||||
$lastRoot = $line;
|
||||
$paths[$line] = [
|
||||
'root' => $line,
|
||||
'web' => DOKU_BASE . 'lib/plugins/filelist/file.php?root=' . rawurlencode($line) . '&file=',
|
||||
'web' => DOKU_BASE . 'lib/plugins/luxtools/file.php?root=' . rawurlencode($line) . '&file=',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,10 @@ class Path
|
||||
$path = static::cleanPath($path, $addTrailingSlash);
|
||||
|
||||
$paths = $this->paths;
|
||||
if ($paths === []) {
|
||||
throw new \Exception('No paths configured');
|
||||
}
|
||||
|
||||
$allowed = array_keys($paths);
|
||||
usort($allowed, static fn($a, $b) => strlen($a) - strlen($b));
|
||||
$allowed = array_map('preg_quote_cb', $allowed);
|
||||
|
||||
9
README
9
README
@@ -1,14 +1,17 @@
|
||||
filelist plugin for DokuWiki
|
||||
LuxTools plugin for DokuWiki
|
||||
|
||||
Lists files matching a given glob pattern.
|
||||
|
||||
All documentation for this plugin can be found at
|
||||
https://www.dokuwiki.org/plugin:filelist
|
||||
https://www.dokuwiki.org/plugin:luxtools
|
||||
|
||||
If you install this plugin manually, make sure it is installed in
|
||||
lib/plugins/filelist/ - if the folder is called different it
|
||||
lib/plugins/luxtools/ - if the folder is called different it
|
||||
will not work!
|
||||
|
||||
Syntax:
|
||||
{{files>...}}
|
||||
|
||||
Please refer to http://www.dokuwiki.org/extensions for additional info
|
||||
on how to install extensions in DokuWiki.
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\filelist\test;
|
||||
namespace dokuwiki\plugin\luxtools\test;
|
||||
|
||||
use DokuWikiTest;
|
||||
|
||||
/**
|
||||
* General tests for the filelist plugin
|
||||
* General tests for the luxtools plugin
|
||||
*
|
||||
* @group plugin_filelist
|
||||
* @group plugin_luxtools
|
||||
* @group plugins
|
||||
*/
|
||||
class GeneralTest extends DokuWikiTest
|
||||
@@ -31,7 +31,7 @@ class GeneralTest extends DokuWikiTest
|
||||
$this->assertArrayHasKey('desc', $info);
|
||||
$this->assertArrayHasKey('url', $info);
|
||||
|
||||
$this->assertEquals('filelist', $info['base']);
|
||||
$this->assertEquals('luxtools', $info['base']);
|
||||
$this->assertRegExp('/^https?:\/\//', $info['url']);
|
||||
$this->assertTrue(mail_isvalid($info['email']));
|
||||
$this->assertRegExp('/^\d\d\d\d-\d\d-\d\d$/', $info['date']);
|
||||
@@ -61,7 +61,7 @@ class GeneralTest extends DokuWikiTest
|
||||
$this->assertEquals(
|
||||
gettype($conf),
|
||||
gettype($meta),
|
||||
'Both ' . DOKU_PLUGIN . 'filelist/conf/default.php and ' . DOKU_PLUGIN . 'filelist/conf/metadata.php have to exist and contain the same keys.'
|
||||
'Both ' . DOKU_PLUGIN . 'luxtools/conf/default.php and ' . DOKU_PLUGIN . 'luxtools/conf/metadata.php have to exist and contain the same keys.'
|
||||
);
|
||||
|
||||
if ($conf !== null && $meta !== null) {
|
||||
@@ -69,7 +69,7 @@ class GeneralTest extends DokuWikiTest
|
||||
$this->assertArrayHasKey(
|
||||
$key,
|
||||
$meta,
|
||||
'Key $meta[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'filelist/conf/metadata.php'
|
||||
'Key $meta[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'luxtools/conf/metadata.php'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class GeneralTest extends DokuWikiTest
|
||||
$this->assertArrayHasKey(
|
||||
$key,
|
||||
$conf,
|
||||
'Key $conf[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'filelist/conf/default.php'
|
||||
'Key $conf[\'' . $key . '\'] missing in ' . DOKU_PLUGIN . 'luxtools/conf/default.php'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\filelist\test;
|
||||
namespace dokuwiki\plugin\luxtools\test;
|
||||
|
||||
use dokuwiki\plugin\filelist\Path;
|
||||
use dokuwiki\plugin\luxtools\Path;
|
||||
use DokuWikiTest;
|
||||
|
||||
/**
|
||||
* Path related tests for the filelist plugin
|
||||
* Path related tests for the luxtools plugin
|
||||
*
|
||||
* @group plugin_filelist
|
||||
* @group plugin_luxtools
|
||||
* @group plugins
|
||||
*/
|
||||
class PathTest extends DokuWikiTest
|
||||
@@ -40,15 +40,15 @@ EOT
|
||||
$expect = [
|
||||
'C:/xampp/htdocs/wiki/' => [
|
||||
'root' => 'C:/xampp/htdocs/wiki/',
|
||||
'web' => '/lib/plugins/filelist/file.php?root=C%3A%2Fxampp%2Fhtdocs%2Fwiki%2F&file=',
|
||||
'web' => '/lib/plugins/luxtools/file.php?root=C%3A%2Fxampp%2Fhtdocs%2Fwiki%2F&file=',
|
||||
],
|
||||
'\\\\server/share/path/' => [
|
||||
'root' => '\\\\server/share/path/',
|
||||
'web' => '/lib/plugins/filelist/file.php?root=%5C%5Cserver%2Fshare%2Fpath%2F&file=',
|
||||
'web' => '/lib/plugins/luxtools/file.php?root=%5C%5Cserver%2Fshare%2Fpath%2F&file=',
|
||||
],
|
||||
'/linux/file/path/' => [
|
||||
'root' => '/linux/file/path/',
|
||||
'web' => '/lib/plugins/filelist/file.php?root=%2Flinux%2Ffile%2Fpath%2F&file=',
|
||||
'web' => '/lib/plugins/luxtools/file.php?root=%2Flinux%2Ffile%2Fpath%2F&file=',
|
||||
],
|
||||
'/linux/another/path/' => [
|
||||
'root' => '/linux/another/path/',
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace dokuwiki\plugin\filelist\test;
|
||||
namespace dokuwiki\plugin\luxtools\test;
|
||||
|
||||
use DokuWikiTest;
|
||||
use DOMWrap\Document;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for the filelist plugin.
|
||||
* Tests for the luxtools plugin.
|
||||
*
|
||||
* These test assume that the directory filelist has the following content:
|
||||
* These test assume that the directory luxtools has the following content:
|
||||
* - exampledir (directory)
|
||||
* - example2.txt (text file)
|
||||
* - example.txt (text file)
|
||||
* - exampleimage.png (image file)
|
||||
*
|
||||
* @group plugin_filelist
|
||||
* @group plugin_luxtools
|
||||
* @group plugins
|
||||
*/
|
||||
class plugin_filelist_test extends DokuWikiTest
|
||||
class plugin_luxtools_test extends DokuWikiTest
|
||||
{
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
global $conf;
|
||||
|
||||
$this->pluginsEnabled[] = 'filelist';
|
||||
$this->pluginsEnabled[] = 'luxtools';
|
||||
parent::setUp();
|
||||
|
||||
// Setup config so that access to the TMP directory will be allowed
|
||||
$conf ['plugin']['filelist']['paths'] = TMP_DIR . '/filelistdata/' . "\n" . 'W> http://localhost/';
|
||||
$conf ['plugin']['luxtools']['paths'] = TMP_DIR . '/filelistdata/' . "\n" . 'W> http://localhost/';
|
||||
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class plugin_filelist_test extends DokuWikiTest
|
||||
global $conf;
|
||||
|
||||
// Render filelist
|
||||
$instructions = p_get_instructions('{{filelist>' . TMP_DIR . '/filelistdata/*&style=list&direct=1}}');
|
||||
$instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=list&direct=1}}');
|
||||
$xhtml = p_render('xhtml', $instructions, $info);
|
||||
|
||||
// We should find:
|
||||
@@ -94,7 +94,7 @@ class plugin_filelist_test extends DokuWikiTest
|
||||
public function test_recursive()
|
||||
{
|
||||
// Render filelist
|
||||
$instructions = p_get_instructions('{{filelist>' . TMP_DIR . '/filelistdata/*&style=list&direct=1&recursive=1}}');
|
||||
$instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=list&direct=1&recursive=1}}');
|
||||
$xhtml = p_render('xhtml', $instructions, $info);
|
||||
|
||||
// We should find:
|
||||
@@ -119,21 +119,21 @@ class plugin_filelist_test extends DokuWikiTest
|
||||
public function testUnorderedList()
|
||||
{
|
||||
// Render filelist
|
||||
$instructions = p_get_instructions('{{filelist>' . TMP_DIR . '/filelistdata/*&style=list&direct=1&recursive=1}}');
|
||||
$instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=list&direct=1&recursive=1}}');
|
||||
$xhtml = p_render('xhtml', $instructions, $info);
|
||||
|
||||
$doc = new Document();
|
||||
$doc->html($xhtml);
|
||||
|
||||
$structure = [
|
||||
'div.filelist-plugin' => 1,
|
||||
'div.filelist-plugin > ul' => 1,
|
||||
'div.filelist-plugin > ul > li' => 3,
|
||||
'div.filelist-plugin > ul > li:nth-child(1)' => 1,
|
||||
'div.filelist-plugin > ul > li:nth-child(1) a' => 'example.txt',
|
||||
'div.filelist-plugin > ul > li:nth-child(2) ul' => 1,
|
||||
'div.filelist-plugin > ul > li:nth-child(2) ul > li' => 1,
|
||||
'div.filelist-plugin > ul > li:nth-child(2) ul > li a' => 'example2.txt',
|
||||
'div.filetools-plugin' => 1,
|
||||
'div.filetools-plugin > ul' => 1,
|
||||
'div.filetools-plugin > ul > li' => 3,
|
||||
'div.filetools-plugin > ul > li:nth-child(1)' => 1,
|
||||
'div.filetools-plugin > ul > li:nth-child(1) a' => 'example.txt',
|
||||
'div.filetools-plugin > ul > li:nth-child(2) ul' => 1,
|
||||
'div.filetools-plugin > ul > li:nth-child(2) ul > li' => 1,
|
||||
'div.filetools-plugin > ul > li:nth-child(2) ul > li a' => 'example2.txt',
|
||||
];
|
||||
|
||||
$this->structureCheck($doc, $structure);
|
||||
@@ -146,21 +146,21 @@ class plugin_filelist_test extends DokuWikiTest
|
||||
public function testOrderedList()
|
||||
{
|
||||
// Render filelist
|
||||
$instructions = p_get_instructions('{{filelist>' . TMP_DIR . '/filelistdata/*&style=olist&direct=1&recursive=1}}');
|
||||
$instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=olist&direct=1&recursive=1}}');
|
||||
$xhtml = p_render('xhtml', $instructions, $info);
|
||||
|
||||
$doc = new Document();
|
||||
$doc->html($xhtml);
|
||||
|
||||
$structure = [
|
||||
'div.filelist-plugin' => 1,
|
||||
'div.filelist-plugin > ol' => 1,
|
||||
'div.filelist-plugin > ol > li' => 3,
|
||||
'div.filelist-plugin > ol > li:nth-child(1)' => 1,
|
||||
'div.filelist-plugin > ol > li:nth-child(1) a' => 'example.txt',
|
||||
'div.filelist-plugin > ol > li:nth-child(2) ol' => 1,
|
||||
'div.filelist-plugin > ol > li:nth-child(2) ol > li' => 1,
|
||||
'div.filelist-plugin > ol > li:nth-child(2) ol > li a' => 'example2.txt',
|
||||
'div.filetools-plugin' => 1,
|
||||
'div.filetools-plugin > ol' => 1,
|
||||
'div.filetools-plugin > ol > li' => 3,
|
||||
'div.filetools-plugin > ol > li:nth-child(1)' => 1,
|
||||
'div.filetools-plugin > ol > li:nth-child(1) a' => 'example.txt',
|
||||
'div.filetools-plugin > ol > li:nth-child(2) ol' => 1,
|
||||
'div.filetools-plugin > ol > li:nth-child(2) ol > li' => 1,
|
||||
'div.filetools-plugin > ol > li:nth-child(2) ol > li a' => 'example2.txt',
|
||||
];
|
||||
|
||||
$this->structureCheck($doc, $structure);
|
||||
@@ -175,21 +175,62 @@ class plugin_filelist_test extends DokuWikiTest
|
||||
global $conf;
|
||||
|
||||
// Render filelist
|
||||
$instructions = p_get_instructions('{{filelist>' . TMP_DIR . '/filelistdata/*&style=table&direct=1&recursive=1}}');
|
||||
$instructions = p_get_instructions('{{files>' . TMP_DIR . '/filelistdata/*&style=table&direct=1&recursive=1}}');
|
||||
$xhtml = p_render('xhtml', $instructions, $info);
|
||||
|
||||
$doc = new Document();
|
||||
$doc->html($xhtml);
|
||||
|
||||
$structure = [
|
||||
'div.filelist-plugin' => 1,
|
||||
'div.filelist-plugin table' => 1,
|
||||
'div.filelist-plugin table > tbody > tr' => 3,
|
||||
'div.filelist-plugin table > tbody > tr:nth-child(1) a' => 'example.txt',
|
||||
'div.filelist-plugin table > tbody > tr:nth-child(2) a' => 'exampledir/example2.txt',
|
||||
'div.filelist-plugin table > tbody > tr:nth-child(3) a' => 'exampleimage.png',
|
||||
'div.filetools-plugin' => 1,
|
||||
'div.filetools-plugin table' => 1,
|
||||
'div.filetools-plugin table > tbody > tr' => 3,
|
||||
'div.filetools-plugin table > tbody > tr:nth-child(1) a' => 'example.txt',
|
||||
'div.filetools-plugin table > tbody > tr:nth-child(2) a' => 'exampledir/example2.txt',
|
||||
'div.filetools-plugin table > tbody > tr:nth-child(3) a' => 'exampleimage.png',
|
||||
];
|
||||
|
||||
$this->structureCheck($doc, $structure);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks that the images syntax renders a thumbnail gallery.
|
||||
*/
|
||||
public function test_images_gallery()
|
||||
{
|
||||
$instructions = p_get_instructions('{{images>' . TMP_DIR . '/filelistdata/*&direct=1}}');
|
||||
$xhtml = p_render('xhtml', $instructions, $info);
|
||||
|
||||
$doc = new Document();
|
||||
$doc->html($xhtml);
|
||||
|
||||
$structure = [
|
||||
'div.filetools-plugin.filetools-gallery' => 1,
|
||||
'div.filetools-plugin.filetools-gallery a' => 1,
|
||||
'div.filetools-plugin.filetools-gallery img' => 1,
|
||||
];
|
||||
$this->structureCheck($doc, $structure);
|
||||
|
||||
$this->assertStringContainsString('exampleimage.png', $xhtml);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks that the open syntax renders an inline button.
|
||||
*/
|
||||
public function test_open_button()
|
||||
{
|
||||
$instructions = p_get_instructions('{{open>/tmp/somewhere|Open here}}');
|
||||
$xhtml = p_render('xhtml', $instructions, $info);
|
||||
|
||||
$doc = new Document();
|
||||
$doc->html($xhtml);
|
||||
|
||||
$structure = [
|
||||
'button.filetools-open' => 1,
|
||||
];
|
||||
$this->structureCheck($doc, $structure);
|
||||
|
||||
$this->assertStringContainsString('Open here', $xhtml);
|
||||
$this->assertStringContainsString('data-path="/tmp/somewhere"', $xhtml);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Options for the filelist plugin
|
||||
* Options for the filetools plugin
|
||||
*/
|
||||
|
||||
$conf['paths'] = '';
|
||||
$conf['allow_in_comments'] = 0;
|
||||
$conf['defaults'] = '';
|
||||
$conf['extensions'] = '';
|
||||
|
||||
// Local opener service used by {{open>...}}.
|
||||
$conf['open_service_url'] = 'http://127.0.0.1:8765';
|
||||
$conf['open_service_token'] = '';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* Metadata for configuration manager plugin
|
||||
* Additions for the filelist plugin
|
||||
* Additions for the filetools plugin
|
||||
*
|
||||
* @author Gina Haeussge <osd@foosel.net>
|
||||
*/
|
||||
@@ -11,3 +11,6 @@ $meta['paths'] = array('');
|
||||
$meta['allow_in_comments'] = array('onoff');
|
||||
$meta['defaults'] = array('string');
|
||||
$meta['extensions'] = array('string');
|
||||
|
||||
$meta['open_service_url'] = array('string');
|
||||
$meta['open_service_token'] = array('string');
|
||||
|
||||
4
file.php
4
file.php
@@ -2,7 +2,7 @@
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols
|
||||
|
||||
use dokuwiki\plugin\filelist\Path;
|
||||
use dokuwiki\plugin\luxtools\Path;
|
||||
|
||||
if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../');
|
||||
if (!defined('NOSESSION')) define('NOSESSION', true); // we do not use a session or authentication here (better caching)
|
||||
@@ -11,7 +11,7 @@ require_once(DOKU_INC . 'inc/init.php');
|
||||
|
||||
global $INPUT;
|
||||
|
||||
$syntax = plugin_load('syntax', 'filelist');
|
||||
$syntax = plugin_load('syntax', 'luxtools');
|
||||
if (!$syntax) die('plugin disabled?');
|
||||
|
||||
$pathUtil = new Path($syntax->getConf('paths'));
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
<?php
|
||||
|
||||
$lang['allow_in_comments'] = 'Filelistsyntax in Kommentaren erlauben.';
|
||||
$lang['allow_in_comments'] = 'Files-Syntax in Kommentaren erlauben.';
|
||||
|
||||
$lang['open_service_url'] = 'URL des lokalen Öffner-Dienstes für {{open>...}} (z.B. http://127.0.0.1:8765).';
|
||||
$lang['open_service_token'] = 'Token für den lokalen Öffner-Dienst (X-Filetools-Token).';
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?php
|
||||
|
||||
$lang['allow_in_comments'] = 'Whether to allow the filelist syntax to be used in comments.';
|
||||
$lang['allow_in_comments'] = 'Whether to allow the files syntax to be used in comments.';
|
||||
$lang['defaults'] = 'Default options. Use the same syntax as in inline configuration';
|
||||
$lang['extensions'] = 'Comma-separated list of allowed file extensions to list';
|
||||
|
||||
$lang['open_service_url'] = 'Local opener service URL for the {{open>...}} button (e.g. http://127.0.0.1:8765).';
|
||||
$lang['open_service_token'] = 'Token sent to the local opener service (X-Filetools-Token).';
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
*
|
||||
* @author Mark C. Prins <mprins@users.sf.net>
|
||||
*/
|
||||
$lang['allow_in_comments'] = 'Of de filelist syntax toegestaan is voor gebruik in commentaar.';
|
||||
$lang['allow_in_comments'] = 'Of de files syntax toegestaan is voor gebruik in commentaar.';
|
||||
$lang['defaults'] = 'Default options. Gebruik dezelfde syntax als de inline configuratie.';
|
||||
$lang['extensions'] = 'Komma-gescheiden lijst van toegestane bestandsextensies voor de lijst.';
|
||||
|
||||
$lang['open_service_url'] = 'Lokale opener service-URL voor de {{open>...}} knop (bijv. http://127.0.0.1:8765).';
|
||||
$lang['open_service_token'] = 'Token dat naar de lokale opener service wordt gestuurd (X-Filetools-Token).';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
base filelist
|
||||
author Gina Häußge, Dokufreaks
|
||||
email freaks@dokuwiki.org
|
||||
date 2024-03-13
|
||||
name Filelist Plugin
|
||||
base luxtools
|
||||
author Gina Häußge, Dokufreaks, luxick
|
||||
email dokuwiki@luxick.de
|
||||
date 2026-01-05
|
||||
name LuxTools
|
||||
desc Lists files matching a given glob pattern.
|
||||
url https://www.dokuwiki.org/plugin:filelist
|
||||
url https://www.dokuwiki.org/plugin:luxtools
|
||||
|
||||
123
script.js
Normal file
123
script.js
Normal file
@@ -0,0 +1,123 @@
|
||||
/* global window, document */
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function getServiceUrl(el) {
|
||||
var url = el.getAttribute('data-service-url') || '';
|
||||
url = (url || '').trim();
|
||||
if (!url) return '';
|
||||
// strip trailing slashes
|
||||
return url.replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
function getServiceToken(el) {
|
||||
var token = el.getAttribute('data-service-token') || '';
|
||||
return (token || '').trim();
|
||||
}
|
||||
|
||||
function pingOpenViaImage(el, rawPath) {
|
||||
var baseUrl = getServiceUrl(el);
|
||||
if (!baseUrl) return;
|
||||
|
||||
var token = getServiceToken(el);
|
||||
var url = baseUrl + '/open?path=' + encodeURIComponent(rawPath);
|
||||
if (token) url += '&token=' + encodeURIComponent(token);
|
||||
|
||||
// Fire-and-forget without CORS.
|
||||
try {
|
||||
var img = new window.Image();
|
||||
img.src = url;
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function openViaService(el, rawPath) {
|
||||
var baseUrl = getServiceUrl(el);
|
||||
if (!baseUrl) return Promise.reject(new Error('No opener service configured'));
|
||||
|
||||
var headers = {
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
var token = getServiceToken(el);
|
||||
if (token) headers['X-Filetools-Token'] = token;
|
||||
|
||||
return window.fetch(baseUrl + '/open', {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
credentials: 'omit',
|
||||
headers: headers,
|
||||
body: JSON.stringify({ path: rawPath })
|
||||
}).then(function (res) {
|
||||
if (!res.ok) {
|
||||
return res.json().catch(function () { return null; }).then(function (body) {
|
||||
var msg = (body && body.message) ? body.message : ('HTTP ' + res.status);
|
||||
throw new Error(msg);
|
||||
});
|
||||
}
|
||||
return res.json().catch(function () { return { ok: true }; });
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeToFileUrl(path) {
|
||||
if (!path) return '';
|
||||
|
||||
// already a file URL
|
||||
if (/^file:\/\//i.test(path)) return path;
|
||||
|
||||
// UNC path: \\server\share\path
|
||||
if (/^\\\\/.test(path)) {
|
||||
var p = path.replace(/^\\\\/, '');
|
||||
p = p.replace(/\\/g, '/');
|
||||
return 'file://///' + p;
|
||||
}
|
||||
|
||||
// Windows drive: C:\path\to\file
|
||||
if (/^[a-zA-Z]:\\/.test(path)) {
|
||||
var drive = path[0].toUpperCase();
|
||||
var rest = path.slice(2).replace(/\\/g, '/');
|
||||
return 'file:///' + drive + ':' + rest;
|
||||
}
|
||||
|
||||
// POSIX absolute: /home/user/file
|
||||
if (path[0] === '/') {
|
||||
return 'file://' + path;
|
||||
}
|
||||
|
||||
// Fall back to using the provided string.
|
||||
return path;
|
||||
}
|
||||
|
||||
function onClick(event) {
|
||||
var el = event.target;
|
||||
if (!el || !el.classList || !el.classList.contains('filetools-open')) return;
|
||||
|
||||
var raw = el.getAttribute('data-path') || '';
|
||||
if (!raw) return;
|
||||
|
||||
// Prefer local opener service.
|
||||
openViaService(el, raw)
|
||||
.catch(function (err) {
|
||||
// If the browser blocks the request before it reaches localhost (mixed-content,
|
||||
// extensions, stricter CORS handling), fall back to a no-CORS GET ping.
|
||||
pingOpenViaImage(el, raw);
|
||||
|
||||
// Fallback to old behavior (often blocked in modern browsers).
|
||||
var url = normalizeToFileUrl(raw);
|
||||
if (!url) return;
|
||||
console.warn('Local opener service failed, falling back to file:// navigation:', err);
|
||||
try {
|
||||
window.open(url, '_blank', 'noopener');
|
||||
} catch (e) {
|
||||
try {
|
||||
window.location.href = url;
|
||||
} catch (e2) {
|
||||
console.error('Failed to open file URL:', e2);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('click', onClick, false);
|
||||
})();
|
||||
147
syntax.php
147
syntax.php
@@ -1,140 +1,31 @@
|
||||
<?php
|
||||
|
||||
use dokuwiki\Extension\SyntaxPlugin;
|
||||
use dokuwiki\plugin\filelist\Crawler;
|
||||
use dokuwiki\plugin\filelist\Output;
|
||||
use dokuwiki\plugin\filelist\Path;
|
||||
require_once(__DIR__ . '/syntax/files.php');
|
||||
|
||||
/**
|
||||
* Filelist Plugin: Lists files matching a given glob pattern.
|
||||
* LuxTools plugin bootstrap.
|
||||
*
|
||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
||||
* @author Gina Haeussge <osd@foosel.net>
|
||||
* The actual {{files>...}} syntax implementation lives in syntax/files.php.
|
||||
*/
|
||||
class syntax_plugin_filelist extends SyntaxPlugin
|
||||
class syntax_plugin_luxtools extends syntax_plugin_luxtools_files
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function getType()
|
||||
{
|
||||
return 'substition';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getPType()
|
||||
{
|
||||
return 'block';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSort()
|
||||
{
|
||||
return 222;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function connectTo($mode)
|
||||
{
|
||||
$this->Lexer->addSpecialPattern('\{\{filelist>.+?\}\}', $mode, 'plugin_filelist');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function handle($match, $state, $pos, Doku_Handler $handler)
|
||||
{
|
||||
global $INPUT;
|
||||
|
||||
// do not allow the syntax in discussion plugin comments
|
||||
if (!$this->getConf('allow_in_comments') && $INPUT->has('comment')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = substr($match, strlen('{{filelist>'), -2);
|
||||
[$path, $flags] = explode('&', $match, 2);
|
||||
|
||||
// load default config options
|
||||
$flags = $this->getConf('defaults') . '&' . $flags;
|
||||
$flags = explode('&', $flags);
|
||||
|
||||
$params = [
|
||||
'sort' => 'name',
|
||||
'order' => 'asc',
|
||||
'style' => 'list',
|
||||
'tableheader' => 0,
|
||||
'recursive' => 0,
|
||||
'titlefile' => '_title.txt',
|
||||
'cache' => 0,
|
||||
'randlinks' => 0,
|
||||
'showsize' => 0,
|
||||
'showdate' => 0,
|
||||
'listsep' => ', ',
|
||||
];
|
||||
foreach ($flags as $flag) {
|
||||
[$name, $value] = sexplode('=', $flag, 2, '');
|
||||
$params[trim($name)] = trim(trim($value), '"'); // quotes can be use to keep whitespace
|
||||
}
|
||||
|
||||
// separate path and pattern
|
||||
$path = Path::cleanPath($path, false);
|
||||
$parts = explode('/', $path);
|
||||
$pattern = array_pop($parts);
|
||||
$base = implode('/', $parts) . '/';
|
||||
|
||||
return [$base, $pattern, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create output
|
||||
*/
|
||||
public function render($format, Doku_Renderer $renderer, $data)
|
||||
{
|
||||
[$base, $pattern, $params] = $data;
|
||||
|
||||
if ($format != 'xhtml' && $format != 'odt') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// disable caching
|
||||
if ($params['cache'] === 0) {
|
||||
$renderer->nocache();
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$pathHelper = new Path($this->getConf('paths'));
|
||||
$pathInfo = $pathHelper->getPathInfo($base);
|
||||
} catch (Exception $e) {
|
||||
$renderer->cdata('[n/a: ' . $this->getLang('error_outsidejail') . ']');
|
||||
return true;
|
||||
}
|
||||
|
||||
$crawler = new Crawler($this->getConf('extensions'));
|
||||
$crawler->setSortBy($params['sort']);
|
||||
$crawler->setSortReverse($params['order'] === 'desc');
|
||||
|
||||
$result = $crawler->crawl(
|
||||
$pathInfo['root'],
|
||||
$pathInfo['local'],
|
||||
$pattern,
|
||||
$params['recursive'],
|
||||
$params['titlefile']
|
||||
);
|
||||
|
||||
// if we got nothing back, display a message
|
||||
if ($result == []) {
|
||||
$renderer->cdata('[n/a: ' . $this->getLang('error_nomatch') . ']');
|
||||
return true;
|
||||
}
|
||||
|
||||
$output = new Output($renderer, $pathInfo['root'], $pathInfo['web'], $result);
|
||||
|
||||
switch ($params['style']) {
|
||||
case 'list':
|
||||
case 'olist':
|
||||
$output->renderAsList($params);
|
||||
break;
|
||||
case 'table':
|
||||
$output->renderAsTable($params);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
// Intentionally empty: syntax is registered by syntax_plugin_luxtools_files.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility alias for older codebases that referenced the legacy class name.
|
||||
*
|
||||
* Note: plugin id/base is now `luxtools`.
|
||||
*/
|
||||
class syntax_plugin_filetools extends syntax_plugin_luxtools_files
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function connectTo($mode)
|
||||
{
|
||||
// Intentionally empty: syntax is registered by syntax_plugin_luxtools_files.
|
||||
}
|
||||
}
|
||||
|
||||
138
syntax/files.php
Normal file
138
syntax/files.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
use dokuwiki\Extension\SyntaxPlugin;
|
||||
use dokuwiki\plugin\luxtools\Crawler;
|
||||
use dokuwiki\plugin\luxtools\Output;
|
||||
use dokuwiki\plugin\luxtools\Path;
|
||||
|
||||
/**
|
||||
* LuxTools Plugin: Files syntax.
|
||||
*
|
||||
* Lists files matching a given glob pattern.
|
||||
*/
|
||||
class syntax_plugin_luxtools_files extends SyntaxPlugin
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function getType()
|
||||
{
|
||||
return 'substition';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getPType()
|
||||
{
|
||||
return 'block';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSort()
|
||||
{
|
||||
return 222;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function connectTo($mode)
|
||||
{
|
||||
$this->Lexer->addSpecialPattern('\{\{files>.+?\}\}', $mode, 'plugin_luxtools_files');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function handle($match, $state, $pos, Doku_Handler $handler)
|
||||
{
|
||||
global $INPUT;
|
||||
|
||||
// do not allow the syntax in discussion plugin comments
|
||||
if (!$this->getConf('allow_in_comments') && $INPUT->has('comment')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = substr($match, strlen('{{files>'), -2);
|
||||
[$path, $flags] = explode('&', $match, 2);
|
||||
|
||||
// load default config options
|
||||
$flags = $this->getConf('defaults') . '&' . $flags;
|
||||
$flags = explode('&', $flags);
|
||||
|
||||
$params = [
|
||||
'sort' => 'name',
|
||||
'order' => 'asc',
|
||||
'style' => 'list',
|
||||
'tableheader' => 0,
|
||||
'recursive' => 0,
|
||||
'titlefile' => '_title.txt',
|
||||
'cache' => 0,
|
||||
'randlinks' => 0,
|
||||
'showsize' => 0,
|
||||
'showdate' => 0,
|
||||
'listsep' => ', ',
|
||||
];
|
||||
foreach ($flags as $flag) {
|
||||
[$name, $value] = sexplode('=', $flag, 2, '');
|
||||
$params[trim($name)] = trim(trim($value), '"'); // quotes can be use to keep whitespace
|
||||
}
|
||||
|
||||
// separate path and pattern
|
||||
$path = Path::cleanPath($path, false);
|
||||
$parts = explode('/', $path);
|
||||
$pattern = array_pop($parts);
|
||||
$base = implode('/', $parts) . '/';
|
||||
|
||||
return [$base, $pattern, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create output
|
||||
*/
|
||||
public function render($format, Doku_Renderer $renderer, $data)
|
||||
{
|
||||
[$base, $pattern, $params] = $data;
|
||||
|
||||
if ($format != 'xhtml' && $format != 'odt') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// disable caching
|
||||
if ($params['cache'] === 0) {
|
||||
$renderer->nocache();
|
||||
}
|
||||
|
||||
try {
|
||||
$pathHelper = new Path($this->getConf('paths'));
|
||||
$pathInfo = $pathHelper->getPathInfo($base);
|
||||
} catch (Exception $e) {
|
||||
$renderer->cdata('[n/a: ' . $this->getLang('error_outsidejail') . ']');
|
||||
return true;
|
||||
}
|
||||
|
||||
$crawler = new Crawler($this->getConf('extensions'));
|
||||
$crawler->setSortBy($params['sort']);
|
||||
$crawler->setSortReverse($params['order'] === 'desc');
|
||||
|
||||
$result = $crawler->crawl(
|
||||
$pathInfo['root'],
|
||||
$pathInfo['local'],
|
||||
$pattern,
|
||||
$params['recursive'],
|
||||
$params['titlefile']
|
||||
);
|
||||
|
||||
// if we got nothing back, display a message
|
||||
if ($result == []) {
|
||||
$renderer->cdata('[n/a: ' . $this->getLang('error_nomatch') . ']');
|
||||
return true;
|
||||
}
|
||||
|
||||
$output = new Output($renderer, $pathInfo['root'], $pathInfo['web'], $result);
|
||||
|
||||
switch ($params['style']) {
|
||||
case 'list':
|
||||
case 'olist':
|
||||
$output->renderAsList($params);
|
||||
break;
|
||||
case 'table':
|
||||
$output->renderAsTable($params);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
189
syntax/images.php
Normal file
189
syntax/images.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
use dokuwiki\Extension\SyntaxPlugin;
|
||||
use dokuwiki\plugin\luxtools\Crawler;
|
||||
use dokuwiki\plugin\luxtools\Output;
|
||||
use dokuwiki\plugin\luxtools\Path;
|
||||
|
||||
/**
|
||||
* LuxTools Plugin: Image gallery syntax.
|
||||
*
|
||||
* Renders a thumbnail gallery of images matching a glob pattern.
|
||||
*/
|
||||
class syntax_plugin_luxtools_images extends SyntaxPlugin
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function getType()
|
||||
{
|
||||
return 'substition';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getPType()
|
||||
{
|
||||
return 'block';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSort()
|
||||
{
|
||||
return 222;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function connectTo($mode)
|
||||
{
|
||||
$this->Lexer->addSpecialPattern('\{\{images>.+?\}\}', $mode, 'plugin_luxtools_images');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function handle($match, $state, $pos, Doku_Handler $handler)
|
||||
{
|
||||
global $INPUT;
|
||||
|
||||
// do not allow the syntax in discussion plugin comments
|
||||
if (!$this->getConf('allow_in_comments') && $INPUT->has('comment')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$match = substr($match, strlen('{{images>'), -2);
|
||||
[$path, $flags] = explode('&', $match, 2);
|
||||
|
||||
// load default config options
|
||||
$flags = $this->getConf('defaults') . '&' . $flags;
|
||||
$flags = explode('&', $flags);
|
||||
|
||||
$params = [
|
||||
'sort' => 'name',
|
||||
'order' => 'asc',
|
||||
'recursive' => 0,
|
||||
'titlefile' => '_title.txt',
|
||||
'cache' => 0,
|
||||
'randlinks' => 0,
|
||||
];
|
||||
|
||||
foreach ($flags as $flag) {
|
||||
[$name, $value] = sexplode('=', $flag, 2, '');
|
||||
$params[trim($name)] = trim(trim($value), '"'); // quotes can be use to keep whitespace
|
||||
}
|
||||
|
||||
// separate path and pattern
|
||||
$path = Path::cleanPath($path, false);
|
||||
$parts = explode('/', $path);
|
||||
$pattern = array_pop($parts);
|
||||
$base = implode('/', $parts) . '/';
|
||||
|
||||
return [$base, $pattern, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create output
|
||||
*/
|
||||
public function render($format, Doku_Renderer $renderer, $data)
|
||||
{
|
||||
[$base, $pattern, $params] = $data;
|
||||
|
||||
if ($format != 'xhtml' && $format != 'odt') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// disable caching
|
||||
if ($params['cache'] === 0) {
|
||||
$renderer->nocache();
|
||||
}
|
||||
|
||||
try {
|
||||
$pathHelper = new Path($this->getConf('paths'));
|
||||
$pathInfo = $pathHelper->getPathInfo($base);
|
||||
} catch (Exception $e) {
|
||||
$renderer->cdata('[n/a: ' . $this->getLang('error_outsidejail') . ']');
|
||||
return true;
|
||||
}
|
||||
|
||||
$crawler = new Crawler($this->getConf('extensions'));
|
||||
$crawler->setSortBy($params['sort']);
|
||||
$crawler->setSortReverse($params['order'] === 'desc');
|
||||
|
||||
$result = $crawler->crawl(
|
||||
$pathInfo['root'],
|
||||
$pathInfo['local'],
|
||||
$pattern,
|
||||
$params['recursive'],
|
||||
$params['titlefile']
|
||||
);
|
||||
|
||||
$items = $this->flattenResultTree($result);
|
||||
$items = $this->filterImages($items);
|
||||
|
||||
// if we got nothing back, display a message
|
||||
if ($items == []) {
|
||||
$renderer->cdata('[n/a: ' . $this->getLang('error_nomatch') . ']');
|
||||
return true;
|
||||
}
|
||||
|
||||
$output = new Output($renderer, $pathInfo['root'], $pathInfo['web'], $items);
|
||||
|
||||
if ($format == 'xhtml') {
|
||||
$output->renderAsGallery($params);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback for non-XHTML formats: render as a list of links
|
||||
$params['style'] = 'list';
|
||||
$params['showsize'] = 0;
|
||||
$params['showdate'] = 0;
|
||||
$params['listsep'] = ', ';
|
||||
$output->renderAsList($params);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens the crawl result tree into a list of file items.
|
||||
*
|
||||
* @param array $items
|
||||
* @param string $prefix
|
||||
* @return array
|
||||
*/
|
||||
protected function flattenResultTree($items, $prefix = '')
|
||||
{
|
||||
$result = [];
|
||||
foreach ($items as $file) {
|
||||
if ($file['children'] !== false) {
|
||||
$result = array_merge(
|
||||
$result,
|
||||
$this->flattenResultTree($file['children'], $prefix . $file['name'] . '/')
|
||||
);
|
||||
} else {
|
||||
$file['name'] = $prefix . $file['name'];
|
||||
$result[] = $file;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep only image files.
|
||||
*
|
||||
* @param array $items
|
||||
* @return array
|
||||
*/
|
||||
protected function filterImages($items)
|
||||
{
|
||||
$images = [];
|
||||
foreach ($items as $item) {
|
||||
if (!isset($item['path']) || !is_string($item['path'])) continue;
|
||||
if (!is_file($item['path'])) continue;
|
||||
|
||||
try {
|
||||
[, $mime,] = mimetype($item['path'], false);
|
||||
} catch (Throwable $e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_string($mime) && str_starts_with($mime, 'image/')) {
|
||||
$images[] = $item;
|
||||
}
|
||||
}
|
||||
return $images;
|
||||
}
|
||||
}
|
||||
91
syntax/open.php
Normal file
91
syntax/open.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
use dokuwiki\Extension\SyntaxPlugin;
|
||||
|
||||
/**
|
||||
* LuxTools Plugin: Open local path syntax.
|
||||
*
|
||||
* Renders an inline button. Clicking it triggers client-side JS that attempts
|
||||
* to open the configured path in the default file manager (best-effort).
|
||||
*/
|
||||
class syntax_plugin_luxtools_open extends SyntaxPlugin
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function getType()
|
||||
{
|
||||
return 'substition';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getPType()
|
||||
{
|
||||
// inline
|
||||
return 'normal';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSort()
|
||||
{
|
||||
return 222;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function connectTo($mode)
|
||||
{
|
||||
$this->Lexer->addSpecialPattern('\{\{open>.+?\}\}', $mode, 'plugin_luxtools_open');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function handle($match, $state, $pos, Doku_Handler $handler)
|
||||
{
|
||||
$match = substr($match, strlen('{{open>'), -2);
|
||||
[$path, $caption] = array_pad(explode('|', $match, 2), 2, '');
|
||||
|
||||
$path = trim($path);
|
||||
$caption = trim($caption);
|
||||
if ($caption === '') $caption = $path !== '' ? $path : 'Open';
|
||||
|
||||
// Basic scheme filtering to avoid javascript: style injections.
|
||||
// Allow either file:// URLs, or plain paths (Windows/UNC/Linux style).
|
||||
if (preg_match('/^[a-zA-Z][a-zA-Z0-9+.-]*:/', $path)) {
|
||||
if (!str_starts_with(strtolower($path), 'file://')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return [$path, $caption];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function render($format, Doku_Renderer $renderer, $data)
|
||||
{
|
||||
if ($data === false) return false;
|
||||
[$path, $caption] = $data;
|
||||
|
||||
if ($format !== 'xhtml') {
|
||||
// no meaningful representation in non-browser formats
|
||||
$renderer->cdata($caption);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($path === '') {
|
||||
$renderer->cdata('[n/a]');
|
||||
return true;
|
||||
}
|
||||
|
||||
$serviceUrl = trim((string)$this->getConf('open_service_url'));
|
||||
$serviceToken = trim((string)$this->getConf('open_service_token'));
|
||||
|
||||
$attrs = ' type="button" class="filetools-open"'
|
||||
. ' data-path="' . hsc($path) . '"';
|
||||
if ($serviceUrl !== '') {
|
||||
$attrs .= ' data-service-url="' . hsc($serviceUrl) . '"';
|
||||
}
|
||||
if ($serviceToken !== '') {
|
||||
$attrs .= ' data-service-token="' . hsc($serviceToken) . '"';
|
||||
}
|
||||
|
||||
$renderer->doc .= '<button' . $attrs . '>' . hsc($caption) . '</button>';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user