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) <noreply@anthropic.com>
This commit is contained in:
+6
-3
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,14 +27,46 @@
|
||||
<option value="database">COM_MOKOBACKUP_TYPE_DATABASE</option>
|
||||
<option value="files">COM_MOKOBACKUP_TYPE_FILES</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
|
||||
<fieldset name="archive" label="COM_MOKOBACKUP_FIELDSET_ARCHIVE">
|
||||
<field
|
||||
name="config"
|
||||
type="textarea"
|
||||
label="COM_MOKOBACKUP_FIELD_CONFIG"
|
||||
description="COM_MOKOBACKUP_FIELD_CONFIG_DESC"
|
||||
rows="8"
|
||||
filter="raw"
|
||||
hint='{"archive_format":"zip","compression_level":5,"split_size":0,"backup_dir":"administrator/components/com_mokobackup/backups"}'
|
||||
name="archive_format"
|
||||
type="list"
|
||||
label="COM_MOKOBACKUP_FIELD_ARCHIVE_FORMAT"
|
||||
description="COM_MOKOBACKUP_FIELD_ARCHIVE_FORMAT_DESC"
|
||||
default="zip"
|
||||
>
|
||||
<option value="zip">ZIP</option>
|
||||
</field>
|
||||
<field
|
||||
name="compression_level"
|
||||
type="list"
|
||||
label="COM_MOKOBACKUP_FIELD_COMPRESSION"
|
||||
description="COM_MOKOBACKUP_FIELD_COMPRESSION_DESC"
|
||||
default="5"
|
||||
>
|
||||
<option value="0">COM_MOKOBACKUP_COMPRESSION_NONE</option>
|
||||
<option value="1">COM_MOKOBACKUP_COMPRESSION_FASTEST</option>
|
||||
<option value="5">COM_MOKOBACKUP_COMPRESSION_NORMAL</option>
|
||||
<option value="9">COM_MOKOBACKUP_COMPRESSION_BEST</option>
|
||||
</field>
|
||||
<field
|
||||
name="split_size"
|
||||
type="number"
|
||||
label="COM_MOKOBACKUP_FIELD_SPLIT_SIZE"
|
||||
description="COM_MOKOBACKUP_FIELD_SPLIT_SIZE_DESC"
|
||||
default="0"
|
||||
min="0"
|
||||
hint="0 = no splitting"
|
||||
/>
|
||||
<field
|
||||
name="backup_dir"
|
||||
type="text"
|
||||
label="COM_MOKOBACKUP_FIELD_BACKUP_DIR"
|
||||
description="COM_MOKOBACKUP_FIELD_BACKUP_DIR_DESC"
|
||||
default="administrator/components/com_mokobackup/backups"
|
||||
maxlength="512"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
@@ -62,13 +94,158 @@
|
||||
|
||||
<fieldset name="filters" label="COM_MOKOBACKUP_FIELDSET_FILTERS">
|
||||
<field
|
||||
name="filters"
|
||||
name="exclude_dirs"
|
||||
type="textarea"
|
||||
label="COM_MOKOBACKUP_FIELD_FILTERS"
|
||||
description="COM_MOKOBACKUP_FIELD_FILTERS_DESC"
|
||||
rows="12"
|
||||
label="COM_MOKOBACKUP_FIELD_EXCLUDE_DIRS"
|
||||
description="COM_MOKOBACKUP_FIELD_EXCLUDE_DIRS_DESC"
|
||||
rows="6"
|
||||
filter="raw"
|
||||
hint='{"exclude_dirs":["tmp","cache","logs"],"exclude_files":[],"exclude_tables":["#__session"]}'
|
||||
hint="tmp cache logs administrator/logs"
|
||||
/>
|
||||
<field
|
||||
name="exclude_files"
|
||||
type="textarea"
|
||||
label="COM_MOKOBACKUP_FIELD_EXCLUDE_FILES"
|
||||
description="COM_MOKOBACKUP_FIELD_EXCLUDE_FILES_DESC"
|
||||
rows="4"
|
||||
filter="raw"
|
||||
hint=".gitignore *.bak *.tmp"
|
||||
/>
|
||||
<field
|
||||
name="exclude_tables"
|
||||
type="textarea"
|
||||
label="COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES"
|
||||
description="COM_MOKOBACKUP_FIELD_EXCLUDE_TABLES_DESC"
|
||||
rows="4"
|
||||
filter="raw"
|
||||
hint="#__session #__mail_queue"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
<fieldset name="remote" label="COM_MOKOBACKUP_FIELDSET_REMOTE">
|
||||
<field
|
||||
name="remote_storage"
|
||||
type="list"
|
||||
label="COM_MOKOBACKUP_FIELD_REMOTE_STORAGE"
|
||||
description="COM_MOKOBACKUP_FIELD_REMOTE_STORAGE_DESC"
|
||||
default="none"
|
||||
>
|
||||
<option value="none">COM_MOKOBACKUP_REMOTE_NONE</option>
|
||||
<option value="ftp">COM_MOKOBACKUP_REMOTE_FTP</option>
|
||||
<option value="google_drive">COM_MOKOBACKUP_REMOTE_GDRIVE</option>
|
||||
</field>
|
||||
<field
|
||||
name="remote_keep_local"
|
||||
type="radio"
|
||||
label="COM_MOKOBACKUP_FIELD_KEEP_LOCAL"
|
||||
description="COM_MOKOBACKUP_FIELD_KEEP_LOCAL_DESC"
|
||||
default="1"
|
||||
class="btn-group"
|
||||
>
|
||||
<option value="1">JYES</option>
|
||||
<option value="0">JNO</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
|
||||
<fieldset name="ftp" label="COM_MOKOBACKUP_FIELDSET_FTP">
|
||||
<field
|
||||
name="ftp_host"
|
||||
type="text"
|
||||
label="COM_MOKOBACKUP_FIELD_FTP_HOST"
|
||||
description="COM_MOKOBACKUP_FIELD_FTP_HOST_DESC"
|
||||
maxlength="255"
|
||||
showon="remote_storage:ftp"
|
||||
/>
|
||||
<field
|
||||
name="ftp_port"
|
||||
type="number"
|
||||
label="COM_MOKOBACKUP_FIELD_FTP_PORT"
|
||||
description="COM_MOKOBACKUP_FIELD_FTP_PORT_DESC"
|
||||
default="21"
|
||||
min="1"
|
||||
max="65535"
|
||||
showon="remote_storage:ftp"
|
||||
/>
|
||||
<field
|
||||
name="ftp_username"
|
||||
type="text"
|
||||
label="COM_MOKOBACKUP_FIELD_FTP_USERNAME"
|
||||
maxlength="255"
|
||||
showon="remote_storage:ftp"
|
||||
/>
|
||||
<field
|
||||
name="ftp_password"
|
||||
type="password"
|
||||
label="COM_MOKOBACKUP_FIELD_FTP_PASSWORD"
|
||||
maxlength="255"
|
||||
showon="remote_storage:ftp"
|
||||
/>
|
||||
<field
|
||||
name="ftp_path"
|
||||
type="text"
|
||||
label="COM_MOKOBACKUP_FIELD_FTP_PATH"
|
||||
description="COM_MOKOBACKUP_FIELD_FTP_PATH_DESC"
|
||||
default="/backups"
|
||||
maxlength="512"
|
||||
showon="remote_storage:ftp"
|
||||
/>
|
||||
<field
|
||||
name="ftp_passive"
|
||||
type="radio"
|
||||
label="COM_MOKOBACKUP_FIELD_FTP_PASSIVE"
|
||||
description="COM_MOKOBACKUP_FIELD_FTP_PASSIVE_DESC"
|
||||
default="1"
|
||||
class="btn-group"
|
||||
showon="remote_storage:ftp"
|
||||
>
|
||||
<option value="1">JYES</option>
|
||||
<option value="0">JNO</option>
|
||||
</field>
|
||||
<field
|
||||
name="ftp_ssl"
|
||||
type="radio"
|
||||
label="COM_MOKOBACKUP_FIELD_FTP_SSL"
|
||||
description="COM_MOKOBACKUP_FIELD_FTP_SSL_DESC"
|
||||
default="0"
|
||||
class="btn-group"
|
||||
showon="remote_storage:ftp"
|
||||
>
|
||||
<option value="1">JYES</option>
|
||||
<option value="0">JNO</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
|
||||
<fieldset name="google_drive" label="COM_MOKOBACKUP_FIELDSET_GDRIVE">
|
||||
<field
|
||||
name="gdrive_client_id"
|
||||
type="text"
|
||||
label="COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_ID"
|
||||
description="COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_ID_DESC"
|
||||
maxlength="255"
|
||||
showon="remote_storage:google_drive"
|
||||
/>
|
||||
<field
|
||||
name="gdrive_client_secret"
|
||||
type="password"
|
||||
label="COM_MOKOBACKUP_FIELD_GDRIVE_CLIENT_SECRET"
|
||||
maxlength="255"
|
||||
showon="remote_storage:google_drive"
|
||||
/>
|
||||
<field
|
||||
name="gdrive_refresh_token"
|
||||
type="text"
|
||||
label="COM_MOKOBACKUP_FIELD_GDRIVE_REFRESH_TOKEN"
|
||||
description="COM_MOKOBACKUP_FIELD_GDRIVE_REFRESH_TOKEN_DESC"
|
||||
maxlength="512"
|
||||
showon="remote_storage:google_drive"
|
||||
/>
|
||||
<field
|
||||
name="gdrive_folder_id"
|
||||
type="text"
|
||||
label="COM_MOKOBACKUP_FIELD_GDRIVE_FOLDER_ID"
|
||||
description="COM_MOKOBACKUP_FIELD_GDRIVE_FOLDER_ID_DESC"
|
||||
maxlength="255"
|
||||
showon="remote_storage:google_drive"
|
||||
/>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -34,6 +34,14 @@ HTMLHelper::_('behavior.keepalive');
|
||||
</div>
|
||||
<?php echo HTMLHelper::_('uitab.endTab'); ?>
|
||||
|
||||
<?php echo HTMLHelper::_('uitab.addTab', 'profileTab', 'archive', Text::_('COM_MOKOBACKUP_TAB_ARCHIVE')); ?>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<?php echo $this->form->renderFieldset('archive'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php echo HTMLHelper::_('uitab.endTab'); ?>
|
||||
|
||||
<?php echo HTMLHelper::_('uitab.addTab', 'profileTab', 'filters', Text::_('COM_MOKOBACKUP_TAB_FILTERS')); ?>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@@ -42,6 +50,16 @@ HTMLHelper::_('behavior.keepalive');
|
||||
</div>
|
||||
<?php echo HTMLHelper::_('uitab.endTab'); ?>
|
||||
|
||||
<?php echo HTMLHelper::_('uitab.addTab', 'profileTab', 'remote', Text::_('COM_MOKOBACKUP_TAB_REMOTE')); ?>
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<?php echo $this->form->renderFieldset('remote'); ?>
|
||||
<?php echo $this->form->renderFieldset('ftp'); ?>
|
||||
<?php echo $this->form->renderFieldset('google_drive'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php echo HTMLHelper::_('uitab.endTab'); ?>
|
||||
|
||||
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
* Task form: select which backup profile to run.
|
||||
* This form appears in System > Scheduled Tasks when creating a
|
||||
* "MokoJoomBackup: Run Backup Profile" task.
|
||||
-->
|
||||
<form>
|
||||
<fieldset name="run_profile">
|
||||
<field
|
||||
name="profile_id"
|
||||
type="sql"
|
||||
label="PLG_TASK_MOKOBACKUP_FIELD_PROFILE"
|
||||
description="PLG_TASK_MOKOBACKUP_FIELD_PROFILE_DESC"
|
||||
query="SELECT id AS value, title AS text FROM #__mokobackup_profiles WHERE published = 1 ORDER BY ordering ASC"
|
||||
default="1"
|
||||
required="true"
|
||||
>
|
||||
<option value="">PLG_TASK_MOKOBACKUP_SELECT_PROFILE</option>
|
||||
</field>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
||||
@@ -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 -"
|
||||
@@ -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."
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
||||
@@ -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 -"
|
||||
@@ -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."
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package MokoJoomBackup
|
||||
* @subpackage plg_task_mokobackup
|
||||
* @author Moko Consulting <hello@mokoconsulting.tech>
|
||||
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
* @license GNU General Public License version 3 or later; see LICENSE
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
* @package MokoJoomBackup
|
||||
* @subpackage plg_task_mokobackup
|
||||
* @author Moko Consulting <hello@mokoconsulting.tech>
|
||||
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
|
||||
* @license GNU General Public License version 3 or later; see LICENSE
|
||||
-->
|
||||
<extension type="plugin" group="task" method="upgrade">
|
||||
<name>plg_task_mokobackup</name>
|
||||
<version>01.00.00-dev</version>
|
||||
<creationDate>2026-06-02</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<description>PLG_TASK_MOKOBACKUP_DESCRIPTION</description>
|
||||
|
||||
<namespace path="src">Joomla\Plugin\Task\MokoBackup</namespace>
|
||||
|
||||
<files>
|
||||
<filename plugin="mokobackup">mokobackup.php</filename>
|
||||
<folder>services</folder>
|
||||
<folder>src</folder>
|
||||
<folder>forms</folder>
|
||||
</files>
|
||||
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/plg_task_mokobackup.ini</language>
|
||||
<language tag="en-GB">language/en-GB/plg_task_mokobackup.sys.ini</language>
|
||||
</languages>
|
||||
</extension>
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package MokoJoomBackup
|
||||
* @subpackage plg_task_mokobackup
|
||||
* @author Moko Consulting <hello@mokoconsulting.tech>
|
||||
* @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;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package MokoJoomBackup
|
||||
* @subpackage plg_task_mokobackup
|
||||
* @author Moko Consulting <hello@mokoconsulting.tech>
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><title></title>
|
||||
@@ -22,6 +22,7 @@
|
||||
<files folder="packages">
|
||||
<file type="component" id="com_mokobackup">com_mokobackup.zip</file>
|
||||
<file type="plugin" id="mokobackup" group="system">plg_system_mokobackup.zip</file>
|
||||
<file type="plugin" id="mokobackup" group="task">plg_task_mokobackup.zip</file>
|
||||
<file type="plugin" id="mokobackup" group="webservices">plg_webservices_mokobackup.zip</file>
|
||||
</files>
|
||||
|
||||
|
||||
@@ -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'))
|
||||
|
||||
Reference in New Issue
Block a user