update to latest dotclear 2.26-dev changes

This commit is contained in:
Jean-Christian Paul Denis 2023-04-06 00:44:13 +02:00
parent cd5b62be60
commit 465b397504
Signed by: JcDenis
GPG Key ID: 1B5B8C5B90B6C951
6 changed files with 152 additions and 232 deletions

View File

@ -88,11 +88,11 @@ class Config extends dcNsProcess
$img_off = sprintf($img, __('not writable'), 'check-off.png'); $img_off = sprintf($img, __('not writable'), 'check-off.png');
$repo = Utils::getRepositoryDir($s->pack_repository); $repo = Utils::getRepositoryDir($s->pack_repository);
$check_repo = Utils::is_writable($repo, '_.zip') ? $img_on : $img_off; $check_repo = Utils::isWritable($repo, '_.zip') ? $img_on : $img_off;
$check_first = !empty($s->pack_filename) && Utils::is_writable($repo, $s->pack_filename) ? $img_on : $img_off; $check_first = !empty($s->pack_filename) && Utils::isWritable($repo, $s->pack_filename) ? $img_on : $img_off;
$check_second = !empty($s->secondpack_filename) && Utils::is_writable($repo, $s->secondpack_filename) ? $img_on : $img_off; $check_second = !empty($s->secondpack_filename) && Utils::isWritable($repo, $s->secondpack_filename) ? $img_on : $img_off;
$is_configured = Utils::is_configured( $is_configured = Utils::isConfigured(
$repo, $repo,
$s->pack_filename, $s->pack_filename,
$s->secondpack_filename $s->secondpack_filename

View File

@ -15,10 +15,11 @@ declare(strict_types=1);
namespace Dotclear\Plugin\pacKman; namespace Dotclear\Plugin\pacKman;
use dcCore; use dcCore;
use dcModuleDefine;
use dcModules; use dcModules;
use files; use Dotclear\Helper\File\Files;
use Dotclear\Helper\File\Path;
use Dotclear\Helper\File\Zip\Unzip; use Dotclear\Helper\File\Zip\Unzip;
use path;
class Core class Core
{ {
@ -39,12 +40,11 @@ class Core
{ {
$res = []; $res = [];
$cache = self::getCache() . DIRECTORY_SEPARATOR;
if (!is_dir($root) || !is_readable($root)) { if (!is_dir($root) || !is_readable($root)) {
return $res; return $res;
} }
$files = files::scanDir($root); $files = Files::scanDir($root);
$zip_files = []; $zip_files = [];
foreach ($files as $file) { foreach ($files as $file) {
if (!preg_match('#(^|/)(.*?)\.zip(/|$)#', $file)) { if (!preg_match('#(^|/)(.*?)\.zip(/|$)#', $file)) {
@ -95,7 +95,7 @@ class Core
foreach ($sandboxes as $type => $sandbox) { foreach ($sandboxes as $type => $sandbox) {
try { try {
files::makeDir($path, true); Files::makeDir($path, true);
// can't load twice _init.php file ! // can't load twice _init.php file !
$unlink = false; $unlink = false;
@ -118,14 +118,14 @@ class Core
if (!$sandbox->getErrors()) { if (!$sandbox->getErrors()) {
$module = $sandbox->getDefine(basename($path)); $module = $sandbox->getDefine(basename($path));
if ($module->isDefined() && $module->get('type') == $type) { if ($module->isDefined() && $module->get('type') == $type) {
$res[$i] = $module->dump(); $res[$i] = $module;
$res[$i]['root'] = $zip_file; $res[$i]->set('root', $zip_file);
$i++; $i++;
} }
} }
} catch (Exception $e) { } catch (Exception $e) {
} }
files::deltree($path); Files::deltree($path);
} }
$zip->close(); $zip->close();
} }
@ -133,20 +133,34 @@ class Core
return $res; return $res;
} }
public static function pack(array $info, string $root, array $files, bool $overwrite = false, array $exclude = [], bool $nocomment = false, bool $fixnewline = false): bool public static function pack(dcModuleDefine $define, string $root, array $files, bool $overwrite = false, array $exclude = [], bool $nocomment = false, bool $fixnewline = false): bool
{ {
if (!($info = self::getInfo($info)) // check define
|| !($root = self::getRoot($root)) if (!$define->isDefined()
|| empty($define->get('root'))
|| !is_dir($define->get('root'))
) { ) {
return false; throw new Exception(__('Failed to get module info'));
} }
$exclude = self::getExclude($exclude); // check root
$root = (string) Path::real($root);
if (!is_dir($root) || !is_writable($root)) {
throw new Exception(__('Directory is not writable'));
}
//set excluded
$exclude = self::quote_exclude(array_merge(My::EXCLUDED_FILES, $exclude));
foreach ($files as $file) { foreach ($files as $file) {
if (!($file = self::getFile($file, $info)) if (empty($file)) {
|| !($dest = self::getOverwrite($overwrite, $root, $file)) continue;
) { }
// check path
$path = $root . DIRECTORY_SEPARATOR . self::getFile($file, $define);
if (file_exists($path) && !$overwrite) {
// don't break loop
continue; continue;
} }
@ -158,14 +172,14 @@ class Core
if ($fixnewline) { if ($fixnewline) {
Zip::$fix_newline = true; Zip::$fix_newline = true;
} }
$zip = new Zip($dest); $zip = new Zip($path);
foreach ($exclude as $e) { foreach ($exclude as $e) {
$zip->addExclusion($e); $zip->addExclusion($e);
} }
$zip->addDirectory( $zip->addDirectory(
(string) path::real($info['root'], false), (string) Path::real($define->get('root'), false),
$info['id'], $define->getId(),
true true
); );
@ -176,41 +190,8 @@ class Core
return true; return true;
} }
private static function getRoot(string $root): string private static function getFile(string $file, dcModuleDefine $define): string
{ {
$root = (string) path::real($root);
if (!is_dir($root) || !is_writable($root)) {
throw new Exception(__('Directory is not writable'));
}
return $root;
}
private static function getInfo(array $info): array
{
if (!isset($info['root'])
|| !isset($info['id'])
|| !is_dir($info['root'])
) {
throw new Exception(__('Failed to get module info'));
}
return $info;
}
private static function getExclude(array $exclude): array
{
$exclude = array_merge(My::EXCLUDED_FILES, $exclude);
return self::quote_exclude($exclude);
}
private static function getFile(string $file, array $info): ?string
{
if (empty($file) || empty($info)) {
return null;
}
$file = str_replace( $file = str_replace(
[ [
'\\', '\\',
@ -222,44 +203,19 @@ class Core
], ],
[ [
'/', '/',
$info['type'], $define->get('type'),
$info['id'], $define->getId(),
$info['version'], $define->get('version'),
$info['author'], $define->get('author'),
time(), time(),
], ],
$file $file
); );
$parts = explode('/', $file); $parts = explode('/', $file);
foreach ($parts as $i => $part) { foreach ($parts as $i => $part) {
$parts[$i] = files::tidyFileName($part); $parts[$i] = Files::tidyFileName($part);
} }
return implode(DIRECTORY_SEPARATOR, $parts) . '.zip'; return implode(DIRECTORY_SEPARATOR, $parts) . '.zip';
} }
private static function getOverwrite(bool $overwrite, string $root, string$file): ?string
{
$path = $root . DIRECTORY_SEPARATOR . $file;
if (file_exists($path) && !$overwrite) {
// don't break loop
//throw new Exception('File already exists');
return null;
}
return $path;
}
private static function getCache(): string
{
$c = DC_TPL_CACHE . DIRECTORY_SEPARATOR . 'packman';
if (!file_exists($c)) {
@files::makeDir($c);
}
if (!is_writable($c)) {
throw new Exception(__('Failed to get temporary directory'));
}
return $c;
}
} }

View File

@ -59,13 +59,13 @@ class Install extends dcNsProcess
); );
while ($record->fetch()) { while ($record->fetch()) {
if (preg_match('/^packman_(.*?)$/', $record->setting_id, $match)) { if (preg_match('/^packman_(.*?)$/', $record->f('setting_id'), $match)) {
$cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcNamespace::NS_TABLE_NAME); $cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcNamespace::NS_TABLE_NAME);
$cur->setting_id = $match[1]; $cur->setField('setting_id', $match[1]);
$cur->setting_ns = My::id(); $cur->setField('setting_ns', My::id());
$cur->update( $cur->update(
"WHERE setting_id = '" . $record->setting_id . "' and setting_ns = 'pacKman' " . "WHERE setting_id = '" . $record->f('setting_id') . "' and setting_ns = 'pacKman' " .
'AND blog_id ' . (null === $record->blog_id ? 'IS NULL ' : ("= '" . dcCore::app()->con->escape($record->blog_id) . "' ")) 'AND blog_id ' . (null === $record->f('blog_id') ? 'IS NULL ' : ("= '" . dcCore::app()->con->escapeStr($record->f('blog_id')) . "' "))
); );
} }
} }

View File

@ -19,10 +19,8 @@ use dcCore;
use dcPage; use dcPage;
use dcThemes; use dcThemes;
use dcNsProcess; use dcNsProcess;
use Dotclear\Helper\File\Files;
/* clearbricks ns */ use Dotclear\Helper\Network\Http;
use files;
use http;
class Manage extends dcNsProcess class Manage extends dcNsProcess
{ {
@ -58,13 +56,8 @@ class Manage extends dcNsProcess
$plugins = dcCore::app()->plugins; $plugins = dcCore::app()->plugins;
# Rights # Rights
$is_writable = Utils::is_writable( $is_writable = Utils::isWritable($dir, $s->pack_filename);
$dir, $is_editable = !empty($type) && !empty($_POST['modules']) && is_array($_POST['modules']);
$s->pack_filename
);
$is_editable = !empty($type)
&& !empty($_POST['modules'])
&& is_array($_POST['modules']);
# Actions # Actions
try { try {
@ -77,25 +70,25 @@ class Manage extends dcNsProcess
$modules = Core::getPackages(Utils::getThemesPath()); $modules = Core::getPackages(Utils::getThemesPath());
} else { } else {
$modules = array_merge( $modules = array_merge(
Core::getPackages(dirname($dir . '/' . $s->pack_filename)), Core::getPackages(dirname($dir . DIRECTORY_SEPARATOR . $s->pack_filename)),
Core::getPackages(dirname($dir . '/' . $s->secondpack_filename)) Core::getPackages(dirname($dir . DIRECTORY_SEPARATOR . $s->secondpack_filename))
); );
} }
foreach ($modules as $f) { foreach ($modules as $module) {
if (preg_match('/' . preg_quote($_REQUEST['package']) . '$/', $f['root']) if (preg_match('/' . preg_quote($_REQUEST['package']) . '$/', $module->get('root'))
&& is_file($f['root']) && is_readable($f['root']) && is_file($module->get('root')) && is_readable($module->get('root'))
) { ) {
# --BEHAVIOR-- packmanBeforeDownloadPackage # --BEHAVIOR-- packmanBeforeDownloadPackage
dcCore::app()->callBehavior('packmanBeforeDownloadPackage', $f, $type); dcCore::app()->callBehavior('packmanBeforeDownloadPackage', $module->dump(), $type);
header('Content-Type: application/zip'); header('Content-Type: application/zip');
header('Content-Length: ' . filesize($f['root'])); header('Content-Length: ' . filesize($module->get('root')));
header('Content-Disposition: attachment; filename="' . basename($f['root']) . '"'); header('Content-Disposition: attachment; filename="' . basename($module->get('root')) . '"');
readfile($f['root']); readfile($module->get('root'));
# --BEHAVIOR-- packmanAfterDownloadPackage # --BEHAVIOR-- packmanAfterDownloadPackage
dcCore::app()->callBehavior('packmanAfterDownloadPackage', $f, $type); dcCore::app()->callBehavior('packmanAfterDownloadPackage', $module->dump(), $type);
exit; exit;
} }
@ -103,7 +96,7 @@ class Manage extends dcNsProcess
# Not found # Not found
header('Content-Type: text/plain'); header('Content-Type: text/plain');
http::head(404, 'Not Found'); Http::head(404, 'Not Found');
exit; exit;
} elseif (!empty($action) && !$is_editable) { } elseif (!empty($action) && !$is_editable) {
dcPage::addErrorNotice( dcPage::addErrorNotice(
@ -111,7 +104,7 @@ class Manage extends dcNsProcess
); );
if (!empty($_POST['redir'])) { if (!empty($_POST['redir'])) {
http::redirect($_POST['redir']); Http::redirect($_POST['redir']);
} else { } else {
dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-' . $type); dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-' . $type);
} }
@ -119,16 +112,14 @@ class Manage extends dcNsProcess
# Pack # Pack
} elseif ($action == 'packup') { } elseif ($action == 'packup') {
foreach ($_POST['modules'] as $root => $id) { foreach ($_POST['modules'] as $root => $id) {
if (!Utils::moduleExists($type, $id)) { if (!dcCore::app()->{$type}->getDefine($id)->isDefined()) {
throw new Exception('No such module'); throw new Exception('No such module');
} }
$module = Utils::getModules($type, $id); $module = dcCore::app()->{$type}->getDefine($id);
$module['id'] = $id;
$module['type'] = $type == 'themes' ? 'theme' : 'plugin';
# --BEHAVIOR-- packmanBeforeCreatePackage # --BEHAVIOR-- packmanBeforeCreatePackage
dcCore::app()->callBehavior('packmanBeforeCreatePackage', $module); dcCore::app()->callBehavior('packmanBeforeCreatePackage', $module->dump());
Core::pack( Core::pack(
$module, $module,
@ -141,7 +132,7 @@ class Manage extends dcNsProcess
); );
# --BEHAVIOR-- packmanAfterCreatePackage # --BEHAVIOR-- packmanAfterCreatePackage
dcCore::app()->callBehavior('packmanAfterCreatePackage', $module); dcCore::app()->callBehavior('packmanAfterCreatePackage', $module->dump());
} }
dcPage::addSuccessNotice( dcPage::addSuccessNotice(
@ -149,7 +140,7 @@ class Manage extends dcNsProcess
); );
if (!empty($_POST['redir'])) { if (!empty($_POST['redir'])) {
http::redirect($_POST['redir']); Http::redirect($_POST['redir']);
} else { } else {
dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-' . $type); dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-' . $type);
} }
@ -158,7 +149,7 @@ class Manage extends dcNsProcess
} elseif ($action == 'delete') { } elseif ($action == 'delete') {
$del_success = false; $del_success = false;
foreach ($_POST['modules'] as $root => $id) { foreach ($_POST['modules'] as $root => $id) {
if (!file_exists($root) || !files::isDeletable($root)) { if (!file_exists($root) || !Files::isDeletable($root)) {
dcPage::addWarningNotice(sprintf(__('Undeletable file "%s"', $root))); dcPage::addWarningNotice(sprintf(__('Undeletable file "%s"', $root)));
} else { } else {
$del_success = true; $del_success = true;
@ -174,7 +165,7 @@ class Manage extends dcNsProcess
} }
if (!empty($_POST['redir'])) { if (!empty($_POST['redir'])) {
http::redirect($_POST['redir']); Http::redirect($_POST['redir']);
} else { } else {
dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type); dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type);
} }
@ -201,7 +192,7 @@ class Manage extends dcNsProcess
); );
if (!empty($_POST['redir'])) { if (!empty($_POST['redir'])) {
http::redirect($_POST['redir']); Http::redirect($_POST['redir']);
} else { } else {
dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type); dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type);
} }
@ -217,7 +208,7 @@ class Manage extends dcNsProcess
foreach ($_POST['modules'] as $root => $id) { foreach ($_POST['modules'] as $root => $id) {
file_put_contents( file_put_contents(
$dest . '/' . basename($root), $dest . DIRECTORY_SEPARATOR . basename($root),
file_get_contents($root) file_get_contents($root)
); );
} }
@ -227,7 +218,7 @@ class Manage extends dcNsProcess
); );
if (!empty($_POST['redir'])) { if (!empty($_POST['redir'])) {
http::redirect($_POST['redir']); Http::redirect($_POST['redir']);
} else { } else {
dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type); dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type);
} }
@ -254,7 +245,7 @@ class Manage extends dcNsProcess
); );
if (!empty($_POST['redir'])) { if (!empty($_POST['redir'])) {
http::redirect($_POST['redir']); Http::redirect($_POST['redir']);
} else { } else {
dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type); dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type);
} }
@ -276,7 +267,7 @@ class Manage extends dcNsProcess
$s = new Settings(); $s = new Settings();
$dir = Utils::getRepositoryDir($s->pack_repository); $dir = Utils::getRepositoryDir($s->pack_repository);
$is_configured = Utils::is_configured( $is_configured = Utils::isConfigured(
$dir, $dir,
$s->pack_filename, $s->pack_filename,
$s->secondpack_filename $s->secondpack_filename
@ -306,13 +297,13 @@ class Manage extends dcNsProcess
'</div>'; '</div>';
} else { } else {
Utils::modules( Utils::modules(
Utils::getModules('plugins'), dcCore::app()->plugins->getDefines((new Settings())->hide_distrib ? ['distributed' => false] : []),
'plugins', 'plugins',
__('Installed plugins') __('Installed plugins')
); );
Utils::modules( Utils::modules(
Utils::getModules('themes'), dcCore::app()->themes->getDefines((new Settings())->hide_distrib ? ['distributed' => false] : []),
'themes', 'themes',
__('Installed themes') __('Installed themes')
); );
@ -331,8 +322,8 @@ class Manage extends dcNsProcess
Utils::repository( Utils::repository(
array_merge( array_merge(
Core::getPackages(dirname($dir . '/' . $s->pack_filename)), Core::getPackages(dirname($dir . DIRECTORY_SEPARATOR . $s->pack_filename)),
Core::getPackages(dirname($dir . '/' . $s->secondpack_filename)) Core::getPackages(dirname($dir . DIRECTORY_SEPARATOR . $s->secondpack_filename))
), ),
'repository', 'repository',
__('Packages repository') __('Packages repository')

View File

@ -20,14 +20,14 @@ class Uninstall
public static function init(): bool public static function init(): bool
{ {
self::$init = defined('DC_RC_PATH'); static::$init = defined('DC_RC_PATH');
return self::$init; return static::$init;
} }
public static function process($uninstaller): ?bool public static function process($uninstaller): ?bool
{ {
if (!self::$init) { if (!static::$init) {
return false; return false;
} }

View File

@ -14,17 +14,23 @@ declare(strict_types=1);
namespace Dotclear\Plugin\pacKman; namespace Dotclear\Plugin\pacKman;
/* dotclear ns */
use dcCore; use dcCore;
use Dotclear\Helper\File\Files;
use Dotclear\Helper\File\Path;
use Dotclear\Helper\File\Zip\Unzip; use Dotclear\Helper\File\Zip\Unzip;
use Dotclear\Helper\File\Zip\Zip; use Dotclear\Helper\File\Zip\Zip;
use Dotclear\Helper\Html\Form\{
Checkbox,
Hidden,
Label,
Para,
Select,
Submit,
Text
};
use Dotclear\Helper\Html\Html;
/* clearbricks ns */
use dt; use dt;
use files;
use form;
use html;
use path;
class Utils class Utils
{ {
@ -33,7 +39,7 @@ class Utils
$e = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT); $e = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT);
$p = array_pop($e); $p = array_pop($e);
return (string) path::real($p); return (string) Path::real($p);
} }
public static function getThemesPath(): string public static function getThemesPath(): string
@ -41,13 +47,8 @@ class Utils
return (string) dcCore::app()->blog?->themes_path; return (string) dcCore::app()->blog?->themes_path;
} }
public static function is_configured(string $repo, string $file_a, string $file_b): bool public static function isConfigured(string $repo, string $file_a, string $file_b): bool
{ {
if (!is_dir(DC_TPL_CACHE) || !is_writable(DC_TPL_CACHE)) {
dcCore::app()->error->add(
__('Cache directory is not writable.')
);
}
if (!is_writable($repo)) { if (!is_writable($repo)) {
dcCore::app()->error->add( dcCore::app()->error->add(
__('Path to repository is not writable.') __('Path to repository is not writable.')
@ -75,7 +76,7 @@ class Utils
return !dcCore::app()->error->flag(); return !dcCore::app()->error->flag();
} }
public static function is_writable(string $path, string $file): bool public static function isWritable(string $path, string $file): bool
{ {
return !(empty($path) || empty($file) || !is_writable(dirname($path . DIRECTORY_SEPARATOR . $file))); return !(empty($path) || empty($file) || !is_writable(dirname($path . DIRECTORY_SEPARATOR . $file)));
} }
@ -143,7 +144,7 @@ class Utils
if (empty($dir)) { if (empty($dir)) {
try { try {
$dir = DC_VAR . DIRECTORY_SEPARATOR . 'packman'; $dir = DC_VAR . DIRECTORY_SEPARATOR . 'packman';
@files::makeDir($dir, true); @Files::makeDir($dir, true);
} catch (Exception $e) { } catch (Exception $e) {
$dir = ''; $dir = '';
} }
@ -152,35 +153,9 @@ class Utils
return $dir; return $dir;
} }
public static function getModules(string $type, ?string $id = null): array
{
$type = $type == 'themes' ? 'themes' : 'plugins';
$modules = array_merge(dcCore::app()->{$type}->getDisabledModules(), dcCore::app()->{$type}->getModules());
if ((new Settings())->hide_distrib) {
$modules = array_diff_key($modules, array_flip(array_values(array_merge(explode(',', DC_DISTRIB_PLUGINS), explode(',', DC_DISTRIB_THEMES)))));
}
if (empty($id)) {
return $modules;
} elseif (array_key_exists($id, $modules)) {
return $modules[$id];
}
return [];
}
public static function moduleExists(string $type, ?string $id): bool
{
$type = $type == 'themes' ? 'themes' : 'plugins';
return array_key_exists((string) $id, array_merge(dcCore::app()->{$type}->getDisabledModules(), dcCore::app()->{$type}->getModules()));
}
public static function modules(array $modules, string $type, string $title): ?bool public static function modules(array $modules, string $type, string $title): ?bool
{ {
if (empty($modules) || !is_array($modules)) { if (empty($modules)) {
return null; return null;
} }
@ -198,42 +173,41 @@ class Utils
'<th class="nowrap">' . __('Root') . '</th>' . '<th class="nowrap">' . __('Root') . '</th>' .
'</tr>'; '</tr>';
foreach (self::sort($modules) as $id => $module) { $i = 1;
self::sort($modules);
foreach ($modules as $module) {
echo echo
'<tr class="line">' . '<tr class="line">' .
'<td class="nowrap"><label class="classic">' . (new Para(null, 'td'))->class('nowrap')->items([
form::checkbox(['modules[' . html::escapeHTML($module['root']) . ']'], html::escapeHTML($id)) . (new Checkbox(['modules[' . Html::escapeHTML($module->get('root')) . ']', 'modules_' . $type . $i], false))->value(Html::escapeHTML($module->getId())),
html::escapeHTML($id) . (new Label(Html::escapeHTML($module->getId()), Label::OUTSIDE_LABEL_AFTER))->for('modules_' . $type . $i)->class('classic'),
'</label></td>' .
])->render() .
'<td class="nowrap count">' . '<td class="nowrap count">' .
html::escapeHTML($module['version']) . Html::escapeHTML($module->get('version')) .
'</td>' . '</td>' .
'<td class="nowrap maximal">' . '<td class="nowrap maximal">' .
__(html::escapeHTML($module['name'])) . __(Html::escapeHTML($module->get('name'))) .
'</td>' . '</td>' .
'<td class="nowrap">' . '<td class="nowrap">' .
dirname((string) path::real($module['root'], false)) . dirname((string) Path::real($module->get('root'), false)) .
'</td>' . '</td>' .
'</tr>'; '</tr>';
$i++;
} }
echo echo
'</table>' . '</table>' .
'<p class="checkboxes-helpers"></p>' . '<p class="checkboxes-helpers"></p>' .
'<p>' . (new Para())->items([
( (new Hidden(['redir'], Html::escapeHTML($_REQUEST['redir'] ?? ''))),
!empty($_REQUEST['redir']) ? (new Hidden(['p'], My::id())),
form::hidden( (new Hidden(['type'], $type)),
['redir'], (new Hidden(['action'], 'packup')),
html::escapeHTML($_REQUEST['redir']) (new Submit(['packup']))->value(__('Pack up selected modules')),
) : '' dcCore::app()->formNonce(false),
) . ])->render() .
form::hidden(['p'], My::id()) .
form::hidden(['type'], $type) .
form::hidden(['action'], 'packup') .
'<input type="submit" name="packup" value="' .
__('Pack up selected modules') . '" />' .
dcCore::app()->formNonce() . '</p>' .
'</form>' . '</form>' .
'</div>'; '</div>';
@ -243,7 +217,7 @@ class Utils
public static function repository(array $modules, string $type, string $title): ?bool public static function repository(array $modules, string $type, string $title): ?bool
{ {
if (empty($modules) || !is_array($modules)) { if (empty($modules)) {
return null; return null;
} }
if (!in_array($type, ['plugins', 'themes', 'repository'])) { if (!in_array($type, ['plugins', 'themes', 'repository'])) {
@ -284,52 +258,57 @@ class Utils
'</tr>'; '</tr>';
$dup = []; $dup = [];
foreach (self::sort($modules) as $module) { $i = 1;
if (isset($dup[$module['root']])) { self::sort($modules);
foreach ($modules as $module) {
if (isset($dup[$module->get('root')])) {
continue; continue;
} }
$dup[$module['root']] = 1; $dup[$module->get('root')] = 1;
echo echo
'<tr class="line">' . '<tr class="line">' .
'<td class="nowrap"><label class="classic" title="' . (new Para(null, 'td'))->class('nowrap')->items([
html::escapeHTML($module['root']) . '">' . (new Checkbox(['modules[' . Html::escapeHTML($module->get('root')) . ']', 'r_modules_' . $type . $i], false))->value(Html::escapeHTML($module->getId())),
form::checkbox(['modules[' . html::escapeHTML($module['root']) . ']'], $module['id']) . (new Label(Html::escapeHTML($module->getId()), Label::OUTSIDE_LABEL_AFTER))->for('r_modules_' . $type . $i)->class('classic')->title(Html::escapeHTML($module->get('root'))),
html::escapeHTML($module['id']) .
'</label></td>' . ])->render() .
'<td class="nowrap count">' . '<td class="nowrap count">' .
html::escapeHTML($module['version']) . Html::escapeHTML($module->get('version')) .
'</td>' . '</td>' .
'<td class="nowrap maximal">' . '<td class="nowrap maximal">' .
__(html::escapeHTML($module['name'])) . __(Html::escapeHTML($module->get('name'))) .
'</td>' . '</td>' .
'<td class="nowrap">' . '<td class="nowrap">' .
'<a class="packman-download" href="' . '<a class="packman-download" href="' .
dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), [ dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), [
'package' => basename($module['root']), 'package' => basename($module->get('root')),
'repo' => $type, 'repo' => $type,
]) . '" title="' . __('Download') . '">' . ]) . '" title="' . __('Download') . '">' .
html::escapeHTML(basename($module['root'])) . '</a>' . Html::escapeHTML(basename($module->get('root'))) . '</a>' .
'</td>' . '</td>' .
'<td class="nowrap">' . '<td class="nowrap">' .
html::escapeHTML(dt::str(__('%Y-%m-%d %H:%M'), (int) @filemtime($module['root']))) . Html::escapeHTML(dt::str(__('%Y-%m-%d %H:%M'), (int) @filemtime($module->get('root')))) .
'</td>' . '</td>' .
'</tr>'; '</tr>';
$i++;
} }
echo echo
'</table>' . '</table>' .
'<div class="two-cols">' . '<div class="two-cols">' .
'<p class="col checkboxes-helpers"></p>' . '<p class="col checkboxes-helpers"></p>' .
'<p class="col right">' . __('Selected modules action:') . ' ' . (new Para())->class('col right')->items([
form::combo(['action'], $combo_action) . (new Text('', __('Selected modules action:') . ' ')),
'<input type="submit" name="packup" value="' . __('ok') . '" />' . (new Select(['action']))->items($combo_action),
form::hidden(['p'], My::id()) . (new Submit(['packup']))->value(__('ok')),
form::hidden(['tab'], 'repository') . (new Hidden(['p'], My::id())),
form::hidden(['type'], $type) . (new Hidden(['tab'], 'repository')),
dcCore::app()->formNonce() . (new Hidden(['type'], $type)),
'</p>' . dcCore::app()->formNonce(false),
])->render() .
'</div>' . '</div>' .
'</form>' . '</form>' .
'</div>'; '</div>';
@ -337,15 +316,9 @@ class Utils
return true; return true;
} }
protected static function sort(array $modules): array protected static function sort(array &$modules): void
{ {
$key = $ver = []; uasort($modules, fn ($a, $b) => $a->get('version') <=> $b->get('version'));
foreach ($modules as $i => $module) { uasort($modules, fn ($a, $b) => strtolower($a->get('id')) <=> strtolower($b->get('id')));
$key[$i] = $module['id'] ?? $i;
$ver[$i] = $module['version'];
}
array_multisort($key, SORT_ASC, $ver, SORT_ASC, $modules);
return $modules;
} }
} }