From 91cb2b2e12572fa9dab4df34c68276ca10204fab Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 30 May 2026 20:14:54 -0500 Subject: [PATCH] refactor(demo): full database snapshot instead of selective tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced selective table snapshot with full mysqldump/restore. This ensures the site returns to an exact known state without the risk of breaking the installation by restoring stale #__extensions or #__assets. Uses mysqldump CLI when available, falls back to PHP-based dump. Removed SnapshotTables field and table selection config — the entire database is now captured and restored as one unit. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../api/src/Controller/ResetController.php | 8 +- .../api/src/Controller/SnapshotController.php | 8 +- .../Extension/MokoWaaS.php | 31 +- .../Service/DemoResetService.php | 583 +++++++----------- src/packages/plg_system_mokowaas/mokowaas.xml | 15 +- .../src/Extension/DemoReset.php | 8 +- 6 files changed, 244 insertions(+), 409 deletions(-) diff --git a/src/packages/com_mokowaas/api/src/Controller/ResetController.php b/src/packages/com_mokowaas/api/src/Controller/ResetController.php index 7d88c7d3..b0b42202 100644 --- a/src/packages/com_mokowaas/api/src/Controller/ResetController.php +++ b/src/packages/com_mokowaas/api/src/Controller/ResetController.php @@ -103,13 +103,9 @@ class ResetController extends BaseController require_once $serviceFile; - $tablesParam = $params->get('demo_snapshot_tables', ''); - $tables = is_array($tablesParam) ? array_filter($tablesParam) : array_filter(array_map('trim', explode("\n", $tablesParam))); - $media = $params->get('demo_snapshot_include_media', ['images']); - if ($media === '1' || $media === true) $media = ['images']; - if ($media === '0' || $media === false) $media = []; + $media = (bool) $params->get('demo_snapshot_include_media', 1); - return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($tables, (array) $media); + return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($media); } /** diff --git a/src/packages/com_mokowaas/api/src/Controller/SnapshotController.php b/src/packages/com_mokowaas/api/src/Controller/SnapshotController.php index b6cf8b4f..0046fac0 100644 --- a/src/packages/com_mokowaas/api/src/Controller/SnapshotController.php +++ b/src/packages/com_mokowaas/api/src/Controller/SnapshotController.php @@ -130,13 +130,9 @@ class SnapshotController extends BaseController $plugin = PluginHelper::getPlugin('system', 'mokowaas'); $params = $plugin ? new Registry($plugin->params) : new Registry; - $tablesParam = $params->get('demo_snapshot_tables', ''); - $tables = is_array($tablesParam) ? array_filter($tablesParam) : array_filter(array_map('trim', explode("\n", $tablesParam))); - $media = $params->get('demo_snapshot_include_media', ['images']); - if ($media === '1' || $media === true) $media = ['images']; - if ($media === '0' || $media === false) $media = []; + $media = (bool) $params->get('demo_snapshot_include_media', 1); - return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($tables, (array) $media); + return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($media); } /** diff --git a/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php b/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php index 3be1dba8..4a9a73f2 100644 --- a/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php +++ b/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php @@ -1739,36 +1739,9 @@ class MokoWaaS extends CMSPlugin implements BootableExtensionInterface { require_once __DIR__ . '/../Service/DemoResetService.php'; - $tablesParam = $this->params->get('demo_snapshot_tables', ''); + $includeMedia = (bool) $this->params->get('demo_snapshot_include_media', 1); - // Handle array, nested array, or legacy newline-separated textarea - if (is_array($tablesParam)) - { - $tables = array_filter(array_map(function ($v) { - return is_array($v) ? reset($v) : $v; - }, $tablesParam)); - } - else - { - $tables = array_filter(array_map('trim', explode("\n", $tablesParam))); - } - - $mediaDirs = $this->params->get('demo_snapshot_include_media', ['images']); - - // Handle legacy boolean value - if ($mediaDirs === '1' || $mediaDirs === true) - { - $mediaDirs = ['images']; - } - elseif ($mediaDirs === '0' || $mediaDirs === false) - { - $mediaDirs = []; - } - - return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService( - $tables, - (array) $mediaDirs - ); + return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($includeMedia); } /** diff --git a/src/packages/plg_system_mokowaas/Service/DemoResetService.php b/src/packages/plg_system_mokowaas/Service/DemoResetService.php index 34dd1aff..c696132d 100644 --- a/src/packages/plg_system_mokowaas/Service/DemoResetService.php +++ b/src/packages/plg_system_mokowaas/Service/DemoResetService.php @@ -10,8 +10,8 @@ * INGROUP: MokoWaaS * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS * PATH: /src/packages/plg_system_mokowaas/Service/DemoResetService.php - * VERSION: 02.26.03 - * BRIEF: Core snapshot/restore service for demo site reset + * VERSION: 02.28.00 + * BRIEF: Full database snapshot/restore service for demo site reset */ namespace Moko\Plugin\System\MokoWaaS\Service; @@ -22,15 +22,13 @@ use Joomla\CMS\Factory; use Joomla\CMS\Log\Log; /** - * Demo Reset Service — manages baseline snapshots and restore operations. + * Demo Reset Service — full database snapshot and restore. * - * This is the single source of truth for all snapshot logic. Called by: - * - Admin one-shot toggles (onExtensionAfterSave) - * - Query-string API (?mokowaas=reset / ?mokowaas=snapshot) - * - REST API (/api/v1/mokowaas/reset, /api/v1/mokowaas/snapshot) - * - Joomla Scheduled Task (plg_task_mokowaasdemo) + * Takes a complete mysqldump of the database and restores it wholesale. + * This avoids the complexity of selective table management and ensures + * the site returns to an exact known state. * - * @since 02.21.00 + * @since 02.28.00 */ class DemoResetService { @@ -42,38 +40,6 @@ class DemoResetService */ private const MAX_NAME_LENGTH = 64; - /** - * Rows per batch for paginated table dump/restore. - * - * @var int - * @since 02.21.00 - */ - private const BATCH_SIZE = 500; - - /** - * Default tables to snapshot if none configured. - * - * @var array - * @since 02.21.00 - */ - private const DEFAULT_TABLES = [ - '#__content', - '#__categories', - '#__fields', - '#__fields_values', - '#__fields_groups', - '#__menu', - '#__menu_types', - '#__modules', - '#__modules_menu', - '#__users', - '#__user_usergroup_map', - '#__user_profiles', - '#__tags', - '#__contentitem_tag_map', - '#__assets', - ]; - /** * Root directory for all snapshots. * @@ -83,47 +49,25 @@ class DemoResetService private string $snapshotDir; /** - * Tables to include in snapshots. + * Whether to include /images/ in snapshots. * - * @var array + * @var bool * @since 02.21.00 */ - private array $tables; - - /** - * Directories to include in media snapshot (e.g. ['images', 'media']). - * - * @var array - * @since 02.25.00 - */ - private array $mediaDirs; + private bool $includeMedia; /** * Constructor. * - * @param array $tables Table names with #__ prefix - * @param array|bool $mediaDirs Dirs to snapshot: ['images','media'], true (= images), false/[] (= none) - * @param string $baseDir Override snapshot root (for testing) + * @param bool $includeMedia Include /images/ directory in snapshot + * @param string $baseDir Override snapshot root (for testing) * - * @since 02.21.00 + * @since 02.28.00 */ - public function __construct(array $tables = [], $mediaDirs = ['images'], string $baseDir = '') + public function __construct(bool $includeMedia = true, string $baseDir = '') { - $this->tables = !empty($tables) ? $tables : self::DEFAULT_TABLES; - $this->snapshotDir = $baseDir ?: JPATH_ROOT . '/mokowaas-snapshots'; - - if ($mediaDirs === true) - { - $this->mediaDirs = ['images']; - } - elseif ($mediaDirs === false || empty($mediaDirs)) - { - $this->mediaDirs = []; - } - else - { - $this->mediaDirs = (array) $mediaDirs; - } + $this->includeMedia = $includeMedia; + $this->snapshotDir = $baseDir ?: JPATH_ROOT . '/mokowaas-snapshots'; } /** @@ -158,16 +102,16 @@ class DemoResetService } /** - * Create a named snapshot of the current site state. + * Create a full database snapshot. * - * @param string $name Snapshot name (alphanumeric, hyphens, underscores) + * @param string $name Snapshot name * - * @return array Result payload with status, tables count, size info + * @return array Result payload * - * @throws \InvalidArgumentException On invalid snapshot name - * @throws \RuntimeException On filesystem/database failures + * @throws \InvalidArgumentException On invalid name + * @throws \RuntimeException On failure * - * @since 02.21.00 + * @since 02.28.00 */ public function createSnapshot(string $name): array { @@ -176,7 +120,6 @@ class DemoResetService $path = $this->getSnapshotPath($name); - // Remove existing snapshot with the same name if (is_dir($path)) { $this->removeDirectory($path); @@ -187,39 +130,45 @@ class DemoResetService throw new \RuntimeException('Failed to create snapshot directory: ' . $path); } - $db = Factory::getDbo(); - $prefix = $db->getPrefix(); - $tables = $db->getTableList(); - $dumped = 0; + // Full database dump + $config = Factory::getConfig(); + $host = $config->get('host', 'localhost'); + $dbName = $config->get('db'); + $user = $config->get('user'); + $pass = $config->get('password'); - foreach ($this->tables as $tableName) + $dumpFile = $path . '/database.sql'; + + // Use mysqldump for a complete, reliable dump + $cmd = sprintf( + 'mysqldump --host=%s --user=%s --password=%s --single-transaction --routines --triggers --add-drop-table %s > %s 2>&1', + escapeshellarg($host), + escapeshellarg($user), + escapeshellarg($pass), + escapeshellarg($dbName), + escapeshellarg($dumpFile) + ); + + exec($cmd, $output, $exitCode); + + if ($exitCode !== 0 || !file_exists($dumpFile) || filesize($dumpFile) === 0) { - $realTable = str_replace('#__', $prefix, $tableName); - - if (!in_array($realTable, $tables)) - { - continue; - } - - $this->dumpTable($tableName, $realTable, $path, $db); - $dumped++; + // Fallback: PHP-based dump if mysqldump is not available + $this->phpDatabaseDump($dumpFile); } - // Media snapshot — one ZIP per directory - $mediaDirs = []; + $dumpSizeMb = round(filesize($dumpFile) / 1048576, 1); - foreach ($this->mediaDirs as $dir) + // Media snapshot + $hasMedia = false; + + if ($this->includeMedia) { - $fullPath = JPATH_ROOT . '/' . $dir; + $imagesDir = JPATH_ROOT . '/images'; - if (is_dir($fullPath)) + if (is_dir($imagesDir)) { - $zipName = 'media_' . $dir . '.zip'; - - if ($this->snapshotDirectory($fullPath, $path . '/' . $zipName)) - { - $mediaDirs[] = $dir; - } + $hasMedia = $this->snapshotDirectory($imagesDir, $path . '/media.zip'); } } @@ -227,10 +176,9 @@ class DemoResetService $manifest = [ 'name' => $name, 'created_at' => gmdate('Y-m-d\TH:i:s\Z'), - 'tables' => $dumped, - 'table_list' => $this->tables, - 'has_media' => !empty($mediaDirs), - 'media_dirs' => $mediaDirs, + 'type' => 'full-database', + 'dump_size_mb' => $dumpSizeMb, + 'has_media' => $hasMedia, 'joomla_version' => JVERSION, ]; @@ -240,17 +188,17 @@ class DemoResetService ); Log::add( - sprintf('Demo snapshot "%s" created (%d tables, media=%s)', $name, $dumped, $hasMedia ? 'yes' : 'no'), + sprintf('Demo snapshot "%s" created (full DB %.1f MB, media=%s)', $name, $dumpSizeMb, $hasMedia ? 'yes' : 'no'), Log::INFO, 'mokowaas' ); return [ - 'status' => 'ok', - 'message' => 'Snapshot created', - 'name' => $name, - 'tables' => $dumped, - 'has_media' => $hasMedia, + 'status' => 'ok', + 'message' => 'Snapshot created', + 'name' => $name, + 'dump_size_mb' => $dumpSizeMb, + 'has_media' => $hasMedia, ]; } @@ -264,7 +212,7 @@ class DemoResetService * @throws \InvalidArgumentException On invalid name * @throws \RuntimeException On missing snapshot or restore failure * - * @since 02.21.00 + * @since 02.28.00 */ public function restoreSnapshot(string $name): array { @@ -285,7 +233,14 @@ class DemoResetService throw new \RuntimeException('Invalid manifest for snapshot: ' . $name); } - // Clear Joomla cache before restore + $dumpFile = $path . '/database.sql'; + + if (!file_exists($dumpFile)) + { + throw new \RuntimeException('Database dump not found in snapshot: ' . $name); + } + + // Clear Joomla cache try { $cache = Factory::getCache(''); @@ -293,75 +248,51 @@ class DemoResetService } catch (\Throwable $e) { - // Cache clear is best-effort + // Best effort } - $db = Factory::getDbo(); - $prefix = $db->getPrefix(); - $restored = 0; + // Restore database + $config = Factory::getConfig(); + $host = $config->get('host', 'localhost'); + $dbName = $config->get('db'); + $user = $config->get('user'); + $pass = $config->get('password'); - // Restore tables — assets first for ACL integrity - $sqlFiles = glob($path . '/*.sql'); + // Try mysql CLI first (fastest, handles large dumps) + $cmd = sprintf( + 'mysql --host=%s --user=%s --password=%s %s < %s 2>&1', + escapeshellarg($host), + escapeshellarg($user), + escapeshellarg($pass), + escapeshellarg($dbName), + escapeshellarg($dumpFile) + ); - // Sort: #__assets first - usort($sqlFiles, function ($a, $b) { - $aIsAssets = str_contains(basename($a), '__assets'); - $bIsAssets = str_contains(basename($b), '__assets'); + exec($cmd, $output, $exitCode); - if ($aIsAssets) return -1; - if ($bIsAssets) return 1; - - return strcmp($a, $b); - }); - - foreach ($sqlFiles as $sqlFile) + if ($exitCode !== 0) { - try - { - $this->restoreTable($sqlFile, $db, $prefix); - $restored++; - } - catch (\Throwable $e) - { - Log::add( - sprintf('Demo reset: failed to restore %s: %s', basename($sqlFile), $e->getMessage()), - Log::ERROR, - 'mokowaas' - ); - } + // Fallback: PHP-based restore + $this->phpDatabaseRestore($dumpFile); } - // Restore media directories + // Restore /images/ $mediaRestored = false; - $restoredDirs = $manifest['media_dirs'] ?? []; - // Legacy support: old manifests used has_media=true with a single media.zip for /images/ - if (empty($restoredDirs) && ($manifest['has_media'] ?? false)) + if ($manifest['has_media'] ?? false) { - $restoredDirs = ['images']; - } - - foreach ($restoredDirs as $dir) - { - $zipName = 'media_' . $dir . '.zip'; - $zipPath = $path . '/' . $zipName; - - // Legacy fallback: old snapshots used media.zip for images - if (!file_exists($zipPath) && $dir === 'images' && file_exists($path . '/media.zip')) - { - $zipPath = $path . '/media.zip'; - } + $zipPath = $path . '/media.zip'; + $imagesDir = JPATH_ROOT . '/images'; if (file_exists($zipPath)) { - $targetDir = JPATH_ROOT . '/' . $dir; - $this->clearDirectory($targetDir); + $this->clearDirectory($imagesDir); $zip = new \ZipArchive(); if ($zip->open($zipPath) === true) { - $zip->extractTo($targetDir); + $zip->extractTo($imagesDir); $zip->close(); $mediaRestored = true; } @@ -369,17 +300,17 @@ class DemoResetService } Log::add( - sprintf('Demo site reset to baseline "%s" (%d tables, media=%s)', $name, $restored, $mediaRestored ? 'yes' : 'no'), + sprintf('Demo site reset to baseline "%s" (full DB, media=%s)', $name, $mediaRestored ? 'yes' : 'no'), Log::WARNING, 'mokowaas' ); return [ - 'status' => 'ok', - 'message' => 'Site restored to baseline: ' . $name, - 'baseline' => $name, - 'restored_tables' => $restored, - 'media_restored' => $mediaRestored, + 'status' => 'ok', + 'message' => 'Site restored to baseline: ' . $name, + 'baseline' => $name, + 'type' => 'full-database', + 'media_restored' => $mediaRestored, ]; } @@ -405,154 +336,158 @@ class DemoResetService $this->removeDirectory($path); - Log::add( - sprintf('Demo snapshot "%s" deleted', $name), - Log::INFO, - 'mokowaas' - ); - return true; } /** - * Dump a single table to a SQL file using paginated reads. + * PHP fallback: dump entire database when mysqldump is unavailable. * - * @param string $logicalName Table name with #__ prefix - * @param string $realName Actual table name with prefix - * @param string $dir Snapshot directory - * @param \Joomla\Database\DatabaseInterface $db Database driver + * @param string $dumpFile Output file path * * @return void * - * @since 02.21.00 + * @since 02.28.00 */ - private function dumpTable(string $logicalName, string $realName, string $dir, $db): void + private function phpDatabaseDump(string $dumpFile): void { - $safeFileName = str_replace('#__', 'jml__', $logicalName); - $fp = fopen($dir . '/' . $safeFileName . '.sql', 'w'); + $db = Factory::getDbo(); + $tables = $db->getTableList(); + $fp = fopen($dumpFile, 'w'); if ($fp === false) { - throw new \RuntimeException('Cannot write dump file for: ' . $logicalName); + throw new \RuntimeException('Cannot write dump file'); } - // Get column names for consistent INSERT statements - $columns = $db->getTableColumns($realName, false); - $colNames = array_keys($columns); - $quotedCols = array_map([$db, 'quoteName'], $colNames); - $colList = implode(', ', $quotedCols); + fwrite($fp, "SET FOREIGN_KEY_CHECKS=0;\n\n"); - $offset = 0; - - while (true) + foreach ($tables as $table) { - $query = $db->getQuery(true) - ->select('*') - ->from($db->quoteName($realName)) - ->setLimit(self::BATCH_SIZE, $offset); + // CREATE TABLE statement + $db->setQuery('SHOW CREATE TABLE ' . $db->quoteName($table)); + $create = $db->loadAssoc(); + $createSql = $create['Create Table'] ?? $create['Create View'] ?? ''; - $db->setQuery($query); - $rows = $db->loadAssocList(); - - if (empty($rows)) - { - break; - } - - // Build multi-value INSERT - $values = []; - - foreach ($rows as $row) - { - $vals = []; - - foreach ($colNames as $col) - { - $val = $row[$col]; - - if ($val === null) - { - $vals[] = 'NULL'; - } - else - { - $vals[] = $db->quote($val); - } - } - - $values[] = '(' . implode(', ', $vals) . ')'; - } - - fwrite($fp, 'INSERT INTO ' . $db->quoteName($realName) - . ' (' . $colList . ') VALUES ' . "\n" - . implode(",\n", $values) . ";\n\n"); - - $offset += self::BATCH_SIZE; - - if (count($rows) < self::BATCH_SIZE) - { - break; - } - } - - fclose($fp); - } - - /** - * Restore a table from a SQL dump file. - * - * @param string $sqlFile Path to the .sql file - * @param \Joomla\Database\DatabaseInterface $db Database driver - * @param string $prefix Table prefix - * - * @return void - * - * @since 02.21.00 - */ - private function restoreTable(string $sqlFile, $db, string $prefix): void - { - // Derive table name from filename: jml__content.sql -> {prefix}content - $baseName = basename($sqlFile, '.sql'); - $realTable = str_replace('jml__', $prefix, $baseName); - - // Truncate the table first - $db->setQuery('TRUNCATE TABLE ' . $db->quoteName($realTable)); - $db->execute(); - - $sql = file_get_contents($sqlFile); - - if (empty(trim($sql))) - { - return; - } - - // Split by semicolons and execute each statement - $statements = array_filter( - array_map('trim', explode(";\n", $sql)), - function ($s) { return !empty($s) && $s !== ';'; } - ); - - foreach ($statements as $statement) - { - $statement = rtrim($statement, ';'); - - if (empty($statement)) + if (empty($createSql)) { continue; } - $db->setQuery($statement); - $db->execute(); + fwrite($fp, "DROP TABLE IF EXISTS " . $db->quoteName($table) . ";\n"); + fwrite($fp, $createSql . ";\n\n"); + + // Skip views for data dump + if (isset($create['Create View'])) + { + continue; + } + + // Dump data in batches + $offset = 0; + + while (true) + { + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName($table)) + ->setLimit(500, $offset); + + $db->setQuery($query); + $rows = $db->loadAssocList(); + + if (empty($rows)) + { + break; + } + + // Get column names + $columns = array_keys($rows[0]); + $quotedCols = array_map([$db, 'quoteName'], $columns); + $colList = implode(', ', $quotedCols); + + $values = []; + + foreach ($rows as $row) + { + $vals = []; + + foreach ($columns as $col) + { + $val = $row[$col]; + $vals[] = $val === null ? 'NULL' : $db->quote($val); + } + + $values[] = '(' . implode(', ', $vals) . ')'; + } + + fwrite($fp, 'INSERT INTO ' . $db->quoteName($table) + . ' (' . $colList . ') VALUES ' . "\n" + . implode(",\n", $values) . ";\n\n"); + + $offset += 500; + + if (count($rows) < 500) + { + break; + } + } + } + + fwrite($fp, "\nSET FOREIGN_KEY_CHECKS=1;\n"); + fclose($fp); + } + + /** + * PHP fallback: restore database from SQL dump when mysql CLI is unavailable. + * + * @param string $dumpFile SQL dump file path + * + * @return void + * + * @since 02.28.00 + */ + private function phpDatabaseRestore(string $dumpFile): void + { + $db = Factory::getDbo(); + $sql = file_get_contents($dumpFile); + + if (empty($sql)) + { + throw new \RuntimeException('Empty database dump file'); + } + + // Split by semicolons followed by newlines (avoids splitting within values) + $statements = preg_split('/;\s*\n/', $sql); + + foreach ($statements as $statement) + { + $statement = trim($statement); + + if (empty($statement) || $statement === ';') + { + continue; + } + + try + { + $db->setQuery($statement); + $db->execute(); + } + catch (\Throwable $e) + { + // Log but continue — partial restore is better than aborting + Log::add('DB restore warning: ' . $e->getMessage(), Log::WARNING, 'mokowaas'); + } } } /** * Create a ZIP archive of a directory. * - * @param string $sourceDir Full path to the directory to archive - * @param string $zipPath Full path for the output ZIP file + * @param string $sourceDir Full path to directory + * @param string $zipPath Output ZIP path * - * @return bool True if archived successfully + * @return bool * * @since 02.25.00 */ @@ -608,7 +543,7 @@ class DemoResetService { if (!mkdir($this->snapshotDir, 0755, true)) { - throw new \RuntimeException('Cannot create snapshot directory: ' . $this->snapshotDir); + throw new \RuntimeException('Cannot create snapshot directory'); } } @@ -620,57 +555,24 @@ class DemoResetService } } - /** - * Get the full path for a named snapshot. - * - * @param string $name Snapshot name - * - * @return string Full directory path - * - * @since 02.21.00 - */ private function getSnapshotPath(string $name): string { return $this->snapshotDir . '/' . $name; } - /** - * Validate a snapshot name to prevent path traversal. - * - * @param string $name Snapshot name to validate - * - * @return void - * - * @throws \InvalidArgumentException On invalid name - * - * @since 02.21.00 - */ private function validateSnapshotName(string $name): void { if ($name === '' || strlen($name) > self::MAX_NAME_LENGTH) { - throw new \InvalidArgumentException( - 'Snapshot name must be 1-' . self::MAX_NAME_LENGTH . ' characters' - ); + throw new \InvalidArgumentException('Snapshot name must be 1-' . self::MAX_NAME_LENGTH . ' characters'); } if (!preg_match('/^[a-zA-Z0-9_-]+$/', $name)) { - throw new \InvalidArgumentException( - 'Snapshot name must contain only letters, numbers, hyphens, and underscores' - ); + throw new \InvalidArgumentException('Snapshot name must contain only letters, numbers, hyphens, and underscores'); } } - /** - * Recursively remove a directory and all contents. - * - * @param string $dir Directory to remove - * - * @return void - * - * @since 02.21.00 - */ private function removeDirectory(string $dir): void { $items = new \RecursiveIteratorIterator( @@ -680,28 +582,12 @@ class DemoResetService foreach ($items as $item) { - if ($item->isDir()) - { - @rmdir($item->getPathname()); - } - else - { - @unlink($item->getPathname()); - } + $item->isDir() ? @rmdir($item->getPathname()) : @unlink($item->getPathname()); } @rmdir($dir); } - /** - * Clear all contents of a directory without removing the directory itself. - * - * @param string $dir Directory to clear - * - * @return void - * - * @since 02.21.00 - */ private function clearDirectory(string $dir): void { if (!is_dir($dir)) @@ -716,14 +602,7 @@ class DemoResetService foreach ($items as $item) { - if ($item->isDir()) - { - @rmdir($item->getPathname()); - } - else - { - @unlink($item->getPathname()); - } + $item->isDir() ? @rmdir($item->getPathname()) : @unlink($item->getPathname()); } } } diff --git a/src/packages/plg_system_mokowaas/mokowaas.xml b/src/packages/plg_system_mokowaas/mokowaas.xml index 9cb366f0..ac630061 100644 --- a/src/packages/plg_system_mokowaas/mokowaas.xml +++ b/src/packages/plg_system_mokowaas/mokowaas.xml @@ -318,17 +318,12 @@ label="PLG_SYSTEM_MOKOWAAS_DEMO_NEXT_RESET_LABEL" description="PLG_SYSTEM_MOKOWAAS_DEMO_NEXT_RESET_DESC" /> - - - - + description="PLG_SYSTEM_MOKOWAAS_DEMO_MEDIA_DESC" + class="btn-group btn-group-yesno"> + + get('demo_snapshot_tables', ''); - $tables = is_array($tablesParam) ? array_filter($tablesParam) : array_filter(array_map('trim', explode("\n", $tablesParam))); - $media = $sysParams->get('demo_snapshot_include_media', ['images']); - if ($media === '1' || $media === true) $media = ['images']; - if ($media === '0' || $media === false) $media = []; + $media = (bool) $sysParams->get('demo_snapshot_include_media', 1); - $service = new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($tables, (array) $media); + $service = new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($media); try {