From 62d952a85afe7fbdd126081199b3fe2aed31ac60 Mon Sep 17 00:00:00 2001 From: Lewis Larsen Date: Sat, 27 Jul 2024 23:10:23 +0100 Subject: [PATCH] feat: Added cron preset modal --- .../Forms/CreateBackupTaskForm.php | 189 +++++++++++++++++- lang/da.json | 47 +++++ .../forms/create-backup-task-form.blade.php | 79 +++++++- 3 files changed, 310 insertions(+), 5 deletions(-) diff --git a/app/Livewire/BackupTasks/Forms/CreateBackupTaskForm.php b/app/Livewire/BackupTasks/Forms/CreateBackupTaskForm.php index 6115a280..eba4ae38 100644 --- a/app/Livewire/BackupTasks/Forms/CreateBackupTaskForm.php +++ b/app/Livewire/BackupTasks/Forms/CreateBackupTaskForm.php @@ -8,7 +8,6 @@ use App\Models\NotificationStream; use App\Models\RemoteServer; use App\Models\Tag; -use App\Models\User; use App\Rules\UniqueScheduledTimePerRemoteServer; use Carbon\Carbon; use Illuminate\Database\Eloquent\Collection; @@ -90,6 +89,13 @@ class CreateBackupTaskForm extends Component /** @var array|null */ public ?array $selectedStreams = []; + public bool $showCronPresets = false; + public string $cronPresetSearch = ''; + /** + * @var array + */ + public array $cronPresets = []; + /** @var array */ protected array $validationAttributes = [ 'label' => 'Label', @@ -108,6 +114,60 @@ class CreateBackupTaskForm extends Component 'cronExpression' => 'Cron Expression', ]; + /** + * Open the cron presets modal. + */ + public function openCronPresets(): void + { + $this->showCronPresets = true; + } + + /** + * Close the cron presets modal. + */ + public function closeCronPresets(): void + { + $this->showCronPresets = false; + } + + /** + * Set the cron expression from a preset. + */ + public function setPreset(string $preset): void + { + $this->cronExpression = $preset; + $this->dispatch('close-modal', 'cron-presets'); + $this->cronPresetSearch = ''; + } + + /** + * Get filtered and grouped cron presets based on the current search term. + * + * @return array> + */ + public function getFilteredCronPresets(): array + { + $groupedPresets = [ + $this->ensureString(__('Daily Backups')) => [], + $this->ensureString(__('Weekly Backups')) => [], + $this->ensureString(__('Monthly Backups')) => [], + $this->ensureString(__('Custom Intervals')) => [], + $this->ensureString(__('Business Hours')) => [], + ]; + + foreach ($this->cronPresets as $expression => $description) { + if (! $this->matchesSearch($description)) { + continue; + } + + $translatedDescription = $this->ensureString(__($description)); + $group = $this->determinePresetGroup($translatedDescription); + $groupedPresets[$group][$expression] = $translatedDescription; + } + + return $groupedPresets; + } + /** * Initialize the component state. * @@ -120,6 +180,7 @@ public function mount(): void $this->initializeBackupTimes(); $this->updatedBackupType(); $this->updatedUseCustomCron(); + $this->initializeCronPresets(); } /** @@ -239,6 +300,13 @@ public function render(): View */ public function rules(): array { + $cronRegex = '/^(\*|(\*\/)?([0-5]?[0-9])([-,]([0-5]?[0-9]))*)' // minute + . '\s+(\*|(\*\/)?([0-1]?[0-9]|2[0-3])([-,]([0-1]?[0-9]|2[0-3]))*)' // hour + . '\s+(\*|(\*\/)?([1-2]?[0-9]|3[0-1])([-,]([1-2]?[0-9]|3[0-1]))*)' // day of month + . '\s+(\*|(\*\/)?([1-9]|1[0-2])([-,]([1-9]|1[0-2]))*)' // month + . '\s+(\*|(\*\/)?([0-7])([-,]([0-7]))*)' // day of week + . '$/'; + $baseRules = [ 'isolatedUsername' => ['nullable', 'string'], 'isolatedPassword' => ['nullable', 'string'], @@ -263,7 +331,7 @@ public function rules(): array 'cronExpression' => [ 'nullable', 'string', - 'regex:/^(\*|([0-5]?\d)) (\*|([01]?\d|2[0-3])) (\*|([0-2]?\d|3[01])) (\*|([1-9]|1[0-2])) (\*|([0-7]))$/', + 'regex:' . $cronRegex, 'required_if:useCustomCron,true', ], ]; @@ -338,6 +406,123 @@ public function getSummary(): array ); } + /** + * Ensure the given value is a string. + * + * @param string|array $value + */ + private function ensureString($value): string + { + if (is_array($value)) { + return implode(', ', $value); + } + + return $value; + } + + /** + * Determine the group for a given cron preset description. + */ + private function determinePresetGroup(string $description): string + { + $lowercaseDescription = strtolower($description); + + if (str_contains($lowercaseDescription, strtolower($this->ensureString(__('Every day'))))) { + return $this->ensureString(__('Daily Backups')); + } + + if (str_contains($lowercaseDescription, strtolower($this->ensureString(__('Every week'))))) { + return $this->ensureString(__('Weekly Backups')); + } + + if (str_contains($lowercaseDescription, strtolower($this->ensureString(__('Every month')))) || + str_contains($lowercaseDescription, strtolower($this->ensureString(__('Every 3 months'))))) { + return $this->ensureString(__('Monthly Backups')); + } + + if (str_contains($lowercaseDescription, strtolower($this->ensureString(__('Monday to Friday')))) || + str_contains($lowercaseDescription, strtolower($this->ensureString(__('weekday'))))) { + return $this->ensureString(__('Business Hours')); + } + + return $this->ensureString(__('Custom Intervals')); + } + + /** + * Check if the description matches the current search term. + */ + private function matchesSearch(string $description): bool + { + if ($this->cronPresetSearch === '' || $this->cronPresetSearch === '0') { + return true; + } + + return str_contains(strtolower($description), strtolower($this->cronPresetSearch)); + } + + /** + * Initialize the cron presets with their translated descriptions. + */ + private function initializeCronPresets(): void + { + $this->cronPresets = [ + // Daily backups + '0 0 * * *' => __('Every day at midnight'), + '0 2 * * *' => __('Every day at 2 AM'), + '0 4 * * *' => __('Every day at 4 AM'), + '0 1 * * *' => __('Every day at 1 AM (off-peak hours)'), + '0 23 * * *' => __('Every day at 11 PM'), + + // Multiple times per day + '0 */6 * * *' => __('Every 6 hours'), + '0 */12 * * *' => __('Every 12 hours'), + '0 */4 * * *' => __('Every 4 hours'), + '0 */8 * * *' => __('Every 8 hours'), + + // Specific days of the week + '0 0 * * 5' => __('Every Friday at midnight'), + '0 0 * * 1' => __('Every Monday at midnight'), + '0 2 * * 6' => __('Every Saturday at 2 AM'), + + // Multiple days per week + '0 3 * * 1,4' => __('Every Monday and Thursday at 3 AM'), + '0 2 * * 2,5' => __('Every Tuesday and Friday at 2 AM'), + + // Weekly backups + '0 0 * * 0' => __('Every week on Sunday at midnight'), + '0 1 * * 1' => __('Every week on Monday at 1 AM'), + + // Monthly backups + '0 0 1 * *' => __('Every month on the 1st at midnight'), + '0 2 1 * *' => __('Every month on the 1st at 2 AM'), + '0 3 15 * *' => __('Every month on the 15th at 3 AM'), + + // Quarterly backups + '0 0 1 */3 *' => __('Every 3 months on the 1st at midnight'), + + // Yearly backup + '0 0 1 1 *' => __('Every year on Jan 1st at midnight'), + + // Less frequent backups + '0 0 */3 * *' => __('Every 3 days at midnight'), + '0 0 */7 * *' => __('Every 7 days at midnight'), + '0 1 */5 * *' => __('Every 5 days at 1 AM'), + + // Business hours + '0 9-17 * * 1-5' => __('Every hour from 9 AM to 5 PM, Monday to Friday'), + '0 8,18 * * 1-5' => __('Twice daily at 8 AM and 6 PM, Monday to Friday'), + + // End of business day + '0 18 * * 1-5' => __('Every weekday at 6 PM'), + + // First and last day of the month + '0 1 1,L * *' => __('On the first and last day of every month at 1 AM'), + + // Every weekend + '0 2 * * 6,0' => __('Every Saturday and Sunday at 2 AM'), + ]; + } + /** * Initialize default values for the form. */ diff --git a/lang/da.json b/lang/da.json index 44f586bc..12ac50e4 100644 --- a/lang/da.json +++ b/lang/da.json @@ -62,6 +62,7 @@ "been run today.": "kørt i dag.", "Before removing your account, please take a moment to download any data or information that you wish to retain.": "Før du fjerner din konto, bør du tage et øjeblik til at downloade eventuelle data eller oplysninger, du ønsker at beholde.", "Bucket Name": "Bucket-navn", + "Business Hours": "Arbejdstimer", "Cancel": "Annuller", "Cancel Setup": "Annuller opsætning", "Checking": "Kontrollerer", @@ -75,6 +76,7 @@ "Click to run this task": "Klik for at køre denne opgave", "Click to view this log": "Klik for at se denne log", "Close": "Luk", + "Common Cron Job Presets": "Almindelige Cron Job Forudindstillinger", "Configuration": "Konfiguration", "Configure Backup Destination": "Konfigurér backupdestination", "Configured Notification streams for your backup tasks.": "Konfigurerede notifikationskanaler til dine backup-opgaver.", @@ -96,8 +98,10 @@ "Cron Expression": "Cron-udtryk", "Current Password": "Nuværende adgangskode", "Custom Cron Expression": "Brugerdefineret Cron-udtryk", + "Custom Intervals": "Tilpassede Intervaller", "Custom S3": "Tilpasset S3", "Daily": "Dagligt", + "Daily Backups": "Daglige Backups", "Dark": "Mørk", "Database": "Database", "database": "database", @@ -122,6 +126,37 @@ "Ensure your account is using a long, random password to stay secure.": "Sørg for, at din konto bruger en lang, tilfældig adgangskode for at forblive sikker.", "Enter an alternative email address to use for your Gravatar picture. If left blank, your primary email will be used.": "Indtast en alternativ e-mailadresse til brug for dit Gravatar-billede. Hvis feltet er tomt, vil din primære e-mail blive brugt.", "Error! Unable to locate an SSH Key.": "Fejl! Kan ikke finde en SSH-nøgle.", + "Every 12 hours": "Hver 12. time", + "Every 3 days at midnight": "Hver 3. dag ved midnat", + "Every 3 months": "Hver 3. måned", + "Every 3 months on the 1st at midnight": "Hver 3. måned den 1. ved midnat", + "Every 4 hours": "Hver 4. time", + "Every 5 days at 1 AM": "Hver 5. dag klokken 1 om natten", + "Every 6 hours": "Hver 6. time", + "Every 7 days at midnight": "Hver 7. dag ved midnat", + "Every 8 hours": "Hver 8. time", + "Every day": "Hver dag", + "Every day at 1 AM (off-peak hours)": "Hver dag klokken 1 om natten (uden for spidsbelastning)", + "Every day at 11 PM": "Hver dag klokken 23", + "Every day at 2 AM": "Hver dag klokken 2 om natten", + "Every day at 4 AM": "Hver dag klokken 4 om morgenen", + "Every day at midnight": "Hver dag ved midnat", + "Every Friday at midnight": "Hver fredag ved midnat", + "Every hour from 9 AM to 5 PM, Monday to Friday": "Hver time fra 9 til 17, mandag til fredag", + "Every Monday and Thursday at 3 AM": "Hver mandag og torsdag klokken 3 om natten", + "Every Monday at midnight": "Hver mandag ved midnat", + "Every month": "Hver måned", + "Every month on the 15th at 3 AM": "Hver måned den 15. klokken 3 om natten", + "Every month on the 1st at 2 AM": "Hver måned den 1. klokken 2 om natten", + "Every month on the 1st at midnight": "Hver måned den 1. ved midnat", + "Every Saturday and Sunday at 2 AM": "Hver lørdag og søndag klokken 2 om natten", + "Every Saturday at 2 AM": "Hver lørdag klokken 2 om natten", + "Every Tuesday and Friday at 2 AM": "Hver tirsdag og fredag klokken 2 om natten", + "Every week": "Hver uge", + "Every week on Monday at 1 AM": "Hver uge mandag klokken 1 om natten", + "Every week on Sunday at midnight": "Hver uge søndag ved midnat", + "Every weekday at 6 PM": "Hver hverdag klokken 18", + "Every year on Jan 1st at midnight": "Hvert år den 1. januar ved midnat", "Excluded Database Tables": "Ekskluderede databasetabeller", "Failed": "Mislykkedes", "Failed Backups": "Mislykkede backups", @@ -160,13 +195,16 @@ "Make your first Backup Task": "Opret din første sikkerhedskopieringsopgave", "Maximum Backups to Keep": "Maksimalt antal sikkerhedskopier, der skal opbevares", "MIT License": "MIT-licensen", + "Monday to Friday": "Mandag til fredag", "Monthly Backup Task Activity": "Månedlig aktivitet for sikkerhedskopieringsopgaver", + "Monthly Backups": "Månedlige Backups", "Name": "Navn", "Never": "Aldrig", "New Password": "Ny adgangskode", "Next": "Næste", "No": "Nej", "No log available": "Ingen log tilgængelig", + "No matching presets found.": "Ingen matchende forudindstillinger fundet.", "No Notification Streams": "Ingen notifikationskanaler", "None": "Ingen", "Not Set": "Ikke indstillet", @@ -178,6 +216,7 @@ "Notifications": "Notifikationer", "Notifications about this backup task will be sent on the notification streams you choose.": "Notifikationer om denne backup-opgave vil blive sendt via de notifikationskanaler, du vælger.", "Offline": "Offline", + "On the first and last day of every month at 1 AM": "På den første og sidste dag i hver måned klokken 1 om natten", "Once": "Én gang", "Once your account is removed, all of its resources and data will be permanently removed. Additionally, all backup tasks, backup destinations, and linked servers will be removed from our systems.": "Når din konto er fjernet, vil alle dens ressourcer og data blive permanent fjernet. Derudover vil alle sikkerhedskopieringsopgaver, sikkerhedskopieringsdestinationer og tilknyttede servere blive fjernet fra vores systemer.", "One or more of the selected tags do not exist.": "En eller flere af de valgte tags findes ikke.", @@ -235,6 +274,7 @@ "Please select your preferred language from the dropdown list. This will change the language used throughout the application.": "Vælg venligst dit foretrukne sprog fra rullelisten. Dette vil ændre sproget, der bruges i hele applikationen.", "Please specify a language for your account.": "Angiv venligst et sprog for din konto.", "Please specify a timezone.": "Angiv venligst en tidszone.", + "Presets": "Forudindstillinger", "Previous": "Forrige", "Previously Executed Backup Tasks": "Tidligere udførte sikkerhedskopieringsopgaver", "Proceed": "Fortsæt", @@ -279,7 +319,9 @@ "Scheduled": "Planlagt", "Scheduled backup tasks that are set to run soon.": "Planlagte sikkerhedskopieringsopgaver, der skal køres snart.", "Scheduled for": "Planlagt til", + "Search Presets": "Søg Forudindstillinger", "Secret Key": "Hemmelig nøgle", + "Select a preset to quickly set up common backup schedules. The cron expression will be automatically filled in for you.": "Vælg en forudindstilling for hurtigt at opsætte almindelige backup-planer. Cron-udtrykket vil automatisk blive udfyldt for dig.", "Server & Destination": "Server & Destination", "Server Label": "Servernavn", "Servers": "Servere", @@ -358,7 +400,9 @@ "to create your SSH key.": "for at oprette din SSH-nøgle.", "to help you generate a valid cron expression.": "til at hjælpe dig med at generere en gyldig cron-udtryk.", "To set the SSH passphrase:": "For at indstille SSH-adgangssætningen:", + "Twice daily at 8 AM and 6 PM, Monday to Friday": "To gange dagligt klokken 8 og 18, mandag til fredag", "Type": "Type", + "Type to search...": "Skriv for at søge...", "Unfortunately :app was not able to connect. Find the error message below.": "Desværre kunne :app ikke oprette forbindelse. Find fejlmeddelelsen nedenfor.", "Unknown": "Ukendt", "Unreachable": "Utilgængelig", @@ -370,6 +414,7 @@ "Update Tag": "Opdater mærke", "Update your account's profile information and email address.": "Opdater din kontos profiloplysninger og e-mailadresse.", "Update your avatar on Gravatar": "Opdater din avatar på Gravatar", + "Use": "Brug", "Use Custom Cron": "Brug brugerdefineret cron", "Use Path Style Endpoint": "Brug sti-baseret endpoint", "Used": "Brugt", @@ -387,8 +432,10 @@ "We recommend using a tool such as": "Vi anbefaler at bruge et værktøj som f.eks", "We support Ubuntu and Debian distributions primarily.": "Vi understøtter primært Ubuntu- og Debian-distributioner.", "Website": "Hjemmeside", + "weekday": "hverdag", "Weekly": "Ugentligt", "Weekly Backup Summary Emails": "Ugentlige Backup Resumé Emails", + "Weekly Backups": "Ugentlige Backups", "When your account is removed, all the data we hold on you will be permanently erased. This includes any backup tasks, backup destinations you have configured with us, and servers you have linked.": "Når din konto er fjernet, vil alle de data, vi har om dig, blive permanent slettet. Dette inkluderer eventuelle sikkerhedskopieringsopgaver, sikkerhedskopieringsdestinationer du har konfigureret hos os, og servere du har tilknyttet.", "Yes": "Ja", "You are welcome to re-join at any time.": "Du er velkommen til at tilmelde dig igen når som helst.", diff --git a/resources/views/livewire/backup-tasks/forms/create-backup-task-form.blade.php b/resources/views/livewire/backup-tasks/forms/create-backup-task-form.blade.php index e6fb5714..5e810317 100644 --- a/resources/views/livewire/backup-tasks/forms/create-backup-task-form.blade.php +++ b/resources/views/livewire/backup-tasks/forms/create-backup-task-form.blade.php @@ -288,12 +288,23 @@ @else
- +
+ + + @svg('heroicon-o-clock', 'w-5 h-5 mr-1') + {{ __('Presets') }} + +
{{ __('We recommend using a tool such as') }} - Crontab Guru @@ -480,4 +491,66 @@ class="relative bg-gray-50 dark:bg-gray-700 rounded-lg p-4 transition-all durati
+ + + +
+

+
+ @svg('heroicon-o-calendar-days', ['class' => 'h-6 w-6 text-primary-600 dark:text-primary-400']) +
+ {{ __('Common Cron Job Presets') }} +

+

+ {{ __('Select a preset to quickly set up common backup schedules. The cron expression will be automatically filled in for you.') }} +

+
+ +
+ @svg('heroicon-o-magnifying-glass', 'w-5 h-5 text-gray-400 absolute left-3 top-3') + +
+
+
+ @forelse ($this->getFilteredCronPresets() as $group => $presets) +
+

{{ $group }}

+
+ @foreach ($presets as $expression => $description) +
+
+ {{ $description }} +
+ + {{ $expression }} + +
+ {{ __('Cron Expression') }} +
+
+
+ + {{ __('Use') }} + +
+ @endforeach +
+
+ @empty +

{{ __('No matching presets found.') }}

+ @endforelse +
+
+