From 9f6669fb163e080f39800ef194928fa5b24ce1d4 Mon Sep 17 00:00:00 2001 From: Miguel Ribeiro Date: Wed, 3 Jul 2024 18:10:58 +0200 Subject: [PATCH] feat: ability to add custom css styles --- endpoints/settings/customcss.php | 37 +++++ includes/getsettings.php | 9 ++ includes/header.php | 8 + includes/version.php | 2 +- migrations/000023.php | 12 ++ scripts/settings.js | 224 +--------------------------- scripts/theme.js | 247 +++++++++++++++++++++++++++++++ service-worker.js | 1 + settings.php | 14 ++ 9 files changed, 330 insertions(+), 224 deletions(-) create mode 100644 endpoints/settings/customcss.php create mode 100644 migrations/000023.php create mode 100644 scripts/theme.js diff --git a/endpoints/settings/customcss.php b/endpoints/settings/customcss.php new file mode 100644 index 000000000..2b29858ca --- /dev/null +++ b/endpoints/settings/customcss.php @@ -0,0 +1,37 @@ + false, + "message" => translate('session_expired', $i18n) + ])); +} + +if ($_SERVER["REQUEST_METHOD"] === "POST") { + $postData = file_get_contents("php://input"); + $data = json_decode($postData, true); + + $customCss = $data['customCss']; + + $stmt = $db->prepare('DELETE FROM custom_css_style WHERE user_id = :userId'); + $stmt->bindParam(':userId', $userId, SQLITE3_INTEGER); + $stmt->execute(); + + $stmt = $db->prepare('INSERT INTO custom_css_style (css, user_id) VALUES (:customCss, :userId)'); + $stmt->bindParam(':customCss', $customCss, SQLITE3_TEXT); + $stmt->bindParam(':userId', $userId, SQLITE3_INTEGER); + + if ($stmt->execute()) { + die(json_encode([ + "success" => true, + "message" => translate("success", $i18n) + ])); + } else { + die(json_encode([ + "success" => false, + "message" => translate("error", $i18n) + ])); + } +} diff --git a/includes/getsettings.php b/includes/getsettings.php index da531002b..7b35a0873 100644 --- a/includes/getsettings.php +++ b/includes/getsettings.php @@ -39,6 +39,15 @@ $settings['customColors'] = $customColors; } +$query = "SELECT * FROM custom_css_style WHERE user_id = :userId"; +$stmt = $db->prepare($query); +$stmt->bindValue(':userId', $userId, SQLITE3_INTEGER); +$result = $stmt->execute(); +$customCss = $result->fetchArray(SQLITE3_ASSOC); +if ($customCss) { + $settings['customCss'] = $customCss['css']; +} + $query = "SELECT * FROM admin"; $result = $db->query($query); $adminSettings = $result->fetchArray(SQLITE3_ASSOC); diff --git a/includes/header.php b/includes/header.php index add54ba9a..d9d5c9833 100644 --- a/includes/header.php +++ b/includes/header.php @@ -33,6 +33,11 @@ $colorTheme = $settings['color_theme']; } + $customCss = ""; + if (isset($settings['customCss'])) { + $customCss = $settings['customCss']; + } + $isAdmin = $_SESSION['userId'] == 1; function hex2rgb($hex) { @@ -78,6 +83,9 @@ function hex2rgb($hex) { window.lang = ""; window.colorTheme = ""; + diff --git a/includes/version.php b/includes/version.php index ce401978f..356290fba 100644 --- a/includes/version.php +++ b/includes/version.php @@ -1,3 +1,3 @@ \ No newline at end of file diff --git a/migrations/000023.php b/migrations/000023.php new file mode 100644 index 000000000..c126d5aa8 --- /dev/null +++ b/migrations/000023.php @@ -0,0 +1,12 @@ +exec('CREATE TABLE IF NOT EXISTS custom_css_style ( + css TEXT DEFAULT "", + user_id INTEGER, + FOREIGN KEY (user_id) REFERENCES user(id) +)'); \ No newline at end of file diff --git a/scripts/settings.js b/scripts/settings.js index 18b3ddc58..350c2edb8 100644 --- a/scripts/settings.js +++ b/scripts/settings.js @@ -872,104 +872,6 @@ function addFixerKeyButton() { document.getElementById("addFixerKey").disabled = false; }); } - -function switchTheme() { - const darkThemeCss = document.querySelector("#dark-theme"); - darkThemeCss.disabled = !darkThemeCss.disabled; - - const themeChoice = darkThemeCss.disabled ? 'light' : 'dark'; - document.cookie = `theme=${themeChoice}; expires=Fri, 31 Dec 9999 23:59:59 GMT`; - - document.body.className = themeChoice; - - const button = document.getElementById("switchTheme"); - button.disabled = true; - - fetch('endpoints/settings/theme.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({theme: themeChoice === 'dark'}) - }) - .then(response => response.json()) - .then(data => { - if (data.success) { - showSuccessMessage(data.message); - } else { - showErrorMessage(data.errorMessage); - } - button.disabled = false; - }).catch(error => { - button.disabled = false; - }); -} - -function setDarkTheme(theme) { - const darkThemeButton = document.querySelector("#theme-dark"); - const lightThemeButton = document.querySelector("#theme-light"); - const automaticThemeButton = document.querySelector("#theme-automatic"); - const darkThemeCss = document.querySelector("#dark-theme"); - const themes = {0: 'light', 1: 'dark', 2: 'automatic'}; - const themeValue = themes[theme]; - const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; - - darkThemeButton.disabled = true; - lightThemeButton.disabled = true; - automaticThemeButton.disabled = true; - - fetch('endpoints/settings/theme.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({theme: theme}) - }) - .then(response => response.json()) - .then(data => { - if (data.success) { - darkThemeButton.disabled = false; - lightThemeButton.disabled = false; - automaticThemeButton.disabled = false; - darkThemeButton.classList.remove('selected'); - lightThemeButton.classList.remove('selected'); - automaticThemeButton.classList.remove('selected'); - - document.cookie = `theme=${themeValue}; expires=Fri, 31 Dec 9999 23:59:59 GMT`; - - if (theme == 0) { - darkThemeCss.disabled = true; - document.body.className = 'light'; - lightThemeButton.classList.add('selected'); - } - - if (theme == 1) { - darkThemeCss.disabled = false; - document.body.className = 'dark'; - darkThemeButton.classList.add('selected'); - } - - if (theme == 2) { - darkThemeCss.disabled = !prefersDarkMode; - document.body.className = prefersDarkMode ? 'dark' : 'light'; - automaticThemeButton.classList.add('selected'); - document.cookie = `inUseTheme=${prefersDarkMode ? 'dark' : 'light'}; expires=Fri, 31 Dec 9999 23:59:59 GMT`; - } - - showSuccessMessage(data.message); - } else { - showErrorMessage(data.errorMessage); - darkThemeButton.disabled = false; - lightThemeButton.disabled = false; - automaticThemeButton.disabled = false; - } - }).catch(error => { - darkThemeButton.disabled = false; - lightThemeButton.disabled = false; - automaticThemeButton.disabled = false; - }); -} - function storeSettingsOnDB(endpoint, value) { fetch('endpoints/settings/' + endpoint + '.php', { method: 'POST', @@ -1052,128 +954,4 @@ var sortable = Sortable.create(el, { onEnd: function (evt) { saveCategorySorting(); }, -}); - - -function setTheme(themeColor) { - var currentTheme = 'blue'; - var themeIds = ['red-theme', 'green-theme', 'yellow-theme', 'purple-theme']; - - themeIds.forEach(function(id) { - var themeStylesheet = document.getElementById(id); - if (themeStylesheet && !themeStylesheet.disabled) { - currentTheme = id.replace('-theme', ''); - themeStylesheet.disabled = true; - } - }); - - if (themeColor !== "blue") { - var enableTheme = document.getElementById(themeColor + '-theme'); - enableTheme.disabled = false; - } - - var images = document.querySelectorAll('img'); - images.forEach(function(img) { - if (img.src.includes('siteicons/' + currentTheme)) { - img.src = img.src.replace(currentTheme, themeColor); - } - }); - - var labels = document.querySelectorAll('.theme-preview'); - labels.forEach(function(label) { - label.classList.remove('is-selected'); - }); - - var targetLabel = document.querySelector(`.theme-preview.${themeColor}`); - if (targetLabel) { - targetLabel.classList.add('is-selected'); - } - - document.cookie = `colorTheme=${themeColor}; expires=Fri, 31 Dec 9999 23:59:59 GMT`; - - fetch('endpoints/settings/colortheme.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ color: themeColor }) - }) - .then(response => response.json()) - .then(data => { - if (data.success) { - showSuccessMessage(data.message); - } else { - showErrorMessage(data.message); - } - }) - .catch(error => { - showErrorMessage(translate('unknown_error')); - }); - -} - -function resetCustomColors() { - const button = document.getElementById("reset-colors"); - button.disabled = true; - - fetch('endpoints/settings/resettheme.php', { - method: 'DELETE', - }) - .then(response => response.json()) - .then(data => { - if (data.success) { - showSuccessMessage(data.message); - const custom_theme_colors = document.getElementById('custom_theme_colors'); - if (custom_theme_colors) { - custom_theme_colors.remove(); - } - document.documentElement.style.removeProperty('--main-color'); - document.documentElement.style.removeProperty('--accent-color'); - document.documentElement.style.removeProperty('--hover-color'); - document.getElementById("mainColor").value = "#FFFFFF"; - document.getElementById("accentColor").value = "#FFFFFF"; - document.getElementById("hoverColor").value = "#FFFFFF"; - } else { - showErrorMessage(data.message); - } - button.disabled = false; - }) - .catch(error => { - showErrorMessage(translate('unknown_error')); - button.disabled = false; - }); -} - -function saveCustomColors() { - const button = document.getElementById("save-colors"); - button.disabled = true; - - const mainColor = document.getElementById("mainColor").value; - const accentColor = document.getElementById("accentColor").value; - const hoverColor = document.getElementById("hoverColor").value; - - fetch('endpoints/settings/customtheme.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ mainColor: mainColor, accentColor: accentColor, hoverColor: hoverColor }) - }) - .then(response => response.json()) - .then(data => { - if (data.success) { - showSuccessMessage(data.message); - document.documentElement.style.setProperty('--main-color', mainColor); - document.documentElement.style.setProperty('--accent-color', accentColor); - document.documentElement.style.setProperty('--hover-color', hoverColor); - } else { - showErrorMessage(data.message); - } - button.disabled = false; - }) - .catch(error => { - showErrorMessage(translate('unknown_error')); - button.disabled = false; - }); - -} \ No newline at end of file +}); \ No newline at end of file diff --git a/scripts/theme.js b/scripts/theme.js new file mode 100644 index 000000000..f267fba20 --- /dev/null +++ b/scripts/theme.js @@ -0,0 +1,247 @@ +function switchTheme() { + const darkThemeCss = document.querySelector("#dark-theme"); + darkThemeCss.disabled = !darkThemeCss.disabled; + + const themeChoice = darkThemeCss.disabled ? 'light' : 'dark'; + document.cookie = `theme=${themeChoice}; expires=Fri, 31 Dec 9999 23:59:59 GMT`; + + document.body.className = themeChoice; + + const button = document.getElementById("switchTheme"); + button.disabled = true; + + fetch('endpoints/settings/theme.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ theme: themeChoice === 'dark' }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showSuccessMessage(data.message); + } else { + showErrorMessage(data.errorMessage); + } + button.disabled = false; + }).catch(error => { + button.disabled = false; + }); +} + +function setDarkTheme(theme) { + const darkThemeButton = document.querySelector("#theme-dark"); + const lightThemeButton = document.querySelector("#theme-light"); + const automaticThemeButton = document.querySelector("#theme-automatic"); + const darkThemeCss = document.querySelector("#dark-theme"); + const themes = { 0: 'light', 1: 'dark', 2: 'automatic' }; + const themeValue = themes[theme]; + const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; + + darkThemeButton.disabled = true; + lightThemeButton.disabled = true; + automaticThemeButton.disabled = true; + + fetch('endpoints/settings/theme.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ theme: theme }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + darkThemeButton.disabled = false; + lightThemeButton.disabled = false; + automaticThemeButton.disabled = false; + darkThemeButton.classList.remove('selected'); + lightThemeButton.classList.remove('selected'); + automaticThemeButton.classList.remove('selected'); + + document.cookie = `theme=${themeValue}; expires=Fri, 31 Dec 9999 23:59:59 GMT`; + + if (theme == 0) { + darkThemeCss.disabled = true; + document.body.className = 'light'; + lightThemeButton.classList.add('selected'); + } + + if (theme == 1) { + darkThemeCss.disabled = false; + document.body.className = 'dark'; + darkThemeButton.classList.add('selected'); + } + + if (theme == 2) { + darkThemeCss.disabled = !prefersDarkMode; + document.body.className = prefersDarkMode ? 'dark' : 'light'; + automaticThemeButton.classList.add('selected'); + document.cookie = `inUseTheme=${prefersDarkMode ? 'dark' : 'light'}; expires=Fri, 31 Dec 9999 23:59:59 GMT`; + } + + showSuccessMessage(data.message); + } else { + showErrorMessage(data.errorMessage); + darkThemeButton.disabled = false; + lightThemeButton.disabled = false; + automaticThemeButton.disabled = false; + } + }).catch(error => { + darkThemeButton.disabled = false; + lightThemeButton.disabled = false; + automaticThemeButton.disabled = false; + }); +} + +function setTheme(themeColor) { + var currentTheme = 'blue'; + var themeIds = ['red-theme', 'green-theme', 'yellow-theme', 'purple-theme']; + + themeIds.forEach(function(id) { + var themeStylesheet = document.getElementById(id); + if (themeStylesheet && !themeStylesheet.disabled) { + currentTheme = id.replace('-theme', ''); + themeStylesheet.disabled = true; + } + }); + + if (themeColor !== "blue") { + var enableTheme = document.getElementById(themeColor + '-theme'); + enableTheme.disabled = false; + } + + var images = document.querySelectorAll('img'); + images.forEach(function(img) { + if (img.src.includes('siteicons/' + currentTheme)) { + img.src = img.src.replace(currentTheme, themeColor); + } + }); + + var labels = document.querySelectorAll('.theme-preview'); + labels.forEach(function(label) { + label.classList.remove('is-selected'); + }); + + var targetLabel = document.querySelector(`.theme-preview.${themeColor}`); + if (targetLabel) { + targetLabel.classList.add('is-selected'); + } + + document.cookie = `colorTheme=${themeColor}; expires=Fri, 31 Dec 9999 23:59:59 GMT`; + + fetch('endpoints/settings/colortheme.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ color: themeColor }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showSuccessMessage(data.message); + } else { + showErrorMessage(data.message); + } + }) + .catch(error => { + showErrorMessage(translate('unknown_error')); + }); + + } + + function resetCustomColors() { + const button = document.getElementById("reset-colors"); + button.disabled = true; + + fetch('endpoints/settings/resettheme.php', { + method: 'DELETE', + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showSuccessMessage(data.message); + const custom_theme_colors = document.getElementById('custom_theme_colors'); + if (custom_theme_colors) { + custom_theme_colors.remove(); + } + document.documentElement.style.removeProperty('--main-color'); + document.documentElement.style.removeProperty('--accent-color'); + document.documentElement.style.removeProperty('--hover-color'); + document.getElementById("mainColor").value = "#FFFFFF"; + document.getElementById("accentColor").value = "#FFFFFF"; + document.getElementById("hoverColor").value = "#FFFFFF"; + } else { + showErrorMessage(data.message); + } + button.disabled = false; + }) + .catch(error => { + showErrorMessage(translate('unknown_error')); + button.disabled = false; + }); + } + + function saveCustomColors() { + const button = document.getElementById("save-colors"); + button.disabled = true; + + const mainColor = document.getElementById("mainColor").value; + const accentColor = document.getElementById("accentColor").value; + const hoverColor = document.getElementById("hoverColor").value; + + fetch('endpoints/settings/customtheme.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ mainColor: mainColor, accentColor: accentColor, hoverColor: hoverColor }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showSuccessMessage(data.message); + document.documentElement.style.setProperty('--main-color', mainColor); + document.documentElement.style.setProperty('--accent-color', accentColor); + document.documentElement.style.setProperty('--hover-color', hoverColor); + } else { + showErrorMessage(data.message); + } + button.disabled = false; + }) + .catch(error => { + showErrorMessage(translate('unknown_error')); + button.disabled = false; + }); + + } + + function saveCustomCss() { + const button = document.getElementById("save-css"); + button.disabled = true; + + const customCss = document.getElementById("customCss").value; + + fetch('endpoints/settings/customcss.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ customCss: customCss }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showSuccessMessage(data.message); + } else { + showErrorMessage(data.message); + } + button.disabled = false; + }) + .catch(error => { + showErrorMessage(translate('unknown_error')); + button.disabled = false; + }); + } \ No newline at end of file diff --git a/service-worker.js b/service-worker.js index 3dbefd3a2..c2ee0a3ac 100644 --- a/service-worker.js +++ b/service-worker.js @@ -31,6 +31,7 @@ self.addEventListener('install', function (event) { 'scripts/dashboard.js', 'scripts/stats.js', 'scripts/settings.js', + 'scripts/theme.js', 'scripts/notifications.js', 'scripts/registration.js', 'scripts/login.js', diff --git a/settings.php b/settings.php index 35a18b988..e118b0d81 100644 --- a/settings.php +++ b/settings.php @@ -1172,6 +1172,19 @@ class="color-picker fa-solid fa-eye-dropper"> onClick="saveCustomColors()" class="buton thin mobile-grow" id="save-colors"> +
+

+
+
+ +
+
+ +
+
+
@@ -1228,6 +1241,7 @@ class="color-picker fa-solid fa-eye-dropper">
+