diff --git a/src/Config.php b/src/Config.php index ed78a29..3a81b5b 100644 --- a/src/Config.php +++ b/src/Config.php @@ -88,11 +88,11 @@ class Config extends dcNsProcess $img_off = sprintf($img, __('not writable'), 'check-off.png'); $repo = Utils::getRepositoryDir($s->pack_repository); - $check_repo = Utils::is_writable($repo, '_.zip') ? $img_on : $img_off; - $check_first = !empty($s->pack_filename) && Utils::is_writable($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_repo = Utils::isWritable($repo, '_.zip') ? $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::isWritable($repo, $s->secondpack_filename) ? $img_on : $img_off; - $is_configured = Utils::is_configured( + $is_configured = Utils::isConfigured( $repo, $s->pack_filename, $s->secondpack_filename diff --git a/src/Core.php b/src/Core.php index 7372c3b..681b30e 100644 --- a/src/Core.php +++ b/src/Core.php @@ -15,10 +15,11 @@ declare(strict_types=1); namespace Dotclear\Plugin\pacKman; use dcCore; +use dcModuleDefine; use dcModules; -use files; +use Dotclear\Helper\File\Files; +use Dotclear\Helper\File\Path; use Dotclear\Helper\File\Zip\Unzip; -use path; class Core { @@ -39,12 +40,11 @@ class Core { $res = []; - $cache = self::getCache() . DIRECTORY_SEPARATOR; if (!is_dir($root) || !is_readable($root)) { return $res; } - $files = files::scanDir($root); + $files = Files::scanDir($root); $zip_files = []; foreach ($files as $file) { if (!preg_match('#(^|/)(.*?)\.zip(/|$)#', $file)) { @@ -95,7 +95,7 @@ class Core foreach ($sandboxes as $type => $sandbox) { try { - files::makeDir($path, true); + Files::makeDir($path, true); // can't load twice _init.php file ! $unlink = false; @@ -118,14 +118,14 @@ class Core if (!$sandbox->getErrors()) { $module = $sandbox->getDefine(basename($path)); if ($module->isDefined() && $module->get('type') == $type) { - $res[$i] = $module->dump(); - $res[$i]['root'] = $zip_file; + $res[$i] = $module; + $res[$i]->set('root', $zip_file); $i++; } } } catch (Exception $e) { } - files::deltree($path); + Files::deltree($path); } $zip->close(); } @@ -133,20 +133,34 @@ class Core 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)) - || !($root = self::getRoot($root)) + // check define + 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) { - if (!($file = self::getFile($file, $info)) - || !($dest = self::getOverwrite($overwrite, $root, $file)) - ) { + if (empty($file)) { + continue; + } + + // check path + $path = $root . DIRECTORY_SEPARATOR . self::getFile($file, $define); + if (file_exists($path) && !$overwrite) { + // don't break loop continue; } @@ -158,14 +172,14 @@ class Core if ($fixnewline) { Zip::$fix_newline = true; } - $zip = new Zip($dest); + $zip = new Zip($path); foreach ($exclude as $e) { $zip->addExclusion($e); } $zip->addDirectory( - (string) path::real($info['root'], false), - $info['id'], + (string) Path::real($define->get('root'), false), + $define->getId(), true ); @@ -176,41 +190,8 @@ class Core 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( [ '\\', @@ -222,44 +203,19 @@ class Core ], [ '/', - $info['type'], - $info['id'], - $info['version'], - $info['author'], + $define->get('type'), + $define->getId(), + $define->get('version'), + $define->get('author'), time(), ], $file ); $parts = explode('/', $file); foreach ($parts as $i => $part) { - $parts[$i] = files::tidyFileName($part); + $parts[$i] = Files::tidyFileName($part); } 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; - } } diff --git a/src/Install.php b/src/Install.php index 21c6081..b5db231 100644 --- a/src/Install.php +++ b/src/Install.php @@ -59,13 +59,13 @@ class Install extends dcNsProcess ); while ($record->fetch()) { - if (preg_match('/^packman_(.*?)$/', $record->setting_id, $match)) { - $cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcNamespace::NS_TABLE_NAME); - $cur->setting_id = $match[1]; - $cur->setting_ns = My::id(); + if (preg_match('/^packman_(.*?)$/', $record->f('setting_id'), $match)) { + $cur = dcCore::app()->con->openCursor(dcCore::app()->prefix . dcNamespace::NS_TABLE_NAME); + $cur->setField('setting_id', $match[1]); + $cur->setField('setting_ns', My::id()); $cur->update( - "WHERE setting_id = '" . $record->setting_id . "' and setting_ns = 'pacKman' " . - 'AND blog_id ' . (null === $record->blog_id ? 'IS NULL ' : ("= '" . dcCore::app()->con->escape($record->blog_id) . "' ")) + "WHERE setting_id = '" . $record->f('setting_id') . "' and setting_ns = 'pacKman' " . + 'AND blog_id ' . (null === $record->f('blog_id') ? 'IS NULL ' : ("= '" . dcCore::app()->con->escapeStr($record->f('blog_id')) . "' ")) ); } } diff --git a/src/Manage.php b/src/Manage.php index 42e72e7..15f5533 100644 --- a/src/Manage.php +++ b/src/Manage.php @@ -19,10 +19,8 @@ use dcCore; use dcPage; use dcThemes; use dcNsProcess; - -/* clearbricks ns */ -use files; -use http; +use Dotclear\Helper\File\Files; +use Dotclear\Helper\Network\Http; class Manage extends dcNsProcess { @@ -58,13 +56,8 @@ class Manage extends dcNsProcess $plugins = dcCore::app()->plugins; # Rights - $is_writable = Utils::is_writable( - $dir, - $s->pack_filename - ); - $is_editable = !empty($type) - && !empty($_POST['modules']) - && is_array($_POST['modules']); + $is_writable = Utils::isWritable($dir, $s->pack_filename); + $is_editable = !empty($type) && !empty($_POST['modules']) && is_array($_POST['modules']); # Actions try { @@ -77,25 +70,25 @@ class Manage extends dcNsProcess $modules = Core::getPackages(Utils::getThemesPath()); } else { $modules = array_merge( - Core::getPackages(dirname($dir . '/' . $s->pack_filename)), - Core::getPackages(dirname($dir . '/' . $s->secondpack_filename)) + Core::getPackages(dirname($dir . DIRECTORY_SEPARATOR . $s->pack_filename)), + Core::getPackages(dirname($dir . DIRECTORY_SEPARATOR . $s->secondpack_filename)) ); } - foreach ($modules as $f) { - if (preg_match('/' . preg_quote($_REQUEST['package']) . '$/', $f['root']) - && is_file($f['root']) && is_readable($f['root']) + foreach ($modules as $module) { + if (preg_match('/' . preg_quote($_REQUEST['package']) . '$/', $module->get('root')) + && is_file($module->get('root')) && is_readable($module->get('root')) ) { # --BEHAVIOR-- packmanBeforeDownloadPackage - dcCore::app()->callBehavior('packmanBeforeDownloadPackage', $f, $type); + dcCore::app()->callBehavior('packmanBeforeDownloadPackage', $module->dump(), $type); header('Content-Type: application/zip'); - header('Content-Length: ' . filesize($f['root'])); - header('Content-Disposition: attachment; filename="' . basename($f['root']) . '"'); - readfile($f['root']); + header('Content-Length: ' . filesize($module->get('root'))); + header('Content-Disposition: attachment; filename="' . basename($module->get('root')) . '"'); + readfile($module->get('root')); # --BEHAVIOR-- packmanAfterDownloadPackage - dcCore::app()->callBehavior('packmanAfterDownloadPackage', $f, $type); + dcCore::app()->callBehavior('packmanAfterDownloadPackage', $module->dump(), $type); exit; } @@ -103,7 +96,7 @@ class Manage extends dcNsProcess # Not found header('Content-Type: text/plain'); - http::head(404, 'Not Found'); + Http::head(404, 'Not Found'); exit; } elseif (!empty($action) && !$is_editable) { dcPage::addErrorNotice( @@ -111,7 +104,7 @@ class Manage extends dcNsProcess ); if (!empty($_POST['redir'])) { - http::redirect($_POST['redir']); + Http::redirect($_POST['redir']); } else { dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-' . $type); } @@ -119,16 +112,14 @@ class Manage extends dcNsProcess # Pack } elseif ($action == 'packup') { foreach ($_POST['modules'] as $root => $id) { - if (!Utils::moduleExists($type, $id)) { + if (!dcCore::app()->{$type}->getDefine($id)->isDefined()) { throw new Exception('No such module'); } - $module = Utils::getModules($type, $id); - $module['id'] = $id; - $module['type'] = $type == 'themes' ? 'theme' : 'plugin'; + $module = dcCore::app()->{$type}->getDefine($id); # --BEHAVIOR-- packmanBeforeCreatePackage - dcCore::app()->callBehavior('packmanBeforeCreatePackage', $module); + dcCore::app()->callBehavior('packmanBeforeCreatePackage', $module->dump()); Core::pack( $module, @@ -141,7 +132,7 @@ class Manage extends dcNsProcess ); # --BEHAVIOR-- packmanAfterCreatePackage - dcCore::app()->callBehavior('packmanAfterCreatePackage', $module); + dcCore::app()->callBehavior('packmanAfterCreatePackage', $module->dump()); } dcPage::addSuccessNotice( @@ -149,7 +140,7 @@ class Manage extends dcNsProcess ); if (!empty($_POST['redir'])) { - http::redirect($_POST['redir']); + Http::redirect($_POST['redir']); } else { dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-' . $type); } @@ -158,7 +149,7 @@ class Manage extends dcNsProcess } elseif ($action == 'delete') { $del_success = false; 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))); } else { $del_success = true; @@ -174,7 +165,7 @@ class Manage extends dcNsProcess } if (!empty($_POST['redir'])) { - http::redirect($_POST['redir']); + Http::redirect($_POST['redir']); } else { dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type); } @@ -201,7 +192,7 @@ class Manage extends dcNsProcess ); if (!empty($_POST['redir'])) { - http::redirect($_POST['redir']); + Http::redirect($_POST['redir']); } else { 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) { file_put_contents( - $dest . '/' . basename($root), + $dest . DIRECTORY_SEPARATOR . basename($root), file_get_contents($root) ); } @@ -227,7 +218,7 @@ class Manage extends dcNsProcess ); if (!empty($_POST['redir'])) { - http::redirect($_POST['redir']); + Http::redirect($_POST['redir']); } else { dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type); } @@ -254,7 +245,7 @@ class Manage extends dcNsProcess ); if (!empty($_POST['redir'])) { - http::redirect($_POST['redir']); + Http::redirect($_POST['redir']); } else { dcCore::app()->adminurl?->redirect('admin.plugin.' . My::id(), [], '#packman-repository-' . $type); } @@ -276,7 +267,7 @@ class Manage extends dcNsProcess $s = new Settings(); $dir = Utils::getRepositoryDir($s->pack_repository); - $is_configured = Utils::is_configured( + $is_configured = Utils::isConfigured( $dir, $s->pack_filename, $s->secondpack_filename @@ -306,13 +297,13 @@ class Manage extends dcNsProcess ''; } else { Utils::modules( - Utils::getModules('plugins'), + dcCore::app()->plugins->getDefines((new Settings())->hide_distrib ? ['distributed' => false] : []), 'plugins', __('Installed plugins') ); Utils::modules( - Utils::getModules('themes'), + dcCore::app()->themes->getDefines((new Settings())->hide_distrib ? ['distributed' => false] : []), 'themes', __('Installed themes') ); @@ -331,8 +322,8 @@ class Manage extends dcNsProcess Utils::repository( array_merge( - Core::getPackages(dirname($dir . '/' . $s->pack_filename)), - Core::getPackages(dirname($dir . '/' . $s->secondpack_filename)) + Core::getPackages(dirname($dir . DIRECTORY_SEPARATOR . $s->pack_filename)), + Core::getPackages(dirname($dir . DIRECTORY_SEPARATOR . $s->secondpack_filename)) ), 'repository', __('Packages repository') diff --git a/src/Uninstall.php b/src/Uninstall.php index b03566b..ba3a000 100644 --- a/src/Uninstall.php +++ b/src/Uninstall.php @@ -20,14 +20,14 @@ class Uninstall 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 { - if (!self::$init) { + if (!static::$init) { return false; } diff --git a/src/Utils.php b/src/Utils.php index af23955..4f2ee0b 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -14,17 +14,23 @@ declare(strict_types=1); namespace Dotclear\Plugin\pacKman; -/* dotclear ns */ use dcCore; +use Dotclear\Helper\File\Files; +use Dotclear\Helper\File\Path; use Dotclear\Helper\File\Zip\Unzip; 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 files; -use form; -use html; -use path; class Utils { @@ -33,7 +39,7 @@ class Utils $e = explode(PATH_SEPARATOR, DC_PLUGINS_ROOT); $p = array_pop($e); - return (string) path::real($p); + return (string) Path::real($p); } public static function getThemesPath(): string @@ -41,13 +47,8 @@ class Utils 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)) { dcCore::app()->error->add( __('Path to repository is not writable.') @@ -75,7 +76,7 @@ class Utils 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))); } @@ -143,7 +144,7 @@ class Utils if (empty($dir)) { try { $dir = DC_VAR . DIRECTORY_SEPARATOR . 'packman'; - @files::makeDir($dir, true); + @Files::makeDir($dir, true); } catch (Exception $e) { $dir = ''; } @@ -152,35 +153,9 @@ class Utils 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 { - if (empty($modules) || !is_array($modules)) { + if (empty($modules)) { return null; } @@ -198,42 +173,41 @@ class Utils '' . __('Root') . '' . ''; - foreach (self::sort($modules) as $id => $module) { + $i = 1; + self::sort($modules); + foreach ($modules as $module) { echo '' . - '' . + (new Para(null, 'td'))->class('nowrap')->items([ + (new Checkbox(['modules[' . Html::escapeHTML($module->get('root')) . ']', 'modules_' . $type . $i], false))->value(Html::escapeHTML($module->getId())), + (new Label(Html::escapeHTML($module->getId()), Label::OUTSIDE_LABEL_AFTER))->for('modules_' . $type . $i)->class('classic'), + + ])->render() . '' . - html::escapeHTML($module['version']) . + Html::escapeHTML($module->get('version')) . '' . '' . - __(html::escapeHTML($module['name'])) . + __(Html::escapeHTML($module->get('name'))) . '' . '' . - dirname((string) path::real($module['root'], false)) . + dirname((string) Path::real($module->get('root'), false)) . '' . ''; + + $i++; } echo '' . '

' . - '

' . - ( - !empty($_REQUEST['redir']) ? - form::hidden( - ['redir'], - html::escapeHTML($_REQUEST['redir']) - ) : '' - ) . - form::hidden(['p'], My::id()) . - form::hidden(['type'], $type) . - form::hidden(['action'], 'packup') . - '' . - dcCore::app()->formNonce() . '

' . + (new Para())->items([ + (new Hidden(['redir'], Html::escapeHTML($_REQUEST['redir'] ?? ''))), + (new Hidden(['p'], My::id())), + (new Hidden(['type'], $type)), + (new Hidden(['action'], 'packup')), + (new Submit(['packup']))->value(__('Pack up selected modules')), + dcCore::app()->formNonce(false), + ])->render() . '' . ''; @@ -243,7 +217,7 @@ class Utils public static function repository(array $modules, string $type, string $title): ?bool { - if (empty($modules) || !is_array($modules)) { + if (empty($modules)) { return null; } if (!in_array($type, ['plugins', 'themes', 'repository'])) { @@ -284,52 +258,57 @@ class Utils ''; $dup = []; - foreach (self::sort($modules) as $module) { - if (isset($dup[$module['root']])) { + $i = 1; + self::sort($modules); + foreach ($modules as $module) { + if (isset($dup[$module->get('root')])) { continue; } - $dup[$module['root']] = 1; + $dup[$module->get('root')] = 1; echo '' . - '' . + (new Para(null, 'td'))->class('nowrap')->items([ + (new Checkbox(['modules[' . Html::escapeHTML($module->get('root')) . ']', 'r_modules_' . $type . $i], false))->value(Html::escapeHTML($module->getId())), + (new Label(Html::escapeHTML($module->getId()), Label::OUTSIDE_LABEL_AFTER))->for('r_modules_' . $type . $i)->class('classic')->title(Html::escapeHTML($module->get('root'))), + + ])->render() . '' . - html::escapeHTML($module['version']) . + Html::escapeHTML($module->get('version')) . '' . '' . - __(html::escapeHTML($module['name'])) . + __(Html::escapeHTML($module->get('name'))) . '' . '' . '' . - html::escapeHTML(basename($module['root'])) . '' . + Html::escapeHTML(basename($module->get('root'))) . '' . '' . '' . - 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')))) . '' . ''; + + $i++; } echo '' . '
' . '

' . - '

' . __('Selected modules action:') . ' ' . - form::combo(['action'], $combo_action) . - '' . - form::hidden(['p'], My::id()) . - form::hidden(['tab'], 'repository') . - form::hidden(['type'], $type) . - dcCore::app()->formNonce() . - '

' . + (new Para())->class('col right')->items([ + (new Text('', __('Selected modules action:') . ' ')), + (new Select(['action']))->items($combo_action), + (new Submit(['packup']))->value(__('ok')), + (new Hidden(['p'], My::id())), + (new Hidden(['tab'], 'repository')), + (new Hidden(['type'], $type)), + dcCore::app()->formNonce(false), + ])->render() . '
' . '' . ''; @@ -337,15 +316,9 @@ class Utils return true; } - protected static function sort(array $modules): array + protected static function sort(array &$modules): void { - $key = $ver = []; - foreach ($modules as $i => $module) { - $key[$i] = $module['id'] ?? $i; - $ver[$i] = $module['version']; - } - array_multisort($key, SORT_ASC, $ver, SORT_ASC, $modules); - - return $modules; + uasort($modules, fn ($a, $b) => $a->get('version') <=> $b->get('version')); + uasort($modules, fn ($a, $b) => strtolower($a->get('id')) <=> strtolower($b->get('id'))); } }