diff --git a/.gitignore b/.gitignore index 3d547882..1b91d454 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,9 @@ logs/* .well-known .idea *.iml +img/ !templates/Froxlor/ !templates/Sparkle/ !templates/misc/ -templates/Froxlor/assets/img/logo_custom.png vendor/ diff --git a/actions/admin/settings/100.panel.php b/actions/admin/settings/100.panel.php index 3485bbdf..e73c5183 100644 --- a/actions/admin/settings/100.panel.php +++ b/actions/admin/settings/100.panel.php @@ -296,6 +296,24 @@ return array( 'default' => '', 'save_method' => 'storeSettingField' ), + 'panel_logo_image_header' => array( + 'label' => $lng['serversettings']['logo_image_header'], + 'settinggroup' => 'panel', + 'varname' => 'logo_image_header', + 'type' => 'image', + 'image_name' => 'logo_header', + 'default' => '', + 'save_method' => 'storeSettingImage' + ), + 'panel_logo_image_login' => array( + 'label' => $lng['serversettings']['logo_image_login'], + 'settinggroup' => 'panel', + 'varname' => 'logo_image_login', + 'type' => 'image', + 'image_name' => 'logo_login', + 'default' => '', + 'save_method' => 'storeSettingImage' + ), ) ) ) diff --git a/composer.json b/composer.json index d5feaf8c..82326ce4 100644 --- a/composer.json +++ b/composer.json @@ -43,12 +43,13 @@ "ext-curl": "*", "ext-json": "*", "ext-openssl": "*", + "ext-fileinfo": "*", "phpmailer/phpmailer": "~6.0", "monolog/monolog": "^1.24", "robthree/twofactorauth": "^1.6", "froxlor/idna-convert-legacy": "^2.1", "voku/anti-xss": "^4.1" - }, + }, "require-dev": { "phpunit/phpunit": "^9", "php": ">=7.3", diff --git a/install/froxlor.sql b/install/froxlor.sql index 64db3116..763a1fec 100644 --- a/install/froxlor.sql +++ b/install/froxlor.sql @@ -715,8 +715,10 @@ opcache.interned_strings_buffer'), ('panel', 'imprint_url', ''), ('panel', 'terms_url', ''), ('panel', 'privacy_url', ''), + ('panel', 'logo_image_header', ''), + ('panel', 'logo_image_login', ''), ('panel', 'version', '0.10.26'), - ('panel', 'db_version', '202106270'); + ('panel', 'db_version', '202107070'); DROP TABLE IF EXISTS `panel_tasks`; @@ -933,7 +935,7 @@ CREATE TABLE IF NOT EXISTS `ftp_quotalimits` ( -INSERT INTO `ftp_quotalimits` (`name`, `quota_type`, `per_session`, `limit_type`, `bytes_in_avail`, `bytes_out_avail`, `bytes_xfer_avail`, `files_in_avail`, `files_out_avail`, `files_xfer_avail`) VALUES +INSERT INTO `ftp_quotalimits` (`name`, `quota_type`, `per_session`, `limit_type`, `bytes_in_avail`, `bytes_out_avail`, `bytes_xfer_avail`, `files_in_avail`, `files_out_avail`, `files_xfer_avail`) VALUES ('froxlor', 'user', 'false', 'hard', 0, 0, 0, 0, 0, 0); diff --git a/install/updates/froxlor/0.10/update_0.10.inc.php b/install/updates/froxlor/0.10/update_0.10.inc.php index 3f03dff2..1f2d360e 100644 --- a/install/updates/froxlor/0.10/update_0.10.inc.php +++ b/install/updates/froxlor/0.10/update_0.10.inc.php @@ -14,7 +14,7 @@ use Froxlor\Settings; * @author Froxlor team (2010-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Install - * + * */ if (! defined('_CRON_UPDATE')) { if (! defined('AREA') || (defined('AREA') && AREA != 'admin') || ! isset($userinfo['loginname']) || (isset($userinfo['loginname']) && $userinfo['loginname'] == '')) { @@ -825,3 +825,40 @@ if (\Froxlor\Froxlor::isDatabaseVersion('202106160')) { \Froxlor\Froxlor::updateToDbVersion('202106270'); } + +if (\Froxlor\Froxlor::isDatabaseVersion('202106270')) { + showUpdateStep("Adding custom logo image settings", true); + Settings::AddNew("panel.logo_image_header", ''); + Settings::AddNew("panel.logo_image_login", ''); + lastStepStatus(0); + + // Migrating old custom logo over, if exists + $custom_logo_file_old = \Froxlor\Froxlor::getInstallDir() . '/templates/Sparkle/assets/img/logo_custom.png'; + if (file_exists($custom_logo_file_old)) { + showUpdateStep("Migrating existing custom logo to new settings", true); + + $path = \Froxlor\Froxlor::getInstallDir().'/img/'; + if (!is_dir($path) && !mkdir($path, 0775)) { + throw new \Exception("img directory does not exist and cannot be created"); + } + if (!is_writable($path)) { + if (!chmod($path, '0775')) { + throw new \Exception("Cannot write to img directory"); + } + } + + // Save as new custom logo header + $save_to = 'logo_header.png'; + copy($custom_logo_file_old, $path.$save_to); + Settings::Set("panel.logo_image_header", "img/{$save_to}?v=".time()); + + // Save as new custom logo login + $save_to = 'logo_login.png'; + copy($custom_logo_file_old, $path.$save_to); + Settings::Set("panel.logo_image_login", "img/{$save_to}?v=".time()); + + lastStepStatus(0); + } + + \Froxlor\Froxlor::updateToDbVersion('202107070'); +} diff --git a/lib/Froxlor/Froxlor.php b/lib/Froxlor/Froxlor.php index e9ccbe30..9e9fb3f2 100644 --- a/lib/Froxlor/Froxlor.php +++ b/lib/Froxlor/Froxlor.php @@ -10,7 +10,7 @@ final class Froxlor const VERSION = '0.10.26'; // Database version (YYYYMMDDC where C is a daily counter) - const DBVERSION = '202106270'; + const DBVERSION = '202107070'; // Distribution branding-tag (used for Debian etc.) const BRANDING = ''; @@ -63,7 +63,7 @@ final class Froxlor * * @param string $to_check * version to check, if empty current version is used - * + * * @return bool true if version to check does not match, else false */ public static function hasUpdates($to_check = null) @@ -84,7 +84,7 @@ final class Froxlor * * @param int $to_check * version to check, if empty current dbversion is used - * + * * @return bool true if version to check does not match, else false */ public static function hasDbUpdates($to_check = null) @@ -105,7 +105,7 @@ final class Froxlor * * @param int $to_check * version to check - * + * * @return bool true if version to check matches, else false */ public static function isDatabaseVersion($to_check = null) @@ -124,7 +124,7 @@ final class Froxlor * * @param string $new_version * new-version - * + * * @return bool true on success, else false */ public static function updateToDbVersion($new_version = null) @@ -150,7 +150,7 @@ final class Froxlor * * @param string $new_version * new-version - * + * * @return bool true on success, else false */ public static function updateToVersion($new_version = null) @@ -191,7 +191,7 @@ final class Froxlor * * @param string $to_check * version to check - * + * * @return bool true if version to check matches, else false */ public static function isFroxlorVersion($to_check = null) diff --git a/lib/Froxlor/SImExporter.php b/lib/Froxlor/SImExporter.php index 7120aaad..5a549f97 100644 --- a/lib/Froxlor/SImExporter.php +++ b/lib/Froxlor/SImExporter.php @@ -16,9 +16,9 @@ use Froxlor\Database\Database; * @author Froxlor team (2018-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Classes - * + * * @since 0.9.39 - * + * */ /** @@ -60,6 +60,13 @@ class SImExporter public static function export() { + $settings_definitions = []; + foreach (\Froxlor\PhpHelper::loadConfigArrayDir('./actions/admin/settings/')['groups'] AS $group) { + foreach ($group['fields'] AS $field) { + $settings_definitions[$field['settinggroup']][$field['varname']] = $field; + } + } + $result_stmt = Database::query(" SELECT * FROM `" . TABLE_PANEL_SETTINGS . "` ORDER BY `settingid` ASC "); @@ -69,13 +76,26 @@ class SImExporter if (! in_array($index, self::$no_export)) { $_data[$index] = $row['value']; } + + if (array_key_exists($row['settinggroup'], $settings_definitions) && array_key_exists($row['varname'], $settings_definitions[$row['settinggroup']])) { + // Export image file + if ($settings_definitions[$row['settinggroup']][$row['varname']]['type'] === "image") { + if ($row['value'] === "") { + continue; + } + + $_data[$index.'.image_data'] = base64_encode(file_get_contents(explode('?', $row['value'], 2)[0])); + } + } } + // add checksum for validation $_data['_sha'] = sha1(var_export($_data, true)); $_export = json_encode($_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); if (! $_export) { throw new \Exception("Error exporting settings: " . json_last_error_msg()); } + return $_export; } @@ -120,6 +140,26 @@ class SImExporter } // store new data foreach ($_data as $index => $value) { + $index_split = explode('.', $index, 3); + + // Catch image_data and save it + if (isset($index_split[2]) && $index_split[2] === 'image_data' && !empty($_data[$index_split[0].'.'.$index_split[1]])) { + $path = \Froxlor\Froxlor::getInstallDir().'/img/'; + if (!is_dir($path) && !mkdir($path, '0775')) { + throw new \Exception("img directory does not exist and cannot be created"); + } + + // Make sure we can write to the upload directory + if (!is_writable($path)) { + if (!chmod($path, '0775')) { + throw new \Exception("Cannot write to img directory"); + } + } + + file_put_contents(\Froxlor\Froxlor::getInstallDir() . '/' . explode('?', $_data[$index_split[0].'.'.$index_split[1]], 2)[0], base64_decode($value)); + continue; + } + Settings::Set($index, $value); } // save to DB diff --git a/lib/Froxlor/Settings/Store.php b/lib/Froxlor/Settings/Store.php index e4a8035e..69e365fa 100644 --- a/lib/Froxlor/Settings/Store.php +++ b/lib/Froxlor/Settings/Store.php @@ -367,4 +367,67 @@ class Store return $returnvalue; } + + public static function storeSettingImage($fieldname, $fielddata) + { + if (isset($fielddata['settinggroup'], $fielddata['varname']) && is_array($fielddata) && $fielddata['settinggroup'] !== '' && $fielddata['varname'] !== '') { + $save_to = null; + $path = \Froxlor\Froxlor::getInstallDir().'/img/'; + + // New file? + if ($_FILES[$fieldname]['tmp_name']) { + // Make sure upload directory exists + if (!is_dir($path) && !mkdir($path, '0775')) { + throw new \Exception("img directory does not exist and cannot be created"); + } + + // Make sure we can write to the upload directory + if (!is_writable($path)) { + if (!chmod($path, '0775')) { + throw new \Exception("Cannot write to img directory"); + } + } + + // Make sure mime-type matches an image + if (!in_array(mime_content_type($_FILES[$fieldname]['tmp_name']), ['image/jpeg','image/jpg','image/png','image/gif'])) { + throw new \Exception("Uploaded file not a valid image"); + } + + // Determine file extension + $spl = explode('.', $_FILES[$fieldname]['name']); + $file_extension = strtolower(array_pop($spl)); + unset($spl); + + // Move file + if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path.$fielddata['image_name'].'.'.$file_extension)) { + throw new \Exception("Unable to save image to img folder"); + } + + $save_to = 'img/'.$fielddata['image_name'].'.'.$file_extension.'?v='.time(); + } + + // Delete file? + if ($fielddata['value'] !== "" && array_key_exists($fieldname.'_delete', $_POST) && $_POST[$fieldname.'_delete']) { + @unlink(\Froxlor\Froxlor::getInstallDir() . '/' . explode('?', $fielddata['value'], 2)[0]); + $save_to = ''; + } + + // Nothing changed + if ($save_to === null) { + return array( + $fielddata['settinggroup'] . '.' . $fielddata['varname'] => $fielddata['value'] + ); + } + + if (Settings::Set($fielddata['settinggroup'] . '.' . $fielddata['varname'], $save_to) === false) { + return false; + } + + return array( + $fielddata['settinggroup'] . '.' . $fielddata['varname'] => $save_to + ); + } + + return false; + } } diff --git a/lib/Froxlor/UI/Data.php b/lib/Froxlor/UI/Data.php index a32ffa31..ee34f012 100644 --- a/lib/Froxlor/UI/Data.php +++ b/lib/Froxlor/UI/Data.php @@ -52,6 +52,12 @@ class Data return $newfieldvalue; } + public static function getFormFieldDataImage($fieldname, $fielddata, $input) + { + // We always make the system think we have new data to trigger the save function where we actually check everything + return time(); + } + public static function manipulateFormFieldDataDate($fieldname, $fielddata, $newfieldvalue) { if (isset($fielddata['date_timestamp']) && $fielddata['date_timestamp'] === true) { diff --git a/lib/Froxlor/UI/Fields.php b/lib/Froxlor/UI/Fields.php index c138538a..681e701c 100644 --- a/lib/Froxlor/UI/Fields.php +++ b/lib/Froxlor/UI/Fields.php @@ -89,6 +89,15 @@ class Fields return $returnvalue; } + public static function getFormFieldOutputImage($fieldname, $fielddata, $do_show = true) + { + global $lng; + $label = $fielddata['label']; + $value = htmlentities($fielddata['value']); + eval("\$returnvalue = \"" . \Froxlor\UI\Template::getTemplate("formfields/image", true) . "\";"); + return $returnvalue; + } + public static function getFormFieldOutputDate($fieldname, $fielddata, $do_show = true) { if (isset($fielddata['date_timestamp']) && $fielddata['date_timestamp'] === true) { diff --git a/lib/init.php b/lib/init.php index a0a8a431..f34565c1 100644 --- a/lib/init.php +++ b/lib/init.php @@ -380,11 +380,8 @@ if (! array_key_exists('variants', $_themeoptions) || ! array_key_exists($themev // check for custom header-graphic $hl_path = 'templates/' . $theme . '/assets/img'; -$header_logo = $hl_path . '/logo.png'; - -if (file_exists($hl_path . '/logo_custom.png')) { - $header_logo = $hl_path . '/logo_custom.png'; -} +$header_logo = Settings::Get('panel.logo_image_header') ?: $hl_path . '/logo.png'; +$header_logo_login = Settings::Get('panel.logo_image_login') ?: $hl_path . '/logo.png'; /** * Redirects to index.php (login page) if no session exists diff --git a/lng/english.lng.php b/lng/english.lng.php index 117690d6..afb00a0a 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -2117,3 +2117,9 @@ $lng['privacy'] = 'Privacy policy'; $lng['serversettings']['privacy_url']['title'] = 'URL to privacy policy'; $lng['serversettings']['privacy_url']['description'] = 'Specify an URL to your privacy policy site / imprint site. The link will be visible on the login screen and on the footer when logged in.'; $lng['admin']['domaindefaultalias'] = 'Default ServerAlias value for new domains'; + +$lng['serversettings']['logo_image_header']['title'] = 'Logo Image (Header)'; +$lng['serversettings']['logo_image_header']['description'] = 'Upload your own logo image to be shown in the header after login (recommended height 30px)'; +$lng['serversettings']['logo_image_login']['title'] = 'Logo Image (Login)'; +$lng['serversettings']['logo_image_login']['description'] = 'Upload your own logo image to be shown during login'; +$lng['panel']['image_field_delete'] = 'Delete the existing current image'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 1aeba318..c9cf309c 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1763,3 +1763,9 @@ $lng['privacy'] = 'Datenschutzerklärung'; $lng['serversettings']['privacy_url']['title'] = 'URL zur Datenschutzerklärung'; $lng['serversettings']['privacy_url']['description'] = 'Die URL zur Datenschutzerklärungs-Seite. Der Link ist auf der Login-Seite und wenn eingeloggt, in der Fußzeile sichtbar.'; $lng['admin']['domaindefaultalias'] = 'Standard ServerAlias-Angabe für neue Domains'; + +$lng['serversettings']['logo_image_header']['title'] = 'Logo Bild (Header)'; +$lng['serversettings']['logo_image_header']['description'] = 'Das hochgeladene Bild wird als Logo oben links nach dem Login angezeigt (empfohlene Höhe sind 30px)'; +$lng['serversettings']['logo_image_login']['title'] = 'Logo Bild (Login)'; +$lng['serversettings']['logo_image_login']['description'] = 'Das hochgeladene Bild wird als Logo während des Logins angezeigt'; +$lng['panel']['image_field_delete'] = 'Das momentan vorhandene Bild löschen'; diff --git a/templates/Sparkle/2fa/entercode.tpl b/templates/Sparkle/2fa/entercode.tpl index 2f2d84f6..b79090e6 100644 --- a/templates/Sparkle/2fa/entercode.tpl +++ b/templates/Sparkle/2fa/entercode.tpl @@ -1,7 +1,7 @@ $header