Inital implementation of image listing
This commit is contained in:
34
Output.php
34
Output.php
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -192,4 +192,25 @@ class plugin_filetools_test extends DokuWikiTest
|
|||||||
|
|
||||||
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
136
syntax.php
136
syntax.php
@@ -1,140 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use dokuwiki\Extension\SyntaxPlugin;
|
require_once(__DIR__ . '/syntax/files.php');
|
||||||
use dokuwiki\plugin\filetools\Crawler;
|
|
||||||
use dokuwiki\plugin\filetools\Output;
|
|
||||||
use dokuwiki\plugin\filetools\Path;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filelist Plus Plugin: Lists files matching a given glob pattern.
|
* File Tools plugin compatibility shim.
|
||||||
*
|
*
|
||||||
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
* Keep this class so existing code that does `plugin_load('syntax', 'filetools')`
|
||||||
* @author Gina Haeussge <osd@foosel.net>
|
* continues to work (config/lang access).
|
||||||
|
*
|
||||||
|
* The actual {{files>...}} syntax implementation lives in syntax/files.php.
|
||||||
*/
|
*/
|
||||||
class syntax_plugin_filetools extends SyntaxPlugin
|
class syntax_plugin_filetools extends syntax_plugin_filetools_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('\{\{files>.+?\}\}', $mode, 'plugin_filetools');
|
// Intentionally empty: syntax is registered by syntax_plugin_filetools_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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
138
syntax/files.php
Normal file
138
syntax/files.php
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use dokuwiki\Extension\SyntaxPlugin;
|
||||||
|
use dokuwiki\plugin\filetools\Crawler;
|
||||||
|
use dokuwiki\plugin\filetools\Output;
|
||||||
|
use dokuwiki\plugin\filetools\Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File Tools Plugin: Files syntax.
|
||||||
|
*
|
||||||
|
* Lists files matching a given glob pattern.
|
||||||
|
*/
|
||||||
|
class syntax_plugin_filetools_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_filetools_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\filetools\Crawler;
|
||||||
|
use dokuwiki\plugin\filetools\Output;
|
||||||
|
use dokuwiki\plugin\filetools\Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File Tools Plugin: Image gallery syntax.
|
||||||
|
*
|
||||||
|
* Renders a thumbnail gallery of images matching a glob pattern.
|
||||||
|
*/
|
||||||
|
class syntax_plugin_filetools_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_filetools_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user