Merge branch '2.1.x' of github.com:Froxlor/Froxlor into 2.1.x

This commit is contained in:
Michael Kaufmann 2023-06-08 17:18:24 +02:00
commit a2fca3fe69
9 changed files with 128 additions and 105 deletions

View File

@ -38,7 +38,7 @@ use Froxlor\UI\Response;
$id = (int)Request::any('id');
if (($page == 'backups' || $page == 'overview') && $userinfo['change_serversettings'] == '1') {
if (($page == 'backups' || $page == 'overview')) {
if ($action == '') {
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "viewed admin_backups");
@ -53,10 +53,6 @@ if (($page == 'backups' || $page == 'overview') && $userinfo['change_serversetti
UI::view('user/table.html.twig', [
'listing' => Listing::format($collection, $admin_list_data, 'backups_list'),
'actions_links' => [
[
'href' => $linker->getLink(['section' => 'backups', 'page' => $page, 'action' => 'add']),
'label' => lng('admin.backups_add')
],
[
'href' => $linker->getLink(['section' => 'backups', 'page' => $page, 'action' => 'restore']),
'label' => lng('admin.backups_restore'),
@ -67,7 +63,8 @@ if (($page == 'backups' || $page == 'overview') && $userinfo['change_serversetti
'href' => $linker->getLink(['section' => 'backups', 'page' => 'storages']),
'label' => lng('admin.backup_storages'),
'icon' => 'fa-solid fa-hard-drive',
'class' => 'btn-outline-secondary'
'class' => 'btn-outline-secondary',
'visible' => $userinfo['change_serversettings'] == '1'
]
]
]);
@ -181,4 +178,6 @@ if (($page == 'backups' || $page == 'overview') && $userinfo['change_serversetti
}
}
}
} else {
Response::dynamicError('403');
}

View File

@ -63,24 +63,55 @@ class Backups extends ApiCommand implements ResourceEntity
*/
public function listing()
{
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list backups");
$query_fields = [];
$result_stmt = Database::prepare("
if ($this->isAdmin()) {
// if we're an admin, list all backups of all the admins customers
// or optionally for one specific customer identified by id or loginname
$customerid = $this->getParam('customerid', true, 0);
$loginname = $this->getParam('loginname', true, '');
if (!empty($customerid) || !empty($loginname)) {
$result = $this->apiCall('Customers.get', [
'id' => $customerid,
'loginname' => $loginname
]);
$custom_list_result = [
$result
];
} else {
$_custom_list_result = $this->apiCall('Customers.listing');
$custom_list_result = $_custom_list_result['list'];
}
$customer_ids = [];
foreach ($custom_list_result as $customer) {
$customer_ids[] = $customer['customerid'];
}
if (empty($customer_ids)) {
throw new Exception("Required resource unsatisfied.", 405);
}
} else {
$customer_ids = [
$this->getUserDetail('customerid')
];
}
$this->logger()->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO, "[API] list backups");
$query_fields = [];
$result_stmt = Database::prepare("
SELECT `b`.*, `a`.`loginname` as `adminname`
FROM `" . TABLE_PANEL_BACKUPS . "` `b`
LEFT JOIN `" . TABLE_PANEL_ADMINS . "` `a` USING(`adminid`)
WHERE `b`.`customerid` IN (" . implode(', ', $customer_ids) . ")
");
Database::pexecute($result_stmt, $query_fields, true, true);
$result = [];
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row;
}
return $this->response([
'count' => count($result),
'list' => $result
]);
Database::pexecute($result_stmt, $query_fields, true, true);
$result = [];
while ($row = $result_stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row;
}
return $this->response([
'count' => count($result),
'list' => $result
]);
throw new Exception("Not allowed to execute given command.", 403);
}
@ -93,7 +124,7 @@ class Backups extends ApiCommand implements ResourceEntity
*/
public function listingCount()
{
if ($this->isAdmin() && $this->getUserDetail('change_serversettings') == 1) {
if ($this->isAdmin()) {
$result_stmt = Database::prepare("
SELECT COUNT(*) as num_backups
FROM `" . TABLE_PANEL_BACKUPS . "`

View File

@ -1059,17 +1059,19 @@ class SubDomains extends ApiCommand implements ResourceEntity
$this->getUserDetail('customerid') => $this->getUserDetail('standardsubdomain')
];
}
// prepare select statement
$domains_stmt = Database::prepare("
SELECT COUNT(*) as num_subdom
FROM `" . TABLE_PANEL_DOMAINS . "` `d`
WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ")
AND `d`.`email_only` = '0'
AND `d`.`id` NOT IN (" . implode(', ', $customer_stdsubs) . ")
");
$result = Database::pexecute_first($domains_stmt, null, true, true);
if ($result) {
return $this->response($result['num_subdom']);
if (!empty($customer_ids)) {
// prepare select statement
$domains_stmt = Database::prepare("
SELECT COUNT(*) as num_subdom
FROM `" . TABLE_PANEL_DOMAINS . "` `d`
WHERE `d`.`customerid` IN (" . implode(', ', $customer_ids) . ")
AND `d`.`email_only` = '0'
AND `d`.`id` NOT IN (" . implode(', ', $customer_stdsubs) . ")
");
$result = Database::pexecute_first($domains_stmt, null, true, true);
if ($result) {
return $this->response($result['num_subdom']);
}
}
return $this->response(0);
}

View File

@ -27,6 +27,10 @@ namespace Froxlor\Cron\Backup;
use Froxlor\Cron\Forkable;
use Froxlor\Cron\FroxlorCron;
use Froxlor\Database\Database;
use Froxlor\FroxlorLogger;
use Froxlor\Settings;
use PDO;
class BackupCron extends FroxlorCron
{
@ -34,28 +38,45 @@ class BackupCron extends FroxlorCron
public static function run()
{
$users = ['web1', 'web2', 'web3', 'web4', 'web5', 'web6', 'web7', 'web8', 'web9', 'web10'];
if(!Settings::Get('backup.enabled')) {
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_DEBUG, 'BackupCron: disabled - exiting');
return -1;
}
self::runFork([self::class, 'handle'], [
[
'user' => '1',
'data' => 'value1',
],
[
'user' => '2',
'data' => 'value2',
]
]);
$stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_BACKUP_STORAGES . "`");
Database::pexecute($stmt);
$storages = [];
while ($storage = $stmt->fetch(PDO::FETCH_ASSOC)) {
$storages[$storage['id']] = $storage;
}
$stmt = Database::prepare("SELECT
customerid,
loginname,
adminid,
backup,
guid,
documentroot
FROM `" . TABLE_PANEL_CUSTOMERS . "` WHERE `backup` > 0
");
Database::pexecute($stmt);
$customers = [];
while ($customer = $stmt->fetch(PDO::FETCH_ASSOC)) {
$customer['storage'] = $storages[$customer['backup']];
$customers[] = $customer;
}
self::runFork([self::class, 'handle'], $customers);
}
private static function handle(array $userdata)
{
echo "BackupCron: started - creating customer backup for user " . $userdata['user'] . "\n";
echo "BackupCron: started - creating customer backup for user " . $userdata['loginname'] . "\n";
echo $userdata['data'] . "\n";
echo json_encode($userdata['storage']) . "\n";
sleep(rand(1, 3));
echo "BackupCron: finished - creating customer backup for user " . $userdata['user'] . "\n";
echo "BackupCron: finished - creating customer backup for user " . $userdata['loginname'] . "\n";
}
}

View File

@ -244,7 +244,6 @@ return [
],
'server' => [
'label' => lng('admin.server'),
'required_resources' => 'change_serversettings',
'icon' => 'fa-solid fa-server',
'elements' => [
[
@ -265,14 +264,11 @@ return [
[
'url' => 'admin_backups.php?page=overview',
'label' => lng('admin.backups.backups'),
'required_resources' => 'change_serversettings',
'add_shortlink' => 'admin_backups.php?page=overview&action=add',
'show_element' => (Settings::Get('backup.enabled') == true)
],
[
'url' => 'admin_logger.php?page=log',
'label' => lng('menue.logger.logger'),
'required_resources' => 'change_serversettings',
'show_element' => (Settings::Get('logger.enabled') == true)
],
[

View File

@ -84,7 +84,7 @@ return [
'sortable' => true,
],
],
'visible_columns' => Listing::getVisibleColumnsForListing('admin_list', [
'visible_columns' => Listing::getVisibleColumnsForListing('backup_storages_list', [
'id',
'description',
'type',
@ -94,8 +94,6 @@ return [
'show' => [
'icon' => 'fa-solid fa-eye',
'title' => lng('usersettings.custom_notes.title'),
'modal' => [Text::class, 'customerNoteDetailModal'],
'visible' => [Customer::class, 'hasNote']
],
'edit' => [
'icon' => 'fa-solid fa-edit',
@ -117,13 +115,7 @@ return [
'action' => 'delete',
'id' => ':id'
],
'visible' => [Admin::class, 'isNotMe']
],
],
'format_callback' => [
[Style::class, 'deactivated'],
[Style::class, 'diskspaceWarning'],
[Style::class, 'trafficWarning']
]
]
];

View File

@ -24,6 +24,7 @@
*/
use Froxlor\UI\Callbacks\Admin;
use Froxlor\UI\Callbacks\Backup;
use Froxlor\UI\Callbacks\Customer;
use Froxlor\UI\Callbacks\Impersonate;
use Froxlor\UI\Callbacks\ProgressBar;
@ -63,20 +64,32 @@ return [
'label' => lng('admin.admin'),
'field' => 'adminname',
'callback' => [Impersonate::class, 'admin'],
'sortable' => true,
],
'size' => [
'label' => lng('backup.size'),
'field' => 'size',
'sortable' => true,
'callback' => [Text::class, 'size'],
],
'storage_id' => [
'label' => lng('backup.backup_storage.title'),
'field' => 'storage_id',
'class' => 'text-center',
'callback' => [Backup::class, 'backupStorageLink'],
],
'filename' => [
'label' => lng('backup.size'),
'field' => 'filename',
'sortable' => true,
],
'created_at' => [
'label' => lng('backup.created_at'),
'field' => 'created_at',
'sortable' => true,
'callback' => [Text::class, 'timestamp'],
],
],
'visible_columns' => Listing::getVisibleColumnsForListing('admin_list', [
'visible_columns' => Listing::getVisibleColumnsForListing('backups_list', [
'id',
'adminname',
'loginname',
@ -84,22 +97,6 @@ return [
'created_at',
]),
'actions' => [
'show' => [
'icon' => 'fa-solid fa-eye',
'title' => lng('usersettings.custom_notes.title'),
'modal' => [Text::class, 'customerNoteDetailModal'],
'visible' => [Customer::class, 'hasNote']
],
'edit' => [
'icon' => 'fa-solid fa-edit',
'title' => lng('panel.edit'),
'href' => [
'section' => 'backups',
'page' => 'storages',
'action' => 'edit',
'id' => ':id'
],
],
'delete' => [
'icon' => 'fa-solid fa-trash',
'title' => lng('panel.delete'),
@ -110,13 +107,8 @@ return [
'action' => 'delete',
'id' => ':id'
],
'visible' => [Admin::class, 'isNotMe']
'visible' => [Admin::class, 'canChangeServerSettings'],
],
],
'format_callback' => [
[Style::class, 'deactivated'],
[Style::class, 'diskspaceWarning'],
[Style::class, 'trafficWarning']
]
]
];

View File

@ -31,18 +31,13 @@
<div>
{% if actions_links is iterable %}
{% for link in actions_links %}
<a class="btn {{ link.class|default('btn-outline-primary') }}" href="{{ link.href|raw }}">
<i class="{{ link.icon|default('fa-solid fa-plus-circle') }}"></i><span class="d-none d-lg-inline ms-lg-1">{{ link.label }}</span>
</a>
{% if link.visible is not defined or (link.visible is defined and link.visible == true) %}
<a class="btn {{ link.class|default('btn-outline-primary') }}" href="{{ link.href|raw }}">
<i class="{{ link.icon|default('fa-solid fa-plus-circle') }}"></i><span class="d-none d-lg-inline ms-lg-1">{{ link.label }}</span>
</a>
{% endif %}
{% endfor %}
{% endif %}
{# TODO: eventually not used anymore because of using a documentation link
{% if entity_info is defined and entity_info is not empty %}
<div class="alert alert-info" role="alert">
{{ entity_info|raw }}
</div>
{% endif %}
#}
</div>
{% endif %}

View File

@ -33,19 +33,14 @@
<div>
{% if actions_links is iterable %}
{% for link in actions_links %}
<a class="btn {{ link.class|default('btn-outline-primary') }}" href="{{ link.href|raw }}">
<i class="{{ link.icon|default('fa-solid fa-plus-circle') }}"></i>
{% if link.label is defined and link.label is not empty %}<span class="d-none d-lg-inline ms-lg-1">{{ link.label }}</span>{% endif %}
</a>
{% if link.visible is not defined or (link.visible is defined and link.visible == true) %}
<a class="btn {{ link.class|default('btn-outline-primary') }}" href="{{ link.href|raw }}">
<i class="{{ link.icon|default('fa-solid fa-plus-circle') }}"></i>
{% if link.label is defined and link.label is not empty %}<span class="d-none d-lg-inline ms-lg-1">{{ link.label }}</span>{% endif %}
</a>
{% endif %}
{% endfor %}
{% endif %}
{# TODO: eventually not used anymore because of using a documentation link
{% if entity_info is defined and entity_info is not empty %}
<div class="alert alert-info" role="alert">
{{ entity_info|raw }}
</div>
{% endif %}
#}
</div>
{% endif %}
{% endblock %}