Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b542b140c6 | ||
|
|
ac89fc7120 | ||
|
|
150858485d | ||
|
|
e7810e2066 | ||
|
|
4879446567 | ||
|
|
43eff78088 | ||
|
|
55a2ae3801 | ||
|
|
a3b0332d13 | ||
|
|
4b1846883d | ||
|
|
778fd3ba65 | ||
|
|
00456a35e5 | ||
|
|
5958f0516b | ||
|
|
166ffedf04 | ||
|
|
36dfee1263 | ||
|
|
ec0026ecfd | ||
|
|
a721bb3f21 | ||
|
|
83de3dd719 |
@@ -60,7 +60,9 @@ if ($userinfo['change_serversettings'] == '1') {
|
|||||||
|
|
||||||
if (!empty($distribution)) {
|
if (!empty($distribution)) {
|
||||||
if (!file_exists($config_dir . '/' . $distribution . ".xml")) {
|
if (!file_exists($config_dir . '/' . $distribution . ".xml")) {
|
||||||
Response::dynamicError("Unknown distribution");
|
// unknown distribution -> redirect to select a valid distribution for config-templates
|
||||||
|
Settings::Set('system.distribution', '');
|
||||||
|
Response::redirectTo('admin_configfiles.php', ['reselect' => 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update setting if different
|
// update setting if different
|
||||||
|
|||||||
@@ -115,8 +115,8 @@ if ($page == 'overview') {
|
|||||||
$userinfo['traffic_bytes_used'] = $userinfo['traffic_used'] * 1024;
|
$userinfo['traffic_bytes_used'] = $userinfo['traffic_used'] * 1024;
|
||||||
|
|
||||||
if (Settings::Get('system.mail_quota_enabled')) {
|
if (Settings::Get('system.mail_quota_enabled')) {
|
||||||
$userinfo['email_quota_bytes'] = ($userinfo['email_quota'] > -1) ? $userinfo['email_quota'] * 1024 : -1;
|
$userinfo['email_quota_bytes'] = ($userinfo['email_quota'] > -1) ? $userinfo['email_quota'] * 1024 * 1024 : -1;
|
||||||
$userinfo['email_quota_bytes_used'] = $userinfo['email_quota_used'] * 1024;
|
$userinfo['email_quota_bytes_used'] = $userinfo['email_quota_used'] * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($usages) {
|
if ($usages) {
|
||||||
|
|||||||
19
index.php
19
index.php
@@ -74,27 +74,26 @@ if ($action == '2fa_entercode') {
|
|||||||
$code = isset($_POST['2fa_code']) ? $_POST['2fa_code'] : null;
|
$code = isset($_POST['2fa_code']) ? $_POST['2fa_code'] : null;
|
||||||
// verify entered code
|
// verify entered code
|
||||||
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
$tfa = new FroxlorTwoFactorAuth('Froxlor ' . Settings::Get('system.hostname'));
|
||||||
$result = ($_SESSION['secret_2fa'] == 'email' ? true : $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3));
|
|
||||||
// get user-data
|
// get user-data
|
||||||
$table = $_SESSION['uidtable_2fa'];
|
$table = $_SESSION['uidtable_2fa'];
|
||||||
$field = $_SESSION['uidfield_2fa'];
|
$field = $_SESSION['uidfield_2fa'];
|
||||||
$uid = $_SESSION['uid_2fa'];
|
$uid = $_SESSION['uid_2fa'];
|
||||||
$isadmin = $_SESSION['unfo_2fa'];
|
$isadmin = $_SESSION['unfo_2fa'];
|
||||||
|
if ($_SESSION['secret_2fa'] == 'email') {
|
||||||
|
// verify code set to user's data_2fa field
|
||||||
|
$sel_stmt = Database::prepare("SELECT `data_2fa` FROM " . $table . " WHERE `" . $field . "` = :uid");
|
||||||
|
$userinfo_code = Database::pexecute_first($sel_stmt, ['uid' => $uid]);
|
||||||
|
$result = $tfa->verifyCode($userinfo_code['data_2fa'], $code);
|
||||||
|
} else {
|
||||||
|
$result = $tfa->verifyCode($_SESSION['secret_2fa'], $code, 3);
|
||||||
|
}
|
||||||
// either the code is valid when using authenticator-app, or we will select userdata by id and entered code
|
// either the code is valid when using authenticator-app, or we will select userdata by id and entered code
|
||||||
// which is temporarily stored for the customer when using email-2fa
|
// which is temporarily stored for the customer when using email-2fa
|
||||||
if ($result) {
|
if ($result) {
|
||||||
$sel_param = [
|
$sel_param = [
|
||||||
'uid' => $uid
|
'uid' => $uid
|
||||||
];
|
];
|
||||||
if ($_SESSION['secret_2fa'] == 'email') {
|
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
|
||||||
// verify code by selecting user by id and the temp. stored code,
|
|
||||||
// so only if it's the correct code, we get the user-data
|
|
||||||
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid AND `data_2fa` = :code");
|
|
||||||
$sel_param['code'] = $code;
|
|
||||||
} else {
|
|
||||||
// Authenticator-verification has already happened at this point, so just get the user-data
|
|
||||||
$sel_stmt = Database::prepare("SELECT * FROM " . $table . " WHERE `" . $field . "` = :uid");
|
|
||||||
}
|
|
||||||
$userinfo = Database::pexecute_first($sel_stmt, $sel_param);
|
$userinfo = Database::pexecute_first($sel_stmt, $sel_param);
|
||||||
// whoops, no (valid) user? Start again
|
// whoops, no (valid) user? Start again
|
||||||
if (empty($userinfo)) {
|
if (empty($userinfo)) {
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ CREATE TABLE `panel_admins` (
|
|||||||
`api_allowed` tinyint(1) NOT NULL default '1',
|
`api_allowed` tinyint(1) NOT NULL default '1',
|
||||||
PRIMARY KEY (`adminid`),
|
PRIMARY KEY (`adminid`),
|
||||||
UNIQUE KEY `loginname` (`loginname`)
|
UNIQUE KEY `loginname` (`loginname`)
|
||||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
|
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `panel_customers`;
|
DROP TABLE IF EXISTS `panel_customers`;
|
||||||
@@ -299,7 +299,7 @@ CREATE TABLE `panel_domains` (
|
|||||||
KEY `customerid` (`customerid`),
|
KEY `customerid` (`customerid`),
|
||||||
KEY `parentdomain` (`parentdomainid`),
|
KEY `parentdomain` (`parentdomainid`),
|
||||||
KEY `domain` (`domain`)
|
KEY `domain` (`domain`)
|
||||||
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci;
|
) ENGINE=InnoDB CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=DYNAMIC;
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `panel_ipsandports`;
|
DROP TABLE IF EXISTS `panel_ipsandports`;
|
||||||
@@ -678,7 +678,7 @@ opcache.validate_timestamps'),
|
|||||||
('system', 'distribution', ''),
|
('system', 'distribution', ''),
|
||||||
('system', 'update_channel', 'stable'),
|
('system', 'update_channel', 'stable'),
|
||||||
('system', 'updatecheck_data', ''),
|
('system', 'updatecheck_data', ''),
|
||||||
('system', 'update_notify_last', '2.1.1'),
|
('system', 'update_notify_last', ''),
|
||||||
('system', 'traffictool', 'goaccess'),
|
('system', 'traffictool', 'goaccess'),
|
||||||
('system', 'req_limit_per_interval', 60),
|
('system', 'req_limit_per_interval', 60),
|
||||||
('system', 'req_limit_interval', 60),
|
('system', 'req_limit_interval', 60),
|
||||||
@@ -726,8 +726,8 @@ opcache.validate_timestamps'),
|
|||||||
('panel', 'logo_overridecustom', '0'),
|
('panel', 'logo_overridecustom', '0'),
|
||||||
('panel', 'settings_mode', '0'),
|
('panel', 'settings_mode', '0'),
|
||||||
('panel', 'menu_collapsed', '1'),
|
('panel', 'menu_collapsed', '1'),
|
||||||
('panel', 'version', '2.1.1'),
|
('panel', 'version', '2.1.3'),
|
||||||
('panel', 'db_version', '202312050');
|
('panel', 'db_version', '202312120');
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `panel_tasks`;
|
DROP TABLE IF EXISTS `panel_tasks`;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ if (!defined('_CRON_UPDATE')) {
|
|||||||
|
|
||||||
if (Froxlor::isFroxlorVersion('2.0.24')) {
|
if (Froxlor::isFroxlorVersion('2.0.24')) {
|
||||||
Update::showUpdateStep("Cleaning domains table");
|
Update::showUpdateStep("Cleaning domains table");
|
||||||
|
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ROW_FORMAT=DYNAMIC;");
|
||||||
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP COLUMN `ismainbutsubto`;");
|
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` DROP COLUMN `ismainbutsubto`;");
|
||||||
Update::lastStepStatus(0);
|
Update::lastStepStatus(0);
|
||||||
|
|
||||||
@@ -213,3 +214,63 @@ if (Froxlor::isFroxlorVersion('2.1.0')) {
|
|||||||
Update::showUpdateStep("Updating from 2.1.0 to 2.1.1", false);
|
Update::showUpdateStep("Updating from 2.1.0 to 2.1.1", false);
|
||||||
Froxlor::updateToVersion('2.1.1');
|
Froxlor::updateToVersion('2.1.1');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isDatabaseVersion('202312050')) {
|
||||||
|
Update::showUpdateStep("Cleaning up old files");
|
||||||
|
$to_clean = array(
|
||||||
|
"lib/configfiles/centos7.xml",
|
||||||
|
"lib/configfiles/centos8.xml",
|
||||||
|
"lib/configfiles/stretch.xml",
|
||||||
|
"lib/configfiles/xenial.xml",
|
||||||
|
"lib/configfiles/buster.xml",
|
||||||
|
"lib/configfiles/bionic.xml",
|
||||||
|
);
|
||||||
|
$disabled = explode(',', ini_get('disable_functions'));
|
||||||
|
$exec_allowed = !in_array('exec', $disabled);
|
||||||
|
$del_list = "";
|
||||||
|
foreach ($to_clean as $filedir) {
|
||||||
|
$complete_filedir = Froxlor::getInstallDir() . $filedir;
|
||||||
|
if (file_exists($complete_filedir)) {
|
||||||
|
if ($exec_allowed) {
|
||||||
|
FileDir::safe_exec("rm -rf " . escapeshellarg($complete_filedir));
|
||||||
|
} else {
|
||||||
|
$del_list .= "rm -rf " . escapeshellarg($complete_filedir) . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($exec_allowed) {
|
||||||
|
Update::lastStepStatus(0);
|
||||||
|
} else {
|
||||||
|
if (empty($del_list)) {
|
||||||
|
// none of the files existed
|
||||||
|
Update::lastStepStatus(0);
|
||||||
|
} else {
|
||||||
|
Update::lastStepStatus(
|
||||||
|
1,
|
||||||
|
'manual commands needed',
|
||||||
|
'Please run the following commands manually:<br><pre>' . $del_list . '</pre>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Froxlor::updateToDbVersion('202312100');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isDatabaseVersion('202312100')) {
|
||||||
|
|
||||||
|
Update::showUpdateStep("Adjusting table row format of larger tables");
|
||||||
|
Database::query("ALTER TABLE `" . TABLE_PANEL_ADMINS . "` ROW_FORMAT=DYNAMIC;");
|
||||||
|
Database::query("ALTER TABLE `" . TABLE_PANEL_DOMAINS . "` ROW_FORMAT=DYNAMIC;");
|
||||||
|
Update::lastStepStatus(0);
|
||||||
|
|
||||||
|
Froxlor::updateToDbVersion('202312120');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isFroxlorVersion('2.1.1')) {
|
||||||
|
Update::showUpdateStep("Updating from 2.1.1 to 2.1.2", false);
|
||||||
|
Froxlor::updateToVersion('2.1.2');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Froxlor::isFroxlorVersion('2.1.2')) {
|
||||||
|
Update::showUpdateStep("Updating from 2.1.2 to 2.1.3", false);
|
||||||
|
Froxlor::updateToVersion('2.1.3');
|
||||||
|
}
|
||||||
|
|||||||
@@ -1053,7 +1053,7 @@ class Customers extends ApiCommand implements ResourceEntity
|
|||||||
$email = $this->getParam('email', true, $idna_convert->decode($result['email']));
|
$email = $this->getParam('email', true, $idna_convert->decode($result['email']));
|
||||||
$name = $this->getParam('name', true, $result['name']);
|
$name = $this->getParam('name', true, $result['name']);
|
||||||
$firstname = $this->getParam('firstname', true, $result['firstname']);
|
$firstname = $this->getParam('firstname', true, $result['firstname']);
|
||||||
$company_required = empty($result['company']) && ((!empty($name) && empty($firstname)) || (empty($name) && !empty($firstname)) || (empty($name) && empty($firstname)));
|
$company_required = (!empty($name) && empty($firstname)) || (empty($name) && !empty($firstname)) || (empty($name) && empty($firstname));
|
||||||
$company = $this->getParam('company', !$company_required, $result['company']);
|
$company = $this->getParam('company', !$company_required, $result['company']);
|
||||||
$street = $this->getParam('street', true, $result['street']);
|
$street = $this->getParam('street', true, $result['street']);
|
||||||
$zipcode = $this->getParam('zipcode', true, $result['zipcode']);
|
$zipcode = $this->getParam('zipcode', true, $result['zipcode']);
|
||||||
|
|||||||
@@ -259,14 +259,15 @@ class Froxlor extends ApiCommand
|
|||||||
* returns a random password based on froxlor settings for min-length, included characters, etc.
|
* returns a random password based on froxlor settings for min-length, included characters, etc.
|
||||||
*
|
*
|
||||||
* @param int $length
|
* @param int $length
|
||||||
* optional length of password, defaults to 10
|
* optional length of password, defaults to 0 (panel.password_min_length)
|
||||||
*
|
*
|
||||||
* @access admin, customer
|
* @access admin, customer
|
||||||
* @return string
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function generatePassword()
|
public function generatePassword(): string
|
||||||
{
|
{
|
||||||
$length = $this->getParam('length', true, 10);
|
$length = $this->getParam('length', true, 0);
|
||||||
return $this->response(Crypt::generatePassword($length));
|
return $this->response(Crypt::generatePassword($length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ final class InstallCommand extends Command
|
|||||||
$ask_field = false;
|
$ask_field = false;
|
||||||
}
|
}
|
||||||
$fielddata['value'] = $this->formfielddata[$fieldname] ?? ($fielddata['value'] ?? null);
|
$fielddata['value'] = $this->formfielddata[$fieldname] ?? ($fielddata['value'] ?? null);
|
||||||
$fielddata['label'] = strip_tags(str_replace("<br>", " ", $fielddata['label']));
|
$fielddata['label'] = $this->cliTextFormat($fielddata['label'], " ");
|
||||||
if ($ask_field) {
|
if ($ask_field) {
|
||||||
if ($fielddata['type'] == 'password') {
|
if ($fielddata['type'] == 'password') {
|
||||||
$this->formfielddata[$fieldname] = $this->io->askHidden($fielddata['label'], function ($value) use ($fielddata) {
|
$this->formfielddata[$fieldname] = $this->io->askHidden($fielddata['label'], function ($value) use ($fielddata) {
|
||||||
@@ -267,14 +267,16 @@ final class InstallCommand extends Command
|
|||||||
case 4:
|
case 4:
|
||||||
$section = $inst->formfield['install']['sections']['step' . $step] ?? [];
|
$section = $inst->formfield['install']['sections']['step' . $step] ?? [];
|
||||||
$this->io->section($section['title']);
|
$this->io->section($section['title']);
|
||||||
$this->io->note($section['description']);
|
$this->io->note($this->cliTextFormat($section['description']));
|
||||||
$cmdfield = $section['fields']['system'];
|
$cmdfield = $section['fields']['system'];
|
||||||
$this->io->success([
|
$this->io->success([
|
||||||
$cmdfield['label'],
|
$cmdfield['label'],
|
||||||
$cmdfield['value']
|
$cmdfield['value']
|
||||||
]);
|
]);
|
||||||
if (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) {
|
if (!isset($decoded_input['manual_config']) || (bool)$decoded_input['manual_config'] === false) {
|
||||||
passthru($cmdfield['value']);
|
if (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) {
|
||||||
|
passthru($cmdfield['value']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -305,7 +307,7 @@ final class InstallCommand extends Command
|
|||||||
$json_output = [];
|
$json_output = [];
|
||||||
foreach ($fields['install']['sections'] as $section => $section_fields) {
|
foreach ($fields['install']['sections'] as $section => $section_fields) {
|
||||||
foreach ($section_fields['fields'] as $name => $field) {
|
foreach ($section_fields['fields'] as $name => $field) {
|
||||||
if ($name == 'system' || $name == 'manual_config' || $name == 'target_servername') {
|
if ($name == 'system' || $name == 'target_servername') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($field['type'] == 'text' || $field['type'] == 'email') {
|
if ($field['type'] == 'text' || $field['type'] == 'email') {
|
||||||
@@ -318,7 +320,7 @@ final class InstallCommand extends Command
|
|||||||
$fieldval = '******';
|
$fieldval = '******';
|
||||||
} elseif ($field['type'] == 'select') {
|
} elseif ($field['type'] == 'select') {
|
||||||
$fieldval = implode("|", array_keys($field['select_var']));
|
$fieldval = implode("|", array_keys($field['select_var']));
|
||||||
} else if ($field['type'] == 'checkbox') {
|
} elseif ($field['type'] == 'checkbox') {
|
||||||
$fieldval = "1|0";
|
$fieldval = "1|0";
|
||||||
} else {
|
} else {
|
||||||
$fieldval = "?";
|
$fieldval = "?";
|
||||||
@@ -346,4 +348,10 @@ final class InstallCommand extends Command
|
|||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function cliTextFormat(string $text, string $nl_char = "\n"): string
|
||||||
|
{
|
||||||
|
$text = str_replace(['<br>', '<br/>', '<br />'], [$nl_char, $nl_char, $nl_char], $text);
|
||||||
|
return strip_tags($text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ abstract class DnsBase
|
|||||||
'zonefile' => '',
|
'zonefile' => '',
|
||||||
'froxlorhost' => '1'
|
'froxlorhost' => '1'
|
||||||
];
|
];
|
||||||
$domains['none'] = $hostname_arr;
|
$domains[0] = $hostname_arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($domains)) {
|
if (empty($domains)) {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class TrafficCron extends FroxlorCron
|
|||||||
|
|
||||||
public static function run()
|
public static function run()
|
||||||
{
|
{
|
||||||
self::runFork([self::class, 'handle']);
|
self::runFork([self::class, 'handle'], [true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function handle()
|
public static function handle()
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ final class Froxlor
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Main version variable
|
// Main version variable
|
||||||
const VERSION = '2.1.1';
|
const VERSION = '2.1.3';
|
||||||
|
|
||||||
// Database version (YYYYMMDDC where C is a daily counter)
|
// Database version (YYYYMMDDC where C is a daily counter)
|
||||||
const DBVERSION = '202312050';
|
const DBVERSION = '202312120';
|
||||||
|
|
||||||
// Distribution branding-tag (used for Debian etc.)
|
// Distribution branding-tag (used for Debian etc.)
|
||||||
const BRANDING = '';
|
const BRANDING = '';
|
||||||
|
|||||||
@@ -220,8 +220,11 @@ class PhpHelper
|
|||||||
if (is_dir($data_dirname)) {
|
if (is_dir($data_dirname)) {
|
||||||
$data_dirhandle = opendir($data_dirname);
|
$data_dirhandle = opendir($data_dirname);
|
||||||
while (false !== ($data_filename = readdir($data_dirhandle))) {
|
while (false !== ($data_filename = readdir($data_dirhandle))) {
|
||||||
if ($data_filename != '.' && $data_filename != '..' && $data_filename != '' && substr($data_filename,
|
if ($data_filename != '.'
|
||||||
-4) == '.php') {
|
&& $data_filename != '..'
|
||||||
|
&& $data_filename != ''
|
||||||
|
&& substr($data_filename, -4) == '.php'
|
||||||
|
) {
|
||||||
$data_files[] = $data_dirname . $data_filename;
|
$data_files[] = $data_dirname . $data_filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -415,8 +418,8 @@ class PhpHelper
|
|||||||
*/
|
*/
|
||||||
public static function recursive_array_search(
|
public static function recursive_array_search(
|
||||||
string $needle,
|
string $needle,
|
||||||
array $haystack,
|
array $haystack,
|
||||||
array &$keys = [],
|
array &$keys = [],
|
||||||
string $currentKey = ''
|
string $currentKey = ''
|
||||||
): bool {
|
): bool {
|
||||||
foreach ($haystack as $key => $value) {
|
foreach ($haystack as $key => $value) {
|
||||||
@@ -458,6 +461,10 @@ class PhpHelper
|
|||||||
'directory_password',
|
'directory_password',
|
||||||
'ftp_password',
|
'ftp_password',
|
||||||
'mysql_password',
|
'mysql_password',
|
||||||
|
'mysql_root_pass',
|
||||||
|
'mysql_unprivileged_pass',
|
||||||
|
'admin_pass',
|
||||||
|
'admin_pass_confirm',
|
||||||
];
|
];
|
||||||
if (!empty($global)) {
|
if (!empty($global)) {
|
||||||
$tmp = $global;
|
$tmp = $global;
|
||||||
@@ -557,4 +564,17 @@ class PhpHelper
|
|||||||
}
|
}
|
||||||
return $tab . $str;
|
return $tab . $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function array_merge_recursive_distinct(array &$array1, array &$array2)
|
||||||
|
{
|
||||||
|
$merged = $array1;
|
||||||
|
foreach ($array2 as $key => &$value) {
|
||||||
|
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
|
||||||
|
$merged[$key] = self::array_merge_recursive_distinct($merged[$key], $value);
|
||||||
|
} else {
|
||||||
|
$merged[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $merged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class Form
|
|||||||
{
|
{
|
||||||
$returnvalue = [];
|
$returnvalue = [];
|
||||||
if (is_array($fielddata) && isset($fielddata['type']) && $fielddata['type'] == 'select') {
|
if (is_array($fielddata) && isset($fielddata['type']) && $fielddata['type'] == 'select') {
|
||||||
if ((!isset($fielddata['select_var']) || !is_array($fielddata['select_var']) || empty($fielddata['select_var'])) && (isset($fielddata['option_options_method']))) {
|
if ((!is_array($fielddata['select_var']) || empty($fielddata['select_var'])) && (isset($fielddata['option_options_method']))) {
|
||||||
$returnvalue['select_var'] = call_user_func($fielddata['option_options_method']);
|
$returnvalue['select_var'] = call_user_func($fielddata['option_options_method']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,8 +236,8 @@ class Form
|
|||||||
if (\Froxlor\Validate\Form::validateFieldDefinition($groupdetails)) {
|
if (\Froxlor\Validate\Form::validateFieldDefinition($groupdetails)) {
|
||||||
// Prefetch form fields
|
// Prefetch form fields
|
||||||
foreach ($groupdetails['fields'] as $fieldname => $fielddetails) {
|
foreach ($groupdetails['fields'] as $fieldname => $fielddetails) {
|
||||||
if (!$only_enabledisable || ($only_enabledisable && isset($fielddetails['overview_option']))) {
|
if (!$only_enabledisable || isset($fielddetails['overview_option'])) {
|
||||||
$groupdetails['fields'][$fieldname] = self::arrayMergePrefix($fielddetails, $fielddetails['type'], self::prefetchFormFieldData($fieldname, $fielddetails));
|
$groupdetails['fields'][$fieldname] = array_merge($fielddetails, self::prefetchFormFieldData($fieldname, $fielddetails));
|
||||||
$form['groups'][$groupname]['fields'][$fieldname] = $groupdetails['fields'][$fieldname];
|
$form['groups'][$groupname]['fields'][$fieldname] = $groupdetails['fields'][$fieldname];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,7 +347,7 @@ class Form
|
|||||||
if (\Froxlor\Validate\Form::validateFieldDefinition($groupdetails)) {
|
if (\Froxlor\Validate\Form::validateFieldDefinition($groupdetails)) {
|
||||||
// Save fields
|
// Save fields
|
||||||
foreach ($groupdetails['fields'] as $fieldname => $fielddetails) {
|
foreach ($groupdetails['fields'] as $fieldname => $fielddetails) {
|
||||||
if (!$only_enabledisable || ($only_enabledisable && isset($fielddetails['overview_option']))) {
|
if (!$only_enabledisable || (isset($fielddetails['overview_option']))) {
|
||||||
if (isset($changed_fields[$fieldname])) {
|
if (isset($changed_fields[$fieldname])) {
|
||||||
if (($saved_field = self::saveFormField($fieldname, $fielddetails, self::manipulateFormFieldData($fieldname, $fielddetails, $changed_fields[$fieldname]))) !== false) {
|
if (($saved_field = self::saveFormField($fieldname, $fielddetails, self::manipulateFormFieldData($fieldname, $fielddetails, $changed_fields[$fieldname]))) !== false) {
|
||||||
$saved_fields = array_merge($saved_fields, $saved_field);
|
$saved_fields = array_merge($saved_fields, $saved_field);
|
||||||
@@ -364,24 +364,7 @@ class Form
|
|||||||
// Save form
|
// Save form
|
||||||
return self::saveForm($form, $saved_fields);
|
return self::saveForm($form, $saved_fields);
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
|
|
||||||
private static function arrayMergePrefix($array1, $key_prefix, $array2)
|
|
||||||
{
|
|
||||||
if (is_array($array1) && is_array($array2)) {
|
|
||||||
if ($key_prefix != '') {
|
|
||||||
foreach ($array2 as $key => $value) {
|
|
||||||
$array1[$key_prefix . '_' . $key] = $value;
|
|
||||||
unset($array2[$key]);
|
|
||||||
}
|
|
||||||
unset($array2);
|
|
||||||
return $array1;
|
|
||||||
} else {
|
|
||||||
return array_merge($array1, $array2);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return $array1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getFormFieldData($fieldname, $fielddata, &$input)
|
public static function getFormFieldData($fieldname, $fielddata, &$input)
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class Validate
|
|||||||
}
|
}
|
||||||
|
|
||||||
// special case where localhost ip is allowed (mysql-access-hosts for example)
|
// special case where localhost ip is allowed (mysql-access-hosts for example)
|
||||||
if ($allow_localhost && $ip == '127.0.0.1') {
|
if ($allow_localhost && ($ip == '127.0.0.1' || $ip == '::1')) {
|
||||||
return $ip . $cidr;
|
return $ip . $cidr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,9 +112,14 @@ function vite($basehref, array $filenames): string
|
|||||||
$assetDirectory = '/templates/' . $matches[1] . '/build/';
|
$assetDirectory = '/templates/' . $matches[1] . '/build/';
|
||||||
$viteManifest = dirname(__DIR__) . $assetDirectory . '/manifest.json';
|
$viteManifest = dirname(__DIR__) . $assetDirectory . '/manifest.json';
|
||||||
$manifest = json_decode(file_get_contents($viteManifest), true);
|
$manifest = json_decode(file_get_contents($viteManifest), true);
|
||||||
$links[] = $basehref . ltrim($assetDirectory, '/') . $manifest[$filename]['file'];
|
if (!empty($manifest[$filename]['file'])) {
|
||||||
|
$links[] = $basehref . ltrim($assetDirectory, '/') . $manifest[$filename]['file'];
|
||||||
|
} else {
|
||||||
|
// additional asset from config.json that was not prebuilt on release (e.g. custom.css)
|
||||||
|
$links[] = $filename;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$links = $filenames;
|
$links[] = $filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ if ($req_host != Settings::Get('system.hostname') &&
|
|||||||
(!empty(Settings::Get('system.froxloraliases')) && !in_array($req_host, array_map('trim', explode(',', Settings::Get('system.froxloraliases')))))
|
(!empty(Settings::Get('system.froxloraliases')) && !in_array($req_host, array_map('trim', explode(',', Settings::Get('system.froxloraliases')))))
|
||||||
)) {
|
)) {
|
||||||
// not the froxlor system-hostname, show info page for domains not configured in froxlor
|
// not the froxlor system-hostname, show info page for domains not configured in froxlor
|
||||||
$redirect_file = FileDir::getUnknownDomainTemplate($req_host);
|
$redirect_file = FileDir::getUnknownDomainTemplate($req_host ?? "non-detectable http-host");
|
||||||
header('Location: '.$redirect_file);
|
header('Location: '.$redirect_file);
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ if (is_array($_themeoptions) && (!array_key_exists('variants', $_themeoptions) |
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('global', $_themeoptions)) {
|
if (array_key_exists('global', $_themeoptions)) {
|
||||||
$_themeoptions['variants'][$themevariant] = array_merge_recursive($_themeoptions['variants'][$themevariant], $_themeoptions['global']);
|
$_themeoptions['variants'][$themevariant] = PhpHelper::array_merge_recursive_distinct($_themeoptions['global'], $_themeoptions['variants'][$themevariant]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for custom header-graphic
|
// check for custom header-graphic
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ return [
|
|||||||
'field' => 'domains',
|
'field' => 'domains',
|
||||||
'callback' => [PHPConf::class, 'domainList'],
|
'callback' => [PHPConf::class, 'domainList'],
|
||||||
'searchable' => false,
|
'searchable' => false,
|
||||||
|
'sortable' => false,
|
||||||
],
|
],
|
||||||
'fpmdesc' => [
|
'fpmdesc' => [
|
||||||
'label' => lng('admin.phpsettings.fpmdesc'),
|
'label' => lng('admin.phpsettings.fpmdesc'),
|
||||||
|
|||||||
@@ -8,18 +8,18 @@ export default function () {
|
|||||||
rules: {
|
rules: {
|
||||||
'name': {
|
'name': {
|
||||||
required: function () {
|
required: function () {
|
||||||
return $('#company').val().length === 0 || $('#firstname').val().length > 0;
|
return $('#company').val().trim().length === 0 || $('#firstname').val().trim().length > 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'firstname': {
|
'firstname': {
|
||||||
required: function () {
|
required: function () {
|
||||||
return $('#company').val().length === 0 || $('#name').val().length > 0;
|
return $('#company').val().trim().length === 0 || $('#name').val().trim().length > 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'company': {
|
'company': {
|
||||||
required: function () {
|
required: function () {
|
||||||
return $('#name').val().length === 0
|
return $('#name').val().trim().length === 0
|
||||||
&& $('#firstname').val().length === 0;
|
&& $('#firstname').val().trim().length === 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"global": {
|
"global": {
|
||||||
"css": [
|
"css": [
|
||||||
"assets/css/custom.css",
|
"assets/scss/app.scss",
|
||||||
"assets/scss/app.scss"
|
"assets/css/custom.css"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
"assets/js/app.js",
|
"assets/js/app.js",
|
||||||
|
|||||||
@@ -184,7 +184,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% set field = field|merge({'type':'file'}) %}
|
{% set field = field|merge({'type':'file'})|merge({'value':''}) %}
|
||||||
{{ _self.input(id, field) }}
|
{{ _self.input(id, field) }}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user