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
|
<?php
|
||||||
|
|
||||||
namespace dokuwiki\plugin\filelist;
|
namespace dokuwiki\plugin\luxtools;
|
||||||
|
|
||||||
class Crawler
|
class Crawler
|
||||||
{
|
{
|
||||||
@@ -58,7 +58,7 @@ class Crawler
|
|||||||
$path = $root . $local;
|
$path = $root . $local;
|
||||||
|
|
||||||
// do not descent into wiki or data directories
|
// do not descent into wiki or data directories
|
||||||
if(Path::isWikiControlled($path)) return [];
|
if (Path::isWikiControlled($path)) return [];
|
||||||
|
|
||||||
if (($dir = opendir($path)) === false) return [];
|
if (($dir = opendir($path)) === false) return [];
|
||||||
$result = [];
|
$result = [];
|
||||||
|
|||||||
42
Output.php
42
Output.php
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace dokuwiki\plugin\filelist;
|
namespace dokuwiki\plugin\luxtools;
|
||||||
|
|
||||||
class Output
|
class Output
|
||||||
{
|
{
|
||||||
@@ -28,7 +28,7 @@ class Output
|
|||||||
public function renderAsList($params)
|
public function renderAsList($params)
|
||||||
{
|
{
|
||||||
if ($this->renderer instanceof \Doku_Renderer_xhtml) {
|
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);
|
$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.
|
* Renders the files as a table, including details if configured that way.
|
||||||
*
|
*
|
||||||
@@ -46,7 +80,7 @@ class Output
|
|||||||
public function renderAsTable($params)
|
public function renderAsTable($params)
|
||||||
{
|
{
|
||||||
if ($this->renderer instanceof \Doku_Renderer_xhtml) {
|
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);
|
$items = $this->flattenResultTree($this->files);
|
||||||
@@ -277,7 +311,7 @@ class Output
|
|||||||
|
|
||||||
protected function getLang($key)
|
protected function getLang($key)
|
||||||
{
|
{
|
||||||
$syntax = plugin_load('syntax', 'filelist');
|
$syntax = plugin_load('syntax', 'luxtools');
|
||||||
return $syntax->getLang($key);
|
return $syntax->getLang($key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
Path.php
8
Path.php
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace dokuwiki\plugin\filelist;
|
namespace dokuwiki\plugin\luxtools;
|
||||||
|
|
||||||
class Path
|
class Path
|
||||||
{
|
{
|
||||||
@@ -60,7 +60,7 @@ class Path
|
|||||||
$lastRoot = $line;
|
$lastRoot = $line;
|
||||||
$paths[$line] = [
|
$paths[$line] = [
|
||||||
'root' => $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);
|
$path = static::cleanPath($path, $addTrailingSlash);
|
||||||
|
|
||||||
$paths = $this->paths;
|
$paths = $this->paths;
|
||||||
|
if ($paths === []) {
|
||||||
|
throw new \Exception('No paths configured');
|
||||||
|
}
|
||||||
|
|
||||||
$allowed = array_keys($paths);
|
$allowed = array_keys($paths);
|
||||||
usort($allowed, static fn($a, $b) => strlen($a) - strlen($b));
|
usort($allowed, static fn($a, $b) => strlen($a) - strlen($b));
|
||||||
$allowed = array_map('preg_quote_cb', $allowed);
|
$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.
|
Lists files matching a given glob pattern.
|
||||||
|
|
||||||
All documentation for this plugin can be found at
|
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
|
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!
|
will not work!
|
||||||
|
|
||||||
|
Syntax:
|
||||||
|
{{files>...}}
|
||||||
|
|
||||||
Please refer to http://www.dokuwiki.org/extensions for additional info
|
Please refer to http://www.dokuwiki.org/extensions for additional info
|
||||||
on how to install extensions in DokuWiki.
|
on how to install extensions in DokuWiki.
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace dokuwiki\plugin\filelist\test;
|
namespace dokuwiki\plugin\luxtools\test;
|
||||||
|
|
||||||
use DokuWikiTest;
|
use DokuWikiTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General tests for the filelist plugin
|
* General tests for the luxtools plugin
|
||||||
*
|
*
|
||||||
* @group plugin_filelist
|
* @group plugin_luxtools
|
||||||
* @group plugins
|
* @group plugins
|
||||||
*/
|
*/
|
||||||
class GeneralTest extends DokuWikiTest
|
class GeneralTest extends DokuWikiTest
|
||||||
@@ -31,7 +31,7 @@ class GeneralTest extends DokuWikiTest
|
|||||||
$this->assertArrayHasKey('desc', $info);
|
$this->assertArrayHasKey('desc', $info);
|
||||||
$this->assertArrayHasKey('url', $info);
|
$this->assertArrayHasKey('url', $info);
|
||||||
|
|
||||||
$this->assertEquals('filelist', $info['base']);
|
$this->assertEquals('luxtools', $info['base']);
|
||||||
$this->assertRegExp('/^https?:\/\//', $info['url']);
|
$this->assertRegExp('/^https?:\/\//', $info['url']);
|
||||||
$this->assertTrue(mail_isvalid($info['email']));
|
$this->assertTrue(mail_isvalid($info['email']));
|
||||||
$this->assertRegExp('/^\d\d\d\d-\d\d-\d\d$/', $info['date']);
|
$this->assertRegExp('/^\d\d\d\d-\d\d-\d\d$/', $info['date']);
|
||||||
@@ -61,7 +61,7 @@ class GeneralTest extends DokuWikiTest
|
|||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
gettype($conf),
|
gettype($conf),
|
||||||
gettype($meta),
|
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) {
|
if ($conf !== null && $meta !== null) {
|
||||||
@@ -69,7 +69,7 @@ class GeneralTest extends DokuWikiTest
|
|||||||
$this->assertArrayHasKey(
|
$this->assertArrayHasKey(
|
||||||
$key,
|
$key,
|
||||||
$meta,
|
$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(
|
$this->assertArrayHasKey(
|
||||||
$key,
|
$key,
|
||||||
$conf,
|
$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
|
<?php
|
||||||
|
|
||||||
namespace dokuwiki\plugin\filelist\test;
|
namespace dokuwiki\plugin\luxtools\test;
|
||||||
|
|
||||||
use dokuwiki\plugin\filelist\Path;
|
use dokuwiki\plugin\luxtools\Path;
|
||||||
use DokuWikiTest;
|
use DokuWikiTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path related tests for the filelist plugin
|
* Path related tests for the luxtools plugin
|
||||||
*
|
*
|
||||||
* @group plugin_filelist
|
* @group plugin_luxtools
|
||||||
* @group plugins
|
* @group plugins
|
||||||
*/
|
*/
|
||||||
class PathTest extends DokuWikiTest
|
class PathTest extends DokuWikiTest
|
||||||
@@ -40,15 +40,15 @@ EOT
|
|||||||
$expect = [
|
$expect = [
|
||||||
'C:/xampp/htdocs/wiki/' => [
|
'C:/xampp/htdocs/wiki/' => [
|
||||||
'root' => '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/' => [
|
'\\\\server/share/path/' => [
|
||||||
'root' => '\\\\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/' => [
|
'/linux/file/path/' => [
|
||||||
'root' => '/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/' => [
|
'/linux/another/path/' => [
|
||||||
'root' => '/linux/another/path/',
|
'root' => '/linux/another/path/',
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace dokuwiki\plugin\filelist\test;
|
namespace dokuwiki\plugin\luxtools\test;
|
||||||
|
|
||||||
use DokuWikiTest;
|
use DokuWikiTest;
|
||||||
use DOMWrap\Document;
|
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)
|
* - exampledir (directory)
|
||||||
* - example2.txt (text file)
|
* - example2.txt (text file)
|
||||||
* - example.txt (text file)
|
* - example.txt (text file)
|
||||||
* - exampleimage.png (image file)
|
* - exampleimage.png (image file)
|
||||||
*
|
*
|
||||||
* @group plugin_filelist
|
* @group plugin_luxtools
|
||||||
* @group plugins
|
* @group plugins
|
||||||
*/
|
*/
|
||||||
class plugin_filelist_test extends DokuWikiTest
|
class plugin_luxtools_test extends DokuWikiTest
|
||||||
{
|
{
|
||||||
|
|
||||||
public function setUp(): void
|
public function setUp(): void
|
||||||
{
|
{
|
||||||
global $conf;
|
global $conf;
|
||||||
|
|
||||||
$this->pluginsEnabled[] = 'filelist';
|
$this->pluginsEnabled[] = 'luxtools';
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
// Setup config so that access to the TMP directory will be allowed
|
// 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;
|
global $conf;
|
||||||
|
|
||||||
// Render filelist
|
// 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);
|
$xhtml = p_render('xhtml', $instructions, $info);
|
||||||
|
|
||||||
// We should find:
|
// We should find:
|
||||||
@@ -94,7 +94,7 @@ class plugin_filelist_test extends DokuWikiTest
|
|||||||
public function test_recursive()
|
public function test_recursive()
|
||||||
{
|
{
|
||||||
// Render filelist
|
// 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);
|
$xhtml = p_render('xhtml', $instructions, $info);
|
||||||
|
|
||||||
// We should find:
|
// We should find:
|
||||||
@@ -119,21 +119,21 @@ class plugin_filelist_test extends DokuWikiTest
|
|||||||
public function testUnorderedList()
|
public function testUnorderedList()
|
||||||
{
|
{
|
||||||
// Render filelist
|
// 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);
|
$xhtml = p_render('xhtml', $instructions, $info);
|
||||||
|
|
||||||
$doc = new Document();
|
$doc = new Document();
|
||||||
$doc->html($xhtml);
|
$doc->html($xhtml);
|
||||||
|
|
||||||
$structure = [
|
$structure = [
|
||||||
'div.filelist-plugin' => 1,
|
'div.filetools-plugin' => 1,
|
||||||
'div.filelist-plugin > ul' => 1,
|
'div.filetools-plugin > ul' => 1,
|
||||||
'div.filelist-plugin > ul > li' => 3,
|
'div.filetools-plugin > ul > li' => 3,
|
||||||
'div.filelist-plugin > ul > li:nth-child(1)' => 1,
|
'div.filetools-plugin > ul > li:nth-child(1)' => 1,
|
||||||
'div.filelist-plugin > ul > li:nth-child(1) a' => 'example.txt',
|
'div.filetools-plugin > ul > li:nth-child(1) a' => 'example.txt',
|
||||||
'div.filelist-plugin > ul > li:nth-child(2) ul' => 1,
|
'div.filetools-plugin > ul > li:nth-child(2) ul' => 1,
|
||||||
'div.filelist-plugin > ul > li:nth-child(2) ul > li' => 1,
|
'div.filetools-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 > ul > li:nth-child(2) ul > li a' => 'example2.txt',
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->structureCheck($doc, $structure);
|
$this->structureCheck($doc, $structure);
|
||||||
@@ -146,21 +146,21 @@ class plugin_filelist_test extends DokuWikiTest
|
|||||||
public function testOrderedList()
|
public function testOrderedList()
|
||||||
{
|
{
|
||||||
// Render filelist
|
// 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);
|
$xhtml = p_render('xhtml', $instructions, $info);
|
||||||
|
|
||||||
$doc = new Document();
|
$doc = new Document();
|
||||||
$doc->html($xhtml);
|
$doc->html($xhtml);
|
||||||
|
|
||||||
$structure = [
|
$structure = [
|
||||||
'div.filelist-plugin' => 1,
|
'div.filetools-plugin' => 1,
|
||||||
'div.filelist-plugin > ol' => 1,
|
'div.filetools-plugin > ol' => 1,
|
||||||
'div.filelist-plugin > ol > li' => 3,
|
'div.filetools-plugin > ol > li' => 3,
|
||||||
'div.filelist-plugin > ol > li:nth-child(1)' => 1,
|
'div.filetools-plugin > ol > li:nth-child(1)' => 1,
|
||||||
'div.filelist-plugin > ol > li:nth-child(1) a' => 'example.txt',
|
'div.filetools-plugin > ol > li:nth-child(1) a' => 'example.txt',
|
||||||
'div.filelist-plugin > ol > li:nth-child(2) ol' => 1,
|
'div.filetools-plugin > ol > li:nth-child(2) ol' => 1,
|
||||||
'div.filelist-plugin > ol > li:nth-child(2) ol > li' => 1,
|
'div.filetools-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 > ol > li:nth-child(2) ol > li a' => 'example2.txt',
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->structureCheck($doc, $structure);
|
$this->structureCheck($doc, $structure);
|
||||||
@@ -175,21 +175,62 @@ class plugin_filelist_test extends DokuWikiTest
|
|||||||
global $conf;
|
global $conf;
|
||||||
|
|
||||||
// Render filelist
|
// 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);
|
$xhtml = p_render('xhtml', $instructions, $info);
|
||||||
|
|
||||||
$doc = new Document();
|
$doc = new Document();
|
||||||
$doc->html($xhtml);
|
$doc->html($xhtml);
|
||||||
|
|
||||||
$structure = [
|
$structure = [
|
||||||
'div.filelist-plugin' => 1,
|
'div.filetools-plugin' => 1,
|
||||||
'div.filelist-plugin table' => 1,
|
'div.filetools-plugin table' => 1,
|
||||||
'div.filelist-plugin table > tbody > tr' => 3,
|
'div.filetools-plugin table > tbody > tr' => 3,
|
||||||
'div.filelist-plugin table > tbody > tr:nth-child(1) a' => 'example.txt',
|
'div.filetools-plugin table > tbody > tr:nth-child(1) a' => 'example.txt',
|
||||||
'div.filelist-plugin table > tbody > tr:nth-child(2) a' => 'exampledir/example2.txt',
|
'div.filetools-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 table > tbody > tr:nth-child(3) a' => 'exampleimage.png',
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->structureCheck($doc, $structure);
|
$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
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for the filelist plugin
|
* Options for the filetools plugin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$conf['paths'] = '';
|
$conf['paths'] = '';
|
||||||
$conf['allow_in_comments'] = 0;
|
$conf['allow_in_comments'] = 0;
|
||||||
$conf['defaults'] = '';
|
$conf['defaults'] = '';
|
||||||
$conf['extensions'] = '';
|
$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
|
* Metadata for configuration manager plugin
|
||||||
* Additions for the filelist plugin
|
* Additions for the filetools plugin
|
||||||
*
|
*
|
||||||
* @author Gina Haeussge <osd@foosel.net>
|
* @author Gina Haeussge <osd@foosel.net>
|
||||||
*/
|
*/
|
||||||
@@ -11,3 +11,6 @@ $meta['paths'] = array('');
|
|||||||
$meta['allow_in_comments'] = array('onoff');
|
$meta['allow_in_comments'] = array('onoff');
|
||||||
$meta['defaults'] = array('string');
|
$meta['defaults'] = array('string');
|
||||||
$meta['extensions'] = 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
|
// 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('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../');
|
||||||
if (!defined('NOSESSION')) define('NOSESSION', true); // we do not use a session or authentication here (better caching)
|
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;
|
global $INPUT;
|
||||||
|
|
||||||
$syntax = plugin_load('syntax', 'filelist');
|
$syntax = plugin_load('syntax', 'luxtools');
|
||||||
if (!$syntax) die('plugin disabled?');
|
if (!$syntax) die('plugin disabled?');
|
||||||
|
|
||||||
$pathUtil = new Path($syntax->getConf('paths'));
|
$pathUtil = new Path($syntax->getConf('paths'));
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
<?php
|
<?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
|
<?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['defaults'] = 'Default options. Use the same syntax as in inline configuration';
|
||||||
$lang['extensions'] = 'Comma-separated list of allowed file extensions to list';
|
$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>
|
* @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['defaults'] = 'Default options. Gebruik dezelfde syntax als de inline configuratie.';
|
||||||
$lang['extensions'] = 'Komma-gescheiden lijst van toegestane bestandsextensies voor de lijst.';
|
$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
|
base luxtools
|
||||||
author Gina Häußge, Dokufreaks
|
author Gina Häußge, Dokufreaks, luxick
|
||||||
email freaks@dokuwiki.org
|
email dokuwiki@luxick.de
|
||||||
date 2024-03-13
|
date 2026-01-05
|
||||||
name Filelist Plugin
|
name LuxTools
|
||||||
desc Lists files matching a given glob pattern.
|
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
|
<?php
|
||||||
|
|
||||||
use dokuwiki\Extension\SyntaxPlugin;
|
require_once(__DIR__ . '/syntax/files.php');
|
||||||
use dokuwiki\plugin\filelist\Crawler;
|
|
||||||
use dokuwiki\plugin\filelist\Output;
|
|
||||||
use dokuwiki\plugin\filelist\Path;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filelist Plugin: Lists files matching a given glob pattern.
|
* LuxTools plugin bootstrap.
|
||||||
*
|
*
|
||||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
* The actual {{files>...}} syntax implementation lives in syntax/files.php.
|
||||||
* @author Gina Haeussge <osd@foosel.net>
|
|
||||||
*/
|
*/
|
||||||
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 */
|
/** @inheritdoc */
|
||||||
public function connectTo($mode)
|
public function connectTo($mode)
|
||||||
{
|
{
|
||||||
$this->Lexer->addSpecialPattern('\{\{filelist>.+?\}\}', $mode, 'plugin_filelist');
|
// Intentionally empty: syntax is registered by syntax_plugin_luxtools_files.
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/** @inheritdoc */
|
|
||||||
public function handle($match, $state, $pos, Doku_Handler $handler)
|
/**
|
||||||
{
|
* Compatibility alias for older codebases that referenced the legacy class name.
|
||||||
global $INPUT;
|
*
|
||||||
|
* Note: plugin id/base is now `luxtools`.
|
||||||
// do not allow the syntax in discussion plugin comments
|
*/
|
||||||
if (!$this->getConf('allow_in_comments') && $INPUT->has('comment')) {
|
class syntax_plugin_filetools extends syntax_plugin_luxtools_files
|
||||||
return false;
|
{
|
||||||
}
|
/** @inheritdoc */
|
||||||
|
public function connectTo($mode)
|
||||||
$match = substr($match, strlen('{{filelist>'), -2);
|
{
|
||||||
[$path, $flags] = explode('&', $match, 2);
|
// Intentionally empty: syntax is registered by syntax_plugin_luxtools_files.
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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