*/
class BClientScript extends TClientScript {
- const SCRIPTS_VERSION = 18;
+ const SCRIPTS_VERSION = 19;
public function getScriptUrl()
{
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$content .= "[$section]\n";
foreach($options as $option => $value) {
if (is_array($value)) {
+ $str_keys = array_filter(array_keys($value), 'is_string');
+ $is_assoc = (count($str_keys) > 0); // check if array is associative
foreach($value as $k => $v) {
$v = $this->prepareValue($v);
+ if (!$is_assoc) {
+ // array with numeric indexes, set empty key
+ $k = '';
+ }
$content .= "{$option}[$k] = $v\n";
}
} else {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
* Temportary set to 15 minutes for testst purposes.
* In production the value SHOULD BE changed.
*/
- const ACCESS_TOKEN_EXPIRES_TIME = 90000;
+ const ACCESS_TOKEN_EXPIRES_TIME = 120;
/**
* Scope pattern.
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2021 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('System.Web.UI.WebControls.TDataBoundControl');
+Prado::using('Application.Common.Portlets.BSimpleRepeaterItem');
+
+/**
+ * Baculum simple repeater control.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Control
+ * @package Baculum Web
+ */
+class BSimpleRepeater extends TDataBoundControl {
+
+ /**
+ * Stores item template object
+ */
+ private $_itemTemplate;
+
+ /**
+ * Get template for items.
+ *
+ * @return ITemplate template
+ */
+ public function getItemTemplate() {
+ return $this->_itemTemplate;
+ }
+
+ /**
+ * Set template for items.
+ *
+ * @param ITemplate $tpl template
+ * @return none
+ */
+ public function setItemTemplate($tpl) {
+ if ($tpl instanceof \Prado\Web\UI\ITemplate) {
+ $this->_itemTemplate = $tpl;
+ }
+ }
+
+ /**
+ * Data binding.
+ *
+ * @param $data data from data source
+ * @return none
+ */
+ protected function performDataBinding($data) {
+ for ($i = 0; $i < count($data); $i++) {
+ $this->createItem($data[$i]);
+ }
+ }
+
+ /**
+ * Create single repeater item.
+ *
+ * return BSimpleRepeaterItem repeater item
+ */
+ private function createItem($data) {
+ $item = new BSimpleRepeaterItem;
+ if ($item instanceof \Prado\IDataRenderer) {
+ $item->setData($data);
+ }
+ if ($this->_itemTemplate) {
+ $this->_itemTemplate->instantiateIn($item, $this);
+ }
+ return $item;
+ }
+
+ public function render($writer) {
+ $this->renderChildren($writer);
+ }
+}
+?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2021 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('System.Web.UI.TControl');
+
+/**
+ * Baculum simple repeater item control.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Control
+ * @package Baculum Web
+ */
+class BSimpleRepeaterItem extends \Prado\Web\UI\TControl implements \Prado\IDataRenderer {
+
+ private $_data = [];
+
+ public function getData() {
+ return $this->_data;
+ }
+
+ public function setData($data) {
+ $this->_data = $data;
+ }
+}
+?>
$cached = null;
$ret = null;
if (is_null($host)) {
- $host = $this->User->getAPIHosts();
+ $host = $this->User->getDefaultAPIHost();
}
if ($use_cache === true) {
$cached = $this->getSessionCache($host, $params);
*/
public function set(array $params, array $options = array(), $host = null, $show_error = true) {
if (is_null($host)) {
- $host = $this->User->getAPIHosts();
+ $host = $this->User->getDefaultAPIHost();
}
$host_cfg = $this->getHostParams($host);
$uri = $this->getURIResource($host, $params);
*/
public function create(array $params, array $options, $host = null, $show_error = true) {
if (is_null($host)) {
- $host = $this->User->getAPIHosts();
+ $host = $this->User->getDefaultAPIHost();
}
$host_cfg = $this->getHostParams($host);
$uri = $this->getURIResource($host, $params);
*/
public function remove(array $params, $host = null, $show_error = true) {
if (is_null($host)) {
- $host = $this->User->getAPIHosts();
+ $host = $this->User->getDefaultAPIHost();
}
$host_cfg = $this->getHostParams($host);
$uri = $this->getURIResource($host, $params);
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2020 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
// without config there is no way to call api below
return;
}
+
+ if (!$this->IsCallBack && !$this->IsPostBack && !$this->isDefaultAPIHost()) {
+ $this->goToPage('SelectAPIHost');
+ // without API host selected we can't continue
+ return;
+ }
+
Logging::$debug_enabled = (isset($this->web_config['baculum']['debug']) && $this->web_config['baculum']['debug'] == 1);
if (!$this->IsPostBack && !$this->IsCallBack) {
$this->postInitActions();
}
}
+ /**
+ * Check if default API host is set.
+ * If it isn't direct to API host selection page.
+ *
+ * @return none
+ */
+ private function isDefaultAPIHost() {
+ $def_api_host = $this->User->getDefaultAPIHost();
+ $auth = $this->getModule('auth');
+ $page = $this->Service->getRequestedPagePath();
+ $pages_no_host = [$auth->getLoginPage(), 'SelectAPIHost'];
+ return (!is_null($def_api_host) || in_array($page, $pages_no_host));
+ }
+
/**
* Set page session values.
*
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2020 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
*/
const MONITOR = 'Monitor';
const BACULUM_ERROR = 'BaculumError';
+ const SELECT_API_HOST = 'SelectAPIHost';
/**
* Public pages - always allowed
private function getSystemCategories() {
return [
self::MONITOR,
- self::BACULUM_ERROR
+ self::BACULUM_ERROR,
+ self::SELECT_API_HOST
];
}
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2020 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
const EMAIL = 'Email';
const DESCRIPTION = 'Description';
const API_HOSTS = 'ApiHosts';
+ const DEFAULT_API_HOST = 'DefaultApiHost';
const IPS = 'Ips';
const ENABLED = 'Enabled';
const IN_CONFIG = 'InConfig';
/**
* Set API hosts.
- * So far is supported only one API host per user.
- * In the future this method can support more API hosts per user.
*
- * @param string $api_hosts API hosts
+ * @param array $api_hosts user API hosts
* @return none
*/
public function setAPIHosts($api_hosts) {
/**
* API hosts getter.
*
- * @return string API host
+ * @return array user API hosts
*/
public function getAPIHosts() {
+ $api_hosts = [];
$hosts = $this->getState(self::API_HOSTS);
- $hosts = explode(',', $hosts);
- if (count($hosts) == 1 && !empty($hosts[0])) {
- $api_hosts = $hosts[0];
+ /**
+ * This checking is for backward compatibility because previously
+ * hosts were written in session as string. Now it is written as array.
+ */
+ if (is_string($hosts)) {
+ if (!empty($hosts)) {
+ $hosts = explode(',', $hosts);
+ } else {
+ $hosts = [];
+ }
+ } elseif (is_null($hosts)) {
+ $hosts = [];
+ }
+
+ if (count($hosts) > 0) {
+ $api_hosts = $hosts;
} else {
- // default API host
- $api_hosts = HostConfig::MAIN_CATALOG_HOST;
+ // add default API host
+ $api_hosts[] = HostConfig::MAIN_CATALOG_HOST;
}
return $api_hosts;
}
+ /**
+ * Set default API host for user.
+ * It determines which host will be used as default API host to login
+ * to Baculum Web interface. This host needs to have at least the catalog
+ * and the console capabilities.
+ *
+ * @param string $api_host default API host
+ * @return none
+ */
+ public function setDefaultAPIHost($api_host) {
+ $this->setState(self::DEFAULT_API_HOST, $api_host);
+ $application = $this->getManager()->getApplication();
+ $application->getModule('auth')->updateSessionUser($this);
+ }
+
+ /**
+ * Get default API host for user.
+ * If default API host is not set, there happens a try to determine
+ * this host if user has only one API host assigned.
+ *
+ * @return string|null default API host or null if no default host set
+ */
+ public function getDefaultAPIHost() {
+ $def_host = $this->getState(self::DEFAULT_API_HOST);
+ if (!$def_host) {
+ $api_hosts = $this->getAPIHosts();
+ if (count($api_hosts) == 1) {
+ // only one host assigned, so use it as default host
+ $def_host = $api_hosts[0];
+ $this->setDefaultAPIHost($def_host);
+ }
+ }
+ return $def_host;
+ }
+
+ /**
+ * Check if given API host belongs to user API hosts.
+ *
+ * @param string $api_host API host to check
+ * @return boolean true if API host belongs to user API hosts, otherwise false
+ */
+ public function isUserAPIHost($api_host) {
+ $api_hosts = $this->getAPIHosts();
+ return in_array($api_host, $api_hosts);
+ }
+
/**
* IP address restriction setter.
*
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2020 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
'description',
'email',
'roles',
- 'api_hosts',
'enabled',
'ips'
];
continue;
}
$user_config['username'] = $username;
+ // for API hosts backward compatibility
+ if (key_exists('api_hosts', $user_config)) {
+ if (!is_array($user_config['api_hosts'])) {
+ $user_config['api_hosts'] = !empty($user_config['api_hosts']) ? [$user_config['api_hosts']] : [];
+ }
+ } else {
+ $user_config['api_hosts'] = [];
+ }
$config[$username] = $user_config;
}
return $config;
},
"Messages": {
"Name": {
- "Required": false,
+ "Required": true,
"ValueType": "name",
"DefaultValue": 0,
"FieldType": "TextBox",
},
"Messages": {
"Name": {
- "Required": false,
+ "Required": true,
"ValueType": "name",
"DefaultValue": 0,
"FieldType": "TextBox",
},
"Messages": {
"Name": {
- "Required": false,
+ "Required": true,
"ValueType": "name",
"DefaultValue": 0,
"FieldType": "TextBox",
offsetY = 0;
}
var x = (e.clientX + offsetX).toString();
- var y = (e.clientY + offsetY + window.pageYOffset).toString();
+ var y = (e.clientY + offsetY).toString();
$('#' + element).css({
- position: 'absolute',
+ position: 'fixed',
left: x + 'px',
top: y + 'px',
zIndex: 1000
msgid "Advanced options"
msgstr "Advanced options"
-msgid "Default API host for imported users:"
-msgstr "Default API host for imported users:"
+msgid "Default API hosts for imported users:"
+msgstr "Default API hosts for imported users:"
msgid "Log in"
msgstr "Log in"
msgid "Mount volume"
msgstr "Mount volume"
+
+msgid "API hosts:"
+msgstr "API hosts:"
+
+msgid "Please select API host"
+msgstr "Please select API host"
+
+msgid "FD address"
+msgstr "FD address"
+
+msgid "FD port"
+msgstr "FD port"
+
+msgid "FD API host"
+msgstr "FD API host"
+
+msgid "Configure file daemon"
+msgstr "Configure file daemon"
+
+msgid "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+msgstr "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+
+msgid "SD API host"
+msgstr "SD API host"
+
+msgid "SD address"
+msgstr "SD address"
+
+msgid "SD port"
+msgstr "SD port"
+
+msgid "Configure storage daemon"
+msgstr "Configure storage daemon"
msgid "Advanced options"
msgstr "Advanced options"
-msgid "Default API host for imported users:"
-msgstr "Default API host for imported users:"
+msgid "Default API hosts for imported users:"
+msgstr "Default API hosts for imported users:"
msgid "Log in"
msgstr "Log in"
msgid "Mount volume"
msgstr "Mount volume"
+
+msgid "API hosts:"
+msgstr "API hosts:"
+
+msgid "Please select API host"
+msgstr "Please select API host"
+
+msgid "FD address"
+msgstr "FD address"
+
+msgid "FD port"
+msgstr "FD port"
+
+msgid "FD API host"
+msgstr "FD API host"
+
+msgid "Configure file daemon"
+msgstr "Configure file daemon"
+
+msgid "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+msgstr "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+
+msgid "SD API host"
+msgstr "SD API host"
+
+msgid "SD address"
+msgstr "SD address"
+
+msgid "SD port"
+msgstr "SD port"
+
+msgid "Configure storage daemon"
+msgstr "Configure storage daemon"
msgid "Advanced options"
msgstr "Opcje zaawansowane"
-msgid "Default API host for imported users:"
-msgstr "Domyślny host API dla zaimportowanych użytkowników:"
+msgid "Default API hosts for imported users:"
+msgstr "Domyślne hosty API dla zaimportowanych użytkowników:"
msgid "Log in"
msgstr "Zaloguj"
msgid "Mount volume"
msgstr "Montuj wolumen"
+msgid "API hosts:"
+msgstr "Hosty API:"
+
+msgid "Please select API host"
+msgstr "Proszę wybrać host API"
+
+msgid "FD address"
+msgstr "Adres FD"
+
+msgid "FD port"
+msgstr "Port FD"
+
+msgid "FD API host"
+msgstr "Host API FD"
+
+msgid "Configure file daemon"
+msgstr "Konfiguruj file daemon"
+
+msgid "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+msgstr "Wystąpił problem z załadowaniem konfiguracji zasobu. Proszę sprawdzić czy wybrany host API działa i czy dostarcza dostęp do konfiguracji tego zasobu."
+
+msgid "SD API host"
+msgstr "Host API SD"
+
+msgid "SD address"
+msgstr "Adres SD"
+
+msgid "SD port"
+msgstr "Port SD"
+
+msgid "Configure storage daemon"
+msgstr "Konfiguruj storage daemon"
msgid "Advanced options"
msgstr "Opções avançadas"
-msgid "Default API host for imported users:"
-msgstr "Host API padrão para usuários importados:"
+msgid "Default API hosts for imported users:"
+msgstr "Default API hosts for imported users:"
msgid "Log in"
msgstr "Efetuar Login"
msgid "Mount volume"
msgstr "Montar volume"
+msgid "API hosts:"
+msgstr "API hosts:"
+
+msgid "Please select API host"
+msgstr "Please select API host"
+
+msgid "FD address"
+msgstr "FD address"
+
+msgid "FD port"
+msgstr "FD port"
+
+msgid "FD API host"
+msgstr "FD API host"
+
+msgid "Configure file daemon"
+msgstr "Configure file daemon"
+
+msgid "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+msgstr "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+
+msgid "SD API host"
+msgstr "SD API host"
+
+msgid "SD address"
+msgstr "SD address"
+
+msgid "SD port"
+msgstr "SD port"
+
+msgid "Configure storage daemon"
+msgstr "Configure storage daemon"
msgid "Advanced options"
msgstr "Расширенные настройки"
-msgid "Default API host for imported users:"
-msgstr "Хост API по умолчанию для импортированных пользователей:"
+msgid "Default API hosts for imported users:"
+msgstr "Default API hosts for imported users:"
msgid "Log in"
msgstr "Вход"
msgid "Mount volume"
msgstr "Монтировать том"
+msgid "API hosts:"
+msgstr "API hosts:"
+
+msgid "Please select API host"
+msgstr "Please select API host"
+
+msgid "FD address"
+msgstr "FD address"
+
+msgid "FD port"
+msgstr "FD port"
+
+msgid "FD API host"
+msgstr "FD API host"
+
+msgid "Configure file daemon"
+msgstr "Configure file daemon"
+
+msgid "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+msgstr "There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration."
+
+msgid "SD API host"
+msgstr "SD API host"
+
+msgid "SD address"
+msgstr "SD address"
+
+msgid "SD port"
+msgstr "SD port"
+
+msgid "Configure storage daemon"
+msgstr "Configure storage daemon"
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
public $web_config;
+ public function onInit($param) {
+ parent::onInit($param);
+ if ($this->getPage()->IsPostBack || $this->getPage()->IsCallBack) {
+ return;
+ }
+ $api_hosts = $this->User->getAPIHosts();
+ $this->UserAPIHosts->DataSource = array_combine($api_hosts, $api_hosts);
+ $this->UserAPIHosts->SelectedValue = $this->User->getDefaultAPIHost();
+ $this->UserAPIHosts->dataBind();
+ if (count($api_hosts) === 1) {
+ $this->UserAPIHostsContainter->Visible = false;
+ }
+ }
+
+
public function onPreRender($param) {
parent::onPreRender($param);
$this->web_config = $this->Application->getModule('web_config')->getConfig();
}
+
+ public function setAPIHost($sender, $param) {
+ $api_host = $this->UserAPIHosts->SelectedValue;
+ if (!empty($api_host)) {
+ $this->User->setDefaultAPIHost($api_host);
+ $this->getResponse()->reload();
+ }
+ }
}
?>
<span id="msg_envelope" class="w3-tag w3-large w3-green w3-text-white w3-right w3-padding-small w3-margin-top w3-margin-right" style="cursor: pointer;<%=$this->User->isInRole(WebUserRoles::ADMIN) === false ? 'display: none' : ''%>" title="<%[ Display messages log window ]%>">
<i class="fas fa-envelope w3-large"></i>
</span>
+ <com:TLabel ID="UserAPIHostsContainter"CssClass="w3-right w3-margin-top w3-margin-right">
+ <%[ API host: ]%>
+ <com:TDropDownList
+ ID="UserAPIHosts"
+ CssClass="w3-select w3-border w3-small"
+ OnTextChanged="setAPIHost"
+ AutoPostBack="true"
+ Width="200px"
+ Height="34px"
+ />
+ </com:TLabel>
<script type="text/javascript">
var SIZE_VALUES_UNIT = '<%=(count($this->web_config) > 0 && key_exists('size_values_unit', $this->web_config['baculum'])) ? $this->web_config['baculum']['size_values_unit'] : WebConfig::DEF_SIZE_VAL_UNIT%>';
var DATE_TIME_FORMAT = '<%=(count($this->web_config) > 0 && key_exists('date_time_format', $this->web_config['baculum'])) ? $this->web_config['baculum']['date_time_format'] : WebConfig::DEF_DATE_TIME_FORMAT%>';
<h3 class="w3-margin-left">[ClientId <%=$this->getClientId()%>] <%[ Client: ]%> <%=$this->getClientName()%></h3>
<div class="w3-bar w3-green w3-margin-bottom">
<a class="w3-bar-item w3-button tab_btn" href="<%=$this->Service->constructUrl('ClientList')%>"><i class="fa fa-angle-left"></i></a>
- <button id="btn_client_actions" type="button" class="w3-bar-item w3-button tab_btn w3-grey" onclick="W3Tabs.open(this.id, 'client_actions'); clear_node('#new_client div.directive_value');"><%[ Actions ]%></button>
- <button id="btn_client_jobs" type="button" class="w3-bar-item w3-button tab_btn" onclick="W3Tabs.open(this.id, 'client_jobs'); clear_node('#new_client div.directive_value');oJobForClientList.table.responsive.recalc();"><%[ Jobs for Client ]%></button>
- <com:TLinkButton
+ <button id="btn_client_actions" type="button" class="w3-bar-item w3-button tab_btn w3-grey" onclick="W3Tabs.open(this.id, 'client_actions');"><%[ Actions ]%></button>
+ <button id="btn_client_jobs" type="button" class="w3-bar-item w3-button tab_btn" onclick="W3Tabs.open(this.id, 'client_jobs'); oJobForClientList.table.responsive.recalc();"><%[ Jobs for Client ]%></button>
+ <com:TActiveLinkButton
CssClass="w3-bar-item w3-button tab_btn"
- Attributes.onclick="W3Tabs.open(this.id, 'client_config'); clear_node('#new_client div.directive_value'); return false;"
+ Attributes.onclick="W3Tabs.open(this.id, 'client_config');"
Text="<%[ Configure client ]%>"
Visible="<%=!empty($_SESSION['dir'])%>"
+ OnClick="setDIRClientConfig"
/>
+ <a id="btn_filedaemon_config" href="javascript:void(0)" class="w3-bar-item w3-button tab_btn" onclick="load_fd_filedaemon_config(); W3Tabs.open(this.id, 'filedaemon_config');">
+ <%[ Configure file daemon ]%>
+ </a>
+ <com:TLabel ID="UserAPIHostsContainter" CssClass="w3-right w3-margin-right">
+ <%[ FD API host ]%>
+ <com:TActiveDropDownList
+ ID="UserAPIHosts"
+ CssClass="w3-select w3-border"
+ Width="200px"
+ />
+ </com:TLabel>
</div>
<div class="w3-container tab_item" id="client_actions">
<com:TActiveLinkButton
</com:TActiveLinkButton>
<i id="status_client_loading" class="fa fa-sync w3-spin" style="display: none; vertical-align: super; margin-left: 6px;"></i> <span id="status_client_error" class="w3-text-red" style="display: none"></span>
<div id="show_client_container">
- <div id="status_client_text_output" class="w3-code">
- <pre><com:TActiveLabel ID="ShowLog" /></pre>
+ <div class="w3-row" style="display: flex; flex-wrap: wrap;">
+ <div class="w3-card w3-padding w3-margin-right w3-margin-bottom details_card">
+ <h4><%[ General ]%></h4>
+ <table style="width: 90%">
+ <tr>
+ <td><%[ Enabled ]%></td>
+ <td><strong><com:TActiveLabel ID="OEnabled" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ FD address ]%></td>
+ <td><strong><com:TActiveLabel ID="OFDAddress" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ FD port ]%></td>
+ <td><strong><com:TActiveLabel ID="OFDPort" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ Running jobs ]%></td>
+ <td><strong><com:TActiveLabel ID="ORunningJobs" /></strong></td>
+ </tr>
+ </table>
+ </div>
+ <div class="w3-card w3-padding w3-margin-right w3-margin-bottom details_card">
+ <h4><%[ Pruning ]%></h4>
+ <table style="width: 90%">
+ <tr>
+ <td class="w3-half">AutoPrune</td>
+ <td><strong><com:TActiveLabel ID="OAutoPrune" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ Job retention ]%></td>
+ <td><strong><com:TActiveLabel ID="OJobRetention" CssClass="time" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ File retention ]%></td>
+ <td><strong><com:TActiveLabel ID="OFileRetention" /></strong></td>
+ </tr>
+ </table>
+ </div>
</div>
</div>
<div id="status_client_container" style="display: none">
</script>
<div class="w3-container tab_item" id="client_config" style="display: none">
<com:Application.Web.Portlets.BaculaConfigDirectives
- ID="ClientConfig"
+ ID="DIRClientConfig"
ComponentType="dir"
ResourceType="Client"
ShowCancelButton="false"
/>
</div>
+ <div class="w3-container tab_item" id="filedaemon_config" style="display: none">
+ <com:TCallback ID="LoadFileDaemonConfig" OnCallback="loadFDFileDaemonConfig" />
+ <script>
+function load_fd_filedaemon_config() {
+ var cb = <%=$this->LoadFileDaemonConfig->ActiveControl->Javascript%>;
+ cb.dispatch();
+}
+ </script>
+ <com:TCallback ID="LoadFileDaemonResourcesConfig" OnCallback="loadFDResourcesConfig" />
+ <script>
+function load_fd_res_config(resource) {
+ var cb = <%=$this->LoadFileDaemonResourcesConfig->ActiveControl->Javascript%>;
+ cb.setCallbackParameter(resource);
+ cb.dispatch();
+}
+ </script>
+ <div class="w3-row w3-margin-bottom">
+ <a href="javascript:void(0)" onclick="load_fd_filedaemon_config('FileDaemon'); W3SubTabs.open('filedaemon_filedaemon_config_btn', 'filedaemon_filedaemon_config_form');">
+ <div id="filedaemon_filedaemon_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding w3-border-red">File Daemon</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_fd_res_config('Director'); W3SubTabs.open('filedaemon_director_config_btn', 'filedaemon_resources_config_form');">
+ <div id="filedaemon_director_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Director</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_fd_res_config('Messages'); W3SubTabs.open('filedaemon_messages_config_btn', 'filedaemon_resources_config_form');">
+ <div id="filedaemon_messages_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Messages</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_fd_res_config('Schedule'); W3SubTabs.open('filedaemon_schedule_config_btn', 'filedaemon_resources_config_form');">
+ <div id="filedaemon_schedule_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Schedule</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_fd_res_config('Console'); W3SubTabs.open('filedaemon_console_config_btn', 'filedaemon_resources_config_form');">
+ <div id="filedaemon_console_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Console</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_fd_res_config('Statistics'); W3SubTabs.open('filedaemon_statistics_config_btn', 'filedaemon_resources_config_form');">
+ <div id="filedaemon_statistics_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Statistics</div>
+ </a>
+ </div>
+ <div id="filedaemon_filedaemon_config_form" class="subtab_item">
+ <com:TActiveLabel
+ ID="FDFileDaemonConfigErr"
+ Text="<%[ There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration. ]%>"
+ CssClass="w3-text-red"
+ Display="None"
+ />
+ <com:Application.Web.Portlets.BaculaConfigDirectives
+ ID="FDFileDaemonConfig"
+ ComponentType="fd"
+ ResourceType="FileDaemon"
+ ShowCancelButton="false"
+ />
+ </div>
+ <div id="filedaemon_resources_config_form" class="subtab_item" style="display: none">
+ <com:Application.Web.Portlets.BaculaConfigResourceList
+ ID="FileDaemonResourcesConfig"
+ ComponentType="fd"
+ ResourceList="<%=[[ 'name' => 'Name', 'label' => 'Name' ]]%>"
+ />
+ </div>
+ </div>
</com:TContent>
* Bacula(R) is a registered trademark of Kern Sibbald.
*/
+Prado::using('System.Web.UI.ActiveControls.TActiveDropDownList');
Prado::using('System.Web.UI.ActiveControls.TActiveLabel');
Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton');
Prado::using('System.Web.UI.ActiveControls.TCallback');
const CLIENTID = 'ClientId';
const CLIENT_NAME = 'ClientName';
+ const CLIENT_ADDRESS = 'ClientAddress';
public function onInit($param) {
parent::onInit($param);
}
$this->setClientId($clientid);
$clientshow = $this->getModule('api')->get(
- array('clients', $clientid, 'show'),
+ ['clients', $clientid, 'show', '?output=json'],
null,
true
);
if ($clientshow->error === 0) {
- $this->ShowLog->Text = implode(PHP_EOL, $clientshow->output);
+ if (property_exists($clientshow->output, 'enabled')) {
+ $this->OEnabled->Text = $clientshow->output->enabled == 1 ? Prado::localize('Yes') : Prado::localize('No');
+ }
+ if (property_exists($clientshow->output, 'address')) {
+ $this->setClientAddress($clientshow->output->address);
+ $this->OFDAddress->Text = $clientshow->output->address;
+ }
+ if (property_exists($clientshow->output, 'fdport')) {
+ $this->OFDPort->Text = $clientshow->output->fdport;
+ }
+ if (property_exists($clientshow->output, 'maxjobs') && property_exists($clientshow->output, 'numjobs')) {
+ $this->ORunningJobs->Text = $clientshow->output->numjobs . '/' . $clientshow->output->maxjobs;
+ }
+ if (property_exists($clientshow->output, 'autoprune')) {
+ $this->OAutoPrune->Text = $clientshow->output->autoprune = 1 ? Prado::localize('Yes') : Prado::localize('No');
+ }
+ if (property_exists($clientshow->output, 'jobretention')) {
+ $this->OJobRetention->Text = $clientshow->output->jobretention;
+ }
+ if (property_exists($clientshow->output, 'fileretention')) {
+ $this->OFileRetention->Text = $clientshow->output->fileretention;
+ }
}
- $client = $this->getModule('api')->get(
- array('clients', $clientid)
- );
- if ($client->error === 0) {
- $this->setClientName($client->output->name);
+ if (property_exists($clientshow->output, 'name')) {
+ // set name from client show
+ $this->setClientName($clientshow->output->name);
+ } else {
+ // set name from catalog data request
+ $client = $this->getModule('api')->get(
+ array('clients', $clientid)
+ );
+ if ($client->error === 0) {
+ $this->setClientName($client->output->name);
+ }
}
+ $this->setAPIHosts();
}
- public function onPreRender($param) {
- parent::onPreRender($param);
- if ($this->IsCallBack || $this->IsPostBack) {
- return;
- }
+ public function setDIRClientConfig($sender, $param) {
+ $this->FDFileDaemonConfig->unloadDirectives();
if (!empty($_SESSION['dir'])) {
- $this->ClientConfig->setComponentName($_SESSION['dir']);
- $this->ClientConfig->setResourceName($this->getClientName());
- $this->ClientConfig->setLoadValues(true);
- $this->ClientConfig->raiseEvent('OnDirectiveListLoad', $this, null);
+ $this->DIRClientConfig->setComponentName($_SESSION['dir']);
+ $this->DIRClientConfig->setResourceName($this->getClientName());
+ $this->DIRClientConfig->setLoadValues(true);
+ $this->DIRClientConfig->raiseEvent('OnDirectiveListLoad', $this, null);
+ }
+ }
+
+ private function setAPIHosts() {
+ $def_host = null;
+ $api_hosts = $this->getModule('host_config')->getConfig();
+ $user_api_hosts = $this->User->getAPIHosts();
+ $client_address = $this->getClientAddress();
+ foreach ($api_hosts as $name => $attrs) {
+ if (in_array($name, $user_api_hosts) && $attrs['address'] === $client_address) {
+ $def_host = $name;
+ break;
+ }
+ }
+ $this->UserAPIHosts->DataSource = array_combine($user_api_hosts, $user_api_hosts);
+ if ($def_host) {
+ $this->UserAPIHosts->SelectedValue = $def_host;
+ } else {
+ $this->UserAPIHosts->SelectedValue = $this->User->getDefaultAPIHost();
+ }
+ $this->UserAPIHosts->dataBind();
+ if (count($user_api_hosts) === 1) {
+ $this->UserAPIHostsContainter->Visible = false;
+ }
+ }
+
+ private function getFDName() {
+ $fdname = null;
+ if (!$this->User->isUserAPIHost($this->UserAPIHosts->SelectedValue)) {
+ // Validation error. Somebody manually modified select values
+ return $fdname;
+ }
+ $result = $this->getModule('api')->get(['config'], $this->UserAPIHosts->SelectedValue);
+ if ($result->error === 0) {
+ for ($i = 0; $i < count($result->output); $i++) {
+ if ($result->output[$i]->component_type === 'fd' && $result->output[$i]->state) {
+ $fdname = $result->output[$i]->component_name;
+ }
+
+ }
+ }
+ return $fdname;
+ }
+
+ public function loadFDFileDaemonConfig($sender, $param) {
+ $this->DIRClientConfig->unloadDirectives();
+ $component_name = $this->getFDName();
+ if (!is_null($component_name)) {
+ $this->FDFileDaemonConfigErr->Display = 'None';
+ $this->FDFileDaemonConfig->setHost($this->UserAPIHosts->SelectedValue);
+ $this->FDFileDaemonConfig->setComponentName($component_name);
+ $this->FDFileDaemonConfig->setResourceName($component_name);
+ $this->FDFileDaemonConfig->setLoadValues(true);
+ $this->FDFileDaemonConfig->raiseEvent('OnDirectiveListLoad', $this, null);
+ } else {
+ $this->FDFileDaemonConfigErr->Display = 'Dynamic';
+ }
+ }
+
+ public function loadFDResourcesConfig($sender, $param) {
+ $resource_type = $param->getCallbackParameter();
+ $this->DIRClientConfig->unloadDirectives();
+ $this->FDFileDaemonConfig->unloadDirectives();
+ $component_name = $this->getFDName();
+ if (!is_null($component_name) && !empty($resource_type)) {
+ $this->FileDaemonResourcesConfig->setHost($this->UserAPIHosts->SelectedValue);
+ $this->FileDaemonResourcesConfig->setResourceType($resource_type);
+ $this->FileDaemonResourcesConfig->setComponentName($component_name);
+ $this->FileDaemonResourcesConfig->loadResourceListTable();
+ } else {
+ $this->FileDaemonResourcesConfig->showError(true);
}
}
return $this->getViewState(self::CLIENT_NAME);
}
+ /**
+ * Set client address.
+ *
+ * @return none;
+ */
+ public function setClientAddress($address) {
+ $this->setViewState(self::CLIENT_ADDRESS, $address);
+ }
+
+ /**
+ * Get client address.
+ *
+ * @return string address
+ */
+ public function getClientAddress() {
+ return $this->getViewState(self::CLIENT_ADDRESS);
+ }
+
public function status($sender, $param) {
$raw_status = $this->getModule('api')->get(
['clients', $this->getClientId(), 'status']
$this->setComponentName($component_name);
$this->setResourceType($resource_type);
// Non-admin can configure only host assigned to him
- $this->NewResource->setHost($this->User->getAPIHosts());
+ $this->NewResource->setHost($this->User->getDefaultAPIHost());
$this->NewResource->setComponentType($component_type);
$this->NewResource->setComponentName($component_name);
$this->NewResource->setResourceType($resource_type);
public function setHosts() {
$config = $this->getModule('host_config')->getConfig();
$hosts = array('' => Prado::localize('Please select host'));
+ $user_api_hosts = $this->User->getAPIHosts();
foreach ($config as $host => $vals) {
- if ($host !== $this->User->getAPIHosts()) {
+ if (!in_array($host, $user_api_hosts)) {
continue;
}
$item = "Host: $host, Address: {$vals['address']}, Port: {$vals['port']}";
<button type="button" class="w3-button w3-green" onclick="document.location.href='<%=$this->Service->constructUrl('ScheduleStatusList')%>';"><i class="fa fa-clock"></i> <%[ Schedule status ]%></button>
</div>
<div class="w3-container">
- <table id="schedule_list" class="w3-table w3-striped w3-hoverable w3-white w3-margin-bottom">
- <thead>
- <tr>
- <th></th>
- <th><%[ Name ]%></th>
- <th class="w3-center"><%[ Actions ]%></th>
- </tr>
- </thead>
- <tbody id="schedule_list_body"></tbody>
- <tfoot>
- <tr>
- <th></th>
- <th><%[ Name ]%></th>
- <th class="w3-center"><%[ Actions ]%></th>
- </tr>
- </tfoot>
- </table>
+ <table id="schedule_list" class="w3-table w3-striped w3-hoverable w3-white w3-margin-bottom">
+ <thead>
+ <tr>
+ <th></th>
+ <th><%[ Name ]%></th>
+ <th class="w3-center"><%[ Actions ]%></th>
+ </tr>
+ </thead>
+ <tbody id="schedule_list_body"></tbody>
+ <tfoot>
+ <tr>
+ <th></th>
+ <th><%[ Name ]%></th>
+ <th class="w3-center"><%[ Actions ]%></th>
+ </tr>
+ </tfoot>
</table>
</div>
<script type="text/javascript">
</div> <i class="fa fa-asterisk w3-text-red opt_req"></i>
</div>
<div class="w3-row w3-section">
- <div class="w3-col w3-third"><com:TLabel ForControl="GetUsersDefaultAPIHost" Text="<%[ Default API host for imported users: ]%>" /></div>
+ <div class="w3-col w3-third"><com:TLabel ForControl="GetUsersDefaultAPIHosts" Text="<%[ Default API hosts for imported users: ]%>" /></div>
<div class="w3-half">
- <com:TActiveDropDownList
- ID="GetUsersDefaultAPIHost"
+ <com:TActiveListBox
+ ID="GetUsersDefaultAPIHosts"
+ SelectionMode="Multiple"
+ Rows="6"
CssClass="w3-select w3-border"
AutoPostBack="false"
/>
+ <p class="w3-text-black" style="margin: 0 16px 0 0"><%[ Use CTRL + left-click to multiple item selection ]%></p>
</div>
</div>
<div class="w3-row w3-section" title="<%[ Comma separated IP addresses. Using asterisk character, there is also possible to provide subnet, for example: 192.168.1.* ]%>">
<th class="w3-center"><%[ Description ]%></th>
<th class="w3-center"><%[ E-mail ]%></th>
<th class="w3-center"><%[ Roles ]%></th>
- <th class="w3-center"><%[ API host ]%></th>
+ <th class="w3-center"><%[ API hosts ]%></th>
<th class="w3-center"><%[ IP address restrictions ]%></th>
<th class="w3-center"><%[ Enabled ]%></th>
<th class="w3-center"><%[ Action ]%></th>
<th class="w3-center"><%[ Description ]%></th>
<th class="w3-center"><%[ E-mail ]%></th>
<th class="w3-center"><%[ Roles ]%></th>
- <th class="w3-center"><%[ API host ]%></th>
+ <th class="w3-center"><%[ API hosts ]%></th>
<th class="w3-center"><%[ IP address restrictions ]%></th>
<th class="w3-center"><%[ Enabled ]%></th>
<th class="w3-center"><%[ Action ]%></th>
'<%=$this->UserEmail->ClientID%>',
'<%=$this->UserPassword->ClientID%>',
'<%=$this->UserRoles->ClientID%>',
- '<%=$this->UserAPIHost->ClientID%>',
+ '<%=$this->UserAPIHosts->ClientID%>',
'<%=$this->UserIps->ClientID%>'
].forEach(function(id) {
document.getElementById(id).value = '';
</div> <i class="fa fa-asterisk w3-text-red opt_req"></i>
</div>
<div class="w3-row w3-section">
- <div class="w3-col w3-third"><com:TLabel ForControl="UserAPIHost" Text="<%[ API host: ]%>" CssClass="w3-xlarge"/></div>
+ <div class="w3-col w3-third"><com:TLabel ForControl="UserAPIHosts" Text="<%[ API hosts: ]%>" CssClass="w3-xlarge"/></div>
<div class="w3-half">
- <com:TActiveDropDownList
- ID="UserAPIHost"
+ <com:TActiveListBox
+ ID="UserAPIHosts"
+ SelectionMode="Multiple"
+ Rows="6"
CssClass="w3-select w3-border"
AutoPostBack="false"
/>
+ <p class="w3-text-black" style="margin: 0 16px 0 0"><%[ Use CTRL + left-click to multiple item selection ]%></p>
</div>
</div>
<div class="w3-row w3-section">
*/
public function initUserWindow() {
// set API hosts
- $this->setAPIHosts($this->UserAPIHost);
+ $this->setAPIHosts($this->UserAPIHosts, null, false);
// set roles
$this->setRoles($this->UserRoles);
*
* @param object $control control which contains API host list
* @param mixed $def_val default value or null if no default value to set
+ * @param boolean determines if add first blank item
* @return none
*/
- private function setAPIHosts($control, $def_val = null) {
+ private function setAPIHosts($control, $def_val = null, $add_blank_item = true) {
$api_hosts = array_keys($this->getModule('host_config')->getConfig());
- array_unshift($api_hosts, '');
+ if ($add_blank_item) {
+ array_unshift($api_hosts, '');
+ }
$control->DataSource = array_combine($api_hosts, $api_hosts);
if ($def_val) {
$control->SelectedValue = $def_val;
$this->UserDescription->Text = $config['description'];
$this->UserEmail->Text = $config['email'];
$this->UserPassword->Text = '';
+
+ // set roles
$selected_indices = [];
$roles = explode(',', $config['roles']);
for ($i = 0; $i < $this->UserRoles->getItemCount(); $i++) {
}
}
$this->UserRoles->setSelectedIndices($selected_indices);
- $this->UserAPIHost->SelectedValue = $config['api_hosts'];
+
+ $selected_indices = [];
+ $api_hosts = $config['api_hosts'];
+ for ($i = 0; $i < $this->UserAPIHosts->getItemCount(); $i++) {
+ if (in_array($this->UserAPIHosts->Items[$i]->Value, $api_hosts)) {
+ $selected_indices[] = $i;
+ }
+ }
+
+ $this->UserAPIHosts->setSelectedIndices($selected_indices);
$this->UserIps->Text = $config['ips'];
$this->UserEnabled->Checked = ($config['enabled'] == 1);
}
$config['description'] = $this->UserDescription->Text;
$config['email'] = $this->UserEmail->Text;
+ // set roles config values
$selected_indices = $this->UserRoles->getSelectedIndices();
$roles = [];
foreach ($selected_indices as $indice) {
}
}
$config['roles'] = implode(',', $roles);
- $config['api_hosts'] = $this->UserAPIHost->SelectedValue;
+
+ // set API hosts config values
+ $selected_indices = $this->UserAPIHosts->getSelectedIndices();
+ $api_hosts = [];
+ foreach ($selected_indices as $indice) {
+ for ($i = 0; $i < $this->UserAPIHosts->getItemCount(); $i++) {
+ if ($i === $indice) {
+ $api_hosts[] = $this->UserAPIHosts->Items[$i]->Value;
+ }
+ }
+ }
+ $config['api_hosts'] = $api_hosts;
$config['ips'] = $this->trimIps($this->UserIps->Text);
$config['enabled'] = $this->UserEnabled->Checked ? 1 : 0;
$result = $this->getModule('user_config')->setUserConfig($username, $config);
$this->setRoles($this->GetUsersDefaultRole, WebUserRoles::NORMAL);
// set API hosts
- $this->setAPIHosts($this->GetUsersDefaultAPIHost, HostConfig::MAIN_CATALOG_HOST);
+ $this->setAPIHosts($this->GetUsersDefaultAPIHosts, HostConfig::MAIN_CATALOG_HOST, false);
}
$params = $this->getBasicParams();
$this->setRoles($this->GetUsersDefaultRole, WebUserRoles::NORMAL);
// set API hosts
- $this->setAPIHosts($this->GetUsersDefaultAPIHost, HostConfig::MAIN_CATALOG_HOST);
+ $this->setAPIHosts($this->GetUsersDefaultAPIHosts, HostConfig::MAIN_CATALOG_HOST, false);
}
$ldap = $this->getModule('ldap');
$roles = implode(',', $role_list);
// Get default API hosts for imported users
- $api_hosts = $this->GetUsersDefaultAPIHost->SelectedValue;
+ $selected_indices = $this->GetUsersDefaultAPIHosts->getSelectedIndices();
+ $api_host_list = [];
+ foreach ($selected_indices as $indice) {
+ for ($i = 0; $i < $this->GetUsersDefaultAPIHosts->getItemCount(); $i++) {
+ if ($i === $indice) {
+ $api_host_list[] = $this->GetUsersDefaultAPIHosts->Items[$i]->Value;
+ }
+ }
+ }
+ $api_hosts = implode(',', $api_host_list);
// Get default IP address restrictions for imported users
$ips = $this->trimIps($this->GetUsersDefaultIps->Text);
$this->ConsoleConfig->setLoadValues(false);
$this->getCallbackClient()->callClientFunction('oBaculaConfigSection.show_sections', [true]);
}
- $this->ConsoleConfig->setHost($this->User->getAPIHosts());
+ $this->ConsoleConfig->setHost($this->User->getDefaultAPIHost());
$this->ConsoleConfig->setComponentName($_SESSION['dir']);
$this->ConsoleConfig->raiseEvent('OnDirectiveListLoad', $this, null);
}
public function removeConsoles($sender, $param) {
$consoles = explode('|', $param->getCallbackParameter());
$res = new BaculaConfigResources();
- $config = $res->getConfigData($this->User->getAPIHosts(), 'dir');
+ $config = $res->getConfigData($this->User->getDefaultAPIHost(), 'dir');
for ($i = 0; $i < count($consoles); $i++) {
$res->removeResourceFromConfig(
$config,
$this->getModule('api')->set(
array('config', 'dir'),
array('config' => json_encode($config)),
- $this->User->getAPIHosts(),
+ $this->User->getDefaultAPIHost(),
false
);
--- /dev/null
+<%@ MasterClass="Application.Web.Layouts.Simple" Theme="Baculum-v2"%>
+<com:TContent ID="Main">
+ <div style="width: 100%; height: 100%;">
+ <com:TPanel ID="SelectAPIHostForm" CssClass="w3-display-middle w3-center" Style="width: 100%; max-width: 440px">
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/logo_xl.png" alt="Baculum - The Bacula web interface" class="w3-block" style="margin-bottom: 10px"/>
+ <h3><%[ Please select API host ]%></h3>
+ <div class="w3-section">
+ <label for="<%=$this->UserAPIHosts->ClientID%>" class="w3-show-inline-block" style="width: 95px"><%[ API host: ]%></label> <com:TDropDownList ID="UserAPIHosts" CssClass="w3-input w3-border w3-show-inline-block" Style="width: 335px" AutoPostBack="true" OnTextChanged="setAPIHost" />
+ </div>
+ </com:TPanel>
+ </div>
+</com:TContent>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2021 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('Application.Web.Class.BaculumWebPage');
+
+/**
+ * Select API host page.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Page
+ * @package Baculum Web
+ */
+class SelectAPIHost extends BaculumWebPage {
+
+ public function onInit($param) {
+ parent::onInit($param);
+ if ($this->IsPostBack || $this->IsCallBack) {
+ return;
+ }
+ $api_hosts = $this->User->getAPIHosts();
+ array_unshift($api_hosts, '');
+ $this->UserAPIHosts->DataSource = array_combine($api_hosts, $api_hosts);
+ $this->UserAPIHosts->dataBind();
+ }
+
+ public function setAPIHost($sender, $param) {
+ $api_host = $this->UserAPIHosts->SelectedValue;
+ if (!empty($api_host)) {
+ $this->User->setDefaultAPIHost($api_host);
+ $this->goToDefaultPage();
+ }
+ }
+}
+?>
Visible="<%=!empty($_SESSION['sd'])%>"
OnClick="setStorage"
/>
- <com:TActiveLinkButton
- CssClass="w3-bar-item w3-button tab_btn"
- Attributes.onclick="W3Tabs.open(this.id, 'configure_devices');"
- Text="<%[ Configure devices ]%>"
- Visible="<%=!empty($_SESSION['sd'])%>"
- OnClick="setDevices"
- />
- <com:TActiveLinkButton
- CssClass="w3-bar-item w3-button tab_btn"
- Attributes.onclick="W3Tabs.open(this.id, 'configure_autochanger');clear_node('#configure_storage div.directive_value');"
- Text="<%[ Configure autochanger ]%>"
- Visible="<%=!empty($_SESSION['sd']) && $this->getIsAutochanger()%>"
- OnClick="setAutochanger"
- />
+ <a id="btn_storagedaemon_config" href="javascript:void(0)" class="w3-bar-item w3-button tab_btn" onclick="load_sd_storagedaemon_config(); W3Tabs.open(this.id, 'configure_storage_daemon');">
+ <%[ Configure storage daemon ]%>
+ </a>
<com:TActiveLinkButton
CssClass="w3-bar-item w3-button tab_btn"
Attributes.onclick="W3Tabs.open(this.id, 'manage_autochanger');"
Text="<%[ Manage autochanger ]%>"
- Visible="<%=!empty($_SESSION['sd']) && $this->getIsAutochanger()%>"
+ Visible="<%=$this->getIsAutochanger()%>"
OnCallback="loadAutochanger"
/>
+ <com:TLabel ID="UserAPIHostsContainter" CssClass="w3-right w3-margin-right">
+ <%[ SD API host ]%>
+ <com:TActiveDropDownList
+ ID="UserAPIHosts"
+ CssClass="w3-select w3-border"
+ Width="200px"
+ />
+ </com:TLabel>
</div>
<div class="w3-container tab_item" id="storage_actions">
<com:TValidationSummary
ClientSide.OnLoading="$('#status_storage_loading').show();$('#status_storage_error').hide();"
ClientSide.OnSuccess="$('#status_storage_loading').hide(); $('#show_storage_container').hide();$('#status_storage_container').show();oGraphicalStorageStatus.set_refresh_timeout(document.getElementById('status_storage_refresh_interval').value);"
ClientSide.OnFailure="$('#status_storage_loading').hide();status_storage_show_error(parameter);"
- Attributes.onclick="hide_action_text_output(event);"
+ Attributes.onclick="hide_first_tab_containers(event);"
>
<prop:Text><%=Prado::localize('Status storage')%> <i class="fa fa-file-medical-alt"></i></prop:Text>
</com:TActiveLinkButton>
</script>
<i id="status_storage_loading" class="fa fa-sync w3-spin" style="display: none; vertical-align: super;"></i> <span id="status_storage_error" class="w3-text-red" style="display: none"></span>
<com:TActivePanel ID="Autochanger" Display="None" Height="61px">
- <div class="w3-left w3-margin-right">
- <label><%[ Drive number: ]%></label>
- <com:TActiveTextBox
- ID="Drive"
- AutoPostBack="false"
- Text="0"
- MaxLength="3"
- CssClass="w3-input smallbox"
- />
- <com:TDataTypeValidator
- ID="DriveValidator"
- ValidationGroup="AutoChangerGroup"
- ControlToValidate="Drive"
- ErrorMessage="<%[ Drive number must be integer. ]%>"
- Display="None"
- DataType="Integer"
- />
- </div>
- <div class="w3-left">
- <%[ Slot number: ]%>
- <com:TActiveTextBox
- ID="Slot"
- AutoPostBack="false"
- Text="0"
- MaxLength="3"
- CssClass="w3-input smallbox"
- />
- <com:TDataTypeValidator
- ID="SlotValidator"
- ValidationGroup="AutoChangerGroup"
- ControlToValidate="Slot"
- ErrorMessage="<%[ Slot number must be integer. ]%>"
- Display="None"
- DataType="Integer"
- />
- </div>
+ <div class="w3-left w3-margin-right">
+ <label><%[ Drive number: ]%></label>
+ <com:TActiveTextBox
+ ID="Drive"
+ AutoPostBack="false"
+ Text="0"
+ MaxLength="3"
+ CssClass="w3-input smallbox"
+ />
+ <com:TDataTypeValidator
+ ID="DriveValidator"
+ ValidationGroup="AutoChangerGroup"
+ ControlToValidate="Drive"
+ ErrorMessage="<%[ Drive number must be integer. ]%>"
+ Display="None"
+ DataType="Integer"
+ />
+ </div>
+ <div class="w3-left">
+ <%[ Slot number: ]%>
+ <com:TActiveTextBox
+ ID="Slot"
+ AutoPostBack="false"
+ Text="0"
+ MaxLength="3"
+ CssClass="w3-input smallbox"
+ />
+ <com:TDataTypeValidator
+ ID="SlotValidator"
+ ValidationGroup="AutoChangerGroup"
+ ControlToValidate="Slot"
+ ErrorMessage="<%[ Slot number must be integer. ]%>"
+ Display="None"
+ DataType="Integer"
+ />
+ </div>
</com:TActivePanel>
<div id="storage_action_text_output" class="w3-code" style="display: none; clear: both;">
<pre id="storage_action_log"></pre>
</div>
+ <div id="storage_info_cards" class="w3-row w3-margin-top" style="display: flex; flex-wrap: wrap;">
+ <div class="w3-card w3-padding w3-margin-right w3-margin-bottom details_card">
+ <h4><%[ General ]%></h4>
+ <table style="width: 90%">
+ <tr>
+ <td><%[ SD address ]%></td>
+ <td><strong><com:TActiveLabel ID="OSDAddress" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ SD port ]%></td>
+ <td><strong><com:TActiveLabel ID="OSDPort" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ Running jobs ]%></td>
+ <td><strong><com:TActiveLabel ID="ORunningJobs" /></strong></td>
+ </tr>
+ </table>
+ </div>
+ <div class="w3-card w3-padding w3-margin-right w3-margin-bottom details_card">
+ <h4><%[ Device ]%></h4>
+ <table style="width: 90%">
+ <tr>
+ <td class="w3-half"><%[ Device name ]%></td>
+ <td><strong><com:TActiveLabel ID="ODeviceName" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ Media type ]%></td>
+ <td><strong><com:TActiveLabel ID="OMediaType" CssClass="time" /></strong></td>
+ </tr>
+ <tr>
+ <td><%[ Autochanger ]%></td>
+ <td><strong><com:TActiveLabel ID="OAutoChanger" /></strong></td>
+ </tr>
+ </table>
+ </div>
+ </div>
<div id="status_storage_container" class="w3-clear w3-margin-top" style="display: none">
<div class="w3-right w3-margin-top w3-margin-right" title="<%[ To disable refreshing please type 0. ]%>">
<span style="line-height: 41px"><%[ Refresh interval (sec.): ]%></span> <input type="text" id="status_storage_refresh_interval" class="w3-input w3-border w3-right w3-margin-left" value="10" style="width: 50px"/>
}
};
-function hide_action_text_output(e) {
+function hide_first_tab_containers(e) {
if (e.hasOwnProperty('originalEvent') && e.originalEvent.type == 'click') {
$('#storage_action_text_output').slideUp('fast');
+ $('#storage_info_cards').slideUp('fast');
}
}
</div>
<div class="w3-container tab_item" id="configure_storage" style="display: none">
<com:Application.Web.Portlets.BaculaConfigDirectives
- ID="StorageConfig"
+ ID="DIRStorageConfig"
ComponentType="dir"
ResourceType="Storage"
ShowCancelButton="false"
/>
</div>
- <div class="w3-container tab_item" id="configure_devices" style="display: none">
- <com:TActiveRepeater ID="Devices">
- <prop:HeaderTemplate>
- <table id="storage_list" class="w3-table w3-striped w3-hoverable w3-white w3-margin-bottom">
- <thead>
- <tr>
- <th><%[ Device name ]%></th>
- <th><%[ Autochanger ]%></th>
- <th class="w3-center"><%[ Actions ]%></th>
- </tr>
- </thead>
- </prop:HeaderTemplate>
- <prop:ItemTemplate>
- <tr>
- <td><%#$this->Data%></td>
- <td><%=$this->TemplateControl->IsAutochanger ? $this->TemplateControl->getDeviceName() : '-'%></td>
- <td class="w3-center"><button type="button" class="w3-button w3-green" onclick="document.location.href='<%=$this->Service->constructUrl('DeviceView', array('storageid' => $this->TemplateControl->getStorageId(), 'device' => $this->Data))%>';"><i class="fa fa-list-ul"></i> <%[ Details ]%></button></td>
- </tr>
- </prop:ItemTemplate>
- <prop:FooterTemplate>
- </table>
- </prop:FooterTemplate>
- </com:TActiveRepeater>
- </table>
+ <div class="w3-container tab_item" id="configure_storage_daemon" style="display: none">
+ <com:TCallback ID="LoadStorageDaemonConfig" OnCallback="loadSDStorageDaemonConfig" />
+ <script>
+function load_sd_storagedaemon_config() {
+ var cb = <%=$this->LoadStorageDaemonConfig->ActiveControl->Javascript%>;
+ cb.dispatch();
+}
+ </script>
+ <com:TCallback ID="LoadStorageDaemonResourcesConfig" OnCallback="loadSDResourcesConfig" />
+ <script>
+function load_sd_res_config(resource) {
+ var cb = <%=$this->LoadStorageDaemonResourcesConfig->ActiveControl->Javascript%>;
+ cb.setCallbackParameter(resource);
+ cb.dispatch();
+}
+ </script>
+ <div class="w3-row w3-margin-bottom">
+ <a href="javascript:void(0)" onclick="load_sd_storagedaemon_config(); W3SubTabs.open('sd_storagedaemon_config_btn', 'sd_storagedaemon_config_form');">
+ <div id="sd_storagedaemon_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding w3-border-red">Storage</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_sd_res_config('Director'); W3SubTabs.open('storagedaemon_director_config_btn', 'sd_resources_config_form');">
+ <div id="storagedaemon_director_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Director</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_sd_res_config('Device'); W3SubTabs.open('storagedaemon_device_config_btn', 'sd_resources_config_form');">
+ <div id="storagedaemon_device_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Device</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_sd_res_config('Autochanger'); W3SubTabs.open('storagedaemon_autochanger_config_btn', 'sd_resources_config_form');">
+ <div id="storagedaemon_autochanger_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Autochanger</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_sd_res_config('Messages'); W3SubTabs.open('storagedaemon_messages_config_btn', 'sd_resources_config_form');">
+ <div id="storagedaemon_messages_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Messages</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_sd_res_config('Cloud'); W3SubTabs.open('storagedaemon_cloud_config_btn', 'sd_resources_config_form');">
+ <div id="storagedaemon_cloud_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Cloud</div>
+ </a>
+ <a href="javascript:void(0)" onclick="load_sd_res_config('Statistics'); W3SubTabs.open('storagedaemon_statistics_config_btn', 'sd_resources_config_form');">
+ <div id="storagedaemon_statistics_config_btn" class="subtab_btn w3-col m1 w3-bottombar w3-hover-light-grey w3-padding">Statistics</div>
+ </a>
+ </div>
+ <div id="sd_storagedaemon_config_form" class="subtab_item">
+ <com:TActiveLabel
+ ID="SDStorageDaemonConfigErr"
+ Text="<%[ There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration. ]%>"
+ CssClass="w3-text-red"
+ Display="None"
+ />
+ <com:Application.Web.Portlets.BaculaConfigDirectives
+ ID="SDStorageDaemonConfig"
+ ComponentType="sd"
+ ResourceType="Storage"
+ ShowCancelButton="false"
+ />
+ </div>
+ <div id="sd_resources_config_form" class="subtab_item" style="display: none">
+ <com:Application.Web.Portlets.BaculaConfigResourceList
+ ID="StorageDaemonResourcesConfig"
+ ComponentType="sd"
+ ResourceList="<%=[[ 'name' => 'Name', 'label' => 'Name' ]]%>"
+ />
+ </div>
</div>
<div class="w3-container tab_item" id="configure_autochanger" style="display: none">
<com:Application.Web.Portlets.BaculaConfigDirectives
const STORAGEID = 'StorageId';
const STORAGE_NAME = 'StorageName';
+ const STORAGE_ADDRESS = 'StorageAddress';
const IS_AUTOCHANGER = 'IsAutochanger';
const DEVICE_NAME = 'DeviceName';
}
}
}
- $storage = $this->Application->getModule('api')->get(
- array('storages', $storageid),
+ $this->setStorageId($storageid);
+ $storageshow = $this->getModule('api')->get(
+ ['storages', $storageid, 'show', '?output=json'],
null,
- true,
- self::USE_CACHE
- )->output;
- $this->setStorageId($storage->storageid);
- $this->setStorageName($storage->name);
- $is_autochanger = ($storage->autochanger == 1);
- $this->setIsAutochanger($is_autochanger);
- $this->Autochanger->Display = $is_autochanger ? 'Dynamic': 'None';
- $storageshow = $this->Application->getModule('api')->get(
- array('storages', $storage->storageid, 'show')
- )->output;
- $this->setStorageDevice($storageshow);
- $this->setDevices();
- }
+ true
+ );
+ if ($storageshow->error === 0) {
+ $this->setStorageName($storageshow->output->name);
- public function setStorageDevice($storageshow) {
- /**
- * Note, it cannot be get by api config because user can have bdirjson not configured.
- */
- for ($i = 0; $i < count($storageshow); $i++) {
- if (preg_match('/^\s+DeviceName=(?P<device>[\s\S]+)\sMediaType=/', $storageshow[$i], $match) === 1) {
- $this->setDeviceName($match['device']);
+ if (property_exists($storageshow->output, 'address')) {
+ $this->setStorageAddress($storageshow->output->address);
+ $this->OSDAddress->Text = $storageshow->output->address;
+ }
+ if (property_exists($storageshow->output, 'sdport')) {
+ $this->OSDPort->Text = $storageshow->output->sdport;
+ }
+ if (property_exists($storageshow->output, 'maxjobs') && property_exists($storageshow->output, 'numjobs')) {
+ $this->ORunningJobs->Text = $storageshow->output->numjobs . '/' . $storageshow->output->maxjobs;
+ }
+ if (property_exists($storageshow->output, 'devicename')) {
+ $this->setDeviceName($storageshow->output->devicename);
+ $this->ODeviceName->Text = $storageshow->output->devicename;
+ }
+ if (property_exists($storageshow->output, 'mediatype')) {
+ $this->OMediaType->Text = $storageshow->output->mediatype;
+ }
+ if (property_exists($storageshow->output, 'autochanger')) {
+ $is_autochanger = ($storageshow->output->autochanger == 1);
+ $this->setIsAutochanger($is_autochanger);
+ $this->OAutoChanger->Text = $is_autochanger ? Prado::localize('Yes') : Prado::localize('No');
+ $this->Autochanger->Display = $is_autochanger ? 'Dynamic': 'None';
}
}
+ $this->setAPIHosts();
}
- public function setDevices() {
- $devices = array();
- if ($this->getIsAutochanger() && !empty($_SESSION['sd'])) {
- /**
- * NOTE: Here is called only Main API host. For storage daemons
- * on other hosts it can cause a problem. So far, there
- * is no 100% way to unambiguously determine basing on storage daemon
- * configuration if autochanger comes from Main or from other API host.
- * The problem will be if on Main host is defined autochanger
- * with the same name as autochanger from requested Storage here.
- * @TODO: Find a way to solve it.
- */
- $result = $this->Application->getModule('api')->get(
- array(
- 'config',
- 'sd',
- 'Autochanger',
- $this->getDeviceName()
- )
- );
- if ($result->error === 0 && is_object($result->output)) {
- $devices = $result->output->Device;
+ private function setAPIHosts() {
+ $def_host = null;
+ $api_hosts = $this->getModule('host_config')->getConfig();
+ $user_api_hosts = $this->User->getAPIHosts();
+ $storage_address = $this->getStorageAddress();
+ foreach ($api_hosts as $name => $attrs) {
+ if (in_array($name, $user_api_hosts) && $attrs['address'] === $storage_address) {
+ $def_host = $name;
+ break;
}
+ }
+ $this->UserAPIHosts->DataSource = array_combine($user_api_hosts, $user_api_hosts);
+ if ($def_host) {
+ $this->UserAPIHosts->SelectedValue = $def_host;
} else {
- $devices = array($this->getDeviceName());
+ $this->UserAPIHosts->SelectedValue = $this->User->getDefaultAPIHost();
+ }
+ $this->UserAPIHosts->dataBind();
+ if (count($user_api_hosts) === 1) {
+ $this->UserAPIHostsContainter->Visible = false;
}
- $this->Devices->DataSource = $devices;
- $this->Devices->dataBind();
- }
-
- /**
- * Set storage storageid.
- *
- * @return none;
- */
- public function setStorageId($storageid) {
- $storageid = intval($storageid);
- $this->setViewState(self::STORAGEID, $storageid, 0);
- }
-
- /**
- * Get storage storageid.
- *
- * @return integer storageid
- */
- public function getStorageId() {
- return $this->getViewState(self::STORAGEID, 0);
- }
-
- /**
- * Set storage name.
- *
- * @return none;
- */
- public function setStorageName($storage_name) {
- $this->setViewState(self::STORAGE_NAME, $storage_name);
- }
-
- /**
- * Get storage name.
- *
- * @return string storage name
- */
- public function getStorageName() {
- return $this->getViewState(self::STORAGE_NAME);
- }
-
- /**
- * Set device name.
- *
- * @return none;
- */
- public function setDeviceName($device_name) {
- $this->setViewState(self::DEVICE_NAME, $device_name);
- }
-
- /**
- * Get device name.
- *
- * @return string device name
- */
- public function getDeviceName() {
- return $this->getViewState(self::DEVICE_NAME);
- }
-
- /**
- * Check if storage is autochanger
- *
- * @return bool true if autochanger, otherwise false
- */
- public function getIsAutochanger() {
- return $this->getViewState(self::IS_AUTOCHANGER, false);
- }
-
- /**
- * Set autochanger value for storage
- *
- * @return none;
- */
- public function setIsAutochanger($is_autochanger) {
- settype($is_autochanger, 'bool');
- $this->setViewState(self::IS_AUTOCHANGER, $is_autochanger);
}
public function status($sender, $param) {
$this->getCallbackClient()->callClientFunction('init_graphical_storage_status', [$storage_status]);
}
+ private function getSDAPIHost() {
+ if (!$this->User->isUserAPIHost($this->UserAPIHosts->SelectedValue)) {
+ // Validation error. Somebody manually modified select values
+ return false;
+ }
+ return $this->UserAPIHosts->SelectedValue;
+ }
+
+ private function getSDName() {
+ if (!($host = $this->getSDAPIHost())) {
+ return;
+ }
+ $sdname = null;
+ $result = $this->getModule('api')->get(['config'], $host);
+ if ($result->error === 0) {
+ for ($i = 0; $i < count($result->output); $i++) {
+ if ($result->output[$i]->component_type === 'sd' && $result->output[$i]->state) {
+ $sdname = $result->output[$i]->component_name;
+ }
+
+ }
+ }
+ return $sdname;
+ }
+
+ public function setStorage($sender, $param) {
+ $this->SDStorageDaemonConfig->unloadDirectives();
+ if (!empty($_SESSION['dir'])) {
+ $this->DIRStorageConfig->setComponentName($_SESSION['dir']);
+ $this->DIRStorageConfig->setResourceName($this->getStorageName());
+ $this->DIRStorageConfig->setLoadValues(true);
+ $this->DIRStorageConfig->raiseEvent('OnDirectiveListLoad', $this, null);
+ }
+ }
+
+ public function loadSDStorageDaemonConfig($sender, $param) {
+ if (!($host = $this->getSDAPIHost())) {
+ return;
+ }
+ $this->DIRStorageConfig->unloadDirectives();
+ $component_name = $this->getSDName();
+ if (!is_null($component_name)) {
+ $this->SDStorageDaemonConfigErr->Display = 'None';
+ $this->SDStorageDaemonConfig->setHost($host);
+ $this->SDStorageDaemonConfig->setComponentName($component_name);
+ $this->SDStorageDaemonConfig->setResourceName($component_name);
+ $this->SDStorageDaemonConfig->setLoadValues(true);
+ $this->SDStorageDaemonConfig->raiseEvent('OnDirectiveListLoad', $this, null);
+ } else {
+ $this->SDStorageDaemonConfigErr->Display = 'Dynamic';
+ }
+ }
+
+ public function loadSDResourcesConfig($sender, $param) {
+ if (!($host = $this->getSDAPIHost())) {
+ return;
+ }
+ $resource_type = $param->getCallbackParameter();
+ $this->DIRStorageConfig->unloadDirectives();
+ $this->SDStorageDaemonConfig->unloadDirectives();
+ $component_name = $this->getSDName();
+ if (!is_null($component_name) && !empty($resource_type)) {
+ $this->StorageDaemonResourcesConfig->setHost($host);
+ $this->StorageDaemonResourcesConfig->setResourceType($resource_type);
+ $this->StorageDaemonResourcesConfig->setComponentName($component_name);
+ $this->StorageDaemonResourcesConfig->loadResourceListTable();
+ } else {
+ $this->StorageDaemonResourcesConfig->showError(true);
+ }
+ }
+
private function actionLoading($result, $out_id, $refresh_func) {
$messages_log = $this->getModule('messages_log');
if ($result->error === 0) {
'drive' => $drive
];
$query = '?' . http_build_query($params);
- $result = $this->getModule('api')->set(
- [
- 'storages',
- $this->getStorageId(),
- 'umount',
- $query
- ]
- );
+ $result = $this->getModule('api')->set([
+ 'storages',
+ $this->getStorageId(),
+ 'umount',
+ $query
+ ]);
return $result;
}
'drive' => $drive
];
$query = '?' . http_build_query($params);
- $result = $this->getModule('api')->set(
- [
- 'storages',
- $this->getStorageId(),
- 'release',
- $query
- ]
- );
+ $result = $this->getModule('api')->set([
+ 'storages',
+ $this->getStorageId(),
+ 'release',
+ $query
+ ]);
return $result;
}
}
}
- public function setStorage($sender, $param) {
- $this->StorageConfig->setComponentName($_SESSION['sd']);
- $this->StorageConfig->setResourceName($this->getStorageName());
- $this->StorageConfig->setLoadValues(true);
- $this->StorageConfig->raiseEvent('OnDirectiveListLoad', $this, null);
- }
-
public function loadAutochanger($sender, $param) {
- $result = $this->getModule('api')->get([
- 'devices',
- $this->getDeviceName(),
- 'listall'
- ]);
+ if (!($host = $this->getSDAPIHost())) {
+ return;
+ }
+ $result = $this->getModule('api')->get(
+ [
+ 'devices',
+ $this->getDeviceName(),
+ 'listall'
+ ],
+ $host
+ );
$cb = $this->getCallbackClient();
if ($result->error === 0) {
$cb->show('drive_list_container');
$query
]);
} else {
- $parameters = [
- 'drive' => $data->drive,
- 'slot' => $data->slot
- ];
- $query = '?' . http_build_query($parameters);
- $result = $this->getModule('api')->set([
- 'devices',
- $this->getDeviceName(),
- 'load',
- $query
- ]);
+ if ($host = $this->getSDAPIHost()) {
+ $parameters = [
+ 'drive' => $data->drive,
+ 'slot' => $data->slot
+ ];
+ $query = '?' . http_build_query($parameters);
+ $result = $this->getModule('api')->set(
+ [
+ 'devices',
+ $this->getDeviceName(),
+ 'load',
+ $query
+ ],
+ [],
+ $host
+ );
+ } else {
+ $result = new StdClass;
+ $result->error = DeviceError::ERROR_DEVICE_AUTOCHANGER_DRIVE_DOES_NOT_EXIST;
+ $result->output = Prado::localize('There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration.');
+ }
}
if ($result->error === 0) {
$this->getCallbackClient()->callClientFunction(
}
public function loadedDriveWithoutMount($sender, $param) {
+ if (!($host = $this->getSDAPIHost())) {
+ return;
+ }
$out_id = $param->getCallbackParameter();
$parameters = [
'out_id' => $out_id
];
$query = '?' . http_build_query($parameters);
- $result = $this->getModule('api')->get([
- 'devices',
- $this->getDeviceName(),
- 'load',
- $query
- ]);
+ $result = $this->getModule('api')->get(
+ [
+ 'devices',
+ $this->getDeviceName(),
+ 'load',
+ $query
+ ],
+ $host
+ );
$this->loadedDrive(
'oSlots.refresh_drive_without_mount_loading',
$out_id,
}
private function transferSlots($slotsrc, $slotdest) {
+ if (!($host = $this->getSDAPIHost())) {
+ return;
+ }
$parameters = [
'slotsrc' => $slotsrc,
'slotdest' => $slotdest
];
$query = '?' . http_build_query($parameters);
- $result = $this->getModule('api')->set([
- 'devices',
- $this->getDeviceName(),
- 'transfer',
- $query
- ]);
+ $result = $this->getModule('api')->set(
+ [
+ 'devices',
+ $this->getDeviceName(),
+ 'transfer',
+ $query
+ ],
+ [],
+ $host
+ );
return $result;
}
private function getTransferOutput($out_id) {
+ if (!($host = $this->getSDAPIHost())) {
+ return;
+ }
$parameters = [
'out_id' => $out_id
];
$query = '?' . http_build_query($parameters);
- $result = $this->getModule('api')->get([
- 'devices',
- $this->getDeviceName(),
- 'transfer',
- $query
- ]);
+ $result = $this->getModule('api')->get(
+ [
+ 'devices',
+ $this->getDeviceName(),
+ 'transfer',
+ $query
+ ],
+ $host
+ );
return $result;
}
[false]
);
}
+
+ /**
+ * Set storage storageid.
+ *
+ * @return none;
+ */
+ public function setStorageId($storageid) {
+ $storageid = intval($storageid);
+ $this->setViewState(self::STORAGEID, $storageid, 0);
+ }
+
+ /**
+ * Get storage storageid.
+ *
+ * @return integer storageid
+ */
+ public function getStorageId() {
+ return $this->getViewState(self::STORAGEID, 0);
+ }
+
+ /**
+ * Set storage name.
+ *
+ * @return none;
+ */
+ public function setStorageName($storage_name) {
+ $this->setViewState(self::STORAGE_NAME, $storage_name);
+ }
+
+ /**
+ * Get storage name.
+ *
+ * @return string storage name
+ */
+ public function getStorageName() {
+ return $this->getViewState(self::STORAGE_NAME);
+ }
+
+ /**
+ * Set device name.
+ *
+ * @return none;
+ */
+ public function setDeviceName($device_name) {
+ $this->setViewState(self::DEVICE_NAME, $device_name);
+ }
+
+ /**
+ * Get device name.
+ *
+ * @return string device name
+ */
+ public function getDeviceName() {
+ return $this->getViewState(self::DEVICE_NAME);
+ }
+
+ /**
+ * Check if storage is autochanger
+ *
+ * @return bool true if autochanger, otherwise false
+ */
+ public function getIsAutochanger() {
+ return $this->getViewState(self::IS_AUTOCHANGER, false);
+ }
+
+ /**
+ * Set autochanger value for storage
+ *
+ * @return none;
+ */
+ public function setIsAutochanger($is_autochanger) {
+ settype($is_autochanger, 'bool');
+ $this->setViewState(self::IS_AUTOCHANGER, $is_autochanger);
+ }
+
+ /**
+ * Set storage address.
+ *
+ * @return none;
+ */
+ public function setStorageAddress($address) {
+ $this->setViewState(self::STORAGE_ADDRESS, $address);
+ }
+
+ /**
+ * Get storage address.
+ *
+ * @return string address
+ */
+ public function getStorageAddress() {
+ return $this->getViewState(self::STORAGE_ADDRESS);
+ }
}
?>
--- /dev/null
+<?php
+/*
+ * Bacula(R) - The Network Backup Solution
+ * Baculum - Bacula web interface
+ *
+ * Copyright (C) 2013-2021 Kern Sibbald
+ *
+ * The main author of Baculum is Marcin Haba.
+ * The original author of Bacula is Kern Sibbald, with contributions
+ * from many others, a complete list can be found in the file AUTHORS.
+ *
+ * You may use this file and others of this release according to the
+ * license defined in the LICENSE file, which includes the Affero General
+ * Public License, v3.0 ("AGPLv3") and some additional permissions and
+ * terms pursuant to its AGPLv3 Section 7.
+ *
+ * This notice must be preserved when any source code is
+ * conveyed and/or propagated.
+ *
+ * Bacula(R) is a registered trademark of Kern Sibbald.
+ */
+
+Prado::using('System.Web.UI.ActiveControls.TActiveLabel');
+Prado::using('System.Web.UI.ActiveControls.TActiveRepeater');
+Prado::using('Application.Web.Portlets.BaculaConfigResources');
+Prado::using('Application.Web.Portlets.Portlets');
+
+/**
+ * Bacula config resource list control.
+ *
+ * @author Marcin Haba <marcin.haba@bacula.pl>
+ * @category Control
+ * @package Baculum Web
+ */
+class BaculaConfigResourceList extends Portlets {
+
+ const HOST = 'Host';
+ const COMPONENT_TYPE = 'ComponentType';
+ const COMPONENT_NAME = 'ComponentName';
+ const RESOURCE_TYPE = 'ResourceType';
+ const RESOURCE_LIST = 'ResourceList';
+
+ public function onPreRender($param) {
+ parent::onPreRender($param);
+ if ($this->getPage()->IsCallBack || $this->getPage()->IsPostBack) {
+ return;
+ }
+ $this->prepareTable();
+ }
+
+ private function prepareTable() {
+ $res_list = $this->getResourceList();
+ $this->ResourceListHeaderRepeater->DataSource = $res_list;
+ $this->ResourceListHeaderRepeater->dataBind();
+ $this->ResourceListFooterRepeater->DataSource = $res_list;
+ $this->ResourceListFooterRepeater->dataBind();
+ $this->ResourceListColumnsRepeater->DataSource = $res_list;
+ $this->ResourceListColumnsRepeater->dataBind();
+ }
+
+ public function showError($show) {
+ $cbc = $this->getPage()->getCallbackClient();
+ if ($show) {
+ $cbc->hide($this->ClientID . '_container');
+ $cbc->show($this->ClientID . '_error_msg');
+ } else {
+ $cbc->hide($this->ClientID . '_error_msg');
+ $cbc->show($this->ClientID . '_container');
+ }
+ }
+
+ public function loadResourceListTable() {
+ $this->showError(false);
+ $component_type = $this->getComponentType();
+ $resource_type = $this->getResourceType();
+ $this->ResourceTypeLink->Text = $resource_type;
+ $config = $this->getModule('api')->get(
+ [
+ 'config',
+ $component_type,
+ $resource_type
+ ],
+ $this->getHost()
+ );
+ if ($config->error === 0) {
+ $res_list = $this->getResourceList();
+ $directives = [];
+ for ($i = 0; $i < count($config->output); $i++) {
+ $data = [];
+ for ($j = 0; $j < count($res_list); $j++) {
+ if (property_exists($config->output[$i]->{$resource_type}, $res_list[$j]['name'])) {
+ $data[$res_list[$j]['name']] = $config->output[$i]->{$resource_type}->{$res_list[$j]['name']};
+ }
+ }
+ $directives[] = $data;
+ }
+ $this->getPage()->getCallbackClient()->callClientFunction(
+ 'oBaculaConfigResourceList' . $this->ClientID . '.init',
+ [$directives]
+ );
+ }
+ $this->ResourceConfig->unloadDirectives();
+ }
+
+ public function loadResourceWindow($sender, $param) {
+ list($cmd, $name) = $param->getCallbackParameter();
+ if (!empty($name)) {
+ // edit existing resource
+ $this->ResourceConfig->setResourceName($name);
+ $this->ResourceConfig->setLoadValues(true);
+ } else {
+ // add new resource
+ $this->ResourceConfig->setLoadValues(false);
+ $this->getPage()->getCallbackClient()->callClientFunction(
+ 'oBaculaConfigSection.show_sections',
+ [true]
+ );
+ }
+ $host = $this->getHost();
+ $component_type = $this->getComponentType();
+ $component_name = $this->getComponentName();
+ $resource_type = $this->getResourceType();
+ $this->ResourceConfig->setHost($host);
+ $this->ResourceConfig->setComponentType($component_type);
+ $this->ResourceConfig->setComponentName($component_name);
+ $this->ResourceConfig->setResourceType($resource_type);
+ $this->ResourceConfig->raiseEvent('OnDirectiveListLoad', $this, null);
+ }
+
+ public function unloadResourceWindow($sender, $param) {
+ $this->ResourceConfig->unloadDirectives();
+ }
+
+ public function removeResource($sender, $param) {
+ $host = $this->getHost();
+ $component_type = $this->getComponentType();
+ $resource_type = $this->getResourceType();
+ $resource_name = $param->getCallbackParameter();
+ $result = $this->getModule('api')->get(
+ [
+ 'config',
+ $component_type
+ ],
+ $host
+ );
+ $config = [];
+ if (is_object($result) && $result->error === 0 && is_array($result->output)) {
+ $config = $result->output;
+ }
+ $deps = $this->getModule('data_deps')->checkDependencies(
+ $component_type,
+ $resource_type,
+ $resource_name,
+ $config
+ );
+ if (count($deps) === 0) {
+ // NO DEPENDENCY. Ready to remove.
+ BaculaConfigResources::removeResourceFromConfig(
+ $config,
+ $resource_type,
+ $resource_name
+ );
+ $result = $this->getModule('api')->set(
+ ['config', $component_type],
+ ['config' => json_encode($config)],
+ $host,
+ false
+ );
+ if ($result->error !== 0) {
+ $this->showRemovedResourceError($result->output);
+ } else {
+ $this->loadResourceListTable($sender, $param);
+ }
+ } else {
+ // DEPENDENCIES EXIST. List them on the interface.
+ $error_message = BaculaConfigResources::prepareDependenciesError(
+ $deps,
+ $resource_type,
+ $resource_name
+ );
+ $this->showRemovedResourceError($error_message);
+ }
+ }
+
+ private function showRemovedResourceError($error_message) {
+ $this->RemoveResourceError->Text = $error_message;
+ $err_win_id = 'resource_error_window' . $this->ClientID;
+ $this->getPage()->getCallbackClient()->show($err_win_id);
+ }
+
+ public function getHost() {
+ return $this->getViewState(self::HOST);
+ }
+
+ public function setHost($host) {
+ $this->setViewState(self::HOST, $host);
+ }
+
+ public function getComponentType() {
+ return $this->getViewState(self::COMPONENT_TYPE);
+ }
+
+ public function setComponentType($type) {
+ $this->setViewState(self::COMPONENT_TYPE, $type);
+ }
+
+ public function getComponentName() {
+ return $this->getViewState(self::COMPONENT_NAME);
+ }
+
+ public function setComponentName($name) {
+ $this->setViewState(self::COMPONENT_NAME, $name);
+ }
+
+ public function getResourceType() {
+ return $this->getViewState(self::RESOURCE_TYPE);
+ }
+
+ public function setResourceType($type) {
+ $this->setViewState(self::RESOURCE_TYPE, $type);
+ }
+
+ public function getResourceList() {
+ return $this->getViewState(self::RESOURCE_LIST, []);
+ }
+
+ public function setResourceList($list) {
+ $this->setViewState(self::RESOURCE_LIST, $list);
+ }
+}
+?>
+
--- /dev/null
+<span id="<%=$this->ClientID%>_error_msg" class="w3-text-red" style="display: none">
+ <%[ There was a problem with loading the resource configuration. Please check if selected API host is working and if it provides access to the resource configuration. ]%>
+</span>
+<div id="<%=$this->ClientID%>_container">
+ <div class="w3-container">
+ <a href="javascript:void(0)" class="w3-button w3-margin-bottom w3-green" onclick="oBaculaConfigResourceWindow<%=$this->ClientID%>.load_resource_window();"><i class="fa fa-plus"></i> <%[ Add ]%> <com:TActiveLabel ID="ResourceTypeLink" /></a>
+ </div>
+ <table id="<%=$this->ClientID%>_list" class="w3-table w3-striped w3-hoverable w3-white w3-margin-bottom" style="width: 100%">
+ <thead>
+ <tr>
+ <th></th>
+ <com:Application.Common.Portlets.BSimpleRepeater ID="ResourceListHeaderRepeater">
+ <prop:ItemTemplate>
+ <th class="center"><%=$this->Data['label']%></th>
+ </prop:ItemTemplate>
+ </com:Application.Common.Portlets.BSimpleRepeater>
+ <th><%[ Actions ]%></th>
+ </tr>
+ </thead>
+ <tbody id="<%=$this->ClientID%>_list_body"></tbody>
+ <tfoot>
+ <tr>
+ <th></th>
+ <com:Application.Common.Portlets.BSimpleRepeater ID="ResourceListFooterRepeater">
+ <prop:ItemTemplate>
+ <th class="center"><%=$this->Data['label']%></th>
+ </prop:ItemTemplate>
+ </com:Application.Common.Portlets.BSimpleRepeater>
+ <th><%[ Actions ]%></th>
+ </tr>
+ </tfoot>
+ </table>
+</div>
+<script>
+var oBaculaConfigResourceList<%=$this->ClientID%> = {
+ ids: {
+ list: '<%=$this->ClientID%>_list',
+ list_body: '<%=$this->ClientID%>_list_body'
+ },
+ data: [],
+ table: null,
+ init: function(data) {
+ var self = oBaculaConfigResourceList<%=$this->ClientID%>;
+ self.data = data;
+ if (self.table) {
+ var page = self.table.page();
+ self.table.clear().rows.add(self.data).draw();
+ self.table.page(page).draw(false);
+ } else {
+ self.set_table();
+ }
+ },
+ set_table: function() {
+ this.table = $('#' + this.ids.list).DataTable({
+ data: this.data,
+ deferRender: true,
+ dom: 'lBfrtip',
+ stateSave: true,
+ buttons: [
+ 'copy', 'csv', 'colvis'
+ ],
+ columns: [
+ {
+ className: 'details-control',
+ orderable: false,
+ data: null,
+ defaultContent: '<button type="button" class="w3-button w3-blue"><i class="fa fa-angle-down"></i></button>'
+ }
+ <com:Application.Common.Portlets.BSimpleRepeater ID="ResourceListColumnsRepeater">
+ <prop:ItemTemplate>
+ ,{data: '<%=$this->Data['name']%>'}
+ </prop:ItemTemplate>
+ </com:Application.Common.Portlets.BSimpleRepeater>
+ ,{
+ data: 'Name',
+ render: function (data, type, row) {
+ var span = document.createElement('SPAN');
+ span.className = 'w3-right';
+
+ var edit_btn = document.createElement('BUTTON');
+ edit_btn.className = 'w3-button w3-green w3-margin-right';
+ edit_btn.type = 'button';
+ var i = document.createElement('I');
+ i.className = 'fa fa-list-ul';
+ var label = document.createTextNode(' <%[ Edit ]%>');
+ edit_btn.appendChild(i);
+ edit_btn.innerHTML += ' ';
+ edit_btn.appendChild(label);
+ edit_btn.setAttribute('onclick', 'oBaculaConfigResourceWindow<%=$this->ClientID%>.load_resource_window("' + data + '");');
+
+ var del_btn = document.createElement('BUTTON');
+ del_btn.className = 'w3-button w3-red';
+ del_btn.type = 'button';
+ var i = document.createElement('I');
+ i.className = 'fa fa-trash-alt';
+ var label = document.createTextNode(' <%[ Delete ]%>');
+ del_btn.appendChild(i);
+ del_btn.innerHTML += ' ';
+ del_btn.appendChild(label);
+ del_btn.setAttribute('onclick', 'oBaculaConfigResourceWindow<%=$this->ClientID%>.remove_resource("' + data + '")');
+
+ span.appendChild(edit_btn);
+ span.appendChild(del_btn);
+ return span.outerHTML;
+ }
+ }
+ ],
+ responsive: {
+ details: {
+ type: 'column'
+ }
+ },
+ columnDefs: [{
+ className: 'control',
+ orderable: false,
+ targets: 0
+ },
+ {
+ className: "dt-center",
+ targets: [ 2 ]
+ }],
+ order: [1, 'asc']
+ });
+ }
+};
+//oBaculaConfigResourceList<%=$this->ClientID%>.init();
+</script>
+<com:TCallback ID="RemoveResource" OnCallback="removeResource" />
+<div id="resource_window<%=$this->ClientID%>" class="w3-modal">
+ <div class="w3-modal-content w3-animate-top w3-card-4">
+ <header class="w3-container w3-teal">
+ <span onclick="oBaculaConfigResourceWindow<%=$this->ClientID%>.close_resource_window();" class="w3-button w3-display-topright">×</span>
+ <h2 id="resource_window_title_add<%=$this->ClientID%>" style="display: none"><%[ Add ]%> <%=$this->getResourceType()%></h2>
+ <h2 id="resource_window_title_edit<%=$this->ClientID%>" style="display: none"><%[ Edit ]%> <%=$this->getResourceType()%></h2>
+ </header>
+ <div class="w3-container w3-margin-left w3-margin-right w3-margin-top w3-text-teal">
+ <com:Application.Web.Portlets.BaculaConfigDirectives
+ ID="ResourceConfig"
+ ShowRemoveButton="false"
+ ShowCancelButton="false"
+ ShowBottomButtons="false"
+ SaveDirectiveActionOk="oBaculaConfigResourceWindow<%=$this->ClientID%>.close_resource_window();"
+ OnSave="loadResourceListTable"
+ />
+ </div>
+ </div>
+ <com:TActiveHiddenField ID="ResourceWindowType" />
+</div>
+<div id="resource_error_window<%=$this->ClientID%>" class="w3-modal">
+ <div class="w3-modal-content w3-animate-top w3-card-4">
+ <header class="w3-container w3-red">
+ <span onclick="document.getElementById('resource_error_window<%=$this->ClientID%>').style.display = 'none';" class="w3-button w3-display-topright">×</span>
+ <h2><%[ Error ]%></h2>
+ </header>
+ <div class="w3-container w3-margin-left w3-margin">
+ <com:TActiveLabel ID="RemoveResourceError" CssClass="w3-text-red" />
+ </div>
+ <footer class="w3-container w3-center">
+ <button type="button" class="w3-button w3-red w3-margin-bottom" onclick="document.getElementById('resource_error_window<%=$this->ClientID%>').style.display = 'none';"><i class="fas fa-times"></i> <%[ Close ]%></button>
+ </footer>
+ </div>
+</div>
+<com:TCallback ID="LoadResource" OnCallback="TemplateControl.loadResourceWindow" />
+<com:TCallback ID="UnloadResource" OnCallback="TemplateControl.unloadResourceWindow" />
+<script>
+oBaculaConfigResourceWindow<%=$this->ClientID%> = {
+ load_resource_window: function(name) {
+ var title_add = document.getElementById('resource_window_title_add<%=$this->ClientID%>');
+ var title_edit = document.getElementById('resource_window_title_edit<%=$this->ClientID%>');
+ var cb = <%=$this->LoadResource->ActiveControl->Javascript%>;
+ cb.setCallbackParameter([null, name]);
+ cb.dispatch();
+ if (name) {
+ title_add.style.display = 'none';
+ title_edit.style.display = 'inline-block';
+ } else {
+ title_edit.style.display = 'none';
+ title_add.style.display = 'inline-block';
+ }
+ document.getElementById('resource_window<%=$this->ClientID%>').style.display = 'block';
+ },
+ unload_resource_window: function() {
+ var cb = <%=$this->UnloadResource->ActiveControl->Javascript%>;
+ cb.dispatch();
+ },
+ close_resource_window: function() {
+ document.getElementById('resource_window<%=$this->ClientID%>').style.display = 'none';
+ this.unload_resource_window();
+ },
+ remove_resource: function(name) {
+ var cb = <%=$this->RemoveResource->ActiveControl->Javascript%>;
+ cb.setCallbackParameter(name);
+ cb.dispatch();
+ }
+};
+</script>
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
);
if (count($deps) === 0) {
// NO DEPENDENCY. Ready to remove.
- $this->removeResourceFromConfig(
+ self::removeResourceFromConfig(
$config,
$host_params['resource_type'],
$host_params['resource_name']
}
} else {
// DEPENDENCIES EXIST. List them on the interface.
- $this->showDependenciesError(
+ $error_message = self::prepareDependenciesError(
$deps,
$host_params['resource_type'],
$host_params['resource_name']
);
+ $this->showRemovedResourceError($error_message);
}
}
/**
* Show dependencies error message.
+ * NOTE: Method called also externally (@see BaculaConfigResourceList)
*
* @param array $deps list dependencies for the removing resource
* @param string $resource_type resource type of the removing resource
* @param string $resource_name resource name of the removing resource
- * @return none
+ * @return string error message
*/
- private function showDependenciesError($deps, $resource_type, $resource_name) {
+ public static function prepareDependenciesError($deps, $resource_type, $resource_name) {
$emsg = Prado::localize('Resource %s "%s" is used in the following resources:');
$emsg = sprintf($emsg, $resource_type, $resource_name);
$emsg_deps = Prado::localize('Component: %s, Resource: %s "%s", Directive: %s');
$emsg_sum = Prado::localize('Please unassign resource %s "%s" from these resources and try again.');
$emsg_sum = sprintf($emsg_sum, $resource_type, $resource_name);
$error = array($emsg, implode('<br />', $dependencies), $emsg_sum);
- $error_message = implode('<br /><br />', $error);
- $this->showRemovedResourceError($error_message);
+ return implode('<br /><br />', $error);
}
/**
* Remove resource from config.
* Note, passing config by reference.
+ * NOTE: Method called also externally (@see BaculaConfigResourceList)
*
* @param array $config entire config
* @param string $resource_type resource type to remove
* @param string $resource_name resource name to remove
* @return none
*/
- public function removeResourceFromConfig(&$config, $resource_type, $resource_name) {
+ public static function removeResourceFromConfig(&$config, $resource_type, $resource_name) {
for ($i = 0; $i < count($config); $i++) {
foreach ($config[$i] as $rtype => $resource) {
if (!property_exists($resource, 'Name')) {
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$command_param = $this->getPage()->PostBackEventTarget->getCommandParameter();
}
}
+ if (is_array($command_param) && count($command_param) > 0) {
+ $command_param = $command_param[0];
+ }
return $command_param;
}
* Bacula(R) - The Network Backup Solution
* Baculum - Bacula web interface
*
- * Copyright (C) 2013-2019 Kern Sibbald
+ * Copyright (C) 2013-2021 Kern Sibbald
*
* The main author of Baculum is Marcin Haba.
* The original author of Bacula is Kern Sibbald, with contributions
$load_values = $this->getLoadValues();
$dests = $this->getData();
if (key_exists('Destinations', $dests)) {
- $dests = $dests['Destinations'];
+ $dests = array_filter($dests['Destinations']);
}
$directives = array();
for ($i = 0; $i < count($dests); $i++) {
-<button type="button" class="w3-button w3-green w3-margin" onmousedown="openElementOnCursor(event, '<%=$this->MessagesMenu->ClientID%>_new_messages', 0, 20);"><i class="fa fa-plus"></i> <%[ Add ]%></button>
+<button type="button" class="w3-button w3-green w3-margin" onclick="openElementOnCursor(event, '<%=$this->MessagesMenu->ClientID%>_new_messages', 0, 20);"><i class="fa fa-plus"></i> <%[ Add ]%></button>
<p class="bold"><%[ Tip: checking 'All' message type causes, that rest checked message types are saved with negation ex. Catalog = All, !Debug, !Saved, !Skipped ]%></p>
<com:Application.Web.Portlets.NewMessagesMenu ID="MessagesMenu" />
<com:TActiveRepeater ID="RepeaterMessages" OnItemCreated="createDirectiveListElement" OnItemDataBound="loadMessageTypes">
<!-- webGUI endpoints -->
<url ServiceParameter="Dashboard" pattern="web/" />
<url ServiceParameter="LoginPage" pattern="web/login/" />
+ <url ServiceParameter="SelectAPIHost" pattern="web/api-host/" />
<url ServiceParameter="JobHistoryList" pattern="web/job/history/" />
<url ServiceParameter="JobHistoryView" pattern="web/job/history/{jobid}/" parameters.jobid="\d+" />
<url ServiceParameter="JobList" pattern="web/job/" />