diff --git a/src/ActionDescriptor.php b/src/ActionDescriptor.php index f59a0eb..1a59533 100644 --- a/src/ActionDescriptor.php +++ b/src/ActionDescriptor.php @@ -42,7 +42,7 @@ class ActionDescriptor /** * Get descriptor properties. * - * @return array The properties + * @return array The properties */ public function dump(): array { diff --git a/src/ActionsCleanersStack.php b/src/ActionsCleanersStack.php new file mode 100644 index 0000000..35c6b3a --- /dev/null +++ b/src/ActionsCleanersStack.php @@ -0,0 +1,79 @@ + + */ +class ActionsCleanersStack implements Countable, Iterator +{ + /** @var array $stack The stack */ + private array $stack = []; + + public function exists(int $offset): bool + { + return isset($this->stack[$offset]); + } + + public function get(int $offset): ?ActionDescriptor + { + return $this->stack[$offset] ?? null; + } + + public function set(ActionDescriptor $value): void + { + $this->stack[] = $value; + } + + public function unset(int $offset): void + { + unset($this->stack[$offset]); + } + + public function rewind(): void + { + reset($this->stack); + } + + public function current(): false|ActionDescriptor + { + return current($this->stack); + } + + public function key(): ?int + { + return key($this->stack); + } + + public function next(): void + { + next($this->stack); + } + + public function valid(): bool + { + return key($this->stack) !== null; + } + + public function count(): int + { + return count($this->stack); + } +} diff --git a/src/ActionsStack.php b/src/ActionsStack.php new file mode 100644 index 0000000..1a1e1d2 --- /dev/null +++ b/src/ActionsStack.php @@ -0,0 +1,83 @@ + + */ +class ActionsStack implements Countable, Iterator +{ + /** @var array $stack The stack */ + private array $stack = []; + + public function exists(string $offset): bool + { + return isset($this->stack[$offset]); + } + + public function get(string $offset): ActionsCleanersStack + { + if (!$this->exists($offset)) { + $this->set($offset, new ActionsCleanersStack()); + } + + return $this->stack[$offset]; + } + + public function set(string $offset, ActionsCleanersStack $value): void + { + $this->stack[$offset] = $value; + } + + public function unset(string $offset): void + { + unset($this->stack[$offset]); + } + + public function rewind(): void + { + reset($this->stack); + } + + public function current(): false|ActionsCleanersStack + { + return current($this->stack); + } + + public function key(): ?string + { + return key($this->stack); + } + + public function next(): void + { + next($this->stack); + } + + public function valid(): bool + { + return key($this->stack) !== null; + } + + public function count(): int + { + return count($this->stack); + } +} diff --git a/src/Backend.php b/src/Backend.php index da88439..8a2eafa 100644 --- a/src/Backend.php +++ b/src/Backend.php @@ -46,7 +46,7 @@ class Backend extends dcNsProcess return ''; } - return empty(Uninstaller::instance()->loadModules([$define])->getUserActions($define->getId())) ? '' : + return !count(Uninstaller::instance()->loadModules([$define])->getUserActions($define->getId())) ? '' : sprintf( ' ' . __('Uninstall') . '', dcCore::app()->adminurl?->get('admin.plugin.' . My::id(), ['type' => $define->get('type'), 'id' => $define->getId()]) @@ -90,10 +90,10 @@ class Backend extends dcNsProcess $done = []; foreach ($uninstaller->getDirectActions($define->getId()) as $cleaner => $stack) { foreach ($stack as $action) { - if ($uninstaller->execute($cleaner, $action['action'], $action['ns'])) { - $done[] = $action['success']; + if ($uninstaller->execute($cleaner, $action->id, $action->ns)) { + $done[] = $action->success; } else { - dcCore::app()->error->add($action['error']); + dcCore::app()->error->add($action->error); } } } diff --git a/src/Cleaner/Caches.php b/src/Cleaner/Caches.php index b137b7e..032b56f 100644 --- a/src/Cleaner/Caches.php +++ b/src/Cleaner/Caches.php @@ -15,9 +15,9 @@ declare(strict_types=1); namespace Dotclear\Plugin\Uninstaller\Cleaner; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, ActionDescriptor, CleanerDescriptor, + CleanerParent, TraitCleanerDir, ValueDescriptor }; @@ -28,7 +28,7 @@ use Dotclear\Plugin\Uninstaller\{ * It allows modules to delete an entire sub folder * of DC_TPL_CACHE directory path. */ -class Caches extends AbstractCleaner +class Caches extends CleanerParent { use TraitCleanerDir; diff --git a/src/Cleaner/Logs.php b/src/Cleaner/Logs.php index 6f35d8b..b55fca8 100644 --- a/src/Cleaner/Logs.php +++ b/src/Cleaner/Logs.php @@ -21,9 +21,9 @@ use Dotclear\Database\Statement\{ SelectStatement }; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, ActionDescriptor, CleanerDescriptor, + CleanerParent, ValueDescriptor }; @@ -33,7 +33,7 @@ use Dotclear\Plugin\Uninstaller\{ * It allows modules to delete a "log_table" * of Dotclear dcLog::LOG_TABLE_NAME database table. */ -class Logs extends AbstractCleaner +class Logs extends CleanerParent { public function __construct() { diff --git a/src/Cleaner/Plugins.php b/src/Cleaner/Plugins.php index 27628b9..3e09425 100644 --- a/src/Cleaner/Plugins.php +++ b/src/Cleaner/Plugins.php @@ -15,9 +15,9 @@ declare(strict_types=1); namespace Dotclear\Plugin\Uninstaller\Cleaner; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, ActionDescriptor, CleanerDescriptor, + CleanerParent, ValueDescriptor, TraitCleanerDir }; @@ -27,7 +27,7 @@ use Dotclear\Plugin\Uninstaller\{ * * It allows modules to delete their own folder. */ -class Plugins extends AbstractCleaner +class Plugins extends CleanerParent { use TraitCleanerDir; diff --git a/src/Cleaner/Preferences.php b/src/Cleaner/Preferences.php index f1aa7d8..3a83d17 100644 --- a/src/Cleaner/Preferences.php +++ b/src/Cleaner/Preferences.php @@ -21,9 +21,9 @@ use Dotclear\Database\Statement\{ SelectStatement }; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, ActionDescriptor, CleanerDescriptor, + CleanerParent, ValueDescriptor }; @@ -33,7 +33,7 @@ use Dotclear\Plugin\Uninstaller\{ * It allows modules to delete for users or global a preference workspace. * It also allows to pick-up specific preference id by using delete_related action. */ -class Preferences extends AbstractCleaner +class Preferences extends CleanerParent { public function __construct() { diff --git a/src/Cleaner/Settings.php b/src/Cleaner/Settings.php index 929cbd4..2fe63b1 100644 --- a/src/Cleaner/Settings.php +++ b/src/Cleaner/Settings.php @@ -21,9 +21,9 @@ use Dotclear\Database\Statement\{ SelectStatement }; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, ActionDescriptor, CleanerDescriptor, + CleanerParent, ValueDescriptor }; @@ -33,7 +33,7 @@ use Dotclear\Plugin\Uninstaller\{ * It allows modules to delete for blogs or global a settings namespace. * It also allows to pick-up specific setting id by using delete_related action. */ -class Settings extends AbstractCleaner +class Settings extends CleanerParent { public function __construct() { diff --git a/src/Cleaner/Tables.php b/src/Cleaner/Tables.php index 0458ede..79d44a0 100644 --- a/src/Cleaner/Tables.php +++ b/src/Cleaner/Tables.php @@ -25,9 +25,9 @@ use Dotclear\Database\Statement\{ SelectStatement }; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, ActionDescriptor, CleanerDescriptor, + CleanerParent, ValueDescriptor }; @@ -36,7 +36,7 @@ use Dotclear\Plugin\Uninstaller\{ * * It allows modules to delete or truncate a database table. */ -class Tables extends AbstractCleaner +class Tables extends CleanerParent { public function __construct() { diff --git a/src/Cleaner/Themes.php b/src/Cleaner/Themes.php index 1145d6e..64f9a4c 100644 --- a/src/Cleaner/Themes.php +++ b/src/Cleaner/Themes.php @@ -16,9 +16,9 @@ namespace Dotclear\Plugin\Uninstaller\Cleaner; use dcCore; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, ActionDescriptor, CleanerDescriptor, + CleanerParent, ValueDescriptor, TraitCleanerDir }; @@ -28,7 +28,7 @@ use Dotclear\Plugin\Uninstaller\{ * * It allows modules to delete their own folder. */ -class Themes extends AbstractCleaner +class Themes extends CleanerParent { use TraitCleanerDir; diff --git a/src/Cleaner/Vars.php b/src/Cleaner/Vars.php index 637e838..9f92366 100644 --- a/src/Cleaner/Vars.php +++ b/src/Cleaner/Vars.php @@ -15,9 +15,9 @@ declare(strict_types=1); namespace Dotclear\Plugin\Uninstaller\Cleaner; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, ActionDescriptor, CleanerDescriptor, + CleanerParent, ValueDescriptor, TraitCleanerDir }; @@ -28,7 +28,7 @@ use Dotclear\Plugin\Uninstaller\{ * It allows modules to delete an entire sub folder * of DC_VAR directory path. */ -class Vars extends AbstractCleaner +class Vars extends CleanerParent { use TraitCleanerDir; diff --git a/src/Cleaner/Versions.php b/src/Cleaner/Versions.php index ba93d96..b84d40d 100644 --- a/src/Cleaner/Versions.php +++ b/src/Cleaner/Versions.php @@ -17,7 +17,7 @@ namespace Dotclear\Plugin\Uninstaller\Cleaner; use dcCore; use Dotclear\Database\Statement\SelectStatement; use Dotclear\Plugin\Uninstaller\{ - AbstractCleaner, + CleanerParent, ActionDescriptor, CleanerDescriptor, ValueDescriptor @@ -29,7 +29,7 @@ use Dotclear\Plugin\Uninstaller\{ * It allows modules to delete their versions * from Dotclear dcCore::VERSION_TABLE_NAME database table. */ -class Versions extends AbstractCleaner +class Versions extends CleanerParent { public function __construct() { diff --git a/src/CleanerParent.php b/src/CleanerParent.php index 6957e37..cc0f153 100644 --- a/src/CleanerParent.php +++ b/src/CleanerParent.php @@ -20,7 +20,7 @@ namespace Dotclear\Plugin\Uninstaller; * Cleaner manages only one part of uninstall process. * For exemple Settings, Caches, db, etc... */ -abstract class AbstractCleaner +abstract class CleanerParent { /** @var string $id The cleaner Id */ public readonly string $id; diff --git a/src/CleanersStack.php b/src/CleanersStack.php index 0e12c0f..ddc5f3e 100644 --- a/src/CleanersStack.php +++ b/src/CleanersStack.php @@ -14,62 +14,82 @@ declare(strict_types=1); namespace Dotclear\Plugin\Uninstaller; +use Countable; use dcCore; +use Iterator; use Exception; /** * The cleaners stack. + * + * @implements Iterator */ -class Cleaners +class CleanersStack implements Countable, Iterator { - /** @var array $cleaners The cleaner stack */ - private array $cleaners = []; + /** @var array $stack The cleaner stack */ + private array $stack = []; /** * Contructor load cleaners. */ public function __construct() { - # --BEHAVIOR-- UninstallerCleanersConstruct: Cleaners + # --BEHAVIOR-- UninstallerCleanersConstruct: CleanersStack dcCore::app()->callBehavior('UninstallerCleanersConstruct', $this); } - /** - * Add a cleaner. - * - * @param AbstractCleaner $cleaner The cleaner instance - * - * @return Cleaners Self instance - */ - public function add(AbstractCleaner $cleaner): Cleaners + public function exists(string $offset): bool { - if (!isset($this->cleaners[$cleaner->id])) { - $this->cleaners[$cleaner->id] = $cleaner; + return isset($this->stack[$offset]); + } + + public function get(string $offset): ?CleanerParent + { + return $this->stack[$offset] ?? null; + } + + public function set(CleanerParent $value): CleanersStack + { + if (!isset($this->stack[$value->id])) { + $this->stack[$value->id] = $value; } return $this; } - /** - * Get all clearners. - * - * @return array The cleaners - */ - public function dump(): array + public function unset(string $offset): void { - return $this->cleaners; + unset($this->stack[$offset]); } - /** - * Get a cleaner. - * - * @param string $id The cleaner id - * - * @return null|AbstractCleaner The cleaner - */ - public function get(string $id): ?AbstractCleaner + public function rewind(): void { - return $this->cleaners[$id] ?? null; + reset($this->stack); + } + + public function current(): false|CleanerParent + { + return current($this->stack); + } + + public function key(): ?string + { + return key($this->stack); + } + + public function next(): void + { + next($this->stack); + } + + public function valid(): bool + { + return key($this->stack) !== null; + } + + public function count(): int + { + return count($this->stack); } /** @@ -83,7 +103,7 @@ class Cleaners */ public function execute(string $id, string $action, string $ns): bool { - if (!isset($this->cleaners[$id])) { + if (!isset($this->stack[$id])) { throw new Exception(sprintf(__('Unknown cleaner "%s"'), $id)); } if (in_array($ns, [My::id(), My::root()])) { @@ -93,6 +113,6 @@ class Cleaners # --BEHAVIOR-- UninstallerBeforeAction: string, string, string dcCore::app()->callBehavior('UninstallerBeforeAction', $id, $action, $ns); - return $this->cleaners[$id]->execute($action, $ns); + return $this->stack[$id]->execute($action, $ns); } } diff --git a/src/Manage.php b/src/Manage.php index 67ff92f..4f52f91 100644 --- a/src/Manage.php +++ b/src/Manage.php @@ -25,6 +25,7 @@ use Dotclear\Helper\Html\Form\{ Form, Hidden, Label, + Link, Para, Submit, Text @@ -69,7 +70,7 @@ class Manage extends dcNsProcess // load uninstaller for selected module and check if it has action $uninstaller = Uninstaller::instance()->loadModules([$define]); $actions = $uninstaller->getUserActions($define->getId()); - if (empty($actions)) { + if (!count($actions)) { dcCore::app()->error->add(__('There are no uninstall actions for this module')); self::doRedirect(); } @@ -151,12 +152,12 @@ class Manage extends dcNsProcess } // submit - $fields[] = (new Para())->items([ + $fields[] = (new Para())->separator(' ')->items([ dcCore::app()->formNonce(false), (new Hidden(['type'], self::getType())), (new Hidden(['id'], $define->getId())), (new Submit(['do']))->value(__('Perform selected actions'))->class('delete'), - (new Text('', ' ' . __('Cancel') . '')), + (new Link())->class('button')->text(__('Cancel'))->href(self::getRedirect()), ]); // display form diff --git a/src/Prepend.php b/src/Prepend.php index 43242a7..51abc0f 100644 --- a/src/Prepend.php +++ b/src/Prepend.php @@ -35,17 +35,17 @@ class Prepend extends dcNsProcess } // Add cleaners to Uninstaller - dcCore::app()->addBehavior('UninstallerCleanersConstruct', function (Cleaners $cleaners): void { + dcCore::app()->addBehavior('UninstallerCleanersConstruct', function (CleanersStack $cleaners): void { $cleaners - ->add(new Cleaner\Settings()) - ->add(new Cleaner\Preferences()) - ->add(new Cleaner\Tables()) - ->add(new Cleaner\Versions()) - ->add(new Cleaner\Logs()) - ->add(new Cleaner\Caches()) - ->add(new Cleaner\Vars()) - ->add(new Cleaner\Themes()) - ->add(new Cleaner\Plugins()) + ->set(new Cleaner\Settings()) + ->set(new Cleaner\Preferences()) + ->set(new Cleaner\Tables()) + ->set(new Cleaner\Versions()) + ->set(new Cleaner\Logs()) + ->set(new Cleaner\Caches()) + ->set(new Cleaner\Vars()) + ->set(new Cleaner\Themes()) + ->set(new Cleaner\Plugins()) ; }); diff --git a/src/Uninstaller.php b/src/Uninstaller.php index 9279e2a..33ec8c7 100644 --- a/src/Uninstaller.php +++ b/src/Uninstaller.php @@ -29,8 +29,8 @@ class Uninstaller /** @var string The Uninstall class name */ public const UNINSTALL_CLASS_NAME = 'Uninstall'; - /** @var Cleaners $cleaners The cleaners stack */ - public readonly Cleaners $cleaners; + /** @var CleanersStack $cleaners The cleaners stack */ + public readonly CleanersStack $cleaners; /** @var null|dcModuleDefine $module Current module */ private ?dcModuleDefine $module = null; @@ -41,8 +41,11 @@ class Uninstaller /** @var array List of modules with custom actions render */ private array $renders = []; - /** @var array List of registered actions */ - private array $actions = ['user' => [], 'direct' => []]; + /** @var array List of registered user actions */ + private array $user_actions = []; + + /** @var array List of registered direct actions */ + private array $direct_actions = []; /** @var Uninstaller $uninstaller Uninstaller instance */ private static $uninstaller; @@ -52,7 +55,7 @@ class Uninstaller */ public function __construct() { - $this->cleaners = new Cleaners(); + $this->cleaners = new CleanersStack(); } /** @@ -81,10 +84,11 @@ class Uninstaller public function loadModules(array $modules): Uninstaller { // reset unsintaller - $this->module = null; - $this->modules = []; - $this->renders = []; - $this->actions = ['user' => [], 'direct' => []]; + $this->module = null; + $this->modules = []; + $this->renders = []; + $this->user_actions = []; + $this->direct_actions = []; foreach ($modules as $module) { if (!($module instanceof dcModuleDefine)) { @@ -138,7 +142,12 @@ class Uninstaller */ public function addUserAction(string $cleaner, string $action, string $ns): Uninstaller { - $this->addAction('user', $cleaner, $action, $ns); + if (null !== $this->module && null !== ($res = $this->addAction($cleaner, $action, $ns))) { + if (!isset($this->user_actions[$this->module->getId()])) { + $this->user_actions[$this->module->getId()] = new ActionsStack(); + } + $this->user_actions[$this->module->getId()]->get($cleaner)->set($res); + } return $this; } @@ -159,7 +168,12 @@ class Uninstaller */ public function addDirectAction(string $cleaner, string $action, string $ns): Uninstaller { - $this->addAction('direct', $cleaner, $action, $ns); + if (null !== $this->module && null !== ($res = $this->addAction($cleaner, $action, $ns))) { + if (!isset($this->direct_actions[$this->module->getId()])) { + $this->direct_actions[$this->module->getId()] = new ActionsStack(); + } + $this->direct_actions[$this->module->getId()]->get($cleaner)->set($res); + } return $this; } @@ -169,11 +183,11 @@ class Uninstaller * * @param string $id The module ID * - * @return array List module user actions group by cleaner + * @return ActionsStack List module user actions group by cleaner */ - public function getUserActions(string $id): array + public function getUserActions(string $id): ActionsStack { - return $this->actions['user'][$id] ?? []; + return $this->user_actions[$id] ?? new ActionsStack(); } /** @@ -181,11 +195,11 @@ class Uninstaller * * @param string $id The module ID * - * @return array List module direct actions group by cleaner + * @return ActionsStack List module direct actions group by cleaner */ - public function getDirectActions(string $id): array + public function getDirectActions(string $id): ActionsStack { - return $this->actions['direct'][$id] ?? []; + return $this->direct_actions[$id] ?? new ActionsStack(); } /** @@ -240,12 +254,13 @@ class Uninstaller /** * Add a predefined action to unsintall features. * - * @param string $group The group (user or direct) * @param string $cleaner The cleaner ID * @param string $action The action ID * @param string $ns Name of setting related to module. + * + * @return null|ActionDescriptor The action description */ - private function addAction(string $group, string $cleaner, string $action, string $ns): void + private function addAction(string $cleaner, string $action, string $ns): ?ActionDescriptor { // no current module or no cleaner id or no ns or unknown cleaner action if (null === $this->module @@ -253,11 +268,11 @@ class Uninstaller || empty($ns) || !isset($this->cleaners->get($cleaner)->actions[$action]) ) { - return; + return null; } // fill action properties - $this->actions[$group][$this->module->getId()][$cleaner][] = new ActionDescriptor( + return new ActionDescriptor( id: $action, ns: $ns, select: $this->cleaners->get($cleaner)->actions[$action]->select, diff --git a/src/ValueDescriptor.php b/src/ValueDescriptor.php index 3e21075..d3e953d 100644 --- a/src/ValueDescriptor.php +++ b/src/ValueDescriptor.php @@ -17,8 +17,8 @@ namespace Dotclear\Plugin\Uninstaller; /** * Cleaner value descriptor. * - * Description of a value from AbstractCleaner::value() - * and AbstractCleaner::related() + * Description of a value from CleanerParent::value() + * and CleanerParent::related() */ class ValueDescriptor {