From 201a281e3a587740fc9087a89954255b004ca6bd Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 2 Jun 2026 13:58:50 -0500 Subject: [PATCH] feat: add scheduled tasks, individual fields, remote storage - Add plg_task_mokobackup: Joomla Scheduled Tasks integration so each backup profile can run on its own schedule (like Akeeba Backup Pro) - Replace JSON config/filters with individual form fields and DB columns - Add FTP/FTPS and Google Drive remote storage options per profile - Add archive settings tab (format, compression, split size, backup dir) - Add exclusion filter fields (dirs, files, tables) as newline-separated textareas instead of raw JSON Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 9 +- CLAUDE.md | 9 +- src/packages/com_mokobackup/forms/profile.xml | 201 ++++++++++++++++-- .../language/en-GB/com_mokobackup.ini | 68 +++++- .../com_mokobackup/sql/install.mysql.sql | 56 +++-- .../src/Engine/BackupEngine.php | 29 ++- .../com_mokobackup/tmpl/profile/edit.php | 18 ++ .../plg_task_mokobackup/forms/index.html | 1 + .../plg_task_mokobackup/forms/run_profile.xml | 21 ++ src/packages/plg_task_mokobackup/index.html | 1 + .../language/en-GB/index.html | 1 + .../language/en-GB/plg_task_mokobackup.ini | 12 ++ .../en-GB/plg_task_mokobackup.sys.ini | 3 + .../language/en-US/index.html | 1 + .../language/en-US/plg_task_mokobackup.ini | 8 + .../en-US/plg_task_mokobackup.sys.ini | 3 + .../plg_task_mokobackup/language/index.html | 1 + .../plg_task_mokobackup/mokobackup.php | 11 + .../plg_task_mokobackup/mokobackup.xml | 33 +++ .../plg_task_mokobackup/services/index.html | 1 + .../plg_task_mokobackup/services/provider.php | 37 ++++ .../src/Extension/MokoBackupTask.php | 96 +++++++++ .../src/Extension/index.html | 1 + .../plg_task_mokobackup/src/index.html | 1 + src/pkg_mokobackup.xml | 1 + src/script.php | 11 + 26 files changed, 590 insertions(+), 44 deletions(-) create mode 100644 src/packages/plg_task_mokobackup/forms/index.html create mode 100644 src/packages/plg_task_mokobackup/forms/run_profile.xml create mode 100644 src/packages/plg_task_mokobackup/index.html create mode 100644 src/packages/plg_task_mokobackup/language/en-GB/index.html create mode 100644 src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini create mode 100644 src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini create mode 100644 src/packages/plg_task_mokobackup/language/en-US/index.html create mode 100644 src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini create mode 100644 src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini create mode 100644 src/packages/plg_task_mokobackup/language/index.html create mode 100644 src/packages/plg_task_mokobackup/mokobackup.php create mode 100644 src/packages/plg_task_mokobackup/mokobackup.xml create mode 100644 src/packages/plg_task_mokobackup/services/index.html create mode 100644 src/packages/plg_task_mokobackup/services/provider.php create mode 100644 src/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php create mode 100644 src/packages/plg_task_mokobackup/src/Extension/index.html create mode 100644 src/packages/plg_task_mokobackup/src/index.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 271ddee..90773fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,11 @@ ## [Unreleased] ### Added -- Initial package structure with component, system plugin, and webservices plugin +- Initial package structure with component, system plugin, task plugin, and webservices plugin +- Joomla Scheduled Tasks integration (plg_task_mokobackup) — create multiple tasks, each running a different backup profile on its own schedule +- Individual form fields for all profile settings (no raw JSON) +- Remote storage support: FTP/FTPS and Google Drive offsite backup upload +- Per-profile archive settings: format, compression level, split size, backup directory - Backup engine with step-based execution for large sites - Database dumper with table-level granularity - File scanner with directory exclusion filters @@ -13,5 +17,4 @@ - Admin dashboard with backup history - CLI script for cron/scheduled backups - REST API compatible with MokoBackup MCP server -- System plugin for scheduled backup triggers -- Automatic old backup cleanup with configurable retention +- System plugin for automatic backup cleanup with configurable retention diff --git a/CLAUDE.md b/CLAUDE.md index 50bcfb4..7049fa1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -43,10 +43,15 @@ This is a Joomla **package** extension (`pkg_mokobackup`) containing three sub-e - CLI: `cli/mokobackup.php` for cron-based backups ### plg_system_mokobackup (System Plugin) -- Handles scheduled backup triggers -- Cleanup of expired backup archives +- Cleanup of expired backup archives (age + count limits) - Namespace: `Joomla\Plugin\System\MokoBackup` +### plg_task_mokobackup (Task Plugin) +- Integrates with Joomla's Scheduled Tasks (com_scheduler) +- Registers "Run Backup Profile" task type +- Each scheduled task selects a backup profile — create multiple tasks for different schedules +- Namespace: `Joomla\Plugin\Task\MokoBackup` + ### plg_webservices_mokobackup (WebServices Plugin) - REST API for remote backup management - Wire-compatible with existing mcp_mokobackup MCP server diff --git a/src/packages/com_mokobackup/forms/profile.xml b/src/packages/com_mokobackup/forms/profile.xml index 00ed3fa..22eaf0c 100644 --- a/src/packages/com_mokobackup/forms/profile.xml +++ b/src/packages/com_mokobackup/forms/profile.xml @@ -27,14 +27,46 @@ + + +
+ + + + + + + + + +
@@ -62,13 +94,158 @@
+ + +
+ +
+ + + + + + + + + +
+ +
+ + + + + + + + + + + + + +
+ +
+ + + +
diff --git a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini index 0cf4a7a..4c4106b 100644 --- a/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini +++ b/src/packages/com_mokobackup/language/en-GB/com_mokobackup.ini @@ -44,17 +44,13 @@ COM_MOKOBACKUP_HEADING_SIZE_ASC="Size ascending" COM_MOKOBACKUP_HEADING_TITLE_ASC="Title ascending" COM_MOKOBACKUP_HEADING_TITLE_DESC="Title descending" -; Fields +; General fields COM_MOKOBACKUP_FIELD_TITLE="Title" COM_MOKOBACKUP_FIELD_TITLE_DESC="Profile name" COM_MOKOBACKUP_FIELD_DESCRIPTION="Description" COM_MOKOBACKUP_FIELD_DESCRIPTION_DESC="Brief description of this profile" COM_MOKOBACKUP_FIELD_BACKUP_TYPE="Backup Type" COM_MOKOBACKUP_FIELD_BACKUP_TYPE_DESC="What to include in the backup" -COM_MOKOBACKUP_FIELD_CONFIG="Configuration (JSON)" -COM_MOKOBACKUP_FIELD_CONFIG_DESC="JSON configuration for archive format, compression, and backup directory" -COM_MOKOBACKUP_FIELD_FILTERS="Filters (JSON)" -COM_MOKOBACKUP_FIELD_FILTERS_DESC="JSON filters for excluding directories, files, and database tables" COM_MOKOBACKUP_FIELD_STATUS="Status" COM_MOKOBACKUP_FIELD_ORIGIN="Origin" COM_MOKOBACKUP_FIELD_SIZE="Total Size" @@ -64,6 +60,60 @@ COM_MOKOBACKUP_FIELD_ARCHIVE="Archive Name" COM_MOKOBACKUP_FIELD_FILES_COUNT="Files Count" COM_MOKOBACKUP_FIELD_TABLES_COUNT="Tables Count" +; Archive settings +COM_MOKOBACKUP_FIELD_ARCHIVE_FORMAT="Archive Format" +COM_MOKOBACKUP_FIELD_ARCHIVE_FORMAT_DESC="Format for the backup archive file" +COM_MOKOBACKUP_FIELD_COMPRESSION="Compression Level" +COM_MOKOBACKUP_FIELD_COMPRESSION_DESC="Higher compression = smaller file but slower" +COM_MOKOBACKUP_COMPRESSION_NONE="None (fastest)" +COM_MOKOBACKUP_COMPRESSION_FASTEST="Low (fast)" +COM_MOKOBACKUP_COMPRESSION_NORMAL="Normal (balanced)" +COM_MOKOBACKUP_COMPRESSION_BEST="Maximum (smallest)" +COM_MOKOBACKUP_FIELD_SPLIT_SIZE="Split Size (MB)" +COM_MOKOBACKUP_FIELD_SPLIT_SIZE_DESC="Split archive into parts of this size in MB. 0 = no splitting." +COM_MOKOBACKUP_FIELD_BACKUP_DIR="Backup Directory" +COM_MOKOBACKUP_FIELD_BACKUP_DIR_DESC="Relative path from Joomla root where backup archives are stored" + +; Exclusion filter fields +COM_MOKOBACKUP_FIELD_EXCLUDE_DIRS="Exclude Directories" +COM_MOKOBACKUP_FIELD_EXCLUDE_DIRS_DESC="One directory path per line (relative to Joomla root). These directories will be skipped during file backup." +COM_MOKOBACKUP_FIELD_EXCLUDE_FILES="Exclude Files" +COM_MOKOBACKUP_FIELD_EXCLUDE_FILES_DESC="One filename or pattern per line. Supports wildcards (e.g. *.bak, *.tmp)." +COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES="Exclude Tables" +COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES_DESC="One table name per line (use #__ prefix). These tables will be skipped during database dump." + +; Remote storage fields +COM_MOKOBACKUP_FIELD_REMOTE_STORAGE="Remote Storage" +COM_MOKOBACKUP_FIELD_REMOTE_STORAGE_DESC="Optionally upload backup archives to a remote location after creation" +COM_MOKOBACKUP_REMOTE_NONE="None (local only)" +COM_MOKOBACKUP_REMOTE_FTP="FTP / FTPS" +COM_MOKOBACKUP_REMOTE_GDRIVE="Google Drive" +COM_MOKOBACKUP_FIELD_KEEP_LOCAL="Keep Local Copy" +COM_MOKOBACKUP_FIELD_KEEP_LOCAL_DESC="Keep the local backup file after uploading to remote storage" + +; FTP fields +COM_MOKOBACKUP_FIELD_FTP_HOST="FTP Host" +COM_MOKOBACKUP_FIELD_FTP_HOST_DESC="FTP server hostname or IP address" +COM_MOKOBACKUP_FIELD_FTP_PORT="FTP Port" +COM_MOKOBACKUP_FIELD_FTP_PORT_DESC="FTP server port (default: 21)" +COM_MOKOBACKUP_FIELD_FTP_USERNAME="FTP Username" +COM_MOKOBACKUP_FIELD_FTP_PASSWORD="FTP Password" +COM_MOKOBACKUP_FIELD_FTP_PATH="Remote Path" +COM_MOKOBACKUP_FIELD_FTP_PATH_DESC="Directory on the FTP server to upload backups to" +COM_MOKOBACKUP_FIELD_FTP_PASSIVE="Passive Mode" +COM_MOKOBACKUP_FIELD_FTP_PASSIVE_DESC="Use passive mode for FTP connections (recommended)" +COM_MOKOBACKUP_FIELD_FTP_SSL="Use FTPS (SSL)" +COM_MOKOBACKUP_FIELD_FTP_SSL_DESC="Connect using FTPS (FTP over SSL/TLS)" + +; Google Drive fields +COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_ID="Google Client ID" +COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_ID_DESC="OAuth 2.0 Client ID from Google Cloud Console" +COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_SECRET="Google Client Secret" +COM_MOKOBACKUP_FIELD_GDRIVE_REFRESH_TOKEN="Refresh Token" +COM_MOKOBACKUP_FIELD_GDRIVE_REFRESH_TOKEN_DESC="OAuth 2.0 refresh token for offline access" +COM_MOKOBACKUP_FIELD_GDRIVE_FOLDER_ID="Drive Folder ID" +COM_MOKOBACKUP_FIELD_GDRIVE_FOLDER_ID_DESC="Google Drive folder ID where backups will be uploaded. Find this in the folder URL." + ; Backup types COM_MOKOBACKUP_TYPE_FULL="Full Site (Database + Files)" COM_MOKOBACKUP_TYPE_DATABASE="Database Only" @@ -80,12 +130,18 @@ COM_MOKOBACKUP_FILTER_SEARCH="Search" COM_MOKOBACKUP_FILTER_STATUS="Status" COM_MOKOBACKUP_FILTER_STATUS_ALL="- Select Status -" -; Tabs +; Tabs and fieldsets COM_MOKOBACKUP_TAB_GENERAL="General" +COM_MOKOBACKUP_TAB_ARCHIVE="Archive Settings" COM_MOKOBACKUP_TAB_FILTERS="Exclusion Filters" +COM_MOKOBACKUP_TAB_REMOTE="Remote Storage" COM_MOKOBACKUP_FIELDSET_GENERAL="General" +COM_MOKOBACKUP_FIELDSET_ARCHIVE="Archive Settings" COM_MOKOBACKUP_FIELDSET_STATUS="Status" COM_MOKOBACKUP_FIELDSET_FILTERS="Exclusion Filters" +COM_MOKOBACKUP_FIELDSET_REMOTE="Remote Storage" +COM_MOKOBACKUP_FIELDSET_FTP="FTP Settings" +COM_MOKOBACKUP_FIELDSET_GDRIVE="Google Drive Settings" ; Errors COM_MOKOBACKUP_ERROR_FILE_NOT_FOUND="Backup archive file not found or has been deleted." diff --git a/src/packages/com_mokobackup/sql/install.mysql.sql b/src/packages/com_mokobackup/sql/install.mysql.sql index e24c314..3b52df3 100644 --- a/src/packages/com_mokobackup/sql/install.mysql.sql +++ b/src/packages/com_mokobackup/sql/install.mysql.sql @@ -1,14 +1,32 @@ CREATE TABLE IF NOT EXISTS `#__mokobackup_profiles` ( - `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, - `title` VARCHAR(255) NOT NULL DEFAULT '', - `description` TEXT NOT NULL, - `backup_type` VARCHAR(20) NOT NULL DEFAULT 'full' COMMENT 'full, database, files', - `config` MEDIUMTEXT NOT NULL COMMENT 'JSON: archive format, compression, paths', - `filters` MEDIUMTEXT NOT NULL COMMENT 'JSON: excluded dirs, files, tables', - `published` TINYINT(1) NOT NULL DEFAULT 1, - `ordering` INT(11) NOT NULL DEFAULT 0, - `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', - `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `title` VARCHAR(255) NOT NULL DEFAULT '', + `description` TEXT NOT NULL, + `backup_type` VARCHAR(20) NOT NULL DEFAULT 'full' COMMENT 'full, database, files', + `archive_format` VARCHAR(10) NOT NULL DEFAULT 'zip', + `compression_level` TINYINT(1) UNSIGNED NOT NULL DEFAULT 5 COMMENT '0=none, 9=max', + `split_size` INT(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '0=no split, otherwise MB per part', + `backup_dir` VARCHAR(512) NOT NULL DEFAULT 'administrator/components/com_mokobackup/backups', + `exclude_dirs` TEXT NOT NULL COMMENT 'Newline-separated directory paths to exclude', + `exclude_files` TEXT NOT NULL COMMENT 'Newline-separated filename patterns to exclude', + `exclude_tables` TEXT NOT NULL COMMENT 'Newline-separated table names to exclude', + `remote_storage` VARCHAR(20) NOT NULL DEFAULT 'none' COMMENT 'none, ftp, google_drive', + `ftp_host` VARCHAR(255) NOT NULL DEFAULT '', + `ftp_port` INT(5) UNSIGNED NOT NULL DEFAULT 21, + `ftp_username` VARCHAR(255) NOT NULL DEFAULT '', + `ftp_password` VARCHAR(255) NOT NULL DEFAULT '', + `ftp_path` VARCHAR(512) NOT NULL DEFAULT '/backups', + `ftp_passive` TINYINT(1) NOT NULL DEFAULT 1, + `ftp_ssl` TINYINT(1) NOT NULL DEFAULT 0, + `gdrive_client_id` VARCHAR(255) NOT NULL DEFAULT '', + `gdrive_client_secret` VARCHAR(255) NOT NULL DEFAULT '', + `gdrive_refresh_token` VARCHAR(512) NOT NULL DEFAULT '', + `gdrive_folder_id` VARCHAR(255) NOT NULL DEFAULT '', + `remote_keep_local` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Keep local copy after upload', + `published` TINYINT(1) NOT NULL DEFAULT 1, + `ordering` INT(11) NOT NULL DEFAULT 0, + `created` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `modified` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`id`), KEY `idx_published` (`published`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; @@ -40,8 +58,16 @@ CREATE TABLE IF NOT EXISTS `#__mokobackup_records` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Insert default backup profile -INSERT INTO `#__mokobackup_profiles` (`id`, `title`, `description`, `backup_type`, `config`, `filters`, `published`, `ordering`, `created`, `modified`) -VALUES (1, 'Default Backup Profile', 'Full site backup with default settings', 'full', - '{"archive_format":"zip","compression_level":5,"split_size":0,"backup_dir":"administrator/components/com_mokobackup/backups"}', - '{"exclude_dirs":["administrator/components/com_mokobackup/backups","tmp","cache","logs","administrator/logs"],"exclude_files":[".gitignore",".htaccess.bak"],"exclude_tables":["#__session"]}', - 1, 1, NOW(), NOW()); +INSERT INTO `#__mokobackup_profiles` ( + `id`, `title`, `description`, `backup_type`, + `archive_format`, `compression_level`, `split_size`, `backup_dir`, + `exclude_dirs`, `exclude_files`, `exclude_tables`, + `published`, `ordering`, `created`, `modified` +) VALUES ( + 1, 'Default Backup Profile', 'Full site backup with default settings', 'full', + 'zip', 5, 0, 'administrator/components/com_mokobackup/backups', + 'administrator/components/com_mokobackup/backups\ntmp\ncache\nlogs\nadministrator/logs', + '.gitignore\n.htaccess.bak', + '#__session', + 1, 1, NOW(), NOW() +); diff --git a/src/packages/com_mokobackup/src/Engine/BackupEngine.php b/src/packages/com_mokobackup/src/Engine/BackupEngine.php index 7c0826d..4a86d97 100644 --- a/src/packages/com_mokobackup/src/Engine/BackupEngine.php +++ b/src/packages/com_mokobackup/src/Engine/BackupEngine.php @@ -44,11 +44,13 @@ class BackupEngine return ['success' => false, 'message' => 'Profile not found: ' . $profileId]; } - $config = json_decode($profile->config ?: '{}', true) ?: []; - $filters = json_decode($profile->filters ?: '{}', true) ?: []; + // Read settings directly from profile columns + $excludeDirs = $this->parseNewlineList($profile->exclude_dirs ?? ''); + $excludeFiles = $this->parseNewlineList($profile->exclude_files ?? ''); + $excludeTables = $this->parseNewlineList($profile->exclude_tables ?? ''); // Determine backup directory - $this->backupDir = JPATH_ROOT . '/' . ($config['backup_dir'] ?? 'administrator/components/com_mokobackup/backups'); + $this->backupDir = JPATH_ROOT . '/' . ($profile->backup_dir ?: 'administrator/components/com_mokobackup/backups'); if (!is_dir($this->backupDir)) { mkdir($this->backupDir, 0755, true); @@ -105,7 +107,7 @@ class BackupEngine // Step 1: Database dump (unless files-only) if ($profile->backup_type !== 'files') { $this->log('Starting database dump...'); - $dumper = new DatabaseDumper($filters['exclude_tables'] ?? []); + $dumper = new DatabaseDumper($excludeTables); $sqlDump = $dumper->dump(); $zip->addFromString('database.sql', $sqlDump); $dbSize = strlen($sqlDump); @@ -118,8 +120,8 @@ class BackupEngine $this->log('Starting file scan...'); $scanner = new FileScanner( JPATH_ROOT, - $filters['exclude_dirs'] ?? [], - $filters['exclude_files'] ?? [] + $excludeDirs, + $excludeFiles ); $files = $scanner->scan(); @@ -180,6 +182,21 @@ class BackupEngine } } + /** + * Parse a newline-separated text field into an array of trimmed, non-empty strings. + */ + private function parseNewlineList(string $text): array + { + if (empty($text)) { + return []; + } + + return array_values(array_filter( + array_map('trim', explode("\n", str_replace("\r", '', $text))), + fn($line) => $line !== '' + )); + } + private function log(string $message): void { $this->log[] = '[' . date('H:i:s') . '] ' . $message; diff --git a/src/packages/com_mokobackup/tmpl/profile/edit.php b/src/packages/com_mokobackup/tmpl/profile/edit.php index 1528ebb..6a4e3ee 100644 --- a/src/packages/com_mokobackup/tmpl/profile/edit.php +++ b/src/packages/com_mokobackup/tmpl/profile/edit.php @@ -34,6 +34,14 @@ HTMLHelper::_('behavior.keepalive'); + +
+
+ form->renderFieldset('archive'); ?> +
+
+ +
@@ -42,6 +50,16 @@ HTMLHelper::_('behavior.keepalive');
+ +
+
+ form->renderFieldset('remote'); ?> + form->renderFieldset('ftp'); ?> + form->renderFieldset('google_drive'); ?> +
+
+ +
diff --git a/src/packages/plg_task_mokobackup/forms/index.html b/src/packages/plg_task_mokobackup/forms/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/packages/plg_task_mokobackup/forms/index.html @@ -0,0 +1 @@ + diff --git a/src/packages/plg_task_mokobackup/forms/run_profile.xml b/src/packages/plg_task_mokobackup/forms/run_profile.xml new file mode 100644 index 0000000..ad07f85 --- /dev/null +++ b/src/packages/plg_task_mokobackup/forms/run_profile.xml @@ -0,0 +1,21 @@ + + +
+
+ + + +
+
diff --git a/src/packages/plg_task_mokobackup/index.html b/src/packages/plg_task_mokobackup/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/packages/plg_task_mokobackup/index.html @@ -0,0 +1 @@ + diff --git a/src/packages/plg_task_mokobackup/language/en-GB/index.html b/src/packages/plg_task_mokobackup/language/en-GB/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/packages/plg_task_mokobackup/language/en-GB/index.html @@ -0,0 +1 @@ + diff --git a/src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini b/src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini new file mode 100644 index 0000000..0b8dfb0 --- /dev/null +++ b/src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.ini @@ -0,0 +1,12 @@ +; MokoJoomBackup — Task Plugin language file (en-GB) +PLG_TASK_MOKOBACKUP="Task - MokoJoomBackup" +PLG_TASK_MOKOBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Allows running backup profiles on a schedule via Joomla's Scheduled Tasks system." + +; Task type +PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE_TITLE="MokoJoomBackup: Run Backup Profile" +PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE_DESC="Run a MokoJoomBackup backup using the selected profile. Create multiple tasks with different profiles for different backup schedules (e.g. daily full backup, hourly database-only backup)." + +; Task form fields +PLG_TASK_MOKOBACKUP_FIELD_PROFILE="Backup Profile" +PLG_TASK_MOKOBACKUP_FIELD_PROFILE_DESC="Select which backup profile to run. Each profile defines backup type (full/database/files), exclusion filters, and storage settings." +PLG_TASK_MOKOBACKUP_SELECT_PROFILE="- Select Profile -" diff --git a/src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini b/src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini new file mode 100644 index 0000000..f75bb7e --- /dev/null +++ b/src/packages/plg_task_mokobackup/language/en-GB/plg_task_mokobackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Task Plugin system language file (en-GB) +PLG_TASK_MOKOBACKUP="Task - MokoJoomBackup" +PLG_TASK_MOKOBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Run backup profiles on a schedule via Joomla's Scheduled Tasks." diff --git a/src/packages/plg_task_mokobackup/language/en-US/index.html b/src/packages/plg_task_mokobackup/language/en-US/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/packages/plg_task_mokobackup/language/en-US/index.html @@ -0,0 +1 @@ + diff --git a/src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini b/src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini new file mode 100644 index 0000000..02d0939 --- /dev/null +++ b/src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.ini @@ -0,0 +1,8 @@ +; MokoJoomBackup — Task Plugin language file (en-US) +PLG_TASK_MOKOBACKUP="Task - MokoJoomBackup" +PLG_TASK_MOKOBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Allows running backup profiles on a schedule via Joomla's Scheduled Tasks system." +PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE_TITLE="MokoJoomBackup: Run Backup Profile" +PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE_DESC="Run a MokoJoomBackup backup using the selected profile. Create multiple tasks with different profiles for different backup schedules." +PLG_TASK_MOKOBACKUP_FIELD_PROFILE="Backup Profile" +PLG_TASK_MOKOBACKUP_FIELD_PROFILE_DESC="Select which backup profile to run." +PLG_TASK_MOKOBACKUP_SELECT_PROFILE="- Select Profile -" diff --git a/src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini b/src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini new file mode 100644 index 0000000..c2ba0a8 --- /dev/null +++ b/src/packages/plg_task_mokobackup/language/en-US/plg_task_mokobackup.sys.ini @@ -0,0 +1,3 @@ +; MokoJoomBackup — Task Plugin system language file (en-US) +PLG_TASK_MOKOBACKUP="Task - MokoJoomBackup" +PLG_TASK_MOKOBACKUP_DESCRIPTION="Scheduled task plugin for MokoJoomBackup. Run backup profiles on a schedule via Joomla's Scheduled Tasks." diff --git a/src/packages/plg_task_mokobackup/language/index.html b/src/packages/plg_task_mokobackup/language/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/packages/plg_task_mokobackup/language/index.html @@ -0,0 +1 @@ + diff --git a/src/packages/plg_task_mokobackup/mokobackup.php b/src/packages/plg_task_mokobackup/mokobackup.php new file mode 100644 index 0000000..30fa3ac --- /dev/null +++ b/src/packages/plg_task_mokobackup/mokobackup.php @@ -0,0 +1,11 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; diff --git a/src/packages/plg_task_mokobackup/mokobackup.xml b/src/packages/plg_task_mokobackup/mokobackup.xml new file mode 100644 index 0000000..b87b4de --- /dev/null +++ b/src/packages/plg_task_mokobackup/mokobackup.xml @@ -0,0 +1,33 @@ + + + + plg_task_mokobackup + 01.00.00-dev + 2026-06-02 + Moko Consulting + hello@mokoconsulting.tech + https://mokoconsulting.tech + Copyright (C) 2026 Moko Consulting. All rights reserved. + GPL-3.0-or-later + PLG_TASK_MOKOBACKUP_DESCRIPTION + + Joomla\Plugin\Task\MokoBackup + + + mokobackup.php + services + src + forms + + + + language/en-GB/plg_task_mokobackup.ini + language/en-GB/plg_task_mokobackup.sys.ini + + diff --git a/src/packages/plg_task_mokobackup/services/index.html b/src/packages/plg_task_mokobackup/services/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/packages/plg_task_mokobackup/services/index.html @@ -0,0 +1 @@ + diff --git a/src/packages/plg_task_mokobackup/services/provider.php b/src/packages/plg_task_mokobackup/services/provider.php new file mode 100644 index 0000000..00b1ac0 --- /dev/null +++ b/src/packages/plg_task_mokobackup/services/provider.php @@ -0,0 +1,37 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + */ + +defined('_JEXEC') or die; + +use Joomla\CMS\Extension\PluginInterface; +use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; +use Joomla\DI\Container; +use Joomla\DI\ServiceProviderInterface; +use Joomla\Event\DispatcherInterface; +use Joomla\Plugin\Task\MokoBackup\Extension\MokoBackupTask; + +return new class () implements ServiceProviderInterface { + public function register(Container $container): void + { + $container->set( + PluginInterface::class, + function (Container $container) { + $plugin = new MokoBackupTask( + $container->get(DispatcherInterface::class), + (array) PluginHelper::getPlugin('task', 'mokobackup') + ); + $plugin->setApplication(Factory::getApplication()); + + return $plugin; + } + ); + } +}; diff --git a/src/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php b/src/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php new file mode 100644 index 0000000..723642b --- /dev/null +++ b/src/packages/plg_task_mokobackup/src/Extension/MokoBackupTask.php @@ -0,0 +1,96 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + * + * Joomla Scheduled Task plugin for MokoJoomBackup. + * + * Registers a "Run Backup Profile" task type with com_scheduler. + * Admins can create multiple scheduled tasks in System > Scheduled Tasks, + * each pointing to a different backup profile — just like Akeeba Backup Pro. + */ + +namespace Joomla\Plugin\Task\MokoBackup\Extension; + +defined('_JEXEC') or die; + +use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent; +use Joomla\Component\Scheduler\Administrator\Task\Status; +use Joomla\Component\Scheduler\Administrator\Traits\TaskPluginTrait; +use Joomla\Event\Event; +use Joomla\Event\SubscriberInterface; + +final class MokoBackupTask extends CMSPlugin implements SubscriberInterface +{ + use TaskPluginTrait; + + protected $autoloadLanguage = true; + + /** + * Task map — each entry registers a task type in System > Scheduled Tasks. + * + * The admin can create multiple task instances, each with its own profile_id, + * so different backup profiles run on different schedules. + */ + protected const TASKS_MAP = [ + 'mokobackup.run_profile' => [ + 'langConstPrefix' => 'PLG_TASK_MOKOBACKUP_TASK_RUN_PROFILE', + 'method' => 'runBackupProfile', + 'form' => 'run_profile', + ], + ]; + + public static function getSubscribedEvents(): array + { + return [ + 'onTaskOptionsList' => 'advertiseRoutines', + 'onExecuteTask' => 'standardRoutineHandler', + 'onContentPrepareForm' => 'enhanceTaskItemForm', + ]; + } + + /** + * Execute a backup using the profile selected in the scheduled task. + * + * @param ExecuteTaskEvent $event The task execution event + * + * @return int Status::OK on success, Status::KNOCKOUT on failure + */ + private function runBackupProfile(ExecuteTaskEvent $event): int + { + $params = $event->getArgument('params'); + $profileId = (int) ($params->profile_id ?? 1); + + // Load the backup engine from the component + $engineFile = JPATH_ADMINISTRATOR . '/components/com_mokobackup/src/Engine/BackupEngine.php'; + + if (!file_exists($engineFile)) { + $this->logTask('MokoJoomBackup component not installed — cannot run backup.'); + + return Status::KNOCKOUT; + } + + // The autoloader should handle this via namespace, but ensure class is available + if (!class_exists('\\Joomla\\Component\\MokoBackup\\Administrator\\Engine\\BackupEngine')) { + require_once $engineFile; + } + + $engine = new \Joomla\Component\MokoBackup\Administrator\Engine\BackupEngine(); + $result = $engine->run($profileId, 'Scheduled task backup', 'scheduled'); + + if ($result['success']) { + $this->logTask('Backup complete: ' . $result['message']); + + return Status::OK; + } + + $this->logTask('Backup failed: ' . $result['message']); + + return Status::KNOCKOUT; + } +} diff --git a/src/packages/plg_task_mokobackup/src/Extension/index.html b/src/packages/plg_task_mokobackup/src/Extension/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/packages/plg_task_mokobackup/src/Extension/index.html @@ -0,0 +1 @@ + diff --git a/src/packages/plg_task_mokobackup/src/index.html b/src/packages/plg_task_mokobackup/src/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/packages/plg_task_mokobackup/src/index.html @@ -0,0 +1 @@ + diff --git a/src/pkg_mokobackup.xml b/src/pkg_mokobackup.xml index 5b770b7..74684ed 100644 --- a/src/pkg_mokobackup.xml +++ b/src/pkg_mokobackup.xml @@ -22,6 +22,7 @@ com_mokobackup.zip plg_system_mokobackup.zip + plg_task_mokobackup.zip plg_webservices_mokobackup.zip diff --git a/src/script.php b/src/script.php index 93a94dc..a7b2c88 100644 --- a/src/script.php +++ b/src/script.php @@ -74,6 +74,17 @@ class Pkg_MokoBackupInstallerScript $db->setQuery($query); $db->execute(); + // Enable the task plugin automatically + $query = $db->getQuery(true) + ->update($db->quoteName('#__extensions')) + ->set($db->quoteName('enabled') . ' = 1') + ->where($db->quoteName('type') . ' = ' . $db->quote('plugin')) + ->where($db->quoteName('folder') . ' = ' . $db->quote('task')) + ->where($db->quoteName('element') . ' = ' . $db->quote('mokobackup')); + + $db->setQuery($query); + $db->execute(); + // Enable the webservices plugin automatically $query = $db->getQuery(true) ->update($db->quoteName('#__extensions'))