Compare commits

..

17 Commits
2.1.1 ... 2.1.3

Author SHA1 Message Date
Michael Kaufmann
b542b140c6 set version to 2.1.3 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-22 14:33:11 +01:00
Michael Kaufmann
ac89fc7120 adjust order of css files
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-22 14:30:23 +01:00
Michael Kaufmann
150858485d include custom.css from config.json if preset correctly
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-21 10:59:15 +01:00
Michael Kaufmann
e7810e2066 correctly merge fielddetails with prefetched-formfielddata in form-processing
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-20 09:39:01 +01:00
Michael Kaufmann
4879446567 domains in php-configs are not sortable
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-19 17:17:51 +01:00
Michael Kaufmann
43eff78088 use panel.password_min_length setting for Froxlor.generatePassword() default length parameter; allow '::1' as valid mysql localhost value; wrapper to clean output for cli installation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-16 20:20:58 +01:00
Gamerboy59
55a2ae3801 Add manual_config install var to cli (#1208)
Make the manual_config var, which is available to the web installer, usuable for the cli installer too. If manual_config is set to true skip else (not set or false) proceed with auto config.
2023-12-16 20:13:58 +01:00
Michael Kaufmann
a3b0332d13 set version to 2.1.2 for bugfix release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-15 09:41:16 +01:00
Michael Kaufmann
4b1846883d Merge pull request from GHSA-625g-fm5w-w7w4
* fix possibility to have empty name/surname and empty company

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>

* let js validation for customer add/edit form also trim() entered data to avoid empty values pass the client-side validation

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>

---------

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-15 09:36:06 +01:00
Michael Kaufmann
778fd3ba65 fix wrong size-unit for mailquota-dashboard-info; fixes #1207
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-15 09:32:02 +01:00
Michael Kaufmann
00456a35e5 fix 2fa login when using email validation, thx to wysiwtf; adjusting row-format of larger tables
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-13 16:20:28 +01:00
Michael Kaufmann
5958f0516b do not css-check/clean passwords of the installation process; fixes #1203
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-12 09:33:28 +01:00
Michael Kaufmann
166ffedf04 correctly merge themeoptions array to use correct image on login when using darkmode
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-12 08:59:56 +01:00
Michael Kaufmann
36dfee1263 fix non-empty value for file-input fields when using uploaded logos
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-12 08:58:33 +01:00
Michael Kaufmann
ec0026ecfd fix wrong type when dns zone for system-hostname is active
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-11 14:20:08 +01:00
Michael Kaufmann
a721bb3f21 remove old 0.10.x and 2.0.x distribution-config-xml's for updaters
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-10 20:47:31 +01:00
Michael Kaufmann
83de3dd719 handle unknown distribution if there's a now unsupported distribution selected for the config-templates
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2023-12-10 19:23:58 +01:00
20 changed files with 150 additions and 70 deletions

View File

@@ -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

View File

@@ -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) {

View File

@@ -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') {
// 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"); $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)) {

View File

@@ -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`;

View File

@@ -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');
}

View File

@@ -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']);

View File

@@ -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));
} }

View File

@@ -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,15 +267,17 @@ 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 (!isset($decoded_input['manual_config']) || (bool)$decoded_input['manual_config'] === false) {
if (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) { if (!empty($decoded_input) || $this->io->confirm('Execute command now?', false)) {
passthru($cmdfield['value']); passthru($cmdfield['value']);
} }
}
break; break;
} }
return $result; return $result;
@@ -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);
}
} }

View File

@@ -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)) {

View File

@@ -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()

View File

@@ -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 = '';

View File

@@ -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;
} }
} }
@@ -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;
}
} }

View File

@@ -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)

View File

@@ -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;
} }

View File

@@ -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);
if (!empty($manifest[$filename]['file'])) {
$links[] = $basehref . ltrim($assetDirectory, '/') . $manifest[$filename]['file']; $links[] = $basehref . ltrim($assetDirectory, '/') . $manifest[$filename]['file'];
} else { } else {
$links = $filenames; // additional asset from config.json that was not prebuilt on release (e.g. custom.css)
$links[] = $filename;
}
} else {
$links[] = $filename;
} }
} }

View File

@@ -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

View File

@@ -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'),

View File

@@ -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;
} }
} }
}, },

View File

@@ -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",

View File

@@ -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 %}