diff --git a/admin_backups.php b/admin_backups.php index 08c6bac0..cda3f6f0 100644 --- a/admin_backups.php +++ b/admin_backups.php @@ -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'); } diff --git a/lib/Froxlor/Api/Commands/Backups.php b/lib/Froxlor/Api/Commands/Backups.php index 72c13f18..49b48d7d 100644 --- a/lib/Froxlor/Api/Commands/Backups.php +++ b/lib/Froxlor/Api/Commands/Backups.php @@ -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 . "` diff --git a/lib/Froxlor/Api/Commands/SubDomains.php b/lib/Froxlor/Api/Commands/SubDomains.php index 77cf0377..e49c09cd 100644 --- a/lib/Froxlor/Api/Commands/SubDomains.php +++ b/lib/Froxlor/Api/Commands/SubDomains.php @@ -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); } diff --git a/lib/Froxlor/Cron/Backup/BackupCron.php b/lib/Froxlor/Cron/Backup/BackupCron.php index cd162e58..951cc266 100644 --- a/lib/Froxlor/Cron/Backup/BackupCron.php +++ b/lib/Froxlor/Cron/Backup/BackupCron.php @@ -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"; } } diff --git a/lib/navigation/00.froxlor.main.php b/lib/navigation/00.froxlor.main.php index 70066626..76b0c1e0 100644 --- a/lib/navigation/00.froxlor.main.php +++ b/lib/navigation/00.froxlor.main.php @@ -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) ], [ diff --git a/lib/tablelisting/admin/tablelisting.backup_storages.php b/lib/tablelisting/admin/tablelisting.backup_storages.php index b0811ec8..4447faec 100644 --- a/lib/tablelisting/admin/tablelisting.backup_storages.php +++ b/lib/tablelisting/admin/tablelisting.backup_storages.php @@ -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'] - ] ] ]; diff --git a/lib/tablelisting/admin/tablelisting.backups.php b/lib/tablelisting/admin/tablelisting.backups.php index 96db9d1d..92341374 100644 --- a/lib/tablelisting/admin/tablelisting.backups.php +++ b/lib/tablelisting/admin/tablelisting.backups.php @@ -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'] ] ] ]; diff --git a/templates/Froxlor/user/form.html.twig b/templates/Froxlor/user/form.html.twig index 96019b8d..2e960287 100644 --- a/templates/Froxlor/user/form.html.twig +++ b/templates/Froxlor/user/form.html.twig @@ -31,18 +31,13 @@