diff --git a/core/class/router.class.php b/core/class/router.class.php index 98e348d1..4517ef7a 100644 --- a/core/class/router.class.php +++ b/core/class/router.class.php @@ -761,8 +761,9 @@ class core extends common // Librairies if ($output['vendor'] !== $this->output['vendor']) { $this->addOutput([ - 'vendor' => array_merge($this->output['vendor'], $output['vendor']) + 'vendor' => array_merge($this->output['vendor'], $this->output['vendor']) ]); + } if ($output['title'] !== null) { diff --git a/module/sharefolder/sharefolder.php b/module/sharefolder/sharefolder.php new file mode 100644 index 00000000..bb06207f --- /dev/null +++ b/module/sharefolder/sharefolder.php @@ -0,0 +1,41 @@ + + * @copyright Copyright (C) 2008-2018, Rémi Jean + * @author Frédéric Tempez + * @copyright Copyright (C) 2018-2024, Frédéric Tempez + * @license CC Attribution-NonCommercial-NoDerivatives 4.0 International + * @link http://zwiicms.fr/ + */ + +class sharefolder extends common +{ + + const VERSION = '4.2'; + const REALNAME = 'Dossier partagé'; + const DATADIRECTORY = ''; // Contenu localisé inclus par défaut (page.json et module.json) + + public static $actions = [ + 'config' => self::GROUP_EDITOR, + 'index' => self::GROUP_VISITOR, + ]; + + public function index() { + // Valeurs en sortie + $this->addOutput([ + 'showBarEditButton' => true, + 'showPageContent' => true, + 'view' => 'index', + 'vendor' => [ + 'php-dirlister' + ], + ]); + } + +} \ No newline at end of file diff --git a/module/sharefolder/vendor/php-dirlister/inc.json b/module/sharefolder/vendor/php-dirlister/inc.json new file mode 100644 index 00000000..4eba2988 --- /dev/null +++ b/module/sharefolder/vendor/php-dirlister/inc.json @@ -0,0 +1,3 @@ +[ + "php-dirlister.php" +] \ No newline at end of file diff --git a/module/sharefolder/vendor/php-dirlister/php-dirlister.php b/module/sharefolder/vendor/php-dirlister/php-dirlister.php new file mode 100644 index 00000000..d42ddb5e --- /dev/null +++ b/module/sharefolder/vendor/php-dirlister/php-dirlister.php @@ -0,0 +1,283 @@ + 'Index of [path]', + 'page_subtitle' => 'Total: [items] items, [size]', + 'browse_directories' => true, + 'show_breadcrumbs' => true, + 'show_directories' => true, + 'show_footer' => true, + 'show_parent' => false, + 'show_hidden' => false, + 'directory_first' => true, + 'content_alignment' => 'center', + 'date_format' => 'd M Y H:i', + 'timezone' => 'Asia/Jakarta', + 'ignore_list' => array( + '.DS_Store', + '.git', + '.gitmodules', + '.gitignore', + '.vscode', + 'vendor', + 'node_modules', + ), +); + + +/* +|-------------------------------------------------------------------------- +| ACTUAL PROGRAM STARTS HERE +|-------------------------------------------------------------------------- +*/ + +class PHPDirLister +{ + private $self; + private $path; + private $browse; + private $total; + private $totalSize; + private $config = array(); + + public function __construct(array $config = array()) + { + $this->config = $config; + $this->self = basename($_SERVER['PHP_SELF']); + $this->path = str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])); + $this->total = 0; + $this->totalSize = 0; + + if ($this->config['browse_directories']) { + $_GET['b'] = trim(str_replace('\\', '/', (string) isset($_GET['b']) ? $_GET['b'] : ''), '/ '); + $_GET['b'] = str_replace(array('/..', '../'), '', (string) isset($_GET['b']) ? $_GET['b'] : ''); + + if (!empty($_GET['b']) && $_GET['b'] !== '..' && is_dir($_GET['b'])) { + $ignored = false; + $names = explode('/', $_GET['b']); + + foreach ($names as $name) { + if (in_array($name, $this->config['ignore_list'])) { + $ignored = true; + break; + } + } + + if (!$ignored) { + $this->browse = $_GET['b']; + } + + if (!empty($this->browse)) { + $index = null; + + if (is_file($this->browse . '/index.htm')) { + $index = '/index.htm'; + } elseif (is_file($this->browse . '/index.html')) { + $index = '/index.html'; + } elseif (is_file($this->browse . '/index.php')) { + $index = '/index.php'; + } + + if (!is_null($index)) { + header('Location: ' . $this->browse . $index); + exit; + } + } + } + } + } + + public function getSelf() + { + return $this->self; + } + + public function getPath() + { + return $this->path; + } + + public function getBrowse() + { + return $this->browse; + } + + public function getTotal() + { + return $this->total; + } + + public function getTotalSize() + { + return $this->totalSize; + } + + public function getConfig($key, $default = null) + { + return array_key_exists($key, $this->config) ? $this->config[$key] : $default; + } + + public function getListings($path) + { + $ls = array(); + $lsDir = array(); + + if (($dh = @opendir($path)) === false) { + return $ls; + } + + $path .= (substr($path, -1) !== '/') ? '/' : ''; + + while (($file = readdir($dh)) !== false) { + if ( + $file === $this->self + || $file === '.' + || $file == '..' + || (!$this->config['show_hidden'] && substr($file, 0, 1) === '.') + || in_array($file, $this->config['ignore_list']) + ) { + continue; + } + + $isDir = is_dir($path . $file); + + if (!$this->config['show_directories'] && $isDir) { + continue; + } + + $item = array( + 'name' => $file, + 'is_dir' => $isDir, + 'size' => $isDir ? 0 : filesize($path . $file), + 'time' => filemtime($path . $file), + ); + + if ($isDir) { + $lsDir[] = $item; + } else { + $ls[] = $item; + } + + $this->total++; + $this->totalSize += $item['size']; + } + + return array_merge($lsDir, $ls); + } + + public function sortByName($a, $b) + { + return ( + ($a['is_dir'] === $b['is_dir']) + || ($this->config['directory_first']) ? ($a['is_dir'] < $b['is_dir']) : (strtolower($a['name']) > strtolower($b['name'])) + ) ? 1 : -1; + } + + public function sortBySize($a, $b) + { + return ( + ($a['is_dir'] === $b['is_dir']) ? ($a['size'] > $b['size']) : ($a['is_dir'] < $b['is_dir']) + ) ? 1 : -1; + } + + public function sortByTime($a, $b) + { + return ($a['time'] > $b['time']) ? 1 : -1; + } + + public function humanizeFilesize($val) + { + $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); + $power = min(floor(($val ? log($val) : 0) / log(1024)), count($units) - 1); + $val = sprintf('%.1f %s', round($val / pow(1024, $power), 1), $units[$power]); + + return str_replace('.0 ', ' ', $val); + } + + public function generateTitle($forSubtitle = false) + { + $path = htmlentities($this->path); + $title = htmlentities(str_replace( + array('[items]', '[size]'), + array($this->total, $this->humanizeFilesize($this->totalSize)), + $this->config[$forSubtitle ? 'page_subtitle' : 'page_title'] + )); + + if ($this->config['show_breadcrumbs']) { + $path = sprintf('%s', htmlentities($this->buildLink(array('b' => ''))), $path); + } + + if (!empty($this->getBrowse())) { + $path .= ($this->path !== '/') ? '/' : ''; + $items = explode('/', trim($this->browse, '/')); + + foreach ($items as $i => $item) { + $path .= $this->config['show_breadcrumbs'] + ? sprintf( + '%s', + htmlentities($this->buildLink(array('b' => implode('/', array_slice($items, 0, $i + 1))))), + htmlentities($item) + ) + : htmlentities($item); + $path .= (count($items) > ($i + 1)) ? '/' : ''; + } + } + + return str_replace('[path]', $path, $title); + } + + public function buildLink($changes) + { + $params = $_GET; + + foreach ($changes as $k => $v) { + if (is_null($v)) { + unset($params[$k]); + } else { + $params[$k] = $v; + } + } + + foreach ($params as $k => $v) { + $params[$k] = urlencode($k) . '=' . urlencode($v); + } + + return empty($params) ? $this->self : $this->self . '?' . implode('&', $params); + } +} + +$pdl = new PHPDirLister($config); +$items = $pdl->getListings('.' . (empty($pdl->getBrowse()) ? '' : '/' . $pdl->getBrowse())); +$sorting = isset($_GET['s']) ? $_GET['s'] : null; + +switch ($sorting) { + case 'size': + $sorting = 'size'; + usort($items, function ($a, $b) use ($pdl) { + return $pdl->sortBySize($a, $b); + }); + break; + + case 'time': + $sorting = 'time'; + usort($items, function ($a, $b) use ($pdl) { + return $pdl->sortByTime($a, $b); + }); + break; + + default: + $sorting = 'name'; + usort($items, function ($a, $b) use ($pdl) { + return $pdl->sortByName($a, $b); + }); + break; +} diff --git a/module/sharefolder/view/config/config.php b/module/sharefolder/view/config/config.php new file mode 100644 index 00000000..e69de29b diff --git a/module/sharefolder/view/index/index.css b/module/sharefolder/view/index/index.css new file mode 100644 index 00000000..9426ed9f --- /dev/null +++ b/module/sharefolder/view/index/index.css @@ -0,0 +1,151 @@ + +#wrapper { + width: 80%; + margin: 0 auto; + text-align: left; +} + +body#left { + text-align: left; +} + +body#left #wrapper { + margin: 0 20px; +} + +#wrapper > h1 { + font-size: 21px; + padding: 0 10px; + margin: 20px 0 0; + font-weight: bold; +} + +#wrapper > h2 { + font-size: 11px; + padding: 0 10px; + margin: 10px 0 0; + color: #98a6ad; + font-weight: normal; +} + +#wrapper > a { + color: #003399; + text-decoration: none; +} + +#wrapper > a:hover { + color: #0066cc; + text-decoration: underline; +} + +#wrapper > ul#header { + margin-top: 20px; +} + +#wrapper > ul li { + display: block; + list-style-type: none; + overflow: hidden; + padding: 10px; +} + +#wrapper > ul li:hover { + background-color: #f3f3f3; +} + +#wrapper > ul li .date { + text-align: center; + width: 120px; +} + +#wrapper > ul li .size { + text-align: right; + width: 90px; +} + +#wrapper > ul li .date, +#wrapper > ul li .size { + float: right; + font-size: 12px; + display: block; + color: #666666; +} + +#wrapper > ul#header li { + font-size: 11px; + font-weight: bold; + border-bottom: 1px solid #dee2e6; +} + +#wrapper > ul#header li:hover { + background-color: transparent; +} + +#wrapper > ul#header li * { + color: #888888; + font-size: 11px; +} + +#wrapper > ul#header li a:hover { + color: #333333; +} + +#wrapper > ul#header li .asc span, +#wrapper > ul#header li .desc span { + padding-right: 15px; + background-position: right center; + background-repeat: no-repeat; +} + +#wrapper > ul#header li .asc span { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAABbSURBVCjPY/jPgB8yDCkFB/7v+r/5/+r/i/7P+N/3DYuC7V93/d//fydQ0Zz/9eexKFgtsejLiv8b/8/8X/WtUBGrGyZLdH6f8r/sW64cTkdWSRS+zpQbgiEJAI4UCqdRg1A6AAAAAElFTkSuQmCC'); +} + +#wrapper > ul#header li .desc span { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAABbSURBVCjPY/jPgB8yDDkFmyVWv14kh1PBeoll31f/n/ytUw6rgtUSi76s+L/x/8z/Vd8KFbEomPt16f/1/1f+X/S/7X/qeSwK+v63/K/6X/g/83/S/5hvQywkAdMGCdCoabZeAAAAAElFTkSuQmCC'); +} + +#wrapper > ul li.item { + border-top: 1px solid #f3f3f3; +} + +#wrapper > ul li.item:first-child { + border-top: none; +} + +#wrapper > ul li.item .name { + font-weight: bold; +} + +#wrapper > ul li.item .dir, +#wrapper > ul li.item .file { + padding-left: 20px; + background-position: left center; + background-repeat: no-repeat; +} + +#wrapper > ul li.item .dir { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGrSURBVDjLxZO7ihRBFIa/6u0ZW7GHBUV0UQQTZzd3QdhMQxOfwMRXEANBMNQX0MzAzFAwEzHwARbNFDdwEd31Mj3X7a6uOr9BtzNjYjKBJ6nicP7v3KqcJFaxhBVtZUAK8OHlld2st7Xl3DJPVONP+zEUV4HqL5UDYHr5xvuQAjgl/Qs7TzvOOVAjxjlC+ePSwe6DfbVegLVuT4r14eTr6zvA8xSAoBLzx6pvj4l+DZIezuVkG9fY2H7YRQIMZIBwycmzH1/s3F8AapfIPNF3kQk7+kw9PWBy+IZOdg5Ug3mkAATy/t0usovzGeCUWTjCz0B+Sj0ekfdvkZ3abBv+U4GaCtJ1iEm6ANQJ6fEzrG/engcKw/wXQvEKxSEKQxRGKE7Izt+DSiwBJMUSm71rguMYhQKrBygOIRStf4TiFFRBvbRGKiQLWP29yRSHKBTtfdBmHs0BUpgvtgF4yRFR+NUKi0XZcYjCeCG2smkzLAHkbRBmP0/Uk26O5YnUActBp1GsAI+S5nRJJJal5K1aAMrq0d6Tm9uI6zjyf75dAe6tx/SsWeD//o2/Ab6IH3/h25pOAAAAAElFTkSuQmCC'); +} + +#wrapper > ul li.item .file { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAABVklEQVQYGQXBP0hUAQAH4O/dnaaSXhIY2Z/NDAoaanUNorHJpbHBotZwT2hpL4iioTjUqb0hh3AqwYgw4xITgtCOvLI73/v1fUUArCx0bu5NMr7TfDEzDyAiYnmi1UuSJGn1Xk5GRBSxstCZ3Tur1jRlWnzyVYdqfKv5amZetA6SJGlnKWtZy2LaSZKkdRD1S8/qV+K4+OxQ2w9jSidVPuo03k0XS/2Lja5C3YYbgGVT+rpKm4e1dE85rWnUhA2VyoYTBu3pGZduIxgy4LcBHevo23VgABGNICrDzvij8tNfoxoKpVI0yuGa6Cv1FSqHjoCeQqUcquXtqo4RdZWgEvHPmJpNWa1V17dvvfnyQWFMA6WeEUe1rX/bvZtrRfB8Ivcyd/7YOaVthR1b+3mSR3e+UwTw9ELmM3u5+GXb/nIe3H4PFAHA46u5n8E8nHsNQBEAAADAf9MfuSUN80DGAAAAAElFTkSuQmCC'); +} + +#wrapper > #footer { + color: #98a6ad; + font-size: 11px; + margin-top: 40px; + margin-bottom: 20px; + padding: 0 10px; + text-align: left; +} + +#wrapper > #footer a { + color: #98a6ad; + font-weight: bold; +} + +#wrapper > #footer a:hover { + color: #777777; +} \ No newline at end of file diff --git a/module/sharefolder/view/index/index.php b/module/sharefolder/view/index/index.php new file mode 100644 index 00000000..4fcbd0d1 --- /dev/null +++ b/module/sharefolder/view/index/index.php @@ -0,0 +1,66 @@ +getConfig('timezone', 'UTC')); + +$reverse = isset($_GET['r']) && $_GET['r'] === '1'; +$items = $reverse ? array_reverse($items) : $items; + + +if ($pdl->getConfig('show_parent') && $pdl->getPath() !== '/' && empty($pdl->getBrowse())) { + array_unshift($items, array('name' => '..', 'is_parent' => true, 'is_dir' => true, 'size' => 0, 'time' => 0)); +} +?> +getConfig('content_alignment') === 'left') echo 'id="left"' ?>> + +
+ +

generateTitle() ?>

+

generateTitle(true) ?>

+ + + + + + getConfig('show_footer')) : ?> + + +
= + + + \ No newline at end of file