From: Marcin Haba Date: Sat, 13 Apr 2019 12:13:19 +0000 (+0200) Subject: baculum: New create backup job wizard X-Git-Tag: Release-9.4.3~45 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ff8f25f7d4e1b0512741f6730dacc092ee4b5ed6;p=thirdparty%2Fbacula.git baculum: New create backup job wizard Changes: - New client file browser for selecting FileSet includes/excludes - Resign from using BConditional control - Start using renderer classes - Rename basic directive controls to have consistent names - Show 'save' button for editing existing resources - Show 'create' button for creating new resource - Fix run job Accurate and Priority values --- diff --git a/gui/baculum/protected/Common/Class/Miscellaneous.php b/gui/baculum/protected/Common/Class/Miscellaneous.php index 38f2c0c6d..0c18b1bd4 100644 --- a/gui/baculum/protected/Common/Class/Miscellaneous.php +++ b/gui/baculum/protected/Common/Class/Miscellaneous.php @@ -204,7 +204,15 @@ class Miscellaneous extends TModule { } public function isValidBoolean($val) { - return (preg_match('/^(yes|no|0|1|true|false)$/', $val) === 1); + return (preg_match('/^(yes|no|1|0|true|false)$/i', $val) === 1); + } + + public function isValidBooleanTrue($val) { + return (preg_match('/^(yes|1|true)$/i', $val) === 1); + } + + public function isValidBooleanFalse($val) { + return (preg_match('/^(no|0|false)$/i', $val) === 1); } public function isValidId($id) { diff --git a/gui/baculum/protected/Web/JavaScript/bacula-config.js b/gui/baculum/protected/Web/JavaScript/bacula-config.js index 98ab78d4b..7ae5d6251 100644 --- a/gui/baculum/protected/Web/JavaScript/bacula-config.js +++ b/gui/baculum/protected/Web/JavaScript/bacula-config.js @@ -28,8 +28,14 @@ var BaculaConfigClass = jQuery.klass({ this.show_item(container, true); this.scroll_to_element(container); }, - scroll_to_element: function(selector) { - $('html,body').animate({scrollTop: $(selector).offset().top}, 'slow'); + scroll_to_element: function(selector, additional_offset) { + var offset = $(selector).offset().top; + if (additional_offset) { + offset += additional_offset; + } + $('html,body').animate({ + scrollTop: offset + }, 'slow'); }, get_child_container: function(sender) { var child_container = $('#' + sender).closest('table').next('div'); diff --git a/gui/baculum/protected/Web/Lang/en/messages.mo b/gui/baculum/protected/Web/Lang/en/messages.mo index 233e33fa3..837a5fbed 100644 Binary files a/gui/baculum/protected/Web/Lang/en/messages.mo and b/gui/baculum/protected/Web/Lang/en/messages.mo differ diff --git a/gui/baculum/protected/Web/Lang/en/messages.po b/gui/baculum/protected/Web/Lang/en/messages.po index 612aa9580..e2e7cd7af 100644 --- a/gui/baculum/protected/Web/Lang/en/messages.po +++ b/gui/baculum/protected/Web/Lang/en/messages.po @@ -1835,3 +1835,194 @@ msgstr "Error code:" msgid "Message:" msgstr "Message:" +msgid "Include" +msgstr "Include" + +msgid "Exclude" +msgstr "Exclude" + +msgid "Step 1 - select job type" +msgstr "Step 1 - select job type" + +msgid "New job wizard" +msgstr "New job wizard" + +msgid "This wizard enables you to create in easy way a new job." +msgstr "This wizard enables you to create in easy way a new job." + +msgid "To start please select job type that you wish to create." +msgstr "To start please select job type that you wish to create." + +msgid "Job Type" +msgstr "Job Type" + +msgid "Job Name" +msgstr "Job Name" + +msgid "JobDefs" +msgstr "JobDefs" + +msgid "Step 2 - what you want to backup" +msgstr "Step 2 - what you want to backup" + +msgid "Client and FileSet" +msgstr "Client and FileSet" + +msgid "Please select Client from which you want to backup data and please select FileSet with files/directories definition to backup." +msgstr "Please select Client from which you want to backup data and please select FileSet with files/directories definition to backup." + +msgid "If your FileSet for this backup does not exist yet, you can create it." +msgstr "If your FileSet for this backup does not exist yet, you can create it." + +msgid "FileSet" +msgstr "FileSet" + +msgid "Add new FileSet" +msgstr "Add new FileSet" + +msgid "New FileSet" +msgstr "New FileSet" + +msgid "Step 3 - where you want to backup" +msgstr "Step 3 - where you want to backup" + +msgid "Storage and Pool" +msgstr "Storage and Pool" + +msgid "Please select Storage where you want to backup data and please select Pool with volumes which Storage will use to store backup data." +msgstr "Please select Storage where you want to backup data and please select Pool with volumes which Storage will use to store backup data." + +msgid "Storage" +msgstr "Storage" + +msgid "Show Storage directives for Job" +msgstr "Show Storage directives for Job" + +msgid "Spool setting" +msgstr "Spool setting" + +msgid "Spool Data" +msgstr "Spool Data" + +msgid "Spool Attributes" +msgstr "Spool Attributes" + +msgid "Spool Size" +msgstr "Spool Size" + +msgid "Show Pool directives for Job" +msgstr "Show Pool directives for Job" + +msgid "Pool setting" +msgstr "Pool setting" + +msgid "Full Backup Pool" +msgstr "Full Backup Pool" + +msgid "Incremental Backup Pool" +msgstr "Incremental Backup Pool" + +msgid "Differential Backup Pool" +msgstr "Differential Backup Pool" + +msgid "Step 4 - how would you like to backup" +msgstr "Step 4 - how would you like to backup" + +msgid "Backup Job directives" +msgstr "Backup Job directives" + +msgid "Accurate" +msgstr "Accurate" + +msgid "Step 5 - when would you like to run backup" +msgstr "Step 5 - when would you like to run backup" + +msgid "Schedule" +msgstr "Schedule" + +msgid "Show Reschedule directives" +msgstr "Show Reschedule directives" + +msgid "Reschedule setting" +msgstr "Reschedule setting" + +msgid "Reschedule On Error" +msgstr "Reschedule On Error" + +msgid "Reschedule Incomplete Jobs" +msgstr "Reschedule Incomplete Jobs" + +msgid "Reschedule Interval" +msgstr "Reschedule Interval" + +msgid "Reschedule Times" +msgstr "Reschedule Times" + +msgid "What" +msgstr "What" + +msgid "Maximum Concurrent Jobs" +msgstr "Maximum Concurrent Jobs" + +msgid "Priority" +msgstr "Priority" + +msgid "ReRun Failed Levels" +msgstr "ReRun Failed Levels" + +msgid "General" +msgstr "General" + +msgid "How" +msgstr "How" + +msgid "When" +msgstr "When" + +msgid "Other directives" +msgstr "Other directives" + +msgid "Messages" +msgstr "Messages" + +msgid "Step 6 - summary" +msgstr "Step 6 - summary" + +msgid "Add new Pool" +msgstr "Add new Pool" + +msgid "New Pool" +msgstr "New Pool" + +msgid "Add new Schedule" +msgstr "Add new Schedule" + +msgid "New Schedule" +msgstr "New Schedule" + +msgid "Summary" +msgstr "Summary" + +msgid "inherited from JobDefs" +msgstr "inherited from JobDefs" + +msgid "Create job" +msgstr "Create job" + +msgid "Please select Client" +msgstr "Please select Client" + +msgid "Go to path" +msgstr "Go to path" + +msgid "Add new include path" +msgstr "Add new include path" + +msgid "Add new global exclude path" +msgstr "Add new global exclude path" + +msgid "Include files to FileSet" +msgstr "Include files to FileSet" + +msgid "Apply file selection" +msgstr "Apply file selection" diff --git a/gui/baculum/protected/Web/Lang/ja/messages.mo b/gui/baculum/protected/Web/Lang/ja/messages.mo index d6fb25d12..0cc644f38 100644 Binary files a/gui/baculum/protected/Web/Lang/ja/messages.mo and b/gui/baculum/protected/Web/Lang/ja/messages.mo differ diff --git a/gui/baculum/protected/Web/Lang/ja/messages.po b/gui/baculum/protected/Web/Lang/ja/messages.po index d2456e511..b38e1041d 100644 --- a/gui/baculum/protected/Web/Lang/ja/messages.po +++ b/gui/baculum/protected/Web/Lang/ja/messages.po @@ -1935,3 +1935,195 @@ msgid "" "{user} keyword will be replaced for each logged user into according username." msgstr "" "{user} keyword will be replaced for each logged user into according username." + +msgid "Include" +msgstr "Include" + +msgid "Exclude" +msgstr "Exclude" + +msgid "Step 1 - select job type" +msgstr "Step 1 - select job type" + +msgid "New job wizard" +msgstr "New job wizard" + +msgid "This wizard enables you to create in easy way a new job." +msgstr "This wizard enables you to create in easy way a new job." + +msgid "To start please select job type that you wish to create." +msgstr "To start please select job type that you wish to create." + +msgid "Job Type" +msgstr "Job Type" + +msgid "Job Name" +msgstr "Job Name" + +msgid "JobDefs" +msgstr "JobDefs" + +msgid "Step 2 - what you want to backup" +msgstr "Step 2 - what you want to backup" + +msgid "Client and FileSet" +msgstr "Client and FileSet" + +msgid "Please select Client from which you want to backup data and please select FileSet with files/directories definition to backup." +msgstr "Please select Client from which you want to backup data and please select FileSet with files/directories definition to backup." + +msgid "If your FileSet for this backup does not exist yet, you can create it." +msgstr "If your FileSet for this backup does not exist yet, you can create it." + +msgid "FileSet" +msgstr "FileSet" + +msgid "Add new FileSet" +msgstr "Add new FileSet" + +msgid "New FileSet" +msgstr "New FileSet" + +msgid "Step 3 - where you want to backup" +msgstr "Step 3 - where you want to backup" + +msgid "Storage and Pool" +msgstr "Storage and Pool" + +msgid "Please select Storage where you want to backup data and please select Pool with volumes which Storage will use to store backup data." +msgstr "Please select Storage where you want to backup data and please select Pool with volumes which Storage will use to store backup data." + +msgid "Storage" +msgstr "Storage" + +msgid "Show Storage directives for Job" +msgstr "Show Storage directives for Job" + +msgid "Spool setting" +msgstr "Spool setting" + +msgid "Spool Data" +msgstr "Spool Data" + +msgid "Spool Attributes" +msgstr "Spool Attributes" + +msgid "Spool Size" +msgstr "Spool Size" + +msgid "Show Pool directives for Job" +msgstr "Show Pool directives for Job" + +msgid "Pool setting" +msgstr "Pool setting" + +msgid "Full Backup Pool" +msgstr "Full Backup Pool" + +msgid "Incremental Backup Pool" +msgstr "Incremental Backup Pool" + +msgid "Differential Backup Pool" +msgstr "Differential Backup Pool" + +msgid "Step 4 - how would you like to backup" +msgstr "Step 4 - how would you like to backup" + +msgid "Backup Job directives" +msgstr "Backup Job directives" + +msgid "Accurate" +msgstr "Accurate" + +msgid "Step 5 - when would you like to run backup" +msgstr "Step 5 - when would you like to run backup" + +msgid "Schedule" +msgstr "Schedule" + +msgid "Show Reschedule directives" +msgstr "Show Reschedule directives" + +msgid "Reschedule setting" +msgstr "Reschedule setting" + +msgid "Reschedule On Error" +msgstr "Reschedule On Error" + +msgid "Reschedule Incomplete Jobs" +msgstr "Reschedule Incomplete Jobs" + +msgid "Reschedule Interval" +msgstr "Reschedule Interval" + +msgid "Reschedule Times" +msgstr "Reschedule Times" + +msgid "What" +msgstr "What" + +msgid "Maximum Concurrent Jobs" +msgstr "Maximum Concurrent Jobs" + +msgid "Priority" +msgstr "Priority" + +msgid "ReRun Failed Levels" +msgstr "ReRun Failed Levels" + +msgid "General" +msgstr "General" + +msgid "How" +msgstr "How" + +msgid "When" +msgstr "When" + +msgid "Other directives" +msgstr "Other directives" + +msgid "Messages" +msgstr "Messages" + +msgid "Step 6 - summary" +msgstr "Step 6 - summary" + +msgid "Add new Pool" +msgstr "Add new Pool" + +msgid "New Pool" +msgstr "New Pool" + +msgid "Add new Schedule" +msgstr "Add new Schedule" + +msgid "New Schedule" +msgstr "New Schedule" + +msgid "Summary" +msgstr "Summary" + +msgid "inherited from JobDefs" +msgstr "inherited from JobDefs" + +msgid "Create job" +msgstr "Create job" + +msgid "Please select Client" +msgstr "Please select Client" + +msgid "Go to path" +msgstr "Go to path" + +msgid "Add new include path" +msgstr "Add new include path" + +msgid "Add new global exclude path" +msgstr "Add new global exclude path" + +msgid "Include files to FileSet" +msgstr "Include files to FileSet" + +msgid "Apply file selection" +msgstr "Apply file selection" diff --git a/gui/baculum/protected/Web/Lang/pl/messages.mo b/gui/baculum/protected/Web/Lang/pl/messages.mo index 48c7918df..cdcef410b 100644 Binary files a/gui/baculum/protected/Web/Lang/pl/messages.mo and b/gui/baculum/protected/Web/Lang/pl/messages.mo differ diff --git a/gui/baculum/protected/Web/Lang/pl/messages.po b/gui/baculum/protected/Web/Lang/pl/messages.po index c7f3fb67c..eafdc55df 100644 --- a/gui/baculum/protected/Web/Lang/pl/messages.po +++ b/gui/baculum/protected/Web/Lang/pl/messages.po @@ -1841,3 +1841,195 @@ msgstr "Kod błędu:" msgid "Message:" msgstr "Wiadomość:" + +msgid "Include" +msgstr "Include" + +msgid "Exclude" +msgstr "Exclude" + +msgid "Step 1 - select job type" +msgstr "Krok 1 - wybierz typ zadania" + +msgid "New job wizard" +msgstr "Konfigurator nowego zadania" + +msgid "This wizard enables you to create in easy way a new job." +msgstr "Ten konfigurator umożliwia ci stworzenie nowego zadania w łatwy sposób." + +msgid "To start please select job type that you wish to create." +msgstr "Aby zacząć proszę wybrać typ zadania, które życzysz sobie stworzyć." + +msgid "Job Type" +msgstr "Typ Zadania" + +msgid "Job Name" +msgstr "Nazwa Zadania" + +msgid "JobDefs" +msgstr "JobDefs" + +msgid "Step 2 - what you want to backup" +msgstr "Krok 2 - co chcesz backupować" + +msgid "Client and FileSet" +msgstr "Klient i FileSet" + +msgid "Please select Client from which you want to backup data and please select FileSet with files/directories definition to backup." +msgstr "Proszę wybrać Klienta z którego chcesz backupować dane i proszę wybrać FileSet z definicją plików/katalogów do backupu" + +msgid "If your FileSet for this backup does not exist yet, you can create it." +msgstr "Jeśli twój FileSet dla backupu nie istnieje jeszcze, możesz go stworzyć." + +msgid "FileSet" +msgstr "FileSet" + +msgid "Add new FileSet" +msgstr "Dodaj nowy FileSet" + +msgid "New FileSet" +msgstr "Nowy FileSet" + +msgid "Step 3 - where you want to backup" +msgstr "Krok 3 - gdzie chcesz składować backup" + +msgid "Storage and Pool" +msgstr "Magazyn i Pula wolumenów" + +msgid "Please select Storage where you want to backup data and please select Pool with volumes which Storage will use to store backup data." +msgstr "Proszę wybrać Magazyn danych gdzie chcesz składować backup i proszę wybrać Pulę z wolumenami które Magazyn będzie używał do składowania danych backupu" + +msgid "Storage" +msgstr "Magazyn danych" + +msgid "Show Storage directives for Job" +msgstr "Pokaż opcje Magazynu danych dla zadania" + +msgid "Spool setting" +msgstr "Ustawienia Spool" + +msgid "Spool Data" +msgstr "Spool Data" + +msgid "Spool Attributes" +msgstr "Spool Attributes" + +msgid "Spool Size" +msgstr "Spool Size" + +msgid "Show Pool directives for Job" +msgstr "Pokaż opcje Puli wolumenów dla zadania" + +msgid "Pool setting" +msgstr "Ustawienia Puli wolumenów" + +msgid "Full Backup Pool" +msgstr "Full Backup Pool" + +msgid "Incremental Backup Pool" +msgstr "Incremental Backup Pool" + +msgid "Differential Backup Pool" +msgstr "Differential Backup Pool" + +msgid "Step 4 - how would you like to backup" +msgstr "Krok 4 - jak chciałbyś backupować" + +msgid "Backup Job directives" +msgstr "Opcje zadania backupowego" + +msgid "Accurate" +msgstr "Tryb dokładny" + +msgid "Step 5 - when would you like to run backup" +msgstr "Krok 5 - kiedy chciałbyć uruchamiać backup" + +msgid "Schedule" +msgstr "Harmonogram zadań" + +msgid "Show Reschedule directives" +msgstr "Pokaż opcje Reschedule" + +msgid "Reschedule setting" +msgstr "Ustawienia Reschedule" + +msgid "Reschedule On Error" +msgstr "Reschedule On Error" + +msgid "Reschedule Incomplete Jobs" +msgstr "Reschedule Incomplete Jobs" + +msgid "Reschedule Interval" +msgstr "Reschedule Interval" + +msgid "Reschedule Times" +msgstr "Reschedule Times" + +msgid "What" +msgstr "Co" + +msgid "Maximum Concurrent Jobs" +msgstr "Maximum Concurrent Jobs" + +msgid "Priority" +msgstr "Priorytet" + +msgid "ReRun Failed Levels" +msgstr "ReRun Failed Levels" + +msgid "General" +msgstr "Ogólne" + +msgid "How" +msgstr "Jak" + +msgid "When" +msgstr "Kiedy" + +msgid "Other directives" +msgstr "Inne dyrektywy" + +msgid "Messages" +msgstr "Messages" + +msgid "Step 6 - summary" +msgstr "Krok 6 - podsumowanie" + +msgid "Add new Pool" +msgstr "Dodaj nową Pulę wolumenów" + +msgid "New Pool" +msgstr "Nowa Pula wolumenów" + +msgid "Add new Schedule" +msgstr "Dodaj nowy harmonogram zadań" + +msgid "New Schedule" +msgstr "Nowy harmonogram zadań" + +msgid "Summary" +msgstr "Podsumowanie" + +msgid "inherited from JobDefs" +msgstr "odziedziczone z JobDefs" + +msgid "Create job" +msgstr "Stwórz zadanie" + +msgid "Please select Client" +msgstr "Proszę wybrać Klienta" + +msgid "Go to path" +msgstr "Idź do lokalizacji" + +msgid "Add new include path" +msgstr "Dodaj nową lokalizację listy dołączeń" + +msgid "Add new global exclude path" +msgstr "Dodaj nową globalną lokalizację do listy wykluczeń" + +msgid "Include files to FileSet" +msgstr "Dołącz pliki do FileSet" + +msgid "Apply file selection" +msgstr "Zastosuj wybrane pliki" diff --git a/gui/baculum/protected/Web/Lang/pt/messages.mo b/gui/baculum/protected/Web/Lang/pt/messages.mo index a7ec95d03..322093e98 100644 Binary files a/gui/baculum/protected/Web/Lang/pt/messages.mo and b/gui/baculum/protected/Web/Lang/pt/messages.mo differ diff --git a/gui/baculum/protected/Web/Lang/pt/messages.po b/gui/baculum/protected/Web/Lang/pt/messages.po index 695c9fed3..b61149f08 100644 --- a/gui/baculum/protected/Web/Lang/pt/messages.po +++ b/gui/baculum/protected/Web/Lang/pt/messages.po @@ -1850,3 +1850,194 @@ msgstr "Error code:" msgid "Message:" msgstr "Message:" +msgid "Include" +msgstr "Include" + +msgid "Exclude" +msgstr "Exclude" + +msgid "Step 1 - select job type" +msgstr "Step 1 - select job type" + +msgid "New job wizard" +msgstr "New job wizard" + +msgid "This wizard enables you to create in easy way a new job." +msgstr "This wizard enables you to create in easy way a new job." + +msgid "To start please select job type that you wish to create." +msgstr "To start please select job type that you wish to create." + +msgid "Job Type" +msgstr "Job Type" + +msgid "Job Name" +msgstr "Job Name" + +msgid "JobDefs" +msgstr "JobDefs" + +msgid "Step 2 - what you want to backup" +msgstr "Step 2 - what you want to backup" + +msgid "Client and FileSet" +msgstr "Client and FileSet" + +msgid "Please select Client from which you want to backup data and please select FileSet with files/directories definition to backup." +msgstr "Please select Client from which you want to backup data and please select FileSet with files/directories definition to backup." + +msgid "If your FileSet for this backup does not exist yet, you can create it." +msgstr "If your FileSet for this backup does not exist yet, you can create it." + +msgid "FileSet" +msgstr "FileSet" + +msgid "Add new FileSet" +msgstr "Add new FileSet" + +msgid "New FileSet" +msgstr "New FileSet" + +msgid "Step 3 - where you want to backup" +msgstr "Step 3 - where you want to backup" + +msgid "Storage and Pool" +msgstr "Storage and Pool" + +msgid "Please select Storage where you want to backup data and please select Pool with volumes which Storage will use to store backup data." +msgstr "Please select Storage where you want to backup data and please select Pool with volumes which Storage will use to store backup data." + +msgid "Storage" +msgstr "Storage" + +msgid "Show Storage directives for Job" +msgstr "Show Storage directives for Job" + +msgid "Spool setting" +msgstr "Spool setting" + +msgid "Spool Data" +msgstr "Spool Data" + +msgid "Spool Attributes" +msgstr "Spool Attributes" + +msgid "Spool Size" +msgstr "Spool Size" + +msgid "Show Pool directives for Job" +msgstr "Show Pool directives for Job" + +msgid "Pool setting" +msgstr "Pool setting" + +msgid "Full Backup Pool" +msgstr "Full Backup Pool" + +msgid "Incremental Backup Pool" +msgstr "Incremental Backup Pool" + +msgid "Differential Backup Pool" +msgstr "Differential Backup Pool" + +msgid "Step 4 - how would you like to backup" +msgstr "Step 4 - how would you like to backup" + +msgid "Backup Job directives" +msgstr "Backup Job directives" + +msgid "Accurate" +msgstr "Accurate" + +msgid "Step 5 - when would you like to run backup" +msgstr "Step 5 - when would you like to run backup" + +msgid "Schedule" +msgstr "Schedule" + +msgid "Show Reschedule directives" +msgstr "Show Reschedule directives" + +msgid "Reschedule setting" +msgstr "Reschedule setting" + +msgid "Reschedule On Error" +msgstr "Reschedule On Error" + +msgid "Reschedule Incomplete Jobs" +msgstr "Reschedule Incomplete Jobs" + +msgid "Reschedule Interval" +msgstr "Reschedule Interval" + +msgid "Reschedule Times" +msgstr "Reschedule Times" + +msgid "What" +msgstr "What" + +msgid "Maximum Concurrent Jobs" +msgstr "Maximum Concurrent Jobs" + +msgid "Priority" +msgstr "Priority" + +msgid "ReRun Failed Levels" +msgstr "ReRun Failed Levels" + +msgid "General" +msgstr "General" + +msgid "How" +msgstr "How" + +msgid "When" +msgstr "When" + +msgid "Other directives" +msgstr "Other directives" + +msgid "Messages" +msgstr "Messages" + +msgid "Step 6 - summary" +msgstr "Step 6 - summary" + +msgid "Add new Pool" +msgstr "Add new Pool" + +msgid "New Pool" +msgstr "New Pool" + +msgid "Add new Schedule" +msgstr "Add new Schedule" + +msgid "New Schedule" +msgstr "New Schedule" + +msgid "Summary" +msgstr "Summary" + +msgid "inherited from JobDefs" +msgstr "inherited from JobDefs" + +msgid "Create job" +msgstr "Create job" + +msgid "Please select Client" +msgstr "Please select Client" + +msgid "Go to path" +msgstr "Go to path" + +msgid "Add new include path" +msgstr "Add new include path" + +msgid "Add new global exclude path" +msgstr "Add new global exclude path" + +msgid "Include files to FileSet" +msgstr "Include files to FileSet" + +msgid "Apply file selection" +msgstr "Apply file selection" diff --git a/gui/baculum/protected/Web/Layouts/Wizard.tpl b/gui/baculum/protected/Web/Layouts/Wizard.tpl index 7342b8d15..bf043972d 100644 --- a/gui/baculum/protected/Web/Layouts/Wizard.tpl +++ b/gui/baculum/protected/Web/Layouts/Wizard.tpl @@ -15,8 +15,12 @@ /> /> /> + /> + diff --git a/gui/baculum/protected/Web/Pages/FileSetView.php b/gui/baculum/protected/Web/Pages/FileSetView.php index 4056dc162..275609118 100644 --- a/gui/baculum/protected/Web/Pages/FileSetView.php +++ b/gui/baculum/protected/Web/Pages/FileSetView.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2018 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -38,6 +38,13 @@ class FileSetView extends BaculumWebPage { if ($this->Request->contains('fileset')) { $this->setFileSetName($this->Request['fileset']); } + } + + public function onLoad($param) { + parent::onLoad($param); + if ($this->getPage()->IsCallBack || $this->getPage()->IsPostBack) { + return; + } if (!empty($_SESSION['dir'])) { $this->FileSetConfig->setComponentName($_SESSION['dir']); $this->FileSetConfig->setResourceName($this->getFileSetName()); diff --git a/gui/baculum/protected/Web/Pages/JobHistoryList.page b/gui/baculum/protected/Web/Pages/JobHistoryList.page index 36a1e1dbb..ea18d5763 100644 --- a/gui/baculum/protected/Web/Pages/JobHistoryList.page +++ b/gui/baculum/protected/Web/Pages/JobHistoryList.page @@ -8,6 +8,7 @@
+
+ +<%@ MasterClass="Application.Web.Layouts.Wizard" Theme="Baculum-v2"%> -
-
-
+
+
+
+
+
+

+
-
-
+
+
+
+
+

+
-
-
+
+
+
+
+

+
-
-
+
+
+
+
+
+
+

+
-
-
+
+
+
+
+

+
-
-
+
+
+
+
+

+
-
<%=$this->Parent->ActiveStep->Title%>
+

<%=$this->Parent->ActiveStep->Title%>

-
-
+
+ +  <%[ Cancel ]%> + + + <%[ Next ]%>  + +
-
-
- - +
+ +  <%[ Cancel ]%> + + +  <%[ Previous ]%> + + + <%[ Next ]%>  +
-
-
- - +
+ +  <%[ Previous ]%> + + + <%[ Create job ]%>   + +
-

<%[ New job wizard ]%>

-

<%[ This wizard enables you to create in easy way new job. ]%>

-

<%[ To start, please select job type that you wish to create. ]%>

-
-
-
- - - - - - - - -
+
+

<%[ New job wizard ]%>

+

<%[ This wizard enables you to create in easy way a new job. ]%>

+ +
+
+ +
+
+ +
+
+
-
-
-
- - +
+

<%[ Client and FileSet ]%>

+

<%[ Please select Client from which you want to backup data and please select FileSet with files/directories definition to backup. ]%>

+

<%[ If your FileSet for this backup does not exist yet, you can create it. ]%>

+
+
+ +
+ + + + + + +
+

<%[ Storage and Pool ]%>

+

<%[ Please select Storage where you want to backup data and please select Pool with volumes which Storage will use to store backup data. ]%>

+
+ + + + + + +
- + +
+

<%[ Backup Job directives ]%>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+

<%[ Other directives ]%>

+
+
+ +
+
+ +
+

<%[ Schedule ]%>

+
+ + + + + +
+
- <%[ Source parameters ]%> -
-
<%[ Backup data from client: ]%>
-
<%=$this->BackupClientName->SelectedValue%>
+ <%[ General ]%> +
+
<%[ Job Type ]%>
+
<%=$this->Type->getDirectiveValue()%>
-
-
<%[ Backup selection method: ]%>
-
- - -
+
+
<%[ Job Name ]%>
+
<%=$this->Name->getDirectiveValue()%>
-
-
<%[ Backup for restore: ]%>
-
- -
+
+
<%[ JobDefs ]%>
+
<%=$this->JobDefs->getDirectiveValue()%>
- -
-
<%[ FileSet for restore: ]%>
-
<%=$this->GroupBackupFileSet->SelectedValue%>
-
-
- <%[ Files for restore ]%> -
-
<%[ Selected directories count: ]%>
-
<%=isset($this->getRestoreElements(true)->dirid) ? count($this->getRestoreElements(true)->dirid) : '0'%>
+ <%[ What ]%> +
+
<%[ Client ]%>
+
+ <%=$this->Client->getDirectiveValue()%> + <%=$this->isInJobDefs('Client', $this->Client->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
-
-
<%[ Selected files count: ]%>
-
<%=isset($this->getRestoreElements(true)->fileid) ? count($this->getRestoreElements(true)->fileid) : '0'%>
+
+
<%[ FileSet ]%>
+
+ <%=$this->Fileset->getDirectiveValue()%> + <%=$this->isInJobDefs('Fileset', $this->Fileset->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
- <%[ Destination parameters ]%> -
-
<%[ Restore to client: ]%>
-
<%=$this->RestoreClient->SelectedValue%>
+ <%[ Where ]%> +
+
<%[ Storage ]%>
+
+ <%=$this->Storage->getDirectiveValue()%> + <%=$this->isInJobDefs('Storage', $this->Storage->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
+
+
+
<%[ Spool Data ]%>
+
+ <%=$this->SpoolData->getDirectiveValue() ? Prado::localize('Yes') : Prado::localize('No')%> + <%=$this->isInJobDefs('SpoolData', $this->SpoolData->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
-
-
<%[ Restore to path: ]%>
-
<%=$this->RestorePath->Text%>
+
+
<%[ Spool Attributes ]%>
+
+ <%=$this->SpoolAttributes->getDirectiveValue() ? Prado::localize('Yes') : Prado::localize('No')%> + <%=$this->isInJobDefs('SpoolAttributes', $this->SpoolAttributes->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
+
+
+
<%[ Spool Size ]%>
+
+ <%=$this->SpoolSize->getDirectiveValue() ?: '0'%> + <%=$this->isInJobDefs('SpoolSize', $this->SpoolSize->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
+
+
+
<%[ Pool ]%>
+
+ <%=$this->Pool->getDirectiveValue()%> + <%=$this->isInJobDefs('Pool', $this->Pool->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
+
+
+
<%[ Full Backup Pool ]%>
+
+ <%=$this->FullBackupPool->getDirectiveValue() ?: '-'%> + <%=$this->isInJobDefs('FullBackupPool', $this->FullBackupPool->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
+
+
+
<%[ Incremental Backup Pool ]%>
+
+ <%=$this->IncrementalBackupPool->getDirectiveValue() ?: '-'%> + <%=$this->isInJobDefs('IncrementalBackupPool', $this->IncrementalBackupPool->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
+
+
+
<%[ Differential Backup Pool ]%>
+
+ <%=$this->DifferentialBackupPool->getDirectiveValue() ?: '-'%> + <%=$this->isInJobDefs('DifferentialBackupPool', $this->DifferentialBackupPool->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
- <%[ Restore job options ]%> -
-
<%[ Restore job: ]%>
-
<%=$this->RestoreJob->Text%>
+ <%[ How ]%> +
+
<%[ Accurate ]%>
+
+ <%=$this->Accurate->getDirectiveValue() ? Prado::localize('Yes') : Prado::localize('No') %> + <%=$this->isInJobDefs('Accurate', $this->Accurate->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
-
-
<%[ Replace files: ]%>
-
- - - - +
+
<%[ Maximum Concurrent Jobs ]%>
+
+ <%=$this->MaximumConcurrentJobs->getDirectiveValue() ?: $this->MaximumConcurrentJobs->getDefaultValue()%> + <%=$this->isInJobDefs('MaximumConcurrentJobs', $this->MaximumConcurrentJobs->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%>
-
-
<%[ Restore job priority: ]%>
-
<%=$this->RestoreJobPriority->Text%>
+
+
<%[ Priority ]%>
+
+ <%=$this->Priority->getDirectiveValue() ?: $this->Priority->getDefaultValue()%> + <%=$this->isInJobDefs('Priority', $this->Priority->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
-
-
<%[ File relocation option: ]%>
-
- - - +
+
<%[ ReRun Failed Levels ]%>
+
+ <%=$this->ReRunFailedLevels->getDirectiveValue() ? Prado::localize('Yes') : Prado::localize('No') %> + <%=$this->isInJobDefs('ReRunFailedLevels', $this->ReRunFailedLevels->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%>
-
-
-
<%[ Strip prefix: ]%>
-
<%=$this->RestoreStripPrefix->Text%>
+
+
+ <%[ When ]%> +
+
<%[ Schedule ]%>
+
+ <%=$this->Schedule->getDirectiveValue() ?: '-' %> + <%=$this->isInJobDefs('Schedule', $this->Schedule->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%>
-
-
<%[ Add prefix: ]%>
-
<%=$this->RestoreAddPrefix->Text%>
+
+
+
<%[ Reschedule On Error ]%>
+
+ <%=$this->RescheduleOnError->getDirectiveValue() ? Prado::localize('Yes') : Prado::localize('No') %> + <%=$this->isInJobDefs('RescheduleOnError', $this->RescheduleOnError->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%>
-
-
<%[ Add suffix: ]%>
-
<%=$this->RestoreAddSuffix->Text%>
+
+
+
<%[ Reschedule Incomplete Jobs ]%>
+
+ <%=$this->RescheduleIncompleteJobs->getDirectiveValue() ? Prado::localize('Yes') : Prado::localize('No') %> + <%=$this->isInJobDefs('RescheduleIncompleteJobs', $this->RescheduleIncompleteJobs->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
+
+
+
<%[ Reschedule Interval ]%>
+
+ <%=$this->RescheduleInterval->getDirectiveValue()%> + <%=$this->isInJobDefs('RescheduleInterval', $this->RescheduleInterval->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%>
-
-
-
<%[ RegexWhere: ]%>
-
<%=$this->RestoreRegexWhere->Text%>
+
+
<%[ Reschedule Times ]%>
+
+ <%=$this->RescheduleTimes->getDirectiveValue() ?: '-'%> + <%=$this->isInJobDefs('RescheduleTimes', $this->RescheduleTimes->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%> +
+
+
+
+ <%[ Other directives ]%> +
+
<%[ Messages ]%>
+
+ <%=$this->Messages->getDirectiveValue() ?: '-'%> + <%=$this->isInJobDefs('Messages', $this->Messages->getDirectiveValue()) ? ' (' . Prado::localize('inherited from JobDefs') . ')': ''%>
+
+ diff --git a/gui/baculum/protected/Web/Pages/NewJobWizard.php b/gui/baculum/protected/Web/Pages/NewJobWizard.php index 86655a201..ade0e2046 100644 --- a/gui/baculum/protected/Web/Pages/NewJobWizard.php +++ b/gui/baculum/protected/Web/Pages/NewJobWizard.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2018 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -21,46 +21,475 @@ */ Prado::using('Application.Web.Class.BaculumWebPage'); +Prado::using('System.Web.UI.ActiveControls.TActiveLabel'); Prado::using('System.Web.UI.WebControls.TWizard'); class NewJobWizard extends BaculumWebPage { + const PREV_STEP = 'PrevStep'; + const JOBDEFS = 'JobDefs'; + + public function onLoad($param) { + parent::onLoad($param); + $this->JobDefs->saveDirective(); + $this->Client->saveDirective(); + $this->Fileset->saveDirective(); + $this->Storage->saveDirective(); + $this->Pool->saveDirective(); + $this->FullBackupPool->saveDirective(); + $this->IncrementalBackupPool->saveDirective(); + $this->DifferentialBackupPool->saveDirective(); + $this->Messages->saveDirective(); + $this->Schedule->saveDirective(); + } + + public function onLoadComplete($param) { + parent::onLoadComplete($param); + $step_index = $this->NewJobWizard->getActiveStepIndex(); + $prev_step = $this->getPrevStep(); + $this->setPrevStep($step_index); + if ($prev_step > $step_index) { + return; + } + switch ($step_index) { + case 0: { + $this->loadJobTypes(); + $this->loadJobDefs(); + break; + } + case 1: { + $this->setupJobDefs(); + $this->loadClients(); + $this->loadFilesets(); + $this->loadNewFilesetForm(); + break; + } + case 2: { + $this->loadStorages(); + $this->loadNewPoolForm(); + $this->loadPools(); + break; + } + case 3: { + $this->loadBackupJobDirectives(); + $this->loadMessages(); + break; + } + case 4: { + $this->loadRescheduleDirectives(); + $this->loadNewScheduleForm(); + $this->loadSchedules(); + break; + } + } + } + + /** + * Wizard previous button callback actions. + * + * @param TWizard $sender sender object + * @param TWizardNavigationEventParameter $param sender parameters + * @return none + */ + public function wizardPrev($sender, $param) { + } + + /** + * Wizard next button callback actions. + * + * @param TWizard $sender sender object + * @param TWizardNavigationEventParameter $param sender parameters + * @return none + */ + public function wizardNext($sender, $param) { + } + + /** + * Load Job Types (step 1). + * + * @return none + */ + public function loadJobTypes() { + //$jobtype_list = array('Backup', 'Restore', 'Copy', 'Verify', 'Migrate', 'Admin'); + $jobtype_list = array('Backup'); + asort($jobtype_list); + $this->Type->setData($jobtype_list); + $this->Type->setDirectiveValue($jobtype_list[0]); + $this->Type->onLoad(null); + } + + /** + * Load JobDefs (step 1). + * + * @return none + */ + public function loadJobDefs() { + $jobdefs_list = array(); + $jobdefs = $this->getModule('api')->get(array('config', 'dir', 'jobdefs'))->output; + for ($i = 0; $i < count($jobdefs); $i++) { + $jobdefs_list[] = $jobdefs[$i]->JobDefs->Name; + } + asort($jobdefs_list); + $this->JobDefs->setData($jobdefs_list); + $this->JobDefs->onLoad(null); + } + + /** + * Setup and remember selected JobDefs values to use in next wizard steps. + * + * @return none + */ + public function setupJobDefs() { + $directive_value = $this->JobDefs->getDirectiveValue(); + if (is_null($directive_value)) { + return; + } + $jobdefs = rawurlencode($directive_value); + $result = $this->getModule('api')->get(array( + 'config', 'dir', 'jobdefs', $jobdefs + )); + if ($result->error === 0) { + $value = (array)$result->output; + $this->setJobDefs($value); + } + } + + public function isInJobDefs($directive_name, $directive_value) { + $jobdefs = $this->getJobDefs(); + $ret = false; + if ($directive_name === 'Storage') { + $ret = (key_exists($directive_name, $jobdefs) && $jobdefs[$directive_name][0] === $directive_value); + } else { + $ret = (key_exists($directive_name, $jobdefs) && $jobdefs[$directive_name] === $directive_value); + } + return $ret; + } + /** * Load client list (step 2). * - * @param TActiveDropDownList $sender sender object - * @param TCommandParameter $param parameters object * @return none */ - public function loadClients($sender, $param) { + public function loadClients() { $client_list = array(); $clients = $this->getModule('api')->get(array('clients'))->output; for ($i = 0; $i < count($clients); $i++) { $client_list[$clients[$i]->name] = $clients[$i]->name; } asort($client_list); - $this->Client->dataSource = $client_list; - $this->Client->dataBind(); + $this->Client->setData($client_list); + $jobdefs = $this->getJobDefs(); + if (key_exists('Client', $jobdefs) && is_null($this->Client->getDirectiveValue())) { + $this->Client->setDirectiveValue($jobdefs['Client']); + } + $this->Client->onLoad(null); } /** - * Wizard next button callback actions. + * Load fileset list (step 2). * - * @param TWizard $sender sender object - * @param TWizardNavigationEventParameter $param sender parameters * @return none */ - public function wizardNext($sender, $param) { + public function loadFilesets() { + $fileset_list = array(); + $filesets = $this->getModule('api')->get(array('config', 'dir', 'fileset'))->output; + for ($i = 0; $i < count($filesets); $i++) { + $fileset_list[] = $filesets[$i]->Fileset->Name; + } + asort($fileset_list); + $this->Fileset->setData($fileset_list); + $jobdefs = $this->getJobDefs(); + if (key_exists('Fileset', $jobdefs) && is_null($this->Fileset->getDirectiveValue())) { + $this->Fileset->setDirectiveValue($jobdefs['Fileset']); + } + $this->Fileset->onLoad(null); } /** - * Wizard prev button callback actions. + * Load new fileset form. * - * @param TWizard $sender sender object - * @param TWizardNavigationEventParameter $param sender parameters * @return none */ - public function wizardPrev($sender, $param) { + public function loadNewFilesetForm() { + if ($this->IsCallBack) { + return; + } + if (!empty($_SESSION['dir'])) { + $this->FilesetConfig->setComponentName($_SESSION['dir']); + $this->FilesetConfig->setLoadValues(false); + $this->FilesetConfig->raiseEvent('OnDirectiveListLoad', $this, null); + } + } + + /** + * Load new pool form. + * + * @return none + */ + public function loadNewPoolForm() { + if ($this->IsCallBack) { + return; + } + if (!empty($_SESSION['dir'])) { + $this->PoolConfig->setComponentName($_SESSION['dir']); + $this->PoolConfig->setLoadValues(false); + $this->PoolConfig->raiseEvent('OnDirectiveListLoad', $this, null); + } + } + + /** + * Load new schedule form. + * + * @return none + */ + public function loadNewScheduleForm() { + if ($this->IsCallBack) { + return; + } + if (!empty($_SESSION['dir'])) { + $this->ScheduleConfig->setComponentName($_SESSION['dir']); + $this->ScheduleConfig->setLoadValues(false); + $this->ScheduleConfig->raiseEvent('OnDirectiveListLoad', $this, null); + } + } + + /** + * Load storage list (step 2). + * + * @return none + */ + public function loadStorages() { + $storage_list = array(); + $storages = $this->getModule('api')->get(array('config', 'dir', 'storage'))->output; + for ($i = 0; $i < count($storages); $i++) { + $storage_list[] = $storages[$i]->Storage->Name; + } + asort($storage_list); + $this->Storage->setData($storage_list); + $jobdefs = $this->getJobDefs(); + if (key_exists('Storage', $jobdefs) && is_array($jobdefs['Storage']) && count($jobdefs['Storage']) == 1 && is_null($this->Storage->getDirectiveValue())) { + $this->Storage->setDirectiveValue($jobdefs['Storage'][0]); + } + $this->Storage->onLoad(null); + if (key_exists('SpoolData', $jobdefs) && is_null($this->SpoolData->getDirectiveValue())) { + $this->SpoolData->setDirectiveValue($jobdefs['SpoolData']); + $this->SpoolData->createDirective(); + } + if (key_exists('SpoolAttributes', $jobdefs) && is_null($this->SpoolAttributes->getDirectiveValue())) { + $this->SpoolAttributes->setDirectiveValue($jobdefs['SpoolAttributes']); + $this->SpoolAttributes->createDirective(); + } + if (key_exists('SpoolSize', $jobdefs) && is_null($this->SpoolSize->getDirectiveValue())) { + $this->SpoolSize->setDirectiveValue($jobdefs['SpoolSize']); + $this->SpoolSize->createDirective(); + } + } + + /** + * Load pool list (step 2). + * + * @return none + */ + public function loadPools() { + $pool_list = array(); + $pools = $this->getModule('api')->get(array('config', 'dir', 'pool'))->output; + for ($i = 0; $i < count($pools); $i++) { + $pool_list[] = $pools[$i]->Pool->Name; + } + asort($pool_list); + $this->Pool->setData($pool_list); + $jobdefs = $this->getJobDefs(); + $this->FullBackupPool->setData($pool_list); + if (key_exists('FullBackupPool', $jobdefs) && is_null($this->FullBackupPool->getDirectiveValue())) { + $this->FullBackupPool->setDirectiveValue($jobdefs['FullBackupPool']); + } + $this->FullBackupPool->onLoad(null); + $this->IncrementalBackupPool->setData($pool_list); + if (key_exists('IncrementalBackupPool', $jobdefs) && is_null($this->IncrementalBackupPool->getDirectiveValue())) { + $this->IncrementalBackupPool->setDirectiveValue($jobdefs['IncrementalBackupPool']); + } + $this->IncrementalBackupPool->onLoad(null); + $this->DifferentialBackupPool->setData($pool_list); + if (key_exists('DifferentialBackupPool', $jobdefs) && is_null($this->DifferentialBackupPool->getDirectiveValue())) { + $this->DifferentialBackupPool->setDirectiveValue($jobdefs['DifferentialBackupPool']); + } + $this->DifferentialBackupPool->onLoad(null); + if (key_exists('Pool', $jobdefs) && is_null($this->Pool->getDirectiveValue())) { + $this->Pool->setDirectiveValue($jobdefs['Pool']); + } + $this->Pool->onLoad(null); + } + + public function loadBackupJobDirectives() { + $jobdefs = $this->getJobDefs(); + if (key_exists('Accurate', $jobdefs) && is_null($this->Accurate->getDirectiveValue())) { + $this->Accurate->setDirectiveValue($jobdefs['Accurate']); + $this->Accurate->createDirective(); + } + if (key_exists('MaximumConcurrentJobs', $jobdefs) && is_null($this->MaximumConcurrentJobs->getDirectiveValue())) { + $this->MaximumConcurrentJobs->setDirectiveValue($jobdefs['MaximumConcurrentJobs']); + $this->MaximumConcurrentJobs->createDirective(); + } + if (key_exists('Priority', $jobdefs) && is_null($this->Priority->getDirectiveValue())) { + $this->Priority->setDirectiveValue($jobdefs['Priority']); + $this->Priority->createDirective(); + } + if (key_exists('ReRunFailedLevels', $jobdefs) && is_null($this->ReRunFailedLevels->getDirectiveValue())) { + $this->ReRunFailedLevels->setDirectiveValue($jobdefs['ReRunFailedLevels']); + $this->ReRunFailedLevels->createDirective(); + } + } + + public function loadRescheduleDirectives() { + $jobdefs = $this->getJobDefs(); + if (key_exists('RescheduleOnError', $jobdefs) && is_null($this->RescheduleOnError->getDirectiveValue())) { + $this->RescheduleOnError->setDirectiveValue($jobdefs['RescheduleOnError']); + $this->RescheduleOnError->createDirective(); + } + if (key_exists('RescheduleIncompleteJobs', $jobdefs) && is_null($this->RescheduleIncompleteJobs->getDirectiveValue())) { + $this->RescheduleIncompleteJobs->setDirectiveValue($jobdefs['RescheduleIncompleteJobs']); + $this->RescheduleIncompleteJobs->createDirective(); + } + if (key_exists('RescheduleInterval', $jobdefs) && is_null($this->RescheduleInterval->getDirectiveValue())) { + $this->RescheduleInterval->setDirectiveValue($jobdefs['RescheduleInterval']); + $this->RescheduleInterval->createDirective(); + } + if (key_exists('RescheduleTimes', $jobdefs) && is_null($this->RescheduleTimes->getDirectiveValue())) { + $this->RescheduleTimes->setDirectiveValue($jobdefs['RescheduleTimes']); + $this->RescheduleTimes->createDirective(); + } + } + + /** + * Load messages. + * + * @return none + */ + public function loadMessages() { + $message_list = array(); + $messages = $this->getModule('api')->get(array('config', 'dir', 'messages'))->output; + for ($i = 0; $i < count($messages); $i++) { + $message_list[] = $messages[$i]->Messages->Name; + } + asort($message_list); + $this->Messages->setData($message_list); + $jobdefs = $this->getJobDefs(); + if (key_exists('Messages', $jobdefs)) { + $this->Messages->setDirectiveValue($jobdefs['Messages']); + } + $this->Messages->onLoad(null); + } + + /** + * Load schedule. + * + * @return none + */ + public function loadSchedules() { + $schedule_list = array(); + $schedules = $this->getModule('api')->get(array('config', 'dir', 'schedule'))->output; + for ($i = 0; $i < count($schedules); $i++) { + $schedule_list[] = $schedules[$i]->Schedule->Name; + } + asort($schedule_list); + $this->Schedule->setData($schedule_list); + $jobdefs = $this->getJobDefs(); + if (key_exists('Schedule', $jobdefs)) { + $this->Schedule->setDirectiveValue($jobdefs['Schedule']); + } + $this->Schedule->onLoad(null); + } + + public function wizardCompleted($sender, $param) { + $jobdefs = $this->getJobDefs(); + $job = array( + 'Name' => $this->Name->getDirectiveValue(), + 'Type' => $this->Type->getDirectiveValue(), + ); + $jd = $this->JobDefs->getDirectiveValue(); + $directives = array('Client', 'Fileset', 'Storage', 'SpoolData', 'SpoolAttributes', + 'SpoolSize', 'Pool', 'FullBackupPool', 'IncrementalBackupPool', 'DifferentialBackupPool', + 'Accurate', 'MaximumConcurrentJobs', 'Priority', 'ReRunFailedLevels', 'Schedule', + 'RescheduleOnError', 'RescheduleIncompleteJobs', 'RescheduleInterval', 'RescheduleTimes', + 'Messages' + ); + if (is_string($jd)) { + $job['JobDefs'] = $jd; + } + for ($i = 0; $i < count($directives); $i++) { + $val = $this->{$directives[$i]}->getDirectiveValue(); + if (is_null($val)) { + continue; + } + if (is_null($jd) || !$this->isInJobDefs($directives[$i], $val)) { + $job[$directives[$i]] = $val; + } + } + $params = array( + 'config', + 'dir', + 'Job', + $job['Name'] + ); + $result = $this->getModule('api')->set( + $params, + array('config' => json_encode($job)) + ); + if ($result->error === 0) { + $this->getModule('api')->set(array('console'), array('reload')); + $this->goToPage('JobList'); + } else { + $this->CreateResourceErrMsg->Display = 'None'; + $this->CreateResourceErrMsg->Text = ''; + } + } + + /** + * Cancel wizard. + * + * @return none + */ + public function wizardStop($sender, $param) { + $this->goToDefaultPage(); + } + + /** + * Set selected JobDefs values. + * + * @param $jobdefs selected JobDefs values + * @return none + */ + public function setJobDefs($jobdefs) { + $this->setViewState(self::JOBDEFS, $jobdefs); + } + + /** + * Get selected JobDefs values. + * + * @return array selected JobDefs values + */ + public function getJobDefs() { + return $this->getViewState(self::JOBDEFS, array()); + } + + /** + * Set previous wizard step. + * + * @param integer $step previous step number + * @return none + */ + public function setPrevStep($step) { + $step = intval($step); + $this->setViewState(self::PREV_STEP, $step); + } + + /** + * Get previous wizard step. + * + * @return integer previous wizard step + */ + public function getPrevStep() { + return $this->getViewState(self::PREV_STEP); } } ?> diff --git a/gui/baculum/protected/Web/Portlets/BConditional.php b/gui/baculum/protected/Web/Portlets/BConditional.php index dc3621345..b90ee114e 100644 --- a/gui/baculum/protected/Web/Portlets/BConditional.php +++ b/gui/baculum/protected/Web/Portlets/BConditional.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2016 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -20,10 +20,9 @@ * Bacula(R) is a registered trademark of Kern Sibbald. */ -Prado::using('System.Web.UI.TTemplateControl'); -Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); +Prado::using('System.Web.UI.WebControls.TConditional'); -class BConditional extends TTemplateControl implements IActiveControl { +class BConditional extends TTemplateControl implements IDataRenderer, IActiveControl { const BCONDITION = 'BCondition'; const TYPE_TPL_FALSE = 0; @@ -31,6 +30,12 @@ class BConditional extends TTemplateControl implements IActiveControl { private $item_true_template; private $item_false_template; + private $data; + private $creating_children = false; + + public function onInit($param) { + parent::onInit($param); + } public function __construct() { parent::__construct(); @@ -41,49 +46,46 @@ class BConditional extends TTemplateControl implements IActiveControl { return $this->getAdapter()->getBaseActiveControl(); } - public function onLoad($param) { - $this->prepareControlContent(); - parent::onLoad($param); + public function getData() { + return $this->data; } - public function bubbleEvent($sender, $param) { - if ($param instanceof Prado\Web\UI\TCommandEventParameter) { - $this->raiseBubbleEvent($this, $param); - return true; - } else { - return false; - } + public function setData($data) { + $this->data = $data; } - private function createItemInternal($item_type) { - $item = $this->createItem($item_type); - if (!is_null($item)) { - $this->getControls()->add($item); - } - return $item; + public function setCondition($value) { + settype($value, 'bool'); + $this->setViewState(self::BCONDITION, $value); } - protected function createItem($item_type) { - $template = null; - $item = null; - switch ($item_type) { - case self::TYPE_TPL_TRUE: { - $template = $this->getTrueTemplate(); - break; - } - case self::TYPE_TPL_FALSE: { - $template = $this->getFalseTemplate(); - break; + public function getCondition() { + return $this->getViewState(self::BCONDITION); + } + + public function createChildControls() + { + $this->creating_children = true; + $this->dataBindProperties(); + $result = $this->getCondition(); + $true_template = $this->getTrueTemplate(); + $false_template = $this->getFalseTemplate(); + if ($result) { + if ($true_template) { + $true_template->instantiateIn($this->getTemplateControl(), $this); } + } elseif ($false_template) { + $false_template->instantiateIn($this->getTemplateControl(), $this); } - if (!is_null($template)) { - $item = new BConditionalItem; - $item->setItemType($item_type); - $item->setTemplate($template); - $item->setTemplateControl($this); - $item->setData($this->getTemplateControl()); + $this->setData($this->getTemplateControl()); + $this->creating_children = false; + } + + public function addParsedObject($object) + { + if ($this->creating_children) { + parent::addParsedObject($object); } - return $item; } public function getTrueTemplate() { @@ -105,57 +107,5 @@ class BConditional extends TTemplateControl implements IActiveControl { $this->item_false_template = $template; } } - - public function setBCondition($value) { - settype($value, 'bool'); - $this->setViewState(self::BCONDITION, $value); - } - - public function getBCondition() { - return $this->getViewState(self::BCONDITION); - } - - public function dataBind() { - $this->dataBindProperties(); - $this->prepareControlContent(); - } - - public function prepareControlContent() { - if ($this->getBCondition() === true) { - $this->createItemInternal(self::TYPE_TPL_TRUE); - } else { - $this->createItemInternal(self::TYPE_TPL_FALSE); - } - } -} - -class BConditionalItem extends TTemplateControl implements IDataRenderer, INamingContainer { - private $item_type; - private $data; - - public function getItemType() { - return $this->item_type; - } - - public function setItemType($type) { - $this->item_type = $type; - } - - public function getData() { - return $this->data; - } - - public function setData($data) { - $this->data = $data; - } - - public function bubbleEvent($sender,$param) { - if ($param instanceof Prado\Web\UI\TCommandEventParameter) { - $this->raiseBubbleEvent($this, $param); - return true; - } else { - return false; - } - } } ?> diff --git a/gui/baculum/protected/Web/Portlets/BaculaConfigDirectives.php b/gui/baculum/protected/Web/Portlets/BaculaConfigDirectives.php index 3a89fdb64..2ffe7ff88 100644 --- a/gui/baculum/protected/Web/Portlets/BaculaConfigDirectives.php +++ b/gui/baculum/protected/Web/Portlets/BaculaConfigDirectives.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2018 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -24,14 +24,13 @@ Prado::using('System.Web.UI.ActiveControls.TActiveLabel'); Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton'); Prado::using('System.Web.UI.ActiveControls.TActivePanel'); Prado::using('System.Web.UI.ActiveControls.TActiveRepeater'); -Prado::using('Application.Web.Portlets.BConditional'); Prado::using('Application.Web.Portlets.DirectiveListTemplate'); -Prado::using('Application.Web.Portlets.DirectiveBoolean'); +Prado::using('Application.Web.Portlets.DirectiveCheckBox'); Prado::using('Application.Web.Portlets.DirectiveComboBox'); Prado::using('Application.Web.Portlets.DirectiveInteger'); Prado::using('Application.Web.Portlets.DirectiveListBox'); Prado::using('Application.Web.Portlets.DirectiveSize'); -Prado::using('Application.Web.Portlets.DirectiveText'); +Prado::using('Application.Web.Portlets.DirectiveTextBox'); Prado::using('Application.Web.Portlets.DirectiveTimePeriod'); Prado::using('Application.Web.Portlets.DirectiveRunscript'); Prado::using('Application.Web.Portlets.DirectiveMessages'); @@ -48,11 +47,11 @@ class BaculaConfigDirectives extends DirectiveListTemplate { public $resource_names = array(); private $directive_types = array( - 'DirectiveBoolean', + 'DirectiveCheckBox', 'DirectiveComboBox', 'DirectiveInteger', 'DirectiveListBox', - 'DirectiveText', + 'DirectiveTextBox', 'DirectiveSize', 'DirectiveTimePeriod' ); @@ -117,6 +116,7 @@ class BaculaConfigDirectives extends DirectiveListTemplate { )); } } + $data_desc = $this->Application->getModule('data_desc'); $resource_desc = $data_desc->getDescription($component_type, $resource_type); foreach ($resource_desc as $directive_name => $directive_desc) { @@ -164,6 +164,21 @@ class BaculaConfigDirectives extends DirectiveListTemplate { if (is_object($directive_value)) { $directive_value = (array)$directive_value; } + + if ($directive_name === 'Include' || $directive_name === 'Exclude' || $directive_name === 'Runscript') { + // provide all include blocks at once + $directive_value = array(array( + $directive_name => $directive_value, + )); + if (property_exists($config, 'Exclude')) { + $directive_value[0]['Exclude'] = (array)$config->{'Exclude'}; + } + } + + if ($directive_name === 'Exclude') { + continue; + } + foreach ($directive_value as $key => $value) { $directive = array( 'host' => $host, @@ -180,6 +195,8 @@ class BaculaConfigDirectives extends DirectiveListTemplate { 'field_type' => $field_type, 'label' => $directive_name, 'in_config' => $in_config, + 'parent_name' => null, + 'group_name' => null, 'show' => (($in_config || !$load_values) || $this->getShowAllDirectives()) ); array_push($directives, $directive); @@ -269,13 +286,16 @@ class BaculaConfigDirectives extends DirectiveListTemplate { } $directive_name = $controls[$j]->getDirectiveName(); $directive_value = $controls[$j]->getDirectiveValue(); - $default_value = $resource_desc[$directive_name]->DefaultValue; + $default_value = null; + if (key_exists($directive_name, $resource_desc)) { + $default_value = $resource_desc[$directive_name]->DefaultValue; + } $in_config = $controls[$j]->getInConfig(); if (is_null($directive_value)) { // skip not changed values that don't exist in config continue; } - if ($this->directive_types[$i] === 'DirectiveBoolean') { + if ($this->directive_types[$i] === 'DirectiveCheckBox') { settype($default_value, 'bool'); } elseif ($this->directive_types[$i] === 'DirectiveInteger') { settype($directive_value, 'int'); @@ -295,6 +315,9 @@ class BaculaConfigDirectives extends DirectiveListTemplate { if (is_null($directive_value)) { continue; } + if ($directive_name === 'Exclude') { + continue; + } if (!array_key_exists($directive_name, $directives)) { $directives[$directive_name] = array(); } @@ -306,6 +329,11 @@ class BaculaConfigDirectives extends DirectiveListTemplate { $directives[$directive_name] = array(); } $directives[$directive_name] = array_merge($directives[$directive_name], $directive_value[$directive_name]); + } elseif ($this->directive_list_types[$i] === 'DirectiveFileSet') { + if (key_exists('Exclude', $directive_value) && count($directive_value['Exclude']) > 0) { + $directives['Exclude'] = array($directive_value['Exclude']); + } + $directives[$directive_name] = $directive_value[$directive_name]; } elseif (array_key_exists($directive_name, $directive_value)) { $directives[$directive_name][] = $directive_value[$directive_name]; } elseif (count($directive_value) > 0) { diff --git a/gui/baculum/protected/Web/Portlets/BaculaConfigDirectives.tpl b/gui/baculum/protected/Web/Portlets/BaculaConfigDirectives.tpl index 1751c1564..450170c4a 100644 --- a/gui/baculum/protected/Web/Portlets/BaculaConfigDirectives.tpl +++ b/gui/baculum/protected/Web/Portlets/BaculaConfigDirectives.tpl @@ -47,67 +47,8 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-  <%=Prado::localize('Save')%> +  <%=$this->getLoadValues() ? Prado::localize('Save') : Prado::localize('Create')%> $('.save_progress').show(); diff --git a/gui/baculum/protected/Web/Portlets/DirectiveBoolean.php b/gui/baculum/protected/Web/Portlets/DirectiveCheckBox.php similarity index 89% rename from gui/baculum/protected/Web/Portlets/DirectiveBoolean.php rename to gui/baculum/protected/Web/Portlets/DirectiveCheckBox.php index e36dd2e07..c2486ebbd 100644 --- a/gui/baculum/protected/Web/Portlets/DirectiveBoolean.php +++ b/gui/baculum/protected/Web/Portlets/DirectiveCheckBox.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2016 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -24,7 +24,7 @@ Prado::using('System.Web.UI.ActiveControls.TActiveLabel'); Prado::using('System.Web.UI.ActiveControls.TActiveCheckBox'); Prado::using('Application.Web.Portlets.DirectiveTemplate'); -class DirectiveBoolean extends DirectiveTemplate { +class DirectiveCheckBox extends DirectiveTemplate { public function getValue() { // @TODO: Define boolean directive values (yes/no/0/1...etc.) @@ -40,7 +40,7 @@ class DirectiveBoolean extends DirectiveTemplate { $directive_value = $this->getDirectiveValue(); $default_value = $this->getDefaultValue(); settype($default_value, 'bool'); - if ($this->getInConfig() === false) { + if ($this->getInConfig() === false && empty($directive_value)) { $directive_value = $default_value; } $this->Label->Text = $this->getLabel(); diff --git a/gui/baculum/protected/Web/Portlets/DirectiveBoolean.tpl b/gui/baculum/protected/Web/Portlets/DirectiveCheckBox.tpl similarity index 100% rename from gui/baculum/protected/Web/Portlets/DirectiveBoolean.tpl rename to gui/baculum/protected/Web/Portlets/DirectiveCheckBox.tpl diff --git a/gui/baculum/protected/Web/Portlets/DirectiveComboBox.php b/gui/baculum/protected/Web/Portlets/DirectiveComboBox.php index 757052d33..62adbd31b 100644 --- a/gui/baculum/protected/Web/Portlets/DirectiveComboBox.php +++ b/gui/baculum/protected/Web/Portlets/DirectiveComboBox.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2016 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -26,6 +26,18 @@ Prado::using('Application.Web.Portlets.DirectiveTemplate'); class DirectiveComboBox extends DirectiveTemplate { + public function onLoad($param) { + $this->createDirectiveInternal(); + $this->saveDirective(); + parent::onLoad($param); + } + + public function saveDirective() { + $value = $this->getValue(); + $this->Directive->setSelectedValue($value); + $this->setDirectiveValue($value); + } + public function getValue() { $value = $this->Directive->getSelectedValue(); if (!is_string($value) || empty($value)) { @@ -34,7 +46,7 @@ class DirectiveComboBox extends DirectiveTemplate { return $value; } - public function createDirective() { + public function createDirectiveInternal() { $this->Label->Text = $this->getLabel(); $data = $this->getData(); $resource_names = $this->getResourceNames(); @@ -57,7 +69,7 @@ class DirectiveComboBox extends DirectiveTemplate { $directive_value = $this->getDirectiveValue(); $default_value = $this->getDefaultValue(); - if ($in_config === false) { + if ($in_config === false && empty($directive_value)) { if ($default_value !== 0) { $directive_value = $default_value; } else { diff --git a/gui/baculum/protected/Web/Portlets/DirectiveComboBox.tpl b/gui/baculum/protected/Web/Portlets/DirectiveComboBox.tpl index 7ee02e467..217be8b8b 100644 --- a/gui/baculum/protected/Web/Portlets/DirectiveComboBox.tpl +++ b/gui/baculum/protected/Web/Portlets/DirectiveComboBox.tpl @@ -12,7 +12,7 @@ getResourceType(); $directive_name = $this->getDirectiveName(); $directives = $this->getData(); - $data_source = array(); - $include = array(); + $includes = array(); + $file = array(); $exclude = array(); $options = array(); - if (is_object($directives)) { // Include with options - foreach($directives as $name => $values) { - switch($name) { - case 'File': { - $this->setFile($include, $name, $values); - break; - } - case 'Options': { - $this->setOption($options, $name, $values); - break; + if (!is_array($directives) || $directive_name === 'Exclude') { + return; + } + foreach ($directives as $index => $subres) { + if ($index === 'Include') { + for ($i = 0; $i < count($subres); $i++) { + if (is_null($subres[$i])) { + // load page with new fileset to create + continue; } - case 'Exclude': { - $this->setFile($exclude, $name, $values); - break; + foreach ($subres[$i] as $name => $values) { + switch($name) { + case 'File': { + $this->setFile($file, $name, $values); + break; + } + case 'Options': { + $this->setOption($options, $name, $values); + break; + } + } } + $includes[] = array( + 'file' => $file, + 'options' => $options + ); + $file = $options = array(); } + } elseif ($index === 'Exclude') { + if (!key_exists('File', $subres)) { + // empty exclude + continue; + } + // this is exclude + $this->setFile($exclude, 'File', $subres['File']); } } - - $this->RepeaterFileSetOptions->DataSource = $options; - $this->RepeaterFileSetOptions->dataBind(); - $this->RepeaterFileSetInclude->DataSource = $include; - $this->RepeaterFileSetInclude->dataBind(); + $this->RepeaterFileSetIncludes->DataSource = $includes; + $this->RepeaterFileSetIncludes->dataBind(); $this->RepeaterFileSetExclude->DataSource = $exclude; $this->RepeaterFileSetExclude->dataBind(); - $this->FileSetMenu->setComponentType($component_type); - $this->FileSetMenu->setComponentName($component_name); - $this->FileSetMenu->setResourceType($resource_type); - $this->FileSetMenu->setDirectiveName($directive_name); + $this->FSBrowser->loadClients(null, null); } private function setFile(&$files, $name, $config) { @@ -91,6 +104,10 @@ class DirectiveFileSet extends DirectiveListTemplate { $component_name = $this->getComponentName(); $resource_type = $this->getResourceType(); $resource_name = $this->getResourceName(); + $directive_name = 'File'; + $field_type = 'TextBox'; + $default_value = ''; + $required = false; for ($i = 0; $i < count($config); $i++) { $files[] = array( @@ -101,12 +118,23 @@ class DirectiveFileSet extends DirectiveListTemplate { 'resource_name' => $resource_name, 'directive_name' => $name, 'directive_value' => $config[$i], - 'parent_name' => $name + 'parent_name' => $name, + 'field_type' => $field_type, + 'default_value' => $default_value, + 'required' => $required, + 'data' => null, + 'resource' => null, + 'in_config' => true, + 'label' => $directive_name, + 'show' => true, + 'parent_name' => $name, + 'group_name' => $i ); } } private function setOption(&$options, $name, $config) { + $misc = $this->getModule('misc'); $load_values = $this->getLoadValues(); $host = $this->getHost(); $component_type = $this->getComponentType(); @@ -118,10 +146,17 @@ class DirectiveFileSet extends DirectiveListTemplate { for ($i = 0; $i < count($config); $i++) { foreach ($resource_desc->SubSections as $directive_name => $directive_desc) { - $in_config = property_exists($config[$i], $directive_name); + if ($directive_name == 'File') { + // In options block File cannot be defined + continue; + } + if (is_object($config[$i])) { + $config[$i] = (array)$config[$i]; + } + $in_config = key_exists($directive_name, $config[$i]); $directive_value = null; if ($in_config === true) { - $directive_value = $config[$i]->{$directive_name}; + $directive_value = $config[$i][$directive_name]; } $default_value = null; @@ -142,6 +177,17 @@ class DirectiveFileSet extends DirectiveListTemplate { $field_type = $directive_desc->FieldType; } } + if ($field_type === 'CheckBox') { + /** + * It is because bdirjson returns FileSet options boolean values + * as Yes/No instead of returning true/false as it does for the rest. + */ + if ($misc->isValidBooleanTrue($directive_value)) { + $directive_value = true; + } else if ($misc->isValidBooleanFalse($directive_value)) { + $directive_value = false; + } + } if (!is_array($directive_value)) { $directive_value = array($directive_value); } @@ -156,6 +202,7 @@ class DirectiveFileSet extends DirectiveListTemplate { 'directive_value' => $directive_value[$j], 'default_value' => $default_value, 'required' => $required, + 'resource' => null, 'data' => $data, 'field_type' => $field_type, 'in_config' => $in_config, @@ -170,54 +217,64 @@ class DirectiveFileSet extends DirectiveListTemplate { } public function getDirectiveValue() { - $directive_values = array(); + $directive_values = array('Include' => array(), 'Exclude' => array()); $component_type = $this->getComponentType(); $resource_type = $this->getResourceType(); $resource_desc = $this->Application->getModule('data_desc')->getDescription($component_type, $resource_type); - for ($i = 0; $i < count($this->directive_types); $i++) { - $controls = $this->RepeaterFileSetOptions->findControlsByType($this->directive_types[$i]); - for ($j = 0; $j < count($controls); $j++) { - $directive_name = $controls[$j]->getDirectiveName(); - $directive_value = $controls[$j]->getDirectiveValue(); - $index = $controls[$j]->getGroupName(); - $default_value = $resource_desc['Include']->SubSections->{$directive_name}->DefaultValue; - $in_config = $controls[$j]->getInConfig(); - if (is_null($directive_value)) { - // option not set or removed - continue; - } - if ($this->directive_types[$i] === 'DirectiveBoolean') { - settype($default_value, 'bool'); - } - if ($directive_value === $default_value) { - // value the same as default value, skip it - continue; - } - if (!array_key_exists('Include', $directive_values)) { - $directive_values['Include'] = array('Options' => array()); - } - if (!isset($directive_values['Include']['Options'][$index])) { - $directive_values['Include']['Options'][$index] = array(); - } - $directive_values['Include']['Options'][$index][$directive_name] = $directive_value; - } - $controls = $this->RepeaterFileSetInclude->findControlsByType($this->directive_types[$i]); - for ($j = 0; $j < count($controls); $j++) { - $directive_name = $controls[$j]->getDirectiveName(); - $directive_value = $controls[$j]->getDirectiveValue(); - if (is_null($directive_value)) { - // Include file directive removed - continue; - } - if (!array_key_exists('Include', $directive_values)) { - $directive_values['Include'] = array(); + $counter = 0; + $ctrls = $this->RepeaterFileSetIncludes->getItems(); + foreach ($ctrls as $value) { + for ($i = 0; $i < count($this->directive_types); $i++) { + $controls = $value->RepeaterFileSetOptions->findControlsByType($this->directive_types[$i]); + for ($j = 0; $j < count($controls); $j++) { + $directive_name = $controls[$j]->getDirectiveName(); + $directive_value = $controls[$j]->getDirectiveValue(); + $index = $controls[$j]->getGroupName(); + $default_value = $resource_desc['Include']->SubSections->{$directive_name}->DefaultValue; + $in_config = $controls[$j]->getInConfig(); + if (is_null($directive_value)) { + // option not set or removed + continue; + } + if ($this->directive_types[$i] === 'DirectiveCheckBox') { + settype($default_value, 'bool'); + } + if ($directive_value === $default_value) { + // value the same as default value, skip it + continue; + } + if (!isset($counter, $directive_values['Include'][$counter])) { + $directive_values['Include'][$counter] = array('Options' => array()); + } + if (!isset($directive_values['Include'][$counter]['Options'][$index])) { + $directive_values['Include'][$counter]['Options'][$index] = array(); + } + $directive_values['Include'][$counter]['Options'][$index][$directive_name] = $directive_value; } - if (!array_key_exists($directive_name, $directive_values['Include'])) { - $directive_values['Include'][$directive_name] = array(); + $controls = $value->RepeaterFileSetInclude->findControlsByType($this->directive_types[$i]); + for ($j = 0; $j < count($controls); $j++) { + $directive_name = $controls[$j]->getDirectiveName(); + $directive_value = $controls[$j]->getDirectiveValue(); + if (empty($directive_value)) { + // Include file directive removed + continue; + } + if (!key_exists('Include', $directive_values)) { + $directive_values['Include'] = array(); + } + if (!isset($directive_values['Include'][$counter])) { + $directive_values['Include'][$counter] = array(); + } + if (!key_exists($directive_name, $directive_values['Include'][$counter])) { + $directive_values['Include'][$counter][$directive_name] = array(); + } + array_push($directive_values['Include'][$counter][$directive_name], $directive_value); } - array_push($directive_values['Include'][$directive_name], $directive_value); } + $counter++; + } + for ($i = 0; $i < count($this->directive_types); $i++) { $controls = $this->RepeaterFileSetExclude->findControlsByType($this->directive_types[$i]); for ($j = 0; $j < count($controls); $j++) { $directive_name = $controls[$j]->getDirectiveName(); @@ -226,43 +283,21 @@ class DirectiveFileSet extends DirectiveListTemplate { // Exclude file directive removed continue; } - if (!array_key_exists($directive_name, $directive_values)) { - $directive_values[$directive_name] = array('File' => array()); + if (!key_exists('File', $directive_values['Exclude'])) { + $directive_values['Exclude']['File'] = array(); } - array_push($directive_values[$directive_name]['File'], $directive_value); + array_push($directive_values['Exclude']['File'], $directive_value); } } - return $directive_values; } - public function createFileSetOptions($sender, $param) { - $load_values = $this->getLoadValues(); - $bconditionals = $this->RepeaterFileSetOptions->findControlsByType('BConditionalItem'); - for ($i = 0; $i < count($bconditionals); $i++) { - $item = $bconditionals[$i]->getData(); - for ($j = 0; $j < count($this->directive_types); $j++) { - $control = $this->getChildControl($item, $this->directive_types[$j]); - if (is_object($control)) { - $control->setHost($item->Data['host']); - $control->setComponentType($item->Data['component_type']); - $control->setComponentName($item->Data['component_name']); - $control->setResourceType($item->Data['resource_type']); - $control->setResourceName($item->Data['resource_name']); - $control->setDirectiveName($item->Data['directive_name']); - $control->setDirectiveValue($item->Data['directive_value']); - $control->setDefaultValue($item->Data['default_value']); - $control->setRequired($item->Data['required']); - $control->setData($item->Data['data']); - $control->setLabel($item->Data['label']); - $control->setInConfig($item->Data['in_config']); - $show_all_directives = ($item->Data['in_config'] || !$load_values || $this->SourceTemplateControl->getShowAllDirectives()); - $control->setShow($show_all_directives); - $control->setParentName($item->Data['parent_name']); - $control->setGroupName($item->Data['group_name']); - } - } - } + public function createFileSetIncludes($sender, $param) { + $param->Item->RepeaterFileSetOptions->DataSource = $param->Item->Data['options']; + $param->Item->RepeaterFileSetOptions->dataBind(); + $param->Item->RepeaterFileSetInclude->DataSource = $param->Item->Data['file']; + $param->Item->RepeaterFileSetInclude->dataBind(); + $param->Item->FileSetFileOptMenu->setItemIndex($param->Item->getItemIndex()); } public function createFileSetIncExcElement($sender, $param) { @@ -285,59 +320,81 @@ class DirectiveFileSet extends DirectiveListTemplate { } } - private function getDirectiveData() { - $values = $this->getDirectiveValue(); - $data = array(); - if (array_key_exists('Include', $values) && array_key_exists('File', $values['Include'])) { - $data['File'] = $values['Include']['File']; - if (array_key_exists('Options', $values['Include']) && is_array($values['Include']['Options'])) { - $data['Options'] = array(); - for ($i = 0; $i < count($values['Include']['Options']); $i++) { - $data['Options'][$i] = (object)$values['Include']['Options'][$i]; - } - } - } - if (array_key_exists('Exclude', $values) && array_key_exists('File', $values['Exclude'])) { - $data['Exclude'] = $values['Exclude']['File']; - } - return $data; + public function newIncludeBlock($sender, $param) { + $data = $this->getDirectiveValue(); + $data['Include'][] = array(); + $this->setData($data); + $this->loadConfig(null, null); } public function newIncludeFile($sender, $param) { - $data = $this->getDirectiveData(); - if (array_key_exists('File', $data) && is_array($data['File'])) { - $data['File'][] = ''; - } else { - $data['File'] = array(''); + $data = $this->getDirectiveValue(); + $inc_index = $sender->Parent->getItemIndex(); + $file_index = 0; + if (key_exists($inc_index, $data['Include']) && key_exists('File', $data['Include'][$inc_index])) { + $file_index = count($data['Include'][$inc_index]['File']); } - $data = (object)$data; + $data['Include'][$inc_index]['File'][$file_index] = ''; $this->setData($data); $this->loadConfig(null, null); } public function newExcludeFile($sender, $param) { - $data = $this->getDirectiveData(); - if (array_key_exists('Exclude', $data) && is_array($data['Exclude'])) { - $data['Exclude'][] = ''; + $data = $this->getDirectiveValue(); + $file_index = 0; + if (key_exists('Exclude', $data) && is_array($data['Exclude']) && key_exists('File', $data['Exclude'])) { + $file_index = count($data['Exclude']['File']); } else { - $data['Exclude'] = array(''); + $data['Exclude'] = array('File' => array()); } - $data = (object)$data; + $data['Exclude']['File'][$file_index] = ''; $this->setData($data); $this->loadConfig(null, null); } public function newIncludeOptions($sender, $param) { - $data = $this->getDirectiveData(); - if (array_key_exists('Options', $data) && is_array($data['Options'])) { - $data['Options'][] = new stdClass; - } else { - $data['Options'] = array(new stdClass); + $data = $this->getDirectiveValue(); + $inc_index = $sender->Parent->getItemIndex(); + $opt_index = 0; + if (key_exists($inc_index, $data['Include']) && key_exists('Options', $data['Include'][$inc_index])) { + $opt_index = count($data['Include'][$inc_index]['Options']); } - $data = (object)$data; + $data['Include'][$inc_index]['Options'][$opt_index] = array(); $this->SourceTemplateControl->setShowAllDirectives(true); $this->setData($data); $this->loadConfig(null, null); } + + public function newIncludeExcludeFile($sender, $param) { + $data = $this->getDirectiveValue(); + $inc_index = $this->RepeaterFileSetIncludes->getItems()->getCount() - 1; + $inc_exc = $param->getCallbackParameter(); + if (property_exists($inc_exc, 'Include') && is_array($inc_exc->Include)) { + if (!key_exists($inc_index, $data['Include'])) { + $data['Include'][$inc_index] = array('File' => array()); + } + for ($i = 0; $i < count($inc_exc->Include); $i++) { + if (in_array($inc_exc->Include[$i], $data['Include'][$inc_index]['File'])) { + // path already in includes, skip it to not double it + continue; + } + $data['Include'][$inc_index]['File'][] = $inc_exc->Include[$i]; + } + } + if (property_exists($inc_exc, 'Exclude') && is_array($inc_exc->Exclude)) { + if (!key_exists('File', $data['Exclude'])) { + $data['Exclude'] = array('File' => array()); + } + for ($i = 0; $i < count($inc_exc->Exclude); $i++) { + if (in_array($inc_exc->Exclude[$i], $data['Exclude']['File'])) { + // path already in includes, skip it to not double it + continue; + } + $data['Exclude']['File'][] = $inc_exc->Exclude[$i]; + } + } + $this->setData($data); + $this->loadConfig(null, null); + } } ?> diff --git a/gui/baculum/protected/Web/Portlets/DirectiveFileSet.tpl b/gui/baculum/protected/Web/Portlets/DirectiveFileSet.tpl index eb4ac1e92..4524c47db 100644 --- a/gui/baculum/protected/Web/Portlets/DirectiveFileSet.tpl +++ b/gui/baculum/protected/Web/Portlets/DirectiveFileSet.tpl @@ -1,74 +1,76 @@ -
-

<%=$this->getDirectiveName()%>

- - - - -
-

<%[ Options ]%>

- + + + - <%=($this->getItemIndex() % 31 === 0 ? '

Options #' . (($this->getItemIndex()/31)+1) . '


' : '')%> - - - - - - - - - - - - - - - - - - - - - - - - - -
- +
+

<%#$this->SourceTemplateControl->getDirectiveName()%> #<%#$this->ItemIndex + 1%>

+ + + + +
+

<%[ Options ]%>

+ + +
+ +
+ + +
+

<%[ Files ]%>

+ + +
+ +
-
-
- - -
-

<%[ Files ]%>

- - -
- -
- -
-
-

<%[ Files ]%>

+

<%[ Exclude ]%>

+ + +

<%[ Files ]%>

-
- +
+
+ diff --git a/gui/baculum/protected/Web/Portlets/FileSetOptionRenderer.php b/gui/baculum/protected/Web/Portlets/FileSetOptionRenderer.php new file mode 100644 index 000000000..d10f4b5f4 --- /dev/null +++ b/gui/baculum/protected/Web/Portlets/FileSetOptionRenderer.php @@ -0,0 +1,51 @@ +item_count = $this->getParent()->getItems()->getCount(); + } + + public function render($writer) { + if (self::$index % 30 === 0) { + $writer->write('

Options #' . ((self::$index/30) + 1) . '


'); + } + self::$index++; + + if (self::$index === $this->item_count) { + $this->resetIndex(); + } + parent::render($writer); + } + + public static function resetIndex() { + self::$index = 0; + } +} +?> diff --git a/gui/baculum/protected/Web/Portlets/JobRunscriptRenderer.php b/gui/baculum/protected/Web/Portlets/JobRunscriptRenderer.php new file mode 100644 index 000000000..7f972099a --- /dev/null +++ b/gui/baculum/protected/Web/Portlets/JobRunscriptRenderer.php @@ -0,0 +1,51 @@ +item_count = $this->getParent()->getItems()->getCount(); + } + + public function render($writer) { + if (self::$index % 7 === 0) { + $writer->write('

Runscript #' . ((self::$index/7) + 1) . '


'); + } + self::$index++; + + if (self::$index === $this->item_count) { + $this->resetIndex(); + } + parent::render($writer); + } + + public static function resetIndex() { + self::$index = 0; + } +} +?> diff --git a/gui/baculum/protected/Web/Portlets/MessageTypes.php b/gui/baculum/protected/Web/Portlets/MessageTypes.php index ebd63d2fa..aae494429 100644 --- a/gui/baculum/protected/Web/Portlets/MessageTypes.php +++ b/gui/baculum/protected/Web/Portlets/MessageTypes.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2017 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -23,7 +23,7 @@ Prado::using('System.Web.Ui.ActiveControls.TActiveRepeater'); Prado::using('Application.Web.Portlets.DirectiveListTemplate'); -Prado::using('Application.Web.Portlets.DirectiveBoolean'); +Prado::using('Application.Web.Portlets.DirectiveCheckBox'); class MessageTypes extends DirectiveListTemplate { @@ -33,7 +33,7 @@ class MessageTypes extends DirectiveListTemplate { } public function getDirectiveValues() { - $type_controls = $this->RepeaterMessageTypes->findControlsByType('DirectiveBoolean'); + $type_controls = $this->RepeaterMessageTypes->findControlsByType('DirectiveCheckBox'); $is_all = false; $types = array(); for ($i = 0; $i < count($type_controls); $i++) { @@ -52,7 +52,7 @@ class MessageTypes extends DirectiveListTemplate { } public function createTypeListElement($sender, $param) { - $control = $this->getChildControl($param->Item, 'DirectiveBoolean'); + $control = $this->getChildControl($param->Item, 'DirectiveCheckBox'); if (is_object($control)) { $control->setHost($param->Item->Data['host']); $control->setComponentType($param->Item->Data['component_type']); diff --git a/gui/baculum/protected/Web/Portlets/MessageTypes.tpl b/gui/baculum/protected/Web/Portlets/MessageTypes.tpl index ae0e8c657..1c5b39a96 100644 --- a/gui/baculum/protected/Web/Portlets/MessageTypes.tpl +++ b/gui/baculum/protected/Web/Portlets/MessageTypes.tpl @@ -1,5 +1,5 @@ - + diff --git a/gui/baculum/protected/Web/Portlets/NewFileSetExcMenu.php b/gui/baculum/protected/Web/Portlets/NewFileSetExcMenu.php new file mode 100644 index 000000000..29581f334 --- /dev/null +++ b/gui/baculum/protected/Web/Portlets/NewFileSetExcMenu.php @@ -0,0 +1,37 @@ +setViewState(self::ITEM_INDEX, $index); + } + + public function getItemIndex() { + return $this->getViewState(self::ITEM_INDEX); + } +} +?> diff --git a/gui/baculum/protected/Web/Portlets/NewFileSetExcMenu.tpl b/gui/baculum/protected/Web/Portlets/NewFileSetExcMenu.tpl new file mode 100644 index 000000000..0ebdcfb95 --- /dev/null +++ b/gui/baculum/protected/Web/Portlets/NewFileSetExcMenu.tpl @@ -0,0 +1,14 @@ + diff --git a/gui/baculum/protected/Web/Portlets/NewFileSetFileOptMenu.php b/gui/baculum/protected/Web/Portlets/NewFileSetFileOptMenu.php new file mode 100644 index 000000000..cde0dc4a4 --- /dev/null +++ b/gui/baculum/protected/Web/Portlets/NewFileSetFileOptMenu.php @@ -0,0 +1,37 @@ +setViewState(self::ITEM_INDEX, $index); + } + + public function getItemIndex() { + return $this->getViewState(self::ITEM_INDEX); + } +} +?> diff --git a/gui/baculum/protected/Web/Portlets/NewFileSetFileOptMenu.tpl b/gui/baculum/protected/Web/Portlets/NewFileSetFileOptMenu.tpl new file mode 100644 index 000000000..a08ab63a4 --- /dev/null +++ b/gui/baculum/protected/Web/Portlets/NewFileSetFileOptMenu.tpl @@ -0,0 +1,29 @@ + diff --git a/gui/baculum/protected/Web/Portlets/NewFileSetMenu.php b/gui/baculum/protected/Web/Portlets/NewFileSetIncExcMenu.php similarity index 88% rename from gui/baculum/protected/Web/Portlets/NewFileSetMenu.php rename to gui/baculum/protected/Web/Portlets/NewFileSetIncExcMenu.php index 01d39e007..d77d3691f 100644 --- a/gui/baculum/protected/Web/Portlets/NewFileSetMenu.php +++ b/gui/baculum/protected/Web/Portlets/NewFileSetIncExcMenu.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2017 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -22,5 +22,6 @@ Prado::using('Application.Web.Portlets.DirectiveListTemplate'); -class NewFileSetMenu extends DirectiveListTemplate { +class NewFileSetIncExcMenu extends DirectiveListTemplate { } +?> diff --git a/gui/baculum/protected/Web/Portlets/NewFileSetIncExcMenu.tpl b/gui/baculum/protected/Web/Portlets/NewFileSetIncExcMenu.tpl new file mode 100644 index 000000000..80cbb54e7 --- /dev/null +++ b/gui/baculum/protected/Web/Portlets/NewFileSetIncExcMenu.tpl @@ -0,0 +1,23 @@ + diff --git a/gui/baculum/protected/Web/Portlets/NewFileSetMenu.tpl b/gui/baculum/protected/Web/Portlets/NewFileSetMenu.tpl deleted file mode 100644 index 8ba3b5b88..000000000 --- a/gui/baculum/protected/Web/Portlets/NewFileSetMenu.tpl +++ /dev/null @@ -1,30 +0,0 @@ - diff --git a/gui/baculum/protected/Web/Portlets/NewRunscriptMenu.tpl b/gui/baculum/protected/Web/Portlets/NewRunscriptMenu.tpl index 686a5e265..e79e52deb 100644 --- a/gui/baculum/protected/Web/Portlets/NewRunscriptMenu.tpl +++ b/gui/baculum/protected/Web/Portlets/NewRunscriptMenu.tpl @@ -5,7 +5,7 @@ ID="RunscriptItem" OnCommand="Parent.SourceTemplateControl.newRunscriptDirective" CommandParameter="save" - ClientSide.OnComplete="var el = $('#<%=$this->RunscriptItem->ClientID%>').parents('div.directive_field').find('div').find('h3.runscript'); BaculaConfig.scroll_to_element(el[el.length-1]);" + ClientSide.OnComplete="var el = $('#<%=$this->RunscriptItem->ClientID%>').parents('div').find('h3.options'); BaculaConfig.scroll_to_element(el[el.length-1]);" Text="Runscript" Attributes.onclick="$(this).closest('div.config_new_runscript').hide();" /> diff --git a/gui/baculum/protected/Web/Portlets/RunJob.php b/gui/baculum/protected/Web/Portlets/RunJob.php index 8db4a3fc3..3457f6f2c 100644 --- a/gui/baculum/protected/Web/Portlets/RunJob.php +++ b/gui/baculum/protected/Web/Portlets/RunJob.php @@ -3,7 +3,7 @@ * Bacula(R) - The Network Backup Solution * Baculum - Bacula web interface * - * Copyright (C) 2013-2018 Kern Sibbald + * Copyright (C) 2013-2019 Kern Sibbald * * The main author of Baculum is Marcin Haba. * The original author of Bacula is Kern Sibbald, with contributions @@ -40,6 +40,7 @@ class RunJob extends Portlets { const RESOURCE_SHOW_PATTERN = '/^\s+--> %resource: name=(.+?(?=\s\S+\=.+)|.+$)/i'; const JOB_SHOW_PATTERN = '/^Job:\sname=(?P.+)\sJobType=\d+\slevel=(?P\w+)?\sPriority=(?P\d+)/i'; + const ACCURATE_PATTERN = '/^\s+Accurate=(?P\d)/i'; const DEFAULT_JOB_PRIORITY = 10; @@ -75,6 +76,8 @@ class RunJob extends Portlets { if (empty($jobdata->storage)) { $jobdata->storage = $this->getResourceName('autochanger', $job_show); } + $jobdata->priorjobid = $job_attr['priority']; + $jobdata->accurate = (key_exists('accurate', $job_attr) && $job_attr['accurate'] == 1); } else { $jobs = array(); $job_list = $this->getModule('api')->get(array('jobs', 'resnames'), null, true, self::USE_CACHE)->output; @@ -184,6 +187,10 @@ class RunJob extends Portlets { } $this->Storage->dataBind(); + if (is_object($jobdata) && property_exists($jobdata, 'accurate')) { + $this->Accurate->Checked = $jobdata->accurate; + } + $priority = self::DEFAULT_JOB_PRIORITY; if (is_object($jobdata) && property_exists($jobdata, 'priorjobid') && $jobdata->priorjobid > 0) { $priority = $jobdata->priorjobid; @@ -269,6 +276,9 @@ class RunJob extends Portlets { $attr['jobname'] = $match['jobname']; $attr['level'] = $match['level']; $attr['priority'] = $match['priority']; + } + if (preg_match(self::ACCURATE_PATTERN, $jobshow[$i], $match) === 1) { + $attr['accurate'] = $match['accurate']; break; } } diff --git a/gui/baculum/protected/Web/endpoints.xml b/gui/baculum/protected/Web/endpoints.xml index 4175bd623..22a1daa21 100644 --- a/gui/baculum/protected/Web/endpoints.xml +++ b/gui/baculum/protected/Web/endpoints.xml @@ -28,6 +28,7 @@ + diff --git a/gui/baculum/themes/Baculum-v2/css/baculum.css b/gui/baculum/themes/Baculum-v2/css/baculum.css index c0f7913dc..c427b9412 100644 --- a/gui/baculum/themes/Baculum-v2/css/baculum.css +++ b/gui/baculum/themes/Baculum-v2/css/baculum.css @@ -126,3 +126,82 @@ div.config_directives { .justify { text-align: justify; } + +#fileset_browser_file_container, #fileset_browser_include_container, #fileset_browser_exclude_container { + width: 50%; + overflow-y: auto; + overflow-x: none; + background-color: white; + padding: 0; + height: 384px; +} + +#fileset_browser_include_container { + height: 150px; +} + +#fileset_browser_exclude_container { + height: 162px; +} + +.item { + cursor: pointer; + clear: left; + height: 30px; +} + +.item a { + text-decoration: underline; +} + +.item, .item_included, .item_excluded { + padding: 4px 0 4px 4px; + background-color: transparent; +} + +.item_included, .item_excluded { + width: 100%; +} + +.item:hover, .item_included:hover, .item_excluded:hover { + width: 100%; + height: 30px; + background-color: #e7e7e7; +} + +.item_inc_exc_btn { + display: inline-block; + float: right; + margin-left: 5px; +} + +.item_selected_del_btn { + float: right; + margin: 2px 10px; + cursor: pointer; +} + +.item_name { + width: 69%; + float: left; + padding-left: 5px; +} + +.dir_item_img, .file_item_img, .link_item_img { + width: 30px; + height: 24px; + background-repeat: no-repeat; + float: left; +} + +.dir_item_img { + background-image: url('/themes/Baculum-v2/directory-icon.png'); +} + +.file_item_img { + background-image: url('/themes/Baculum-v2/file-icon.png'); +} + +.link_item_img { + background-image: url('/themes/Baculum-v2/link-icon.png'); +} diff --git a/gui/baculum/themes/Baculum-v2/css/restore-wizard.css b/gui/baculum/themes/Baculum-v2/css/restore-wizard.css index 7a7f20cc6..e40d33d75 100644 --- a/gui/baculum/themes/Baculum-v2/css/restore-wizard.css +++ b/gui/baculum/themes/Baculum-v2/css/restore-wizard.css @@ -69,6 +69,10 @@ tr.file-browser-element:hover { overflow-y: auto; } +.normal { + font-weight: normal; +} + .bold { font-weight: bold; } diff --git a/gui/baculum/themes/Baculum-v2/link-icon.png b/gui/baculum/themes/Baculum-v2/link-icon.png new file mode 100644 index 000000000..7f56713a9 Binary files /dev/null and b/gui/baculum/themes/Baculum-v2/link-icon.png differ