Compare commits
26 Commits
2.1.0-beta
...
2.1.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1debe9d939 | ||
|
|
3d2e81b457 | ||
|
|
ac759cd9a4 | ||
|
|
05c77929e4 | ||
|
|
cefd9226bd | ||
|
|
762f295d3d | ||
|
|
d3e6063027 | ||
|
|
f18c14e119 | ||
|
|
77bcd10729 | ||
|
|
6ee990af0a | ||
|
|
a3fe37b69b | ||
|
|
56388ede54 | ||
|
|
b98035bf3a | ||
|
|
95abe465ef | ||
|
|
780f607332 | ||
|
|
a11d26522a | ||
|
|
462a798cb6 | ||
|
|
7556685881 | ||
|
|
965e2dfd95 | ||
|
|
1f2cce6195 | ||
|
|
f4f84aa397 | ||
|
|
0f37dfb1eb | ||
|
|
7438786a24 | ||
|
|
041c2d176c | ||
|
|
597e765677 | ||
|
|
f757233d61 |
4
.github/workflows/build-mariadb.yml
vendored
4
.github/workflows/build-mariadb.yml
vendored
@@ -8,8 +8,8 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.4', '8.1']
|
||||
mariadb-version: [10.5, 10.4]
|
||||
php-versions: ['7.4', '8.2']
|
||||
mariadb-version: [10.11, 10.5]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
2
.github/workflows/build-mysql.yml
vendored
2
.github/workflows/build-mysql.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.4', '8.1']
|
||||
php-versions: ['7.4', '8.2']
|
||||
mysql-version: [8.0, 5.7]
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -22,8 +22,5 @@ fonts/
|
||||
templates/*
|
||||
!templates/index.html
|
||||
!templates/Froxlor/
|
||||
templates/Froxlor/assets/mix-manifest.json
|
||||
templates/Froxlor/assets/css/
|
||||
templates/Froxlor/assets/js/
|
||||
templates/Froxlor/assets/webfonts/
|
||||
templates/Froxlor/build/
|
||||
!templates/misc/
|
||||
|
||||
10
README.md
10
README.md
@@ -34,19 +34,13 @@ You may find help in the following places:
|
||||
|
||||
The froxlor community discord server can be found here: https://discord.froxlor.org
|
||||
|
||||
### IRC
|
||||
|
||||
froxlor may be found on libera.chat, channel #froxlor:
|
||||
irc://irc.libera.chat/froxlor
|
||||
|
||||
### Forum
|
||||
|
||||
The community is located on https://forum.froxlor.org/
|
||||
|
||||
### Wiki
|
||||
### Documentation
|
||||
|
||||
More documentation may be found in the froxlor - documentation:
|
||||
https://docs.froxlor.org/
|
||||
The documentation may be found at https://docs.froxlor.org/
|
||||
|
||||
## License
|
||||
|
||||
|
||||
11
SECURITY.md
11
SECURITY.md
@@ -10,9 +10,10 @@ With that, good luck hacking us ;)
|
||||
|
||||
## Supported versions
|
||||
|
||||
- ️✅ **2.x** (`main` git-branch)
|
||||
- ❌ 0.10.x (`0.10.x` git-branch)
|
||||
- ❌ 0.9.x (`0.9.x`git-branch)
|
||||
- ️✅ **2.1.x** (`main` git-branch)
|
||||
- ❌ 2.0.x (`2.0.x`-tags)
|
||||
- ❌ 0.10.x (`0.10.x`-tags)
|
||||
- ❌ other git-branches
|
||||
|
||||
## Qualifying Vulnerabilities
|
||||
|
||||
@@ -26,7 +27,7 @@ With that, good luck hacking us ;)
|
||||
|
||||
### Vulnerabilities we accept
|
||||
|
||||
Only reproducable issues on a default/clean setup from the latest stable release of a supported version will be accepted.
|
||||
Only reproducible issues on a default/clean setup from the latest stable release of a supported version will be accepted.
|
||||
|
||||
## Non-Qualifying Vulnerabilities
|
||||
|
||||
@@ -44,4 +45,4 @@ Only reproducable issues on a default/clean setup from the latest stable release
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you think you have found a vulnerability in froxlor, please head over to [https://huntr.dev/repos/froxlor/froxlor](https://huntr.dev/repos/froxlor/froxlor) and use the reporting possibilities there as we are funding the prize-pot for froxlor on this platform. Also, please give us appropriate time to fix the issue and build update-packages before publishing anything into the wild. Alternatively you can send us an email to [team@froxlor.org](team@froxlor.org).
|
||||
If you think you have found a vulnerability in froxlor, please head over to [https://github.com/Froxlor/Froxlor/security/advisories](https://github.com/Froxlor/Froxlor/security/advisories/new) and use the reporting possibilities there. Also, please give us appropriate time to fix the issue and build update-packages before publishing anything into the wild. Alternatively you can email us to [team@froxlor.org](team@froxlor.org).
|
||||
|
||||
@@ -356,23 +356,6 @@ CREATE TABLE `panel_htpasswds` (
|
||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `panel_sessions`;
|
||||
CREATE TABLE `panel_sessions` (
|
||||
`hash` varchar(32) NOT NULL default '',
|
||||
`userid` int(11) unsigned NOT NULL default '0',
|
||||
`ipaddress` varchar(255) NOT NULL default '',
|
||||
`useragent` varchar(255) NOT NULL default '',
|
||||
`lastactivity` int(11) unsigned NOT NULL default '0',
|
||||
`lastpaging` varchar(255) NOT NULL default '',
|
||||
`formtoken` char(32) NOT NULL default '',
|
||||
`language` varchar(64) NOT NULL default '',
|
||||
`adminsession` tinyint(1) unsigned NOT NULL default '0',
|
||||
`theme` varchar(255) NOT NULL default '',
|
||||
PRIMARY KEY (`hash`),
|
||||
KEY `userid` (`userid`)
|
||||
) ENGINE=HEAP;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `panel_settings`;
|
||||
CREATE TABLE `panel_settings` (
|
||||
`settingid` int(11) unsigned NOT NULL auto_increment,
|
||||
@@ -696,7 +679,7 @@ opcache.validate_timestamps'),
|
||||
('system', 'distribution', ''),
|
||||
('system', 'update_channel', 'stable'),
|
||||
('system', 'updatecheck_data', ''),
|
||||
('system', 'update_notify_last', '2.1.0-beta1'),
|
||||
('system', 'update_notify_last', '2.1.0-rc1'),
|
||||
('system', 'traffictool', 'goaccess'),
|
||||
('system', 'req_limit_per_interval', 60),
|
||||
('system', 'req_limit_interval', 60),
|
||||
@@ -744,7 +727,7 @@ opcache.validate_timestamps'),
|
||||
('panel', 'logo_overridecustom', '0'),
|
||||
('panel', 'settings_mode', '0'),
|
||||
('panel', 'menu_collapsed', '1'),
|
||||
('panel', 'version', '2.1.0-beta1'),
|
||||
('panel', 'version', '2.1.0-rc1'),
|
||||
('panel', 'db_version', '202305240');
|
||||
|
||||
|
||||
|
||||
@@ -93,3 +93,18 @@ if (Froxlor::isFroxlorVersion('2.1.0-dev1')) {
|
||||
Update::showUpdateStep("Updating from 2.1.0-dev1 to 2.1.0-beta1", false);
|
||||
Froxlor::updateToVersion('2.1.0-beta1');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.1.0-beta1')) {
|
||||
Update::showUpdateStep("Updating from 2.1.0-beta1 to 2.1.0-beta2", false);
|
||||
|
||||
Update::showUpdateStep("Removing unused table");
|
||||
Database::query("DROP TABLE IF EXISTS `panel_sessions`;");
|
||||
Update::lastStepStatus(0);
|
||||
|
||||
Froxlor::updateToVersion('2.1.0-beta2');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.1.0-beta2')) {
|
||||
Update::showUpdateStep("Updating from 2.1.0-beta2 to 2.1.0-rc1", false);
|
||||
Froxlor::updateToVersion('2.1.0-rc1');
|
||||
}
|
||||
|
||||
@@ -176,8 +176,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
|
||||
if ((int)Settings::Get('system.use_ssl') == 1) {
|
||||
$ssl = (bool)$this->getBoolParam('ssl', true, 0);
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, ''), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, ''), 'ssl_key_file', '', '', [], true);
|
||||
$cert_optional = !($ssl && empty(Settings::Get('system.ssl_cert_file')));
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $cert_optional, ''), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $cert_optional, ''), 'ssl_key_file', '', '', [], true);
|
||||
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, ''), 'ssl_ca_file', '', '', [], true);
|
||||
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, ''), 'ssl_cert_chainfile', '', '', [], true);
|
||||
$sslss = $this->getParam('ssl_specialsettings', true, '');
|
||||
@@ -415,8 +416,9 @@ class IpsAndPorts extends ApiCommand implements ResourceEntity
|
||||
|
||||
if ((int)Settings::Get('system.use_ssl') == 1) {
|
||||
$ssl = (bool)$this->getBoolParam('ssl', true, $result['ssl']);
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', !$ssl, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', !$ssl, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
|
||||
$cert_optional = !($ssl && empty(Settings::Get('system.ssl_cert_file')));
|
||||
$ssl_cert_file = Validate::validate($this->getParam('ssl_cert_file', $cert_optional, $result['ssl_cert_file']), 'ssl_cert_file', '', '', [], true);
|
||||
$ssl_key_file = Validate::validate($this->getParam('ssl_key_file', $cert_optional, $result['ssl_key_file']), 'ssl_key_file', '', '', [], true);
|
||||
$ssl_ca_file = Validate::validate($this->getParam('ssl_ca_file', true, $result['ssl_ca_file']), 'ssl_ca_file', '', '', [], true);
|
||||
$ssl_cert_chainfile = Validate::validate($this->getParam('ssl_cert_chainfile', true, $result['ssl_cert_chainfile']), 'ssl_cert_chainfile', '', '', [], true);
|
||||
$sslss = $this->getParam('ssl_specialsettings', true, $result['ssl_specialsettings']);
|
||||
|
||||
@@ -1078,10 +1078,8 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
$custom_list_result = $_custom_list_result['list'];
|
||||
}
|
||||
$customer_ids = [];
|
||||
$customer_stdsubs = [];
|
||||
foreach ($custom_list_result as $customer) {
|
||||
$customer_ids[] = $customer['customerid'];
|
||||
$customer_stdsubs[$customer['customerid']] = $customer['standardsubdomain'];
|
||||
}
|
||||
} else {
|
||||
if (Settings::IsInList('panel.customer_hide_options', 'domains')) {
|
||||
@@ -1090,9 +1088,6 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
$customer_ids = [
|
||||
$this->getUserDetail('customerid')
|
||||
];
|
||||
$customer_stdsubs = [
|
||||
$this->getUserDetail('customerid') => $this->getUserDetail('standardsubdomain')
|
||||
];
|
||||
}
|
||||
if (!empty($customer_ids)) {
|
||||
// prepare select statement
|
||||
@@ -1101,7 +1096,6 @@ class SubDomains extends ApiCommand implements ResourceEntity
|
||||
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) {
|
||||
|
||||
@@ -56,7 +56,7 @@ final class UpdateCommand extends CliCommand
|
||||
|
||||
// database update only
|
||||
if ($input->getOption('database')) {
|
||||
$result = $this->validateRequirements($input, $output, true);
|
||||
$result = $this->validateRequirements($output, true);
|
||||
if ($result == self::SUCCESS) {
|
||||
if (Froxlor::hasUpdates() || Froxlor::hasDbUpdates()) {
|
||||
$output->writeln('<info>' . lng('updates.dbupdate_required') . '</>');
|
||||
@@ -77,7 +77,7 @@ final class UpdateCommand extends CliCommand
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = $this->validateRequirements($input, $output);
|
||||
$result = $this->validateRequirements($output);
|
||||
|
||||
if ($result != self::SUCCESS) {
|
||||
// requirements failed, exit
|
||||
|
||||
@@ -515,13 +515,7 @@ class Apache extends HttpConfigBase
|
||||
*/
|
||||
private function createStandardDirectoryEntry()
|
||||
{
|
||||
$vhosts_folder = '';
|
||||
if (is_dir(Settings::Get('system.apacheconf_vhost'))) {
|
||||
$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));
|
||||
} else {
|
||||
$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));
|
||||
}
|
||||
$vhosts_filename = FileDir::makeCorrectFile($vhosts_folder . '/05_froxlor_dirfix_nofcgid.conf');
|
||||
$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_dirfix_nofcgid.conf');
|
||||
|
||||
if (!isset($this->virtualhosts_data[$vhosts_filename])) {
|
||||
$this->virtualhosts_data[$vhosts_filename] = '';
|
||||
@@ -545,7 +539,7 @@ class Apache extends HttpConfigBase
|
||||
}
|
||||
$this->virtualhosts_data[$vhosts_filename] .= ' </Directory>' . "\n";
|
||||
|
||||
$ocsp_cache_filename = FileDir::makeCorrectFile($vhosts_folder . '/03_froxlor_ocsp_cache.conf');
|
||||
$ocsp_cache_filename = $this->getCustomVhostFilename('03_froxlor_ocsp_cache.conf');
|
||||
if (Settings::Get('system.use_ssl') == '1' && Settings::Get('system.apache24') == 1) {
|
||||
$this->virtualhosts_data[$ocsp_cache_filename] = 'SSLStaplingCache ' . Settings::Get('system.apache24_ocsp_cache_path') . "\n";
|
||||
} else {
|
||||
@@ -562,14 +556,7 @@ class Apache extends HttpConfigBase
|
||||
private function createStandardErrorHandler()
|
||||
{
|
||||
if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && (Settings::Get('defaultwebsrverrhandler.err401') != '' || Settings::Get('defaultwebsrverrhandler.err403') != '' || Settings::Get('defaultwebsrverrhandler.err404') != '' || Settings::Get('defaultwebsrverrhandler.err500') != '')) {
|
||||
$vhosts_folder = '';
|
||||
if (is_dir(Settings::Get('system.apacheconf_vhost'))) {
|
||||
$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));
|
||||
} else {
|
||||
$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));
|
||||
}
|
||||
|
||||
$vhosts_filename = FileDir::makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf');
|
||||
$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_default_errorhandler.conf');
|
||||
|
||||
if (!isset($this->virtualhosts_data[$vhosts_filename])) {
|
||||
$this->virtualhosts_data[$vhosts_filename] = '';
|
||||
|
||||
@@ -202,4 +202,13 @@ class HttpConfigBase
|
||||
}
|
||||
return FileDir::makeCorrectFile(Settings::Get('system.apacheconf_vhost') . '/' . $filename);
|
||||
}
|
||||
|
||||
protected function getCustomVhostFilename(string $name)
|
||||
{
|
||||
$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));
|
||||
if (is_dir(Settings::Get('system.apacheconf_vhost'))) {
|
||||
$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));
|
||||
}
|
||||
return FileDir::makeCorrectFile($vhosts_folder . '/' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1161,14 +1161,7 @@ class Nginx extends HttpConfigBase
|
||||
private function createStandardErrorHandler()
|
||||
{
|
||||
if (Settings::Get('defaultwebsrverrhandler.enabled') == '1' && (Settings::Get('defaultwebsrverrhandler.err401') != '' || Settings::Get('defaultwebsrverrhandler.err403') != '' || Settings::Get('defaultwebsrverrhandler.err404') != '' || Settings::Get('defaultwebsrverrhandler.err500') != '')) {
|
||||
$vhosts_folder = '';
|
||||
if (is_dir(Settings::Get('system.apacheconf_vhost'))) {
|
||||
$vhosts_folder = FileDir::makeCorrectDir(Settings::Get('system.apacheconf_vhost'));
|
||||
} else {
|
||||
$vhosts_folder = FileDir::makeCorrectDir(dirname(Settings::Get('system.apacheconf_vhost')));
|
||||
}
|
||||
|
||||
$vhosts_filename = FileDir::makeCorrectFile($vhosts_folder . '/05_froxlor_default_errorhandler.conf');
|
||||
$vhosts_filename = $this->getCustomVhostFilename('05_froxlor_default_errorhandler.conf');
|
||||
|
||||
if (!isset($this->nginx_data[$vhosts_filename])) {
|
||||
$this->nginx_data[$vhosts_filename] = '';
|
||||
|
||||
@@ -320,12 +320,15 @@ class Domain
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function triggerLetsEncryptCSRForAliasDestinationDomain(
|
||||
int $aliasDestinationDomainID,
|
||||
int $aliasDestinationDomainID,
|
||||
FroxlorLogger $log
|
||||
) {
|
||||
if ($aliasDestinationDomainID > 0) {
|
||||
$log->logAction(FroxlorLogger::ADM_ACTION, LOG_INFO,
|
||||
"LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID);
|
||||
$log->logAction(
|
||||
FroxlorLogger::ADM_ACTION,
|
||||
LOG_INFO,
|
||||
"LetsEncrypt CSR triggered for domain ID " . $aliasDestinationDomainID
|
||||
);
|
||||
$upd_stmt = Database::prepare("UPDATE
|
||||
`" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
|
||||
SET
|
||||
@@ -349,15 +352,20 @@ class Domain
|
||||
$acmesh = AcmeSh::getAcmeSh();
|
||||
if (file_exists($acmesh)) {
|
||||
$certificate_folder = AcmeSh::getWorkingDirFromEnv($domainname);
|
||||
if (file_exists($certificate_folder)) {
|
||||
$certificate_ecc_folder = AcmeSh::getWorkingDirFromEnv($domainname, true);
|
||||
if (file_exists($certificate_folder) || file_exists($certificate_ecc_folder)) {
|
||||
$params = " --remove -d " . $domainname;
|
||||
if (Settings::Get('system.leecc') > 0) {
|
||||
if (file_exists($certificate_ecc_folder)) {
|
||||
$params .= " --ecc";
|
||||
}
|
||||
// run remove command
|
||||
FileDir::safe_exec($acmesh . $params);
|
||||
// remove certificates directory
|
||||
FileDir::safe_exec('rm -rf ' . $certificate_folder);
|
||||
if (file_exists($certificate_folder)) {
|
||||
FileDir::safe_exec('rm -rf ' . $certificate_folder);
|
||||
} elseif (file_exists($certificate_ecc_folder)) {
|
||||
FileDir::safe_exec('rm -rf ' . $certificate_ecc_folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -31,7 +31,7 @@ final class Froxlor
|
||||
{
|
||||
|
||||
// Main version variable
|
||||
const VERSION = '2.1.0-beta1';
|
||||
const VERSION = '2.1.0-rc1';
|
||||
|
||||
// Database version (YYYYMMDDC where C is a daily counter)
|
||||
const DBVERSION = '202305240';
|
||||
|
||||
@@ -85,7 +85,7 @@ class Update
|
||||
self::$update_tasks[self::$task_counter]['result'] = 1;
|
||||
break;
|
||||
default:
|
||||
self::$update_tasks[self::$task_counter]['result'] = -1;
|
||||
self::$update_tasks[self::$task_counter]['result'] = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class SImExporter
|
||||
public static function export()
|
||||
{
|
||||
$settings_definitions = [];
|
||||
foreach (PhpHelper::loadConfigArrayDir('./actions/admin/settings/')['groups'] as $group) {
|
||||
foreach (PhpHelper::loadConfigArrayDir(Froxlor::getInstallDir() . '/actions/admin/settings/')['groups'] as $group) {
|
||||
foreach ($group['fields'] as $field) {
|
||||
$settings_definitions[$field['settinggroup']][$field['varname']] = $field;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ use Froxlor\UI\Panel\UI;
|
||||
|
||||
class Domain
|
||||
{
|
||||
public static function domainLink(array $attributes)
|
||||
{
|
||||
return '<a href="https://' . $attributes['data'] . '" target="_blank">' . $attributes['data'] . '</a>';
|
||||
}
|
||||
|
||||
public static function domainWithCustomerLink(array $attributes)
|
||||
{
|
||||
$linker = UI::getLinker();
|
||||
|
||||
@@ -31,17 +31,17 @@ class Style
|
||||
{
|
||||
public static function deactivated(array $attributes): string
|
||||
{
|
||||
return $attributes['fields']['deactivated'] ? 'bg-danger' : '';
|
||||
return $attributes['fields']['deactivated'] ? 'table-danger' : '';
|
||||
}
|
||||
|
||||
public static function loginDisabled(array $attributes): string
|
||||
{
|
||||
return $attributes['fields']['login_enabled'] == 'N' ? 'bg-danger' : '';
|
||||
return $attributes['fields']['login_enabled'] == 'N' ? 'table-danger' : '';
|
||||
}
|
||||
|
||||
public static function resultIntegrityBad(array $attributes): string
|
||||
{
|
||||
return $attributes['fields']['result'] ? '' : 'bg-warning';
|
||||
return $attributes['fields']['result'] ? '' : 'table-warning';
|
||||
}
|
||||
|
||||
public static function invalidApiKey(array $attributes): string
|
||||
@@ -53,7 +53,7 @@ class Style
|
||||
$isValid = false;
|
||||
}
|
||||
}
|
||||
return $isValid ? '' : 'bg-danger';
|
||||
return $isValid ? '' : 'table-danger';
|
||||
}
|
||||
|
||||
public static function resultDomainTerminatedOrDeactivated(array $attributes): string
|
||||
@@ -63,25 +63,24 @@ class Style
|
||||
if (!empty($termination_date)) {
|
||||
$cdate = strtotime($termination_date . " 23:59:59");
|
||||
$today = time();
|
||||
$termination_css = 'bg-warning';
|
||||
$termination_css = 'table-warning';
|
||||
if ($cdate < $today) {
|
||||
$termination_css = 'bg-danger text-light';
|
||||
$termination_css = 'table-danger';
|
||||
}
|
||||
}
|
||||
$deactivated = $attributes['fields']['deactivated'] || $attributes['fields']['customer_deactivated'];
|
||||
return $deactivated ? 'bg-info text-light' : $termination_css;
|
||||
return $deactivated ? 'table-info' : $termination_css;
|
||||
}
|
||||
|
||||
public static function resultCustomerLockedOrDeactivated(array $attributes): string
|
||||
{
|
||||
$row_css = '';
|
||||
if ((int)$attributes['fields']['deactivated'] == 1) {
|
||||
$row_css = 'bg-info text-light';
|
||||
} elseif (
|
||||
$attributes['fields']['loginfail_count'] >= Settings::Get('login.maxloginattempts')
|
||||
$row_css = 'table-info';
|
||||
} elseif ($attributes['fields']['loginfail_count'] >= Settings::Get('login.maxloginattempts')
|
||||
&& $attributes['fields']['lastlogin_fail'] > (time() - Settings::Get('login.deactivatetime'))
|
||||
) {
|
||||
$row_css = 'bg-warning';
|
||||
$row_css = 'table-warning';
|
||||
}
|
||||
|
||||
return $row_css;
|
||||
@@ -97,9 +96,9 @@ class Style
|
||||
$style = '';
|
||||
if ((int)$attributes[$field] >= 0) {
|
||||
if (($attributes[$field] / 100) * $report_max < $attributes[$field . '_used']) {
|
||||
$style = 'bg-danger';
|
||||
$style = 'table-danger';
|
||||
} elseif (($attributes[$field] / 100) * ($report_max - 15) < $attributes[$field . '_used']) {
|
||||
$style = 'bg-warning';
|
||||
$style = 'table-warning';
|
||||
}
|
||||
}
|
||||
return $style;
|
||||
|
||||
@@ -92,6 +92,10 @@ class FroxlorTwig extends AbstractExtension
|
||||
new TwigFunction('mix', [
|
||||
$this,
|
||||
'getMix'
|
||||
]),
|
||||
new TwigFunction('vite', [
|
||||
$this,
|
||||
'getVite'
|
||||
])
|
||||
];
|
||||
}
|
||||
@@ -167,4 +171,9 @@ class FroxlorTwig extends AbstractExtension
|
||||
{
|
||||
return mix($mix);
|
||||
}
|
||||
|
||||
public function getVite($basehref = '', $vite = [], $defaults = [])
|
||||
{
|
||||
return vite($basehref, $vite ?? $defaults);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,8 +142,6 @@ class UI
|
||||
header("X-Content-Security-Policy: " . $csp_content);
|
||||
header("X-WebKit-CSP: " . $csp_content);
|
||||
|
||||
header("X-XSS-Protection: 1; mode=block");
|
||||
|
||||
// Don't allow to load Froxlor in an iframe to prevent i.e. clickjacking
|
||||
header("X-Frame-Options: DENY");
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -92,7 +92,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
|
||||
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
|
||||
</daemon>
|
||||
<!-- HTTP Lighttpd -->
|
||||
<daemon name="lighttpd" title="LigHTTPd">
|
||||
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
|
||||
<install><![CDATA[apt-get install lighttpd]]></install>
|
||||
<file name="/etc/lighttpd/lighttpd.conf">
|
||||
<content><![CDATA[
|
||||
|
||||
@@ -92,7 +92,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
|
||||
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
|
||||
</daemon>
|
||||
<!-- HTTP Lighttpd -->
|
||||
<daemon name="lighttpd" title="LigHTTPd">
|
||||
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
|
||||
<install><![CDATA[apt-get install lighttpd]]></install>
|
||||
<file name="/etc/lighttpd/lighttpd.conf">
|
||||
<content><![CDATA[
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -91,7 +91,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
|
||||
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
|
||||
</daemon>
|
||||
<!-- HTTP Lighttpd -->
|
||||
<daemon name="lighttpd" title="LigHTTPd">
|
||||
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
|
||||
<install><![CDATA[apt-get install lighttpd]]></install>
|
||||
<file name="/etc/lighttpd/lighttpd.conf">
|
||||
<content><![CDATA[
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<froxlor>
|
||||
<distribution name="Gentoo" version="3.0"
|
||||
defaulteditor="/usr/bin/nano">
|
||||
defaulteditor="/usr/bin/nano" deprecated="true">
|
||||
<!-- OS defaults to be loaded on installation -->
|
||||
<defaults>
|
||||
<default settinggroup="system" varname="nssextrausers" value="1"></default>
|
||||
@@ -100,7 +100,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
|
||||
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
|
||||
</daemon>
|
||||
<!-- HTTP Lighttpd -->
|
||||
<daemon name="lighttpd" title="LigHTTPd">
|
||||
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
|
||||
<install><![CDATA[emerge www-servers/lighttpd]]></install>
|
||||
<file name="/etc/lighttpd/lighttpd.conf">
|
||||
<content><![CDATA[
|
||||
|
||||
@@ -91,7 +91,7 @@ Alias "/.well-known/acme-challenge" "{{settings.system.letsencryptchallengepath}
|
||||
<command><![CDATA[{{settings.system.apachereload_command}}]]></command>
|
||||
</daemon>
|
||||
<!-- HTTP Lighttpd -->
|
||||
<daemon name="lighttpd" title="LigHTTPd">
|
||||
<daemon name="lighttpd" title="LigHTTPd (deprecated)">
|
||||
<install><![CDATA[apt-get install lighttpd]]></install>
|
||||
<file name="/etc/lighttpd/lighttpd.conf">
|
||||
<content><![CDATA[
|
||||
|
||||
@@ -55,13 +55,16 @@ return [
|
||||
'label' => lng('login.password'),
|
||||
'type' => 'password',
|
||||
'autocomplete' => 'off',
|
||||
'mandatory' => true
|
||||
],
|
||||
'directory_password_suggestion' => [
|
||||
'label' => lng('customer.generated_pwd'),
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword()
|
||||
'mandatory' => true,
|
||||
'next_to' => [
|
||||
'directory_password_suggestion' => [
|
||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword(),
|
||||
'readonly' => true
|
||||
]
|
||||
]
|
||||
],
|
||||
'directory_authname' => [
|
||||
'label' => lng('extras.htpasswdauthname'),
|
||||
|
||||
@@ -49,13 +49,16 @@ return [
|
||||
'directory_password' => [
|
||||
'label' => lng('login.password'),
|
||||
'type' => 'password',
|
||||
'autocomplete' => 'off'
|
||||
],
|
||||
'directory_password_suggestion' => [
|
||||
'label' => lng('customer.generated_pwd'),
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword()
|
||||
'autocomplete' => 'off',
|
||||
'next_to' => [
|
||||
'directory_password_suggestion' => [
|
||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword(),
|
||||
'readonly' => true
|
||||
]
|
||||
]
|
||||
],
|
||||
'directory_authname' => [
|
||||
'label' => lng('extras.htpasswdauthname'),
|
||||
|
||||
@@ -46,14 +46,16 @@ return [
|
||||
'label' => lng('login.password'),
|
||||
'type' => 'password',
|
||||
'autocomplete' => 'off',
|
||||
'mandatory' => true
|
||||
],
|
||||
'mysql_password_suggestion' => [
|
||||
'label' => lng('customer.generated_pwd'),
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword(),
|
||||
'readonly' => true
|
||||
'mandatory' => true,
|
||||
'next_to' => [
|
||||
'mysql_password_suggestion' => [
|
||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword(),
|
||||
'readonly' => true
|
||||
]
|
||||
]
|
||||
],
|
||||
'sendinfomail' => [
|
||||
'label' => lng('customer.sendinfomail'),
|
||||
|
||||
@@ -52,14 +52,16 @@ return [
|
||||
'mysql_password' => [
|
||||
'label' => lng('changepassword.new_password_ifnotempty'),
|
||||
'type' => 'password',
|
||||
'autocomplete' => 'off'
|
||||
],
|
||||
'mysql_password_suggestion' => [
|
||||
'label' => lng('customer.generated_pwd'),
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword(),
|
||||
'readonly' => true
|
||||
'autocomplete' => 'off',
|
||||
'next_to' => [
|
||||
'mysql_password_suggestion' => [
|
||||
'next_to_prefix' => lng('customer.generated_pwd') . ':',
|
||||
'type' => 'text',
|
||||
'visible' => (Settings::Get('panel.password_regex') == ''),
|
||||
'value' => Crypt::generatePassword(),
|
||||
'readonly' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -44,7 +44,8 @@ return [
|
||||
'cols' => 100,
|
||||
'rows' => 15,
|
||||
'value' => $result['ssl_cert_file'],
|
||||
'placeholder' => lng('domain.ssl_certificate_placeholder')
|
||||
'placeholder' => lng('domain.ssl_certificate_placeholder'),
|
||||
'mandatory' => true
|
||||
],
|
||||
'ssl_key_file' => [
|
||||
'label' => lng('admin.ipsandports.ssl_key_file_content'),
|
||||
@@ -53,7 +54,8 @@ return [
|
||||
'cols' => 100,
|
||||
'rows' => 15,
|
||||
'value' => $result['ssl_key_file'],
|
||||
'placeholder' => lng('domain.ssl_key_placeholder')
|
||||
'placeholder' => lng('domain.ssl_key_placeholder'),
|
||||
'mandatory' => true
|
||||
],
|
||||
'ssl_cert_chainfile' => [
|
||||
'label' => lng('admin.ipsandports.ssl_cert_chainfile_content'),
|
||||
|
||||
@@ -74,6 +74,7 @@ function old(string $identifier, string $default = null, string $session = null)
|
||||
* This file contains the hashed filenames of the assets.
|
||||
* It must be always placed in the theme assets folder.
|
||||
*
|
||||
* @deprecated since 2.1.x no longer in use
|
||||
* @param $filename
|
||||
* @return mixed|string
|
||||
*/
|
||||
@@ -91,3 +92,45 @@ function mix($filename)
|
||||
}
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loading the vite manifest file from given theme.
|
||||
* This file contains the hashed filenames of the assets.
|
||||
* It must be always placed in the theme assets folder.
|
||||
*
|
||||
* @param string|null $basehref
|
||||
* @param array $filenames
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
function vite($basehref, array $filenames): string
|
||||
{
|
||||
// Get the hashed filenames from the manifest file
|
||||
$links = [];
|
||||
foreach ($filenames as $filename) {
|
||||
if (preg_match("/templates\/([^\/]+)(.*)/", $filename, $matches)) {
|
||||
$assetDirectory = '/templates/' . $matches[1] . '/build/';
|
||||
$viteManifest = dirname(__DIR__) . $assetDirectory . '/manifest.json';
|
||||
$manifest = json_decode(file_get_contents($viteManifest), true);
|
||||
$links[] = $basehref . ltrim($assetDirectory, '/') . $manifest[$filename]['file'];
|
||||
} else {
|
||||
$links = $filenames;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the links to the correct html tags
|
||||
foreach ($links as $key => $link) {
|
||||
switch (pathinfo($link, PATHINFO_EXTENSION)) {
|
||||
case 'css':
|
||||
$links[$key] = '<link rel="stylesheet" href="'. $link . '">';
|
||||
break;
|
||||
case 'js':
|
||||
$links[$key] = '<script src="' . $link . '" type="module"></script>';
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unknown file extension for file '. $link .' from manifest.json');
|
||||
}
|
||||
}
|
||||
|
||||
return implode("\n", $links);
|
||||
}
|
||||
|
||||
64
lib/init.php
64
lib/init.php
@@ -50,10 +50,12 @@ if (!file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
use Froxlor\CurrentUser;
|
||||
use Froxlor\FileDir;
|
||||
use Froxlor\Froxlor;
|
||||
use Froxlor\FroxlorLogger;
|
||||
use Froxlor\Http\RateLimiter;
|
||||
use Froxlor\Idna\IdnaWrapper;
|
||||
use Froxlor\Install\Update;
|
||||
use Froxlor\Language;
|
||||
use Froxlor\PhpHelper;
|
||||
use Froxlor\Settings;
|
||||
@@ -63,7 +65,6 @@ use Froxlor\UI\Linker;
|
||||
use Froxlor\UI\Panel\UI;
|
||||
use Froxlor\UI\Request;
|
||||
use Froxlor\UI\Response;
|
||||
use Froxlor\Install\Update;
|
||||
|
||||
// include MySQL-tabledefinitions
|
||||
require Froxlor::getInstallDir() . '/lib/tables.inc.php';
|
||||
@@ -110,6 +111,24 @@ if (!isset($sql) || !is_array($sql)) {
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show nice note if requested domain is "unknown" to froxlor and thus is being lead to its vhost
|
||||
*/
|
||||
if ($_SERVER['HTTP_HOST'] != Settings::Get('system.hostname') &&
|
||||
!filter_var($_SERVER['HTTP_HOST'], FILTER_VALIDATE_IP) && (
|
||||
empty(Settings::Get('system.froxloraliases')) ||
|
||||
(!empty(Settings::Get('system.froxloraliases')) && !in_array($_SERVER['HTTP_HOST'], array_map('trim', explode(',', Settings::Get('system.froxloraliases')))))
|
||||
)) {
|
||||
// not the froxlor system-hostname, show info page for domains not configured in froxlor
|
||||
$unconfiguredPath = FileDir::makeCorrectFile(Froxlor::getInstallDir() . '/templates/misc/unconfigured/index.html');
|
||||
if (file_exists($unconfiguredPath)) {
|
||||
echo file_get_contents($unconfiguredPath);
|
||||
} else {
|
||||
echo "This domain requires configuration via the froxlor server management panel, as it is currently not assigned to any customer.";
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
// set error-handler
|
||||
@set_error_handler([
|
||||
'\\Froxlor\\PhpHelper',
|
||||
@@ -182,12 +201,16 @@ if (@file_exists('templates/' . $theme . '/config.json')) {
|
||||
|
||||
// check for existence of variant in theme
|
||||
if (is_array($_themeoptions) && (!array_key_exists('variants', $_themeoptions) || !array_key_exists(
|
||||
$themevariant,
|
||||
$_themeoptions['variants']
|
||||
))) {
|
||||
$themevariant,
|
||||
$_themeoptions['variants']
|
||||
))) {
|
||||
$themevariant = "default";
|
||||
}
|
||||
|
||||
if (array_key_exists('global', $_themeoptions)) {
|
||||
$_themeoptions['variants'][$themevariant] = array_merge_recursive($_themeoptions['variants'][$themevariant], $_themeoptions['global']);
|
||||
}
|
||||
|
||||
// check for custom header-graphic
|
||||
$hl_path = 'templates/' . $theme . '/assets/img';
|
||||
|
||||
@@ -209,8 +232,11 @@ if (Settings::Get('panel.logo_overridecustom') == 0 && file_exists($hl_path . '/
|
||||
}
|
||||
}
|
||||
|
||||
$color_scheme = $_themeoptions['variants'][$themevariant]['color-scheme'] ?? 'auto';
|
||||
|
||||
UI::twig()->addGlobal('header_logo_login', $header_logo_login);
|
||||
UI::twig()->addGlobal('header_logo', $header_logo);
|
||||
UI::twig()->addGlobal('color_scheme', $color_scheme);
|
||||
|
||||
/**
|
||||
* Redirects to index.php (login page) if no session exists
|
||||
@@ -235,7 +261,7 @@ if (CurrentUser::hasSession()) {
|
||||
$log = FroxlorLogger::getInstanceOf($userinfo);
|
||||
if ((CurrentUser::isAdmin() && AREA != 'admin') || (!CurrentUser::isAdmin() && AREA != 'customer')) {
|
||||
// user tries to access an area not meant for him -> redirect to corresponding index
|
||||
Response::redirectTo((CurrentUser::isAdmin() ? 'admin' : 'customer') . '_index.php', $params);
|
||||
Response::redirectTo((CurrentUser::isAdmin() ? 'admin' : 'customer') . '_index.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
@@ -274,29 +300,21 @@ if (AREA == 'admin' || AREA == 'customer') {
|
||||
}
|
||||
UI::twig()->addGlobal('nav_entries', $navigation);
|
||||
|
||||
$js = "";
|
||||
$css = "";
|
||||
if (is_array($_themeoptions) && array_key_exists('js', $_themeoptions['variants'][$themevariant])) {
|
||||
if (is_array($_themeoptions['variants'][$themevariant]['js'])) {
|
||||
foreach ($_themeoptions['variants'][$themevariant]['js'] as $jsfile) {
|
||||
if (file_exists('templates/' . $theme . '/assets/js/' . $jsfile)) {
|
||||
$js .= '<script type="text/javascript" src="' . mix('templates/' . $theme . '/assets/js/' . $jsfile) . '"></script>' . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_array($_themeoptions['variants'][$themevariant]['css'])) {
|
||||
foreach ($_themeoptions['variants'][$themevariant]['css'] as $cssfile) {
|
||||
if (file_exists('templates/' . $theme . '/assets/css/' . $cssfile)) {
|
||||
$css .= '<link href="' . mix('templates/' . $theme . '/assets/css/' . $cssfile) . '" rel="stylesheet" type="text/css" />' . "\n";
|
||||
$theme_assets = [];
|
||||
foreach (['css', 'js'] as $asset) {
|
||||
if (is_array($_themeoptions) && array_key_exists($asset, $_themeoptions['variants'][$themevariant])) {
|
||||
if (is_array($_themeoptions['variants'][$themevariant][$asset])) {
|
||||
foreach ($_themeoptions['variants'][$themevariant][$asset] as $assetfile) {
|
||||
if (file_exists('templates/' . $theme . '/' . $assetfile)) {
|
||||
$theme_assets[] .= 'templates/' . $theme . '/' . $assetfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UI::twig()->addGlobal('theme_js', $js);
|
||||
UI::twig()->addGlobal('theme_css', $css);
|
||||
unset($js);
|
||||
unset($css);
|
||||
UI::twig()->addGlobal('theme_assets', $theme_assets);
|
||||
unset($theme_assets);
|
||||
|
||||
$action = Request::any('action');
|
||||
$page = Request::any('page', 'overview');
|
||||
|
||||
@@ -50,6 +50,7 @@ return [
|
||||
'label' => lng('domains.domainname'),
|
||||
'field' => 'domain_ace',
|
||||
'isdefaultsearchfield' => true,
|
||||
'callback' => [Domain::class, 'domainLink'],
|
||||
],
|
||||
'ipsandports' => [
|
||||
'label' => lng('admin.ipsandports.ipsandports'),
|
||||
|
||||
@@ -2324,7 +2324,7 @@ Atentament, el vostre administrador'
|
||||
]
|
||||
],
|
||||
'install' => [
|
||||
'slogal' => 'Panell de gestió del servidor froxlor',
|
||||
'slogan' => 'Panell de gestió del servidor froxlor',
|
||||
'preflight' => 'Comprovació del sistema',
|
||||
'critical_error' => 'Error crític',
|
||||
'suggestions' => 'No requerit però recomanat',
|
||||
|
||||
@@ -2357,7 +2357,7 @@ Yours sincerely, your administrator',
|
||||
],
|
||||
],
|
||||
'install' => [
|
||||
'slogal' => 'froxlor Server Management Panel',
|
||||
'slogan' => 'froxlor Server Management Panel',
|
||||
'preflight' => 'System check',
|
||||
'critical_error' => 'Critical error',
|
||||
'suggestions' => 'Not required but recommended',
|
||||
|
||||
@@ -2304,7 +2304,7 @@ Atentamente, su administrador'
|
||||
]
|
||||
],
|
||||
'install' => [
|
||||
'slogal' => 'Panel de gestión del servidor froxlor',
|
||||
'slogan' => 'Panel de gestión del servidor froxlor',
|
||||
'preflight' => 'Comprobación del sistema',
|
||||
'critical_error' => 'Error crítico',
|
||||
'suggestions' => 'No requerido pero recomendado',
|
||||
|
||||
9378
package-lock.json
generated
9378
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
package.json
25
package.json
@@ -2,19 +2,24 @@
|
||||
"name": "froxlor",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "mix watch",
|
||||
"build": "mix --production"
|
||||
"dev": "vite",
|
||||
"build": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.2.0",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"bootstrap": "^5.2.1",
|
||||
"chart.js": "^3.9.1",
|
||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"axios": "^1.1.2",
|
||||
"bootstrap": "^5.3.2",
|
||||
"chart.js": "^4.4.0",
|
||||
"jquery": "^3.6.1",
|
||||
"jquery-validation": "^1.19.5",
|
||||
"laravel-mix": "^6.0.42",
|
||||
"jquery-validation": "^1.20.0",
|
||||
"laravel-vite-plugin": "^0.8.0",
|
||||
"lodash": "^4.17.19",
|
||||
"postcss": "^8.1.14",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sass": "^1.49.7",
|
||||
"sass-loader": "^12.5.0"
|
||||
"sass": "^1.69.3",
|
||||
"vite": "^4.0.0",
|
||||
"vue": "^3.2.37"
|
||||
}
|
||||
}
|
||||
|
||||
14
templates/Froxlor/assets/js/app.js
Normal file
14
templates/Froxlor/assets/js/app.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import '@fortawesome/fontawesome-free';
|
||||
import './bootstrap';
|
||||
|
||||
// Vue
|
||||
import {createApp} from 'vue';
|
||||
|
||||
const app = createApp({});
|
||||
|
||||
// Load jquery components
|
||||
Object.entries(import.meta.glob('./jquery/*.js', {eager: true})).forEach(([path, definition]) => {
|
||||
definition.default();
|
||||
});
|
||||
|
||||
app.mount('#app');
|
||||
20
templates/Froxlor/assets/js/bootstrap.js
vendored
Normal file
20
templates/Froxlor/assets/js/bootstrap.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import _ from 'lodash';
|
||||
window._ = _;
|
||||
|
||||
// jQuery
|
||||
import jQuery from 'jquery';
|
||||
window.$ = jQuery;
|
||||
import 'jquery-validation';
|
||||
|
||||
// Bootstrap
|
||||
import * as bootstrap from 'bootstrap';
|
||||
window.bootstrap = bootstrap;
|
||||
|
||||
// ChartJS
|
||||
import Chart from 'chart.js/auto';
|
||||
window.Chart = Chart;
|
||||
|
||||
// Axios
|
||||
import axios from 'axios';
|
||||
window.axios = axios;
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
68
templates/Froxlor/assets/js/jquery/apikeys.js
vendored
Normal file
68
templates/Froxlor/assets/js/jquery/apikeys.js
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
var timer, delay = 500;
|
||||
$('div[data-action="apikeys"] #allowed_from').on('keyup change', function () {
|
||||
var _this = $(this);
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
var akid = _this.closest('div[data-action="apikeys"]').data('entry');
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=editapikey",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
id: akid,
|
||||
allowed_from: _this.val(),
|
||||
valid_until: $('div[data-entry="' + akid + '"] #valid_until').val()
|
||||
},
|
||||
success: function (data) {
|
||||
if (data.message) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
} else {
|
||||
_this.removeClass('is-invalid');
|
||||
_this.addClass('is-valid');
|
||||
_this.val(data.allowed_from);
|
||||
}
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
}
|
||||
});
|
||||
}, delay);
|
||||
});
|
||||
|
||||
$('div[data-action="apikeys"] #valid_until').on('keyup change', function () {
|
||||
var _this = $(this);
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
var akid = _this.closest('div[data-action="apikeys"]').data('entry');
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=editapikey",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
id: akid,
|
||||
valid_until: _this.val(),
|
||||
allowed_from: $('div[data-entry="' + akid + '"] #allowed_from').val()
|
||||
},
|
||||
success: function (data) {
|
||||
if (data.message) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
} else {
|
||||
_this.removeClass('is-invalid');
|
||||
_this.addClass('is-valid');
|
||||
_this.val(data.valid_until);
|
||||
}
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
}
|
||||
});
|
||||
}, delay);
|
||||
});
|
||||
});
|
||||
}
|
||||
54
templates/Froxlor/assets/js/jquery/configfiles.js
vendored
Normal file
54
templates/Froxlor/assets/js/jquery/configfiles.js
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* config files - select all recommended
|
||||
*/
|
||||
$('#selectRecommendedConfig').on('click', function () {
|
||||
$('input[data-recommended]').each(function () {
|
||||
if ($(this).data('recommended') == 1) {
|
||||
$(this).prop('checked', true);
|
||||
} else {
|
||||
$(this).prop('checked', false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/*
|
||||
* export/download JSON file (e.g. for usage with config-services)
|
||||
*/
|
||||
$('#downloadSelectionAsJson').on('click', function () {
|
||||
var formData = $(this).closest('form').serialize();
|
||||
window.location = "lib/ajax.php?action=getConfigJsonExport&" + formData;
|
||||
});
|
||||
|
||||
/*
|
||||
* open modal window to show selected config-commands/files
|
||||
* for selected daemon
|
||||
*/
|
||||
$('.show-config').on('click', function () {
|
||||
const distro = $(this).data('dist');
|
||||
const section = $(this).data('section');
|
||||
const daemon = $(this).data('daemon');
|
||||
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=getConfigDetails",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {distro: distro, section: section, daemon: daemon},
|
||||
success: function (data) {
|
||||
$('#configTplShowLabel').html(data.title);
|
||||
$('#configTplShow .modal-body').html(data.content);
|
||||
const myModal = new bootstrap.Modal(document.getElementById('configTplShow'));
|
||||
myModal.show();
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
$('#configTplShowLabel').html('Error');
|
||||
$('#configTplShow .modal-body').html('<div class="alert alert-danger" role="alert">' + request.responseJSON.message + '</div>');
|
||||
const myModal = new bootstrap.Modal(document.getElementById('configTplShow'));
|
||||
myModal.show();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
77
templates/Froxlor/assets/js/jquery/customer.js
vendored
Normal file
77
templates/Froxlor/assets/js/jquery/customer.js
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
// Make inputs with enabled unlimited checked disabled
|
||||
$("input[name$='_ul']").each(function () {
|
||||
var fieldname = $(this).attr("name").substring(0, $(this).attr("name").length - 3);
|
||||
$("input[name='" + fieldname + "']").prop({
|
||||
readonly: $(this).is(":checked"),
|
||||
required: !$(this).is(":checked")
|
||||
});
|
||||
});
|
||||
// change state when unlimited checkboxes are clicked
|
||||
$("input[name$='_ul']").on('change', function () {
|
||||
var fieldname = $(this).attr("name").substring(0, $(this).attr("name").length - 3);
|
||||
$("input[name='" + fieldname + "']").prop({
|
||||
readonly: $(this).is(":checked"),
|
||||
required: !$(this).is(":checked")
|
||||
});
|
||||
if (!$(this).is(":checked")) {
|
||||
$("input[name='" + fieldname + "']").focus()
|
||||
}
|
||||
});
|
||||
|
||||
// set values from hosting plan when adding/editing a customer according to the plan's values
|
||||
$('#use_plan').on('change', function () {
|
||||
var pid = $(this).val();
|
||||
if (pid > 0) {
|
||||
$.ajax({
|
||||
url: "admin_plans.php?page=overview&action=jqGetPlanValues",
|
||||
type: "POST",
|
||||
data: {
|
||||
planid: pid
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
for (var i in json) {
|
||||
if (i == 'email_imap' || i == 'email_pop3' || i == 'perlenabled' || i == 'phpenabled' || i == 'dnsenabled' || i == 'logviewenabled') {
|
||||
/** handle checkboxes **/
|
||||
if (json[i] == 1) {
|
||||
$("input[name='" + i + "']").prop('checked', true);
|
||||
} else {
|
||||
$("input[name='" + i + "']").prop('checked', false);
|
||||
}
|
||||
} else if (i == 'allowed_phpconfigs') {
|
||||
/** handle array of values **/
|
||||
$("input[name='allowed_phpconfigs[]']").each(function (index) {
|
||||
$(this).prop('checked', false);
|
||||
for (var j in json[i]) {
|
||||
if ($(this).val() == json[i][j]) {
|
||||
$(this).prop('checked', true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (json[i] == -1) {
|
||||
/** handle unlimited checkboxes **/
|
||||
$("input[name='" + i + "_ul']").attr('checked', 'checked');
|
||||
$("input[name='" + i + "']").prop({
|
||||
readonly: true
|
||||
});
|
||||
} else {
|
||||
/** handle normal value **/
|
||||
$("input[name='" + i + "']").val(json[i]);
|
||||
$("input[name='" + i + "']").prop({
|
||||
readonly: false
|
||||
});
|
||||
$("input[name='" + i + "_ul']").prop('checked', false);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
20
templates/Froxlor/assets/js/jquery/dnseditor.js
vendored
Normal file
20
templates/Froxlor/assets/js/jquery/dnseditor.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
// Display helptext to content box according to dns-record type selected
|
||||
$("select[name='dns_type']").on('change', function () {
|
||||
var selVal = $(this).val();
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=loadLanguageString",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {langid: 'dnseditor.notes.' + selVal},
|
||||
success: function (data) {
|
||||
$("#dns_content").next().html(data);
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
console.log(request, status, error)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
111
templates/Froxlor/assets/js/jquery/domains.js
vendored
Normal file
111
templates/Froxlor/assets/js/jquery/domains.js
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* domains
|
||||
*/
|
||||
// disable unusable php-configuration by customer settings
|
||||
$('#customerid').on('change', function () {
|
||||
var cid = $(this).val();
|
||||
$.ajax({
|
||||
url: "admin_domains.php?page=domains&action=jqGetCustomerPHPConfigs",
|
||||
type: "POST",
|
||||
data: {
|
||||
customerid: cid
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json.length > 0) {
|
||||
$('#phpsettingid option').each(function () {
|
||||
var pid = $(this).val();
|
||||
$(this).attr("disabled", "disabled");
|
||||
for (var i in json) {
|
||||
if (pid == json[i]) {
|
||||
$(this).removeAttr("disabled");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// show warning if speciallogfile option is toggled
|
||||
if ($('input[name=speciallogverified]')) {
|
||||
$('input[name=speciallogfile]').on('click', function () {
|
||||
$('#speciallogfilenote').remove();
|
||||
$('#speciallogfile').removeClass('is-invalid');
|
||||
$('#speciallogverified').val(0);
|
||||
$.ajax({
|
||||
url: window.location.pathname.substring(1) + "?page=overview&action=jqSpeciallogfileNote",
|
||||
type: "POST",
|
||||
data: {
|
||||
id: $('input[name=id]').val(), newval: +$('#speciallogfile').is(':checked')
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json.changed) {
|
||||
$('#speciallogfile').addClass('is-invalid');
|
||||
$('#speciallogfile').parent().append(json.info);
|
||||
$('#speciallogverified').val(1);
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* email only domain - hide unnecessary/unused sections
|
||||
*/
|
||||
if ($('#id') && $('#email_only').is(':checked')) {
|
||||
$('#section_b').hide();
|
||||
$('#section_bssl').hide();
|
||||
$('#section_c').hide();
|
||||
$('#section_d').hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle show/hide of sections in case of email only flag
|
||||
*/
|
||||
$('#email_only').on('click', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
// hide unnecessary sections
|
||||
$('#section_b').hide();
|
||||
$('#section_bssl').hide();
|
||||
$('#section_c').hide();
|
||||
$('#section_d').hide();
|
||||
} else {
|
||||
// show sections
|
||||
$('#section_b').show();
|
||||
$('#section_bssl').show();
|
||||
$('#section_c').show();
|
||||
$('#section_d').show();
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* ssl enabled domain - hide unnecessary/unused sections
|
||||
*/
|
||||
if ($('#id') && !$('#sslenabled').is(':checked')) {
|
||||
$('#section_bssl>.formfields>.row').not(":first").addClass("d-none");
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle show/hide of sections in case of ssl enabled flag
|
||||
*/
|
||||
$('#sslenabled').on('click', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
// show sections
|
||||
$('#section_bssl>.formfields>.row').removeClass("d-none");
|
||||
} else {
|
||||
// hide unnecessary sections
|
||||
$('#section_bssl>.formfields>.row').not(":first").addClass("d-none");
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
20
templates/Froxlor/assets/js/jquery/global.js
vendored
Normal file
20
templates/Froxlor/assets/js/jquery/global.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* global
|
||||
*/
|
||||
$('#historyback').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
history.back(1);
|
||||
})
|
||||
|
||||
$('#copySysInfo').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText($('#ccSysInfo').text().trim());
|
||||
})
|
||||
|
||||
$('[data-bs-toggle="popover"]').each(function () {
|
||||
new bootstrap.Popover($(this));
|
||||
})
|
||||
});
|
||||
}
|
||||
63
templates/Froxlor/assets/js/jquery/install.js
vendored
Normal file
63
templates/Froxlor/assets/js/jquery/install.js
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* switch between basic and advanced installation mode
|
||||
*/
|
||||
$('#switchInstallMode').on('click', function () {
|
||||
var checked = $(this).prop('checked');
|
||||
window.location = window.location.pathname + replaceQueryParam('extended', +checked, window.location.search);
|
||||
});
|
||||
|
||||
function replaceQueryParam(param, newval, search) {
|
||||
var regex = new RegExp("([?;&])" + param + "[^&;]*[;&]?");
|
||||
if (search.match(regex)) {
|
||||
search = search.replace(regex, "$1").replace(/&$/, '');
|
||||
}
|
||||
return search + '&' + param + '=' + newval;
|
||||
}
|
||||
|
||||
function checkConfigState() {
|
||||
$.ajax({
|
||||
url: window.location.href,
|
||||
type: "GET",
|
||||
success: function (data, textStatus, request) {
|
||||
if (request.status >= 300) {
|
||||
window.location = "http://" + srvName;
|
||||
}
|
||||
},
|
||||
error: function (request, textStatus, errorThrown) {
|
||||
// continue
|
||||
if (request.status >= 300) {
|
||||
window.location = "http://" + srvName;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var cTimer;
|
||||
|
||||
/**
|
||||
* check manual-config switch
|
||||
*/
|
||||
$('#manual_config').on('click', function () {
|
||||
clearInterval(cTimer);
|
||||
var checked = $(this).prop('checked');
|
||||
if (checked) {
|
||||
// button zum login
|
||||
$('#submitAuto').addClass('d-none');
|
||||
$('#submitManual').removeClass('d-none');
|
||||
} else {
|
||||
cTimer = setInterval(checkConfigState, 1000);
|
||||
// spinner fürs Warten
|
||||
$('#submitAuto').removeClass('d-none');
|
||||
$('#submitManual').addClass('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
if ($('#manual_config').length > 0) {
|
||||
var srvName = $('#target_servername').val();
|
||||
clearInterval(cTimer);
|
||||
cTimer = setInterval(checkConfigState, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
31
templates/Froxlor/assets/js/jquery/ipsandports.js
vendored
Normal file
31
templates/Froxlor/assets/js/jquery/ipsandports.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* ipsandports - check for internal ip and output a notice if private-range ip is given
|
||||
*/
|
||||
$('#ip').on('change', function () {
|
||||
var ipval = $(this).val();
|
||||
if (ipval.length > 0) {
|
||||
$('#ipnote').remove();
|
||||
$('#ip').removeClass('is-invalid');
|
||||
$.ajax({
|
||||
url: "admin_ipsandports.php?page=overview&action=jqCheckIP",
|
||||
type: "POST",
|
||||
data: {
|
||||
ip: ipval
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json != 0) {
|
||||
$('#ip').addClass('is-invalid');
|
||||
$('#ip').parent().append(json);
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
26
templates/Froxlor/assets/js/jquery/newsfeed.js
vendored
Normal file
26
templates/Froxlor/assets/js/jquery/newsfeed.js
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* newsfeed
|
||||
*/
|
||||
if (document.getElementById('newsfeed')) {
|
||||
let role = "";
|
||||
|
||||
if (typeof $("#newsfeed").data("role") !== "undefined") {
|
||||
role = "&role=" + $("#newsfeed").data("role");
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=newsfeed" + role + "&theme=" + window.$theme,
|
||||
type: "GET",
|
||||
success: function (data) {
|
||||
$("#newsfeeditems").html(data);
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
console.log(request, status, error)
|
||||
$("#newsfeeditems").html('<div class="list-group-item text-center"><span class="badge bg-warning" role="alert">Error loading newsfeed</span></div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
61
templates/Froxlor/assets/js/jquery/search.js
vendored
Executable file
61
templates/Froxlor/assets/js/jquery/search.js
vendored
Executable file
@@ -0,0 +1,61 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* search
|
||||
*/
|
||||
let search = $('#search')
|
||||
|
||||
search.on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
search.find('input').on('keyup', function () {
|
||||
let query = $(this).val();
|
||||
let dropdown = $('#search .search-results');
|
||||
// Hide search if query is empty
|
||||
if (!query.length) {
|
||||
dropdown.html('');
|
||||
dropdown.parent().hide();
|
||||
return;
|
||||
}
|
||||
// Show notification for short search query
|
||||
if (query.length && query.length < 3) {
|
||||
dropdown.html('<li class="list-group-item text-body-secondary py-1">Please enter more than 2 characters</li>');
|
||||
dropdown.parent().show();
|
||||
return;
|
||||
}
|
||||
// Search
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=searchglobal&theme=" + window.$theme,
|
||||
type: "POST",
|
||||
data: {
|
||||
searchtext: query
|
||||
},
|
||||
dataType: "json",
|
||||
success: data => {
|
||||
// Show notification if we got no results
|
||||
if (Object.keys(data).length === 0) {
|
||||
dropdown.html('<li class="list-group-item text-body-secondary py-1">Nothing found!</li>');
|
||||
dropdown.parent().show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear dropdown and show results
|
||||
dropdown.html('');
|
||||
dropdown.parent().show();
|
||||
Object.keys(data).forEach(key => {
|
||||
dropdown.append('<li class="list-group-item text-body-secondary text-capitalize fw-bold py-1 border-bottom">' + key + '</li>');
|
||||
data[key].forEach(item => {
|
||||
dropdown.append('<li class="list-group-item mt-1"><a href="' + item.href + '" tabindex="2" class="text-decoration-none">' + item.title + '</a></li>');
|
||||
});
|
||||
});
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
dropdown.html('<li class="list-group-item text-body-secondary py-1">Whoops we got some errors!</li>');
|
||||
dropdown.parent().show();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
46
templates/Froxlor/assets/js/jquery/tablecolumns.js
vendored
Normal file
46
templates/Froxlor/assets/js/jquery/tablecolumns.js
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* table columns - manage columns modal
|
||||
*/
|
||||
$('.manageColumnsModal form').on('submit', function (event) {
|
||||
$.ajax({
|
||||
url: 'lib/ajax.php?action=updatetablelisting&listing=' + $(this).data('listing') + '&theme=' + window.$theme,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: $(this).serialize(),
|
||||
success: function () {
|
||||
window.location.href = '';
|
||||
},
|
||||
error: function (request) {
|
||||
alert(request.responseJSON.message);
|
||||
}
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$('.manageColumnsModal form button[data-action="reset"]').on('click', function () {
|
||||
var form = $(this).parents('form:first');
|
||||
$.ajax({
|
||||
url: 'lib/ajax.php?action=resettablelisting&listing=' + form.data('listing') + '&theme=' + window.$theme,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {},
|
||||
success: function () {
|
||||
window.location.href = '';
|
||||
},
|
||||
error: function (request) {
|
||||
alert(request.responseJSON.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.manageColumnsModal form button[data-action="select-all"]').on('click', function () {
|
||||
$(this).parents('form:first').find('input:checkbox').prop('checked', true);
|
||||
});
|
||||
|
||||
$('.manageColumnsModal form button[data-action="unselect-all"]').on('click', function () {
|
||||
$(this).parents('form:first').find('input:checkbox').prop('checked', false);
|
||||
});
|
||||
});
|
||||
}
|
||||
12
templates/Froxlor/assets/js/jquery/traffic.js
vendored
Normal file
12
templates/Froxlor/assets/js/jquery/traffic.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* traffic - display helptext to content box according to dns-record type selected
|
||||
*/
|
||||
$("select[name='range']").on('change', function () {
|
||||
var selVal = $(this).val();
|
||||
var baseRef = $(this).data('baseref');
|
||||
window.location.href = baseRef + '?range=' + selVal;
|
||||
});
|
||||
});
|
||||
}
|
||||
23
templates/Froxlor/assets/js/jquery/updatecheck.js
vendored
Normal file
23
templates/Froxlor/assets/js/jquery/updatecheck.js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* updatecheck
|
||||
*/
|
||||
if (document.getElementById('updatecheck')) {
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=updatecheck&theme=" + window.$theme,
|
||||
type: "GET",
|
||||
success: function (data) {
|
||||
$("#updatecheck").html(data);
|
||||
new bootstrap.Popover(document.getElementById('ucheck'));
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
console.log(request, status, error)
|
||||
let message = 'Can\'t check version';
|
||||
$("#updatecheck").html('<span id="ucheck" class="text-decoration-none badge bg-warning mt-2 me-2" data-bs-toggle="tooltip" data-bs-placement="left" title="' + message + '"><i class="fa-solid fa-exclamation-triangle"></i> <span class="d-md-none d-xl-inline">' + message + '</span></span>');
|
||||
new bootstrap.Tooltip(document.getElementById('ucheck'));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
42
templates/Froxlor/assets/js/jquery/validation.js
vendored
Normal file
42
templates/Froxlor/assets/js/jquery/validation.js
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
export default function () {
|
||||
$(function () {
|
||||
/*
|
||||
* validation
|
||||
*/
|
||||
$('#customer_add,#customer_edit').each(function () {
|
||||
$(this).validate({
|
||||
rules: {
|
||||
'name': {
|
||||
required: function () {
|
||||
return $('#company').val().length === 0 || $('#firstname').val().length > 0;
|
||||
}
|
||||
},
|
||||
'firstname': {
|
||||
required: function () {
|
||||
return $('#company').val().length === 0 || $('#name').val().length > 0;
|
||||
}
|
||||
},
|
||||
'company': {
|
||||
required: function () {
|
||||
return $('#name').val().length === 0
|
||||
&& $('#firstname').val().length === 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
$('#domain_add,#domain_edit').each(function () {
|
||||
$(this).validate({
|
||||
rules: {
|
||||
'ipandport[]': {
|
||||
required: true,
|
||||
minlength: 1
|
||||
}
|
||||
},
|
||||
errorPlacement: function (error, element) {
|
||||
$(error).prependTo($(element).parent().parent());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -73,6 +73,7 @@ $navbar-bg-mobile: $gray-900;
|
||||
$sidebar-width: 256px;
|
||||
|
||||
// Card
|
||||
$card-bg: $white;
|
||||
$card-cap-bg: none;
|
||||
$card-cap-padding-y: $spacer;
|
||||
$card-border-width: 0;
|
||||
@@ -81,6 +82,7 @@ $card-border-width: 0;
|
||||
$heading-bg: $navbar-bg;
|
||||
$heading-color: $body-color;
|
||||
$heading-border-color: #dee2e6;
|
||||
$heading-border-color-dark: rgba(0,0,0,0.15);
|
||||
|
||||
// Search
|
||||
$search-bg: $navbar-bg;
|
||||
@@ -1,8 +1,8 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
// Bootstrap
|
||||
@import "variables/main";
|
||||
@import "~bootstrap/scss/bootstrap";
|
||||
@import "variables";
|
||||
@import "bootstrap/scss/bootstrap";
|
||||
|
||||
// Theme
|
||||
@import "components/generic";
|
||||
@@ -14,11 +14,22 @@
|
||||
}
|
||||
|
||||
&.deactivated {
|
||||
@extend .text-muted;
|
||||
@extend .text-body-secondary;
|
||||
background: lighten($light-bg, 3%);
|
||||
|
||||
i {
|
||||
@extend .text-muted;
|
||||
@extend .text-body-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
.card, .list-group-item {
|
||||
background: $dark-bg;
|
||||
}
|
||||
.card {
|
||||
.card-header {
|
||||
border-bottom: $border-color-dark solid 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
templates/Froxlor/assets/scss/components/_dropdown.scss
Normal file
24
templates/Froxlor/assets/scss/components/_dropdown.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
.dropdown {
|
||||
.dropdown-menu {
|
||||
.dropdown-item {
|
||||
i {
|
||||
width: 1rem;
|
||||
margin-right: 1rem;
|
||||
text-align: center;
|
||||
color: $body-secondary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
.dropdown {
|
||||
.dropdown-menu {
|
||||
.dropdown-item {
|
||||
i {
|
||||
color: $body-secondary-color-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ footer {
|
||||
@extend .small;
|
||||
|
||||
a {
|
||||
@extend .text-muted;
|
||||
@extend .text-body-secondary;
|
||||
@extend .text-decoration-none;
|
||||
}
|
||||
|
||||
28
templates/Froxlor/assets/scss/components/_form.scss
Normal file
28
templates/Froxlor/assets/scss/components/_form.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
.form-control-plaintext {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-control[readonly] {
|
||||
background-color: $body-tertiary-bg;
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
.formfield {
|
||||
border-bottom: $border-color-dark solid 1px;
|
||||
}
|
||||
.form-control,
|
||||
.form-select {
|
||||
background-color: $dark-bg;
|
||||
}
|
||||
|
||||
.form-switch {
|
||||
.form-check-input:not(:checked):not(:focus) {
|
||||
background-color: $dark-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control[readonly] {
|
||||
background-color: $body-tertiary-bg-dark;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +1,12 @@
|
||||
// Fontawesome
|
||||
@import "~@fortawesome/fontawesome-free/scss/fontawesome";
|
||||
@import "~@fortawesome/fontawesome-free/css/all";
|
||||
@import "@fortawesome/fontawesome-free/scss/fontawesome";
|
||||
@import "@fortawesome/fontawesome-free/css/all";
|
||||
|
||||
// Generic
|
||||
.header-logo {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.form-control-plaintext {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-control[readonly] {
|
||||
background: rgba(0, 0, 0, .15);
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
@@ -87,3 +79,23 @@
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
td.text-end {
|
||||
a.btn-sm:not(:last-child),
|
||||
span.btn-sm:not(:last-child){
|
||||
margin-right:.125rem!important;
|
||||
}
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
.table>thead>*>* {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// Table
|
||||
@include color-mode(light) {
|
||||
.table {
|
||||
--bs-table-bg: white;
|
||||
}
|
||||
}
|
||||
19
templates/Froxlor/assets/scss/components/_heading.scss
Normal file
19
templates/Froxlor/assets/scss/components/_heading.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
.heading {
|
||||
color: $heading-color;
|
||||
background-color: $heading-bg;
|
||||
border-top: $heading-border-color solid 1px;
|
||||
}
|
||||
|
||||
.heading h5 {
|
||||
color: $heading-color;
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
.heading {
|
||||
background: $dark-bg;
|
||||
border-top: $heading-border-color-dark solid 1px;
|
||||
}
|
||||
.heading h5 {
|
||||
color: $body-color-dark;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
@include media-breakpoint-up(md) {
|
||||
.navbar {
|
||||
background: $dark-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
.navbar {
|
||||
background: $navbar-bg-mobile;
|
||||
@@ -31,7 +39,7 @@
|
||||
color: $white;
|
||||
|
||||
&:hover {
|
||||
color: rgba(255,255,255,.45);
|
||||
color: rgba(255, 255, 255, .45);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +61,7 @@
|
||||
.navbar-toggler {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||
}
|
||||
@@ -32,3 +32,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include color-mode(dark) {
|
||||
#search {
|
||||
.search-input {
|
||||
color: $body-color-dark;
|
||||
}
|
||||
.search-results-box {
|
||||
background: $dark-bg;
|
||||
border: $border-color-dark solid 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-bs-theme="{{ color_scheme|default("auto") }}">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
@@ -9,24 +9,15 @@
|
||||
<link rel="icon" type="image/x-icon" href="{{ basehref|default('') }}templates/Froxlor/assets/img/icon.png">
|
||||
{% if csrf_token %}<meta name="csrf-token" content="{{ csrf_token }}" />{% endif %}
|
||||
|
||||
<!-- CSS -->
|
||||
{% if theme_css is empty %}
|
||||
<link href="{{ basehref|default('') }}{{ mix('templates/Froxlor/assets/css/main.css') }}" rel="stylesheet" type="text/css" />
|
||||
{% else %}
|
||||
{{ theme_css|raw }}
|
||||
{% endif %}
|
||||
{% block custom_css %}{% endblock %}
|
||||
<!-- Assets -->
|
||||
{{ vite(basehref, theme_assets, [
|
||||
'templates/Froxlor/assets/scss/app.scss',
|
||||
'templates/Froxlor/assets/js/app.js',
|
||||
])|raw }}
|
||||
|
||||
<!-- Scripts -->
|
||||
{% if theme_js is empty %}
|
||||
<script type="text/javascript" src="{{ basehref|default('') }}{{ mix('templates/Froxlor/assets/js/main.js') }}"></script>
|
||||
{% else %}
|
||||
{{ theme_js|raw }}
|
||||
{% endif %}
|
||||
{% block custom_js %}{% endblock %}
|
||||
<title>Froxlor{% if page_title %} | {{ page_title }}{% endif %}</title>
|
||||
</head>
|
||||
<body class="min-vh-100 d-flex flex-column">
|
||||
<body id="app" class="min-vh-100 d-flex flex-column">
|
||||
{% block navigation %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
{
|
||||
"global": {
|
||||
"css": [
|
||||
"assets/css/custom.css",
|
||||
"assets/scss/app.scss"
|
||||
],
|
||||
"js": [
|
||||
"assets/js/app.js",
|
||||
"assets/js/apikey.js"
|
||||
],
|
||||
"img": {
|
||||
"ui": "logo_white.png",
|
||||
"login": "logo.png"
|
||||
}
|
||||
},
|
||||
"variants": {
|
||||
"default": {
|
||||
"img": {
|
||||
"login": "logo.png",
|
||||
"ui": "logo_white.png"
|
||||
},
|
||||
"css": [
|
||||
"main.css",
|
||||
"custom.css"
|
||||
],
|
||||
"js": [
|
||||
"main.js",
|
||||
"apikey.js"
|
||||
],
|
||||
"description": "Default"
|
||||
"color-scheme": "light",
|
||||
"description": "Default (light)"
|
||||
},
|
||||
"dark": {
|
||||
"color-scheme": "dark",
|
||||
"img": {
|
||||
"login": "logo_white.png",
|
||||
"ui": "logo_white.png"
|
||||
"login": "logo_white.png"
|
||||
},
|
||||
"css": [
|
||||
"dark.css",
|
||||
"custom.css"
|
||||
],
|
||||
"js": [
|
||||
"main.js",
|
||||
"apikey.js"
|
||||
],
|
||||
"description": "Darkmode"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<div class="col-12 text-center mb-2 d-grid gap-2 d-md-block">
|
||||
{% if form_data.buttons is defined and form_data.buttons is iterable %}
|
||||
{% for btn in form_data.buttons %}
|
||||
<button type="{{ btn.type|default("submit") }}" class="btn btn-lg {{ btn.class|default(" btn-primary") }}">{{ btn.label }}</button>
|
||||
<button type="{{ btn.type|default("submit") }}" class="btn btn-lg {{ btn.class|default(" btn-primary") }}{% if not loop.last %} me-md-3{% endif %}">{{ btn.label }}</button>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<button type="reset" class="btn btn-lg btn-outline-secondary me-md-3">{{ lng('panel.reset') }}</button>
|
||||
@@ -51,6 +51,6 @@
|
||||
|
||||
{# add translation for custom validations #}
|
||||
{% if form_data.id is defined and form_data.id in ['customer_add', 'customer_edit', 'domain_add', 'domain_edit'] %}
|
||||
<script>$(function() { $.extend($.validator.messages, {required: "{{ lng('error.requiredfield') }}"}) });</script>
|
||||
<script type="module">$(function() { $.extend($.validator.messages, {required: "{{ lng('error.requiredfield') }}"}) });</script>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
{% endmacro %}
|
||||
|
||||
{% macro plain(id, field) %}
|
||||
<input type="text" readonly class="form-control-plaintext" id="{{ id }}" name="{{ id }}" value="{{ field.value|raw }}">
|
||||
<input type="text" readonly class="form-control-plaintext" id="{{ id }}" name="{{ id }}" value="{{ field.value|raw|e }}">
|
||||
{% if field.next_to is defined %}
|
||||
{% for nid, nfield in field.next_to %}
|
||||
{% if nfield.next_to_prefix is defined %}
|
||||
@@ -159,9 +159,9 @@
|
||||
{% if field.next_to is defined %}
|
||||
<div class="input-group">
|
||||
{% endif %}
|
||||
<input type="{{ field.type }}" {% if field.visible is defined and field.visible == false %} disabled {% endif %} {% if field.type == 'number' and field.min is defined %} min="{{ field.min }}" {% endif %} {% if field.type == 'number' and field.max is defined %} max="{{ field.max }}" {% endif %} {% if field.type != 'number' and field.maxlength is defined %} maxlength="{{ field.maxlength }}" {% endif %} id="{{ id }}" name="{{ id }}" value="{{ field.value|raw }}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.autocomplete is defined %} autocomplete="{{ field.autocomplete }}" {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %} {% if field.type == 'file' and field.accept is defined %} accept="{{ field.accept }}" {% endif %} {% if field.pattern is defined %} pattern="{{ field.pattern }}" {% endif %}/>
|
||||
<input type="{{ field.type }}" {% if field.visible is defined and field.visible == false %} disabled {% endif %} {% if field.type == 'number' and field.min is defined %} min="{{ field.min }}" {% endif %} {% if field.type == 'number' and field.max is defined %} max="{{ field.max }}" {% endif %} {% if field.type != 'number' and field.maxlength is defined %} maxlength="{{ field.maxlength }}" {% endif %} id="{{ id }}" name="{{ id }}" value="{{ field.value|raw|e }}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.autocomplete is defined %} autocomplete="{{ field.autocomplete }}" {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %} {% if field.type == 'file' and field.accept is defined %} accept="{{ field.accept }}" {% endif %} {% if field.pattern is defined %} pattern="{{ field.pattern }}" {% endif %}/>
|
||||
{% if field.type == 'hidden' and field.display is defined %}
|
||||
<input type="text" readonly class="form-control-plaintext" value="{{ field.display|raw }}">
|
||||
<input type="text" readonly class="form-control-plaintext" value="{{ field.display|raw|e }}">
|
||||
{% endif %}
|
||||
{% if field.next_to is defined %}
|
||||
{% for nid, nfield in field.next_to %}
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
</p>
|
||||
|
||||
{% if preflight.criticals %}
|
||||
<p class="text-muted">{{ lng('install.critical_error') }}</p>
|
||||
<p class="text-body-secondary">{{ lng('install.critical_error') }}</p>
|
||||
<ul>
|
||||
{% for ctype, critical in preflight.criticals %}
|
||||
{% if ctype == 'wrong_ownership' %}
|
||||
@@ -94,7 +94,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if preflight.suggestions %}
|
||||
<p class="text-muted">{{ lng('install.suggestions') }}</p>
|
||||
<p class="text-body-secondary">{{ lng('install.suggestions') }}</p>
|
||||
<ul>
|
||||
{% for ctype, suggestion in preflight.suggestions %}
|
||||
{% if ctype == 'missing_extensions' %}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<i class="fa-solid fa-download me-1"></i>
|
||||
{{ lng('update.update') }}
|
||||
</h5>
|
||||
<span class="text-muted">{{ lng('update.description') }}</span>
|
||||
<span class="text-body-secondary">{{ lng('update.description') }}</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<td class="w-75" scope="row">{{ check.title }}</td>
|
||||
<td class="col-auto text-end{% if check.result == 0 %} text-success{% endif %}">
|
||||
<span class="d-none d-md-inline">{{ check.result_txt }}</span>
|
||||
{% if check.result == 0 %} <i class="fa-solid fa-check-circle" {% elseif check.result == 2 %}<span class="d-md-none"> ???</span>{% elseif check.result == 1 %}<span class="d-md-none"> !!!</span>
|
||||
{% if check.result == 0 %} <i class="fa-solid fa-check-circle"></i>{% elseif check.result == 2 %}<span class="d-md-none"> ???</span>{% elseif check.result == 1 %}<span class="d-md-none"> !!!</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<a class="card-link text-muted" href="index.php">
|
||||
<a class="card-link text-body-secondary" href="index.php">
|
||||
<i class="fa-solid fa-angles-left"></i>
|
||||
{{ lng('login.backtologin') }}</a>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
{% if get_setting('panel.allow_preset') == '1' %}
|
||||
<div class="card-footer">
|
||||
<a class="card-link text-muted" href="index.php?action=forgotpwd">{{ lng('login.forgotpwd') }}</a>
|
||||
<a class="card-link text-body-secondary" href="index.php?action=forgotpwd">{{ lng('login.forgotpwd') }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<a class="card-link text-muted" href="index.php">
|
||||
<a class="card-link text-body-secondary" href="index.php">
|
||||
<i class="fa-solid fa-angles-left"></i>
|
||||
{{ lng('login.backtologin') }}</a>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<link href="{{ basehref }}templates/Froxlor/assets/css/main.css" rel="stylesheet">
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="{{ basehref }}templates/Froxlor/assets/js/main.js"></script>
|
||||
<script src="{{ basehref }}templates/Froxlor/assets/js/app.js"></script>
|
||||
|
||||
<title>Froxlor - Error</title>
|
||||
</head>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<link href="{{ basehref }}templates/Froxlor/assets/css/main.css" rel="stylesheet">
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="{{ basehref }}templates/Froxlor/assets/js/main.js"></script>
|
||||
<script src="{{ basehref }}templates/Froxlor/assets/js/app.js"></script>
|
||||
|
||||
<title>Froxlor - Error</title>
|
||||
</head>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% import "Froxlor/misc/version_popover.html.twig" as vc %}
|
||||
<span id="ucheck" class="nav-link {% if isnewerversion == 0 and aucheck < 0 %}text-muted{% elseif isnewerversion == 0 %}text-success{% else %}text-warning{% endif %}"
|
||||
<span id="ucheck" class="nav-link {% if isnewerversion == 0 and aucheck < 0 %}text-body-secondary{% elseif isnewerversion == 0 %}text-success{% else %}text-warning{% endif %}"
|
||||
data-bs-container="body" data-bs-toggle="popover" data-bs-placement="bottom" data-bs-trigger="hover focus click" data-bs-html="true"
|
||||
data-bs-content="{{ vc.vpopover(isnewerversion, additional_info, full_version, dbversion, channel, last_update_check, message) }}"
|
||||
>
|
||||
|
||||
@@ -20,9 +20,12 @@
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('apcuinfo.memnote') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.mem_used_percentage }}%" aria-valuenow="{{ apcuinfo.mem_used }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.mem_avail }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ apcuinfo.mem_used_percentage }}%</small>
|
||||
<div class="progress position-relative" role="progressbar" aria-valuenow="{{ apcuinfo.mem_used }}"
|
||||
aria-valuemin="0" aria-valuemax="{{ apcuinfo.mem_avail }}">
|
||||
<div class="progress-bar bg-success" style="width: {{ apcuinfo.mem_used_percentage }}%"></div>
|
||||
<small
|
||||
class="justify-content-center d-flex position-absolute w-100 text-dark">{{ apcuinfo.mem_used_percentage }}
|
||||
%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@@ -45,10 +48,21 @@
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('apcuinfo.hitmiss') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.num_hits_percentage }}%" aria-valuenow="{{ apcuinfo.num_hits }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.num_hits_and_misses }}"></div>
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: {{ 100 - apcuinfo.num_misses_percentage }}%" aria-valuenow="{{ apcuinfo.num_misses }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.num_hits_and_misses }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ apcuinfo.num_hits_percentage }}%</small>
|
||||
<div class="progress-stacked position-relative">
|
||||
<div class="progress" role="progressbar" style="width: {{ apcuinfo.num_hits_percentage }}%"
|
||||
aria-valuenow="{{ apcuinfo.num_hits }}" aria-valuemin="0"
|
||||
aria-valuemax="{{ apcuinfo.num_hits_and_misses }}">
|
||||
<div class="progress-bar bg-success"></div>
|
||||
</div>
|
||||
<div class="progress" role="progressbar"
|
||||
style="width: {{ 100 - apcuinfo.num_misses_percentage }}%"
|
||||
aria-valuenow="{{ apcuinfo.num_misses }}" aria-valuemin="0"
|
||||
aria-valuemax="{{ apcuinfo.num_hits_and_misses }}">
|
||||
<div class="progress-bar bg-danger"></div>
|
||||
</div>
|
||||
<small
|
||||
class="justify-content-center d-flex position-absolute w-100">{{ apcuinfo.num_hits_percentage }}
|
||||
%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@@ -97,8 +111,13 @@
|
||||
</div>
|
||||
{% if apcuinfo.fragmentation is iterable %}
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.fragmentation.used_percentage }}%" aria-valuenow="{{ apcuinfo.fragmentation.used_bytes }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.fragmentation.total_bytes }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ apcuinfo.fragmentation.used_percentage }}%</small>
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
style="width: {{ apcuinfo.fragmentation.used_percentage }}%"
|
||||
aria-valuenow="{{ apcuinfo.fragmentation.used_bytes }}" aria-valuemin="0"
|
||||
aria-valuemax="{{ apcuinfo.fragmentation.total_bytes }}"></div>
|
||||
<small
|
||||
class="justify-content-center d-flex position-absolute w-100">{{ apcuinfo.fragmentation.used_percentage }}
|
||||
%</small>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
@@ -124,33 +143,33 @@
|
||||
<div class="card table-responsive mb-3">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('apcuinfo.generaltitle') }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.version') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.apcversion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.phpversion') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.phpversion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.hostname') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.host }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.serversoftware') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.server }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.start') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.start_time|date('d.m.Y H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.uptime') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.uptime }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('apcuinfo.generaltitle') }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.version') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.apcversion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.phpversion') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.phpversion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.hostname') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.host }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.serversoftware') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.server }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.start') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.start_time|date('d.m.Y H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.uptime') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.uptime }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -159,15 +178,15 @@
|
||||
<div class="card table-responsive">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('apcuinfo.runtime') }}</th>
|
||||
</tr>
|
||||
{% for k,v in apcuinfo.runtimelines %}
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('apcuinfo.runtime') }}</th>
|
||||
<th class="fw-bold" scope="row">{{ k|raw }}</th>
|
||||
<td class="text-end">{{ v|raw }}</td>
|
||||
</tr>
|
||||
{% for k,v in apcuinfo.runtimelines %}
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ k|raw }}</th>
|
||||
<td class="text-end">{{ v|raw }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<i class="fa-solid fa-wrench"></i>
|
||||
{{ lng('admin.configfiles.serverconfiguration') }}
|
||||
</h5>
|
||||
<span class="text-muted">{{ lng('admin.configfiles.description') }}</span>
|
||||
<span class="text-body-secondary">{{ lng('admin.configfiles.description') }}</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
@@ -112,8 +112,8 @@
|
||||
{{ lng('admin.configfiles.recommendednote') }}
|
||||
</div>
|
||||
<div class="col-12 col-md-6 text-end">
|
||||
<button type="button" class="btn btn-outline-secondary" id="selectRecommendedConfig">{{ lng('admin.configfiles.selectrecommended') }}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="downloadSelectionAsJson">
|
||||
<button type="button" class="btn btn-outline-secondary me-md-3" id="selectRecommendedConfig">{{ lng('admin.configfiles.selectrecommended') }}</button>
|
||||
<button type="button" class="btn btn-outline-secondary me-md-3" id="downloadSelectionAsJson">
|
||||
<i class="fa-solid fa-download"></i>
|
||||
{{ lng('admin.configfiles.downloadselected') }}</button>
|
||||
<button type="submit" class="btn btn-primary">{{ lng('update.proceed') }}</button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "Froxlor/settings/index.html.twig" %}
|
||||
|
||||
{% block actions %}
|
||||
<a class="btn btn-outline-primary" href="{{ linker({'section':'settings','page':'overview','part':'all'}) }}">
|
||||
<a class="btn btn-outline-primary me-2" href="{{ linker({'section':'settings','page':'overview','part':'all'}) }}">
|
||||
<i class="fa-solid fa-grip me-1"></i>
|
||||
{{ lng('admin.configfiles.overview') }}
|
||||
</a>
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
{% if fields._group is defined %} » {{ fields._group.title|raw }}
|
||||
{% endif %}
|
||||
</h5>
|
||||
<span class="text-muted">{{ lng('admin.serversettings_desc') }}</span>
|
||||
<span class="text-body-secondary">{{ lng('admin.serversettings_desc') }}</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
<a class="btn btn-outline-secondary" href="{{ linker({'section':'settings','page':'toggleSettingsMode'}) }}" title="{{ lng('panel.settingsmodetoggle') }}">
|
||||
<a class="btn btn-outline-secondary me-2" href="{{ linker({'section':'settings','page':'toggleSettingsMode'}) }}" title="{{ lng('panel.settingsmodetoggle') }}">
|
||||
{% if get_setting('panel.settings_mode') == 0 %}
|
||||
<i class="fa-solid fa-maximize me-1"></i>
|
||||
{{ lng('panel.settingsmode') }}: {{ lng('panel.settingsmodebasic') }}
|
||||
@@ -47,7 +47,7 @@
|
||||
</div>
|
||||
{% if not field.activated %}
|
||||
<div class="position-absolute top-0 end-0 p-1">
|
||||
<span class="badge text-muted" style="background: #eee">{{ lng('panel.not_activated') }}</span>
|
||||
<span class="badge text-bg-light">{{ lng('panel.not_activated') }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -20,9 +20,14 @@
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('opcacheinfo.memusage') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ opcacheinfo.overview.used_memory_percentage }}%" aria-valuenow="{{ opcacheinfo.overview.used_memory }}" aria-valuemin="0" aria-valuemax="{{ opcacheinfo.overview.total_memory }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ opcacheinfo.overview.used_memory_percentage }}%</small>
|
||||
<div class="progress position-relative" role="progressbar"
|
||||
aria-valuenow="{{ opcacheinfo.overview.used_memory }}" aria-valuemin="0"
|
||||
aria-valuemax="{{ opcacheinfo.overview.total_memory }}">
|
||||
<div class="progress-bar bg-success"
|
||||
style="width: {{ opcacheinfo.overview.used_memory_percentage }}%"></div>
|
||||
<small
|
||||
class="justify-content-center d-flex position-absolute w-100">{{ opcacheinfo.overview.used_memory_percentage }}
|
||||
%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@@ -49,10 +54,22 @@
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('opcacheinfo.hitsc') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ opcacheinfo.overview.hit_rate_percentage }}%" aria-valuenow="{{ opcacheinfo.overview.hits }}" aria-valuemin="0" aria-valuemax="{{ opcacheinfo.overview.hits + opcacheinfo.overview.misses }}"></div>
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: {{ 100 - opcacheinfo.overview.hit_rate_percentage }}%" aria-valuenow="{{ opcacheinfo.overview.misses }}" aria-valuemin="0" aria-valuemax="{{ opcacheinfo.overview.hits + opcacheinfo.overview.misses }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ opcacheinfo.overview.hit_rate_percentage }}%</small>
|
||||
<div class="progress-stacked position-relative">
|
||||
<div class="progress" role="progressbar" aria-valuenow="{{ opcacheinfo.overview.hits }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="{{ opcacheinfo.overview.hits + opcacheinfo.overview.misses }}"
|
||||
style="width: {{ opcacheinfo.overview.hit_rate_percentage }}%">
|
||||
<div class="progress-bar bg-success"></div>
|
||||
</div>
|
||||
<div class="progress" role="progressbar" aria-valuenow="{{ opcacheinfo.overview.misses }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="{{ opcacheinfo.overview.hits + opcacheinfo.overview.misses }}"
|
||||
style="width: {{ 100 - opcacheinfo.overview.hit_rate_percentage }}%">
|
||||
<div class="progress-bar bg-danger"></div>
|
||||
</div>
|
||||
<small
|
||||
class="justify-content-center d-flex position-absolute w-100">{{ opcacheinfo.overview.hit_rate_percentage }}
|
||||
%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@@ -80,8 +97,13 @@
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('opcacheinfo.usedkey') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ opcacheinfo.overview.used_key_percentage }}%" aria-valuenow="{{ opcacheinfo.overview.num_cached_keys }}" aria-valuemin="0" aria-valuemax="{{ opcacheinfo.overview.max_cached_keys }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ opcacheinfo.overview.used_key_percentage }}%</small>
|
||||
<div class="progress-bar bg-success" role="progressbar"
|
||||
style="width: {{ opcacheinfo.overview.used_key_percentage }}%"
|
||||
aria-valuenow="{{ opcacheinfo.overview.num_cached_keys }}" aria-valuemin="0"
|
||||
aria-valuemax="{{ opcacheinfo.overview.max_cached_keys }}"></div>
|
||||
<small
|
||||
class="justify-content-center d-flex position-absolute w-100">{{ opcacheinfo.overview.used_key_percentage }}
|
||||
%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@@ -108,15 +130,18 @@
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.used') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.strings_used_memory }}</span>
|
||||
<span
|
||||
class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.strings_used_memory }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.free') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.strings_free_memory }}</span>
|
||||
<span
|
||||
class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.strings_free_memory }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.strcount') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.number_of_strings }}</span>
|
||||
<span
|
||||
class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.number_of_strings }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -128,53 +153,53 @@
|
||||
<div class="card table-responsive mb-3">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('opcacheinfo.generaltitle') }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.version') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.version }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.phpversion') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.php }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.hostname') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.host }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.serversoftware') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.server }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.start') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.overview.start_time|date('d.m.Y H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.lastreset') }}</th>
|
||||
<td class="text-end">
|
||||
{% if opcacheinfo.overview.last_restart_time > 0 %}
|
||||
{{ opcacheinfo.overview.last_restart_time|date('d.m.Y H:i:s') }}
|
||||
{% else %}
|
||||
{{ lng('panel.never') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('opcacheinfo.generaltitle') }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.version') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.version }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.phpversion') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.php }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.hostname') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.host }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.serversoftware') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.server }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.start') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.overview.start_time|date('d.m.Y H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.lastreset') }}</th>
|
||||
<td class="text-end">
|
||||
{% if opcacheinfo.overview.last_restart_time > 0 %}
|
||||
{{ opcacheinfo.overview.last_restart_time|date('d.m.Y H:i:s') }}
|
||||
{% else %}
|
||||
{{ lng('panel.never') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card table-responsive">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" scope="row">{{ lng('opcacheinfo.funcsavail') }}</th>
|
||||
</tr>
|
||||
{% for funcs in opcacheinfo.functions %}
|
||||
<tr>
|
||||
<th class="text-center" scope="row">{{ lng('opcacheinfo.funcsavail') }}</th>
|
||||
</tr>
|
||||
{% for funcs in opcacheinfo.functions %}
|
||||
<tr>
|
||||
<td>{{ funcs }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -183,29 +208,29 @@
|
||||
<div class="card table-responsive">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('opcacheinfo.runtimeconf') }}</th>
|
||||
</tr>
|
||||
{% for directive in opcacheinfo.directives %}
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('opcacheinfo.runtimeconf') }}</th>
|
||||
<th class="fw-bold" scope="row">{{ directive.k }}</th>
|
||||
<td class="text-end">
|
||||
{% if directive.v is iterable %}
|
||||
{% for vval in directive.v %}
|
||||
{% if vval is iterable %}
|
||||
{% for val2 in vval %}
|
||||
{{ val2|raw }}<br>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ vval|raw }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ directive.v|raw }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% for directive in opcacheinfo.directives %}
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ directive.k }}</th>
|
||||
<td class="text-end">
|
||||
{% if directive.v is iterable %}
|
||||
{% for vval in directive.v %}
|
||||
{% if vval is iterable %}
|
||||
{% for val2 in vval %}
|
||||
{{ val2|raw }}<br>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ vval|raw }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ directive.v|raw }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
$(function () {
|
||||
|
||||
var timer, delay = 500;
|
||||
$('div[data-action="apikeys"] #allowed_from').on('keyup change', function () {
|
||||
var _this = $(this);
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
var akid = _this.closest('div[data-action="apikeys"]').data('entry');
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=editapikey",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { id: akid, allowed_from: _this.val(), valid_until: $('div[data-entry="' + akid + '"] #valid_until').val() },
|
||||
success: function (data) {
|
||||
if (data.message) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
} else {
|
||||
_this.removeClass('is-invalid');
|
||||
_this.addClass('is-valid');
|
||||
_this.val(data.allowed_from);
|
||||
}
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
}
|
||||
});
|
||||
}, delay);
|
||||
});
|
||||
|
||||
$('div[data-action="apikeys"] #valid_until').on('keyup change', function () {
|
||||
var _this = $(this);
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
var akid = _this.closest('div[data-action="apikeys"]').data('entry');
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=editapikey",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { id: akid, valid_until: _this.val(), allowed_from: $('div[data-entry="' + akid + '"] #allowed_from').val() },
|
||||
success: function (data) {
|
||||
if (data.message) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
} else {
|
||||
_this.removeClass('is-invalid');
|
||||
_this.addClass('is-valid');
|
||||
_this.val(data.valid_until);
|
||||
}
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
}
|
||||
});
|
||||
}, delay);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
$(function () {
|
||||
/*
|
||||
* config files - select all recommended
|
||||
*/
|
||||
$('#selectRecommendedConfig').on('click', function () {
|
||||
$('input[data-recommended]').each(function () {
|
||||
if ($(this).data('recommended') == 1) {
|
||||
$(this).prop('checked', true);
|
||||
} else {
|
||||
$(this).prop('checked', false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/*
|
||||
* export/download JSON file (e.g. for usage with config-services)
|
||||
*/
|
||||
$('#downloadSelectionAsJson').on('click', function () {
|
||||
var formData = $(this).closest('form').serialize();
|
||||
window.location = "lib/ajax.php?action=getConfigJsonExport&" + formData;
|
||||
});
|
||||
|
||||
/*
|
||||
* open modal window to show selected config-commands/files
|
||||
* for selected daemon
|
||||
*/
|
||||
$('.show-config').on('click', function () {
|
||||
var distro = $(this).data('dist');
|
||||
var section = $(this).data('section');
|
||||
var daemon = $(this).data('daemon');
|
||||
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=getConfigDetails",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { distro: distro, section: section, daemon: daemon },
|
||||
success: function (data) {
|
||||
$('#configTplShowLabel').html(data.title);
|
||||
$('#configTplShow .modal-body').html(data.content);
|
||||
var myModal = new bootstrap.Modal(document.getElementById('configTplShow'));
|
||||
myModal.show();
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
$('#configTplShowLabel').html('Error');
|
||||
$('#configTplShow .modal-body').html('<div class="alert alert-danger" role="alert">' + request.responseJSON.message + '</div>');
|
||||
var myModal = new bootstrap.Modal(document.getElementById('configTplShow'));
|
||||
myModal.show();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
$(function() {
|
||||
|
||||
// Make inputs with enabled unlimited checked disabled
|
||||
$("input[name$='_ul']").each(function () {
|
||||
var fieldname = $(this).attr("name").substring(0, $(this).attr("name").length - 3);
|
||||
$("input[name='" + fieldname + "']").prop({
|
||||
readonly: $(this).is(":checked"),
|
||||
required: !$(this).is(":checked")
|
||||
});
|
||||
});
|
||||
// change state when unlimited checkboxes are clicked
|
||||
$("input[name$='_ul']").on('change', function () {
|
||||
var fieldname = $(this).attr("name").substring(0, $(this).attr("name").length - 3);
|
||||
$("input[name='" + fieldname + "']").prop({
|
||||
readonly: $(this).is(":checked"),
|
||||
required: !$(this).is(":checked")
|
||||
});
|
||||
if (!$(this).is(":checked")) {
|
||||
$("input[name='" + fieldname + "']").focus()
|
||||
}
|
||||
});
|
||||
|
||||
// set values from hosting plan when adding/editing a customer according to the plan's values
|
||||
$('#use_plan').on('change', function () {
|
||||
var pid = $(this).val();
|
||||
if (pid > 0) {
|
||||
$.ajax({
|
||||
url: "admin_plans.php?page=overview&action=jqGetPlanValues",
|
||||
type: "POST",
|
||||
data: {
|
||||
planid: pid
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
for (var i in json) {
|
||||
if (i == 'email_imap' || i == 'email_pop3' || i == 'perlenabled' || i == 'phpenabled' || i == 'dnsenabled' || i == 'logviewenabled') {
|
||||
/** handle checkboxes **/
|
||||
if (json[i] == 1) {
|
||||
$("input[name='" + i + "']").prop('checked', true);
|
||||
} else {
|
||||
$("input[name='" + i + "']").prop('checked', false);
|
||||
}
|
||||
} else if (i == 'allowed_phpconfigs') {
|
||||
/** handle array of values **/
|
||||
$("input[name='allowed_phpconfigs[]']").each(function (index) {
|
||||
$(this).prop('checked', false);
|
||||
for (var j in json[i]) {
|
||||
if ($(this).val() == json[i][j]) {
|
||||
$(this).prop('checked', true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (json[i] == -1) {
|
||||
/** handle unlimited checkboxes **/
|
||||
$("input[name='" + i + "_ul']").attr('checked', 'checked');
|
||||
$("input[name='" + i + "']").prop({
|
||||
readonly: true
|
||||
});
|
||||
} else {
|
||||
/** handle normal value **/
|
||||
$("input[name='" + i + "']").val(json[i]);
|
||||
$("input[name='" + i + "']").prop({
|
||||
readonly: false
|
||||
});
|
||||
$("input[name='" + i + "_ul']").prop('checked', false);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,19 +0,0 @@
|
||||
$(function () {
|
||||
|
||||
// Display helptext to content box according to dns-record type selected
|
||||
$("select[name='dns_type']").on('change', function () {
|
||||
var selVal = $(this).val();
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=loadLanguageString",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { langid: 'dnseditor.notes.' + selVal },
|
||||
success: function (data) {
|
||||
$("#dns_content").next().html(data);
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
console.log(request, status, error)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,107 +0,0 @@
|
||||
$(function () {
|
||||
|
||||
// disable unusable php-configuration by customer settings
|
||||
$('#customerid').on('change', function () {
|
||||
var cid = $(this).val();
|
||||
$.ajax({
|
||||
url: "admin_domains.php?page=domains&action=jqGetCustomerPHPConfigs",
|
||||
type: "POST",
|
||||
data: {
|
||||
customerid: cid
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json.length > 0) {
|
||||
$('#phpsettingid option').each(function () {
|
||||
var pid = $(this).val();
|
||||
$(this).attr("disabled", "disabled");
|
||||
for (var i in json) {
|
||||
if (pid == json[i]) {
|
||||
$(this).removeAttr("disabled");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// show warning if speciallogfile option is toggled
|
||||
if ($('input[name=speciallogverified]')) {
|
||||
$('input[name=speciallogfile]').on('click', function () {
|
||||
$('#speciallogfilenote').remove();
|
||||
$('#speciallogfile').removeClass('is-invalid');
|
||||
$('#speciallogverified').val(0);
|
||||
$.ajax({
|
||||
url: window.location.pathname.substring(1) + "?page=overview&action=jqSpeciallogfileNote",
|
||||
type: "POST",
|
||||
data: {
|
||||
id: $('input[name=id]').val(), newval: +$('#speciallogfile').is(':checked')
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json.changed) {
|
||||
$('#speciallogfile').addClass('is-invalid');
|
||||
$('#speciallogfile').parent().append(json.info);
|
||||
$('#speciallogverified').val(1);
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* email only domain - hide unnecessary/unused sections
|
||||
*/
|
||||
if ($('#id') && $('#email_only').is(':checked')) {
|
||||
$('#section_b').hide();
|
||||
$('#section_bssl').hide();
|
||||
$('#section_c').hide();
|
||||
$('#section_d').hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle show/hide of sections in case of email only flag
|
||||
*/
|
||||
$('#email_only').on('click', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
// hide unnecessary sections
|
||||
$('#section_b').hide();
|
||||
$('#section_bssl').hide();
|
||||
$('#section_c').hide();
|
||||
$('#section_d').hide();
|
||||
} else {
|
||||
// show sections
|
||||
$('#section_b').show();
|
||||
$('#section_bssl').show();
|
||||
$('#section_c').show();
|
||||
$('#section_d').show();
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* ssl enabled domain - hide unnecessary/unused sections
|
||||
*/
|
||||
if ($('#id') && !$('#sslenabled').is(':checked')) {
|
||||
$('#section_bssl>.formfields>.row').not(":first").addClass("d-none");
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle show/hide of sections in case of ssl enabled flag
|
||||
*/
|
||||
$('#sslenabled').on('click', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
// show sections
|
||||
$('#section_bssl>.formfields>.row').removeClass("d-none");
|
||||
} else {
|
||||
// hide unnecessary sections
|
||||
$('#section_bssl>.formfields>.row').not(":first").addClass("d-none");
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
$(function () {
|
||||
|
||||
$('#historyback').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
history.back(1);
|
||||
})
|
||||
|
||||
$('#copySysInfo').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText($('#ccSysInfo').text().trim());
|
||||
})
|
||||
});
|
||||
@@ -1,62 +0,0 @@
|
||||
$(function () {
|
||||
/*
|
||||
* switch between basic and advanced install mode
|
||||
*/
|
||||
$('#switchInstallMode').on('click', function () {
|
||||
var checked = $(this).prop('checked');
|
||||
window.location = window.location.pathname + replaceQueryParam('extended', +checked, window.location.search);
|
||||
});
|
||||
|
||||
function replaceQueryParam(param, newval, search) {
|
||||
var regex = new RegExp("([?;&])" + param + "[^&;]*[;&]?");
|
||||
if (search.match(regex)) {
|
||||
search = search.replace(regex, "$1").replace(/&$/, '');
|
||||
}
|
||||
return search + '&' + param + '=' + newval;
|
||||
}
|
||||
|
||||
function checkConfigState() {
|
||||
$.ajax({
|
||||
url: window.location.href,
|
||||
type: "GET",
|
||||
success: function (data, textStatus, request) {
|
||||
if (request.status >= 300) {
|
||||
window.location = "http://" + srvName;
|
||||
}
|
||||
},
|
||||
error: function (request, textStatus, errorThrown) {
|
||||
// continue
|
||||
if (request.status >= 300) {
|
||||
window.location = "http://" + srvName;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var cTimer;
|
||||
|
||||
/**
|
||||
* check manual-config switch
|
||||
*/
|
||||
$('#manual_config').on('click', function () {
|
||||
clearInterval(cTimer);
|
||||
var checked = $(this).prop('checked');
|
||||
if (checked) {
|
||||
// button zum login
|
||||
$('#submitAuto').addClass('d-none');
|
||||
$('#submitManual').removeClass('d-none');
|
||||
} else {
|
||||
cTimer = setInterval(checkConfigState, 1000);
|
||||
// spinner fürs warten
|
||||
$('#submitAuto').removeClass('d-none');
|
||||
$('#submitManual').addClass('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
if ($('#manual_config').length > 0) {
|
||||
var srvName = $('#target_servername').val();
|
||||
clearInterval(cTimer);
|
||||
cTimer = setInterval(checkConfigState, 1000);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
$(function() {
|
||||
|
||||
// check for internal ip and output a notice if private-range ip is given
|
||||
$('#ip').on('change', function () {
|
||||
var ipval = $(this).val();
|
||||
if (ipval.length > 0) {
|
||||
$('#ipnote').remove();
|
||||
$('#ip').removeClass('is-invalid');
|
||||
$.ajax({
|
||||
url: "admin_ipsandports.php?page=overview&action=jqCheckIP",
|
||||
type: "POST",
|
||||
data: {
|
||||
ip: ipval
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json != 0) {
|
||||
$('#ip').addClass('is-invalid');
|
||||
$('#ip').parent().append(json);
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
$(function() {
|
||||
/*
|
||||
* newsfeed
|
||||
*/
|
||||
if (document.getElementById('newsfeed')) {
|
||||
let role = "";
|
||||
|
||||
if (typeof $("#newsfeed").data("role") !== "undefined") {
|
||||
role = "&role=" + $("#newsfeed").data("role");
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=newsfeed" + role + "&theme=" + window.$theme,
|
||||
type: "GET",
|
||||
success: function (data) {
|
||||
$("#newsfeeditems").html(data);
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
console.log(request, status, error)
|
||||
$("#newsfeeditems").html('<div class="list-group-item text-center"><span class="badge bg-warning" role="alert">Error loading newsfeed</span></div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
$(function() {
|
||||
let search = $('#search')
|
||||
|
||||
search.on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
search.find('input').on('keyup', function () {
|
||||
let query = $(this).val();
|
||||
let dropdown = $('#search .search-results');
|
||||
// Hide search if query is empty
|
||||
if (!query.length) {
|
||||
dropdown.html('');
|
||||
dropdown.parent().hide();
|
||||
return;
|
||||
}
|
||||
// Show notification for short search query
|
||||
if (query.length && query.length < 3) {
|
||||
dropdown.html('<li class="list-group-item text-muted py-1">Please enter more than 2 characters</li>');
|
||||
dropdown.parent().show();
|
||||
return;
|
||||
}
|
||||
// Search
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=searchglobal&theme=" + window.$theme,
|
||||
type: "POST",
|
||||
data: {
|
||||
searchtext: query
|
||||
},
|
||||
dataType: "json",
|
||||
success: data => {
|
||||
// Show notification if we got no results
|
||||
if (Object.keys(data).length === 0) {
|
||||
dropdown.html('<li class="list-group-item text-muted py-1">Nothing found!</li>');
|
||||
dropdown.parent().show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear dropdown and show results
|
||||
dropdown.html('');
|
||||
dropdown.parent().show();
|
||||
Object.keys(data).forEach(key => {
|
||||
dropdown.append('<li class="list-group-item text-muted text-capitalize fw-bold py-1 border-bottom">' + key + '</li>');
|
||||
data[key].forEach(item => {
|
||||
dropdown.append('<li class="list-group-item mt-1"><a href="' + item.href + '" tabindex="2" class="text-decoration-none">' + item.title + '</a></li>');
|
||||
});
|
||||
});
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
dropdown.html('<li class="list-group-item text-muted py-1">Whoops we got some errors!</li>');
|
||||
dropdown.parent().show();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user