Kaynağa Gözat

User Interface Improvements & Bug Fixes - Theme Support

Add support for custom and system themes
v-add-sys-theme, v-change-sys-theme, v-delete-sys-theme, v-list-sys-themes
Kristan Kenney 6 yıl önce
ebeveyn
işleme
11ea2c752e
50 değiştirilmiş dosya ile 558 ekleme ve 28 silme
  1. 91 0
      bin/v-add-sys-theme
  2. 7 0
      bin/v-change-sys-language
  3. 47 0
      bin/v-change-sys-theme
  4. 0 1
      bin/v-change-user-language
  5. 52 0
      bin/v-delete-sys-theme
  6. 25 24
      bin/v-list-sys-config
  7. 102 0
      bin/v-list-sys-themes
  8. 1 1
      bin/v-update-sys-hestia-git
  9. 43 0
      bin/v-update-sys-themes
  10. 3 0
      func/main.sh
  11. 7 0
      install/hst-install-debian.sh
  12. 7 0
      install/hst-install-ubuntu.sh
  13. 5 0
      install/upgrade/versions/1.0.2.sh
  14. 1 1
      src/hst_autocompile.sh
  15. 1 0
      themes/default.css
  16. 19 0
      web/edit/server/index.php
  17. 58 0
      web/edit/server/theme/index.php
  18. 2 0
      web/inc/i18n/ar.php
  19. 2 0
      web/inc/i18n/bg.php
  20. 2 0
      web/inc/i18n/bs.php
  21. 2 0
      web/inc/i18n/cn.php
  22. 3 0
      web/inc/i18n/cz.php
  23. 2 0
      web/inc/i18n/da.php
  24. 2 0
      web/inc/i18n/de.php
  25. 2 0
      web/inc/i18n/el.php
  26. 1 0
      web/inc/i18n/en.php
  27. 1 0
      web/inc/i18n/es.php
  28. 1 0
      web/inc/i18n/fa.php
  29. 2 0
      web/inc/i18n/fi.php
  30. 1 0
      web/inc/i18n/fr.php
  31. 2 0
      web/inc/i18n/hu.php
  32. 2 0
      web/inc/i18n/id.php
  33. 2 0
      web/inc/i18n/it.php
  34. 2 0
      web/inc/i18n/ja.php
  35. 1 0
      web/inc/i18n/ko.php
  36. 3 0
      web/inc/i18n/no.php
  37. 2 0
      web/inc/i18n/pt.php
  38. 2 0
      web/inc/i18n/ro.php
  39. 3 1
      web/inc/i18n/ru.php
  40. 3 0
      web/inc/i18n/sr.php
  41. 2 0
      web/inc/i18n/th.php
  42. 3 0
      web/inc/i18n/tr.php
  43. 3 0
      web/inc/i18n/ua.php
  44. 1 0
      web/inc/i18n/ur.php
  45. 2 0
      web/inc/i18n/vi.php
  46. 29 0
      web/templates/admin/edit_server.html
  47. 1 0
      web/templates/admin/list_server_info.html
  48. 1 0
      web/templates/admin/panel.html
  49. 1 0
      web/templates/header.html
  50. 1 0
      web/templates/user/panel.html

+ 91 - 0
bin/v-add-sys-theme

@@ -0,0 +1,91 @@
+#!/bin/bash
+# info: install theme from local source or GitHub.
+# options: theme [MODE] [ACTIVE]
+
+# The function for installing a custom theme or downloading one
+# from the HestiaCP theme repository.
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+# Argument definition
+theme=$1
+mode=$2
+active=$3
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+# Define themes repository URL format
+HESTIA_THEMES_REPO="$HESTIA_GIT_REPO/$RELEASE_BRANCH/themes"
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Fallback to downloading from GitHub if no mode specified
+if [ -z "$mode" ]; then
+    mode="git"
+fi
+
+# Initialize local directory if it does not exist
+if [ ! -d "$HESTIA_THEMES_CUSTOM" ]; then
+    mkdir -p $HESTIA_THEMES_CUSTOM
+fi
+
+# Abort if no theme name specified
+if [ -z "$theme" ]; then
+    echo "ERROR: No theme name specified."
+    echo "Usage: v-add-sys-theme theme [GIT | LOCAL] [ACTIVE]"
+    echo "       theme: name of the theme to install."
+    echo "       active: Set downloaded theme as active (optional)"
+
+    exit 1
+fi
+
+# Check if theme name already exists as system theme
+if [ -e $HESTIA_THEMES/$theme.css ]; then
+    echo "ERROR: System theme with the same name already exists: $theme."
+    echo "       To update system themes, run v-update-sys-themes."
+    exit 1
+fi
+
+# Prompt to replace existing theme if detected
+if [ -e $HESTIA_THEMES_CUSTOM/$theme.css ]; then
+    echo "WARNING: Theme file $theme.css already exists."
+    read -p "Would you like to replace it? [Y/N] " replace_theme
+
+    if [ "$replace_theme" = "N" ] || [ "$replace_theme" = "n" ]; then
+        exit 1
+    fi
+fi
+
+# Install theme from GitHub repository
+if [ "$mode" = "git" ]; then
+    # Check if it's a valid file first
+    theme_check=$(curl -s --head -w %{http_code} $HESTIA_THEMES_REPO/$theme.css -o /dev/null)
+    if [ $theme_check -ne "200" ]; then
+        echo "Error: invalid theme name specified."
+        exit 1
+    fi
+
+    # Download the theme file from Git
+    echo "Downloading and installing theme: $theme..."
+    wget $HESTIA_THEMES_REPO/$theme.css -O $HESTIA_THEMES_CUSTOM/$theme.css > /dev/null 2>&1
+fi
+
+if [ "$mode" = "local" ]; then
+    read -p "Please enter the full path to the CSS file to import: " theme_path
+    cp -f $theme_path $HESTIA_THEMES_CUSTOM/
+fi
+
+# Set active theme
+$BIN/v-change-sys-theme $theme
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+exit

+ 7 - 0
bin/v-change-sys-language

@@ -11,6 +11,7 @@
 
 # Argument definition
 language=$1
+update_users=$2
 
 # Includes
 source $HESTIA/func/main.sh
@@ -46,6 +47,12 @@ else
     sed -i "s/LANGUAGE=.*/LANGUAGE='$language'/g" $HESTIA/conf/hestia.conf
 fi
 
+# Update language for all existing users if specified
+if [ "$update_users" = "yes" ]; then
+    for user in $(ls $HESTIA/data/users); do
+        $BIN/v-change-user-language $user $language
+    done
+fi
 
 #----------------------------------------------------------#
 #                       Hestia                             #

+ 47 - 0
bin/v-change-sys-theme

@@ -0,0 +1,47 @@
+#!/bin/bash
+# info: update web templates
+#
+# The function for changing the currently active system theme.
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+# Argument definition
+theme=$1
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Theme argument and file detection
+if [ -z "$theme" ]; then
+    echo "ERROR: No theme specified."
+    exit 1
+else
+    if [ -e "$HESTIA_THEMES/$theme.css" ]; then
+        theme_conf="$HESTIA_THEMES/$theme.css"
+    elif [ -e "$HESTIA_THEMES_CUSTOM/$theme.css" ]; then
+        theme_conf="$HESTIA_THEMES_CUSTOM/$theme.css"
+    else
+        echo "ERROR: Unable to locate specified theme."
+        exit 1
+    fi
+
+    # Replace theme override file
+    rm -f $HESTIA/web/css/active-theme.css
+    cp -f $theme_conf $HESTIA/web/css/active-theme.css
+
+    # Set default theme in configuration file
+    $BIN/v-change-sys-config-value 'THEME' $theme
+fi
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+exit

+ 0 - 1
bin/v-change-user-language

@@ -54,7 +54,6 @@ else
     update_user_value "$user" '$LANGUAGE' "$language"
 fi
 
-
 #----------------------------------------------------------#
 #                       Hestia                             #
 #----------------------------------------------------------#

+ 52 - 0
bin/v-delete-sys-theme

@@ -0,0 +1,52 @@
+#!/bin/bash
+# info: removes a theme from the custom theme library.
+# options: [RESTART]
+
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+# Argument definition
+theme=$1
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+if [ -z "$theme" ]; then
+    # Theme not specified, throw an error.
+    echo "ERROR: No theme specified."
+    exit 1
+else
+    if [ -e $HESTIA_THEMES/$theme.css ]; then
+        # Protect system themes from deletion
+        # Users can use the terminal to work around this if really desired.
+        echo "ERROR: Unable to delete system theme: $theme."
+        exit 1
+    fi
+    if [ -e $HESTIA_THEMES_CUSTOM/$theme.css ]; then
+        # Remove theme if it exists.
+        echo "Deleting $theme..."
+        rm -f $HESTIA_THEMES_CUSTOM/$theme.css > /dev/null 2&>1
+    else
+        # Theme doesn't exist, throw an error.
+        echo "ERROR: Theme $theme does not exist."
+    fi
+fi
+
+# Set default theme in configuration file if deleted theme was active
+if [ "$THEME" = "$theme" ]; then
+    rm -f $HESTIA/web/css/active-theme.css
+    $BIN/v-change-sys-config-value 'THEME' default
+fi
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+exit

+ 25 - 24
bin/v-list-sys-config

@@ -44,6 +44,7 @@ json_list() {
         "REPOSITORY": "'$REPOSITORY'",
         "VERSION": "'$VERSION'",
         "RELEASE_BRANCH": "'$RELEASE_BRANCH'",
+        "THEME": "'$THEME'",
         "LANGUAGE": "'$LANGUAGE'",
         "BACKUP_GZIP": "'$BACKUP_GZIP'",
         "BACKUP": "'$BACKUP'",
@@ -58,24 +59,24 @@ json_list() {
 # Shell list
 shell_list() {
     if [ ! -z "$WEB_SYSTEM" ]; then
-        echo "WEB Server:     $WEB_SYSTEM:$WEB_PORT ($WEB_RGROUPS)"
-        echo "SSL Support:    $WEB_SSL:$WEB_SSL_PORT"
+        echo "WEB Server:       $WEB_SYSTEM:$WEB_PORT ($WEB_RGROUPS)"
+        echo "SSL Support:      $WEB_SSL:$WEB_SSL_PORT"
     fi
     if [ ! -z "$WEB_BACKEND" ]; then
-        echo "WEB Backend:    $WEB_BACKEND"
+        echo "WEB Backend:      $WEB_BACKEND"
     fi
     if [ ! -z "$PROXY_SYSTEM" ]; then
-        echo "Proxy Server:   $PROXY_SYSTEM:$PROXY_PORT"
-        echo "Proxy SSL:      $PROXY_SYSTEM:$PROXY_SSL_PORT"
+        echo "Proxy Server:     $PROXY_SYSTEM:$PROXY_PORT"
+        echo "Proxy SSL:        $PROXY_SYSTEM:$PROXY_SSL_PORT"
     fi
     if [ ! -z "$STATS_SYSTEM" ]; then
-        echo "Web Stats:      ${STATS_SYSTEM//,/, }"
+        echo "Web Stats:        ${STATS_SYSTEM//,/, }"
     fi
     if [ ! -z "$FTP_SYSTEM" ]; then
-        echo "FTP Server:     $FTP_SYSTEM"
+        echo "FTP Server:       $FTP_SYSTEM"
     fi
     if [ ! -z "$MAIL_SYSTEM" ]; then
-        echo -n "Mail Server:    $MAIL_SYSTEM"
+        echo -n "Mail Server:      $MAIL_SYSTEM"
         if [ ! -z "$IMAP_SYSTEM" ]; then
             echo -n " + $IMAP_SYSTEM"
         fi
@@ -87,50 +88,50 @@ shell_list() {
         fi
         echo
         if [ ! -z "$WEBMAIL_ALIAS" ]; then
-            echo "Webmail (subdomain):       $WEBMAIL_ALIAS.domain.tld"
-            echo "Webmail (alias):        hostname/$WEBMAIL_ALIAS"
+            echo "Webmail alias:    $WEBMAIL_ALIAS"
         fi
     fi
     if [ ! -z "$DB_SYSTEM" ]; then
-        echo "Database:       ${DB_SYSTEM//,/, }"
+        echo "Database:         ${DB_SYSTEM//,/, }"
         if [ ! -z "$DB_PMA_URL" ]; then
-            echo "PMA URL:        $DB_PMA_URL"
+            echo "PMA URL:          $DB_PMA_URL"
         fi
         if [ ! -z "$DB_PGA_URL" ]; then
-            echo "PGA URL:        $DB_PGA_URL"
+            echo "PGA URL:          $DB_PGA_URL"
         fi
     fi
     if [ ! -z "$DNS_SYSTEM" ]; then
-        echo -n "DNS server:     $DNS_SYSTEM"
+        echo -n "DNS server:       $DNS_SYSTEM"
         if [ ! -z "$DNS_CLUSTER" ]; then
             echo -n " (cluster)"
         fi
         echo
     fi
     if [ ! -z "$CRON_SYSTEM" ]; then
-        echo "CRON:           $CRON_SYSTEM"
+        echo "CRON:             $CRON_SYSTEM"
     fi
     if [ ! -z "$FIREWALL_SYSTEM" ]; then
-        echo -n "Firewall:       $FIREWALL_SYSTEM"
+        echo -n "Firewall:         $FIREWALL_SYSTEM"
         if [ ! -z "$FIREWALL_EXTENSION" ]; then
             echo -n "+ $FIREWALL_EXTENSION"
         fi
         echo
     fi
     if [ ! -z "$BACKUP_SYSTEM" ]; then
-        echo "Backups:        ${BACKUP_SYSTEM//,/, }"
+        echo "Backups:          ${BACKUP_SYSTEM//,/, }"
         if [ ! -z "$BACKUP" ]; then
-            echo "Backup Dir:     $BACKUP"
+            echo "Backup Dir:         $BACKUP"
         fi
     fi
     if [ ! -z "$DISK_QUOTA" ]; then
-        echo "Disk Quota:     $DISK_QUOTA"
+        echo "Disk Quota:       $DISK_QUOTA"
     fi
     if [ ! -z "$LANGUAGE" ] && [ "$LANGUAGE" != 'en' ]; then
-        echo "Language:       $LANGUAGE"
+        echo "Language:         $LANGUAGE"
     fi
-    echo "Version:        $VERSION"
-    echo "Release Branch: $RELEASE_BRANCH"
+    echo "Version:          $VERSION"
+    echo "Release Branch:   $RELEASE_BRANCH"
+    echo "Theme:            $THEME"
 }
 
 # PLAIN list function
@@ -141,7 +142,7 @@ plain_list() {
     echo -ne "$ANTIVIRUS_SYSTEM\t$ANTISPAM_SYSTEM\t$DB_SYSTEM\t"
     echo -ne "$DNS_SYSTEM\t$DNS_CLUSTER\t$STATS_SYSTEM\t$BACKUP_SYSTEM\t"
     echo -ne "$CRON_SYSTEM\t$DISK_QUOTA\t$FIREWALL_SYSTEM\t"
-    echo -ne "$FIREWALL_EXTENSION\t$REPOSITORY\t$VERSION\t$RELEASE_BRANCH\t$LANGUAGE\t"
+    echo -ne "$FIREWALL_EXTENSION\t$REPOSITORY\t$VERSION\t$RELEASE_BRANCH\t$THEME\t$LANGUAGE\t"
     echo -e "$BACKUP_GZIP\t$BACKUP\t$WEBMAIL_ALIAS\t$DB_PMA_URL\t$DB_PGA_URL"
 }
 
@@ -164,7 +165,7 @@ csv_list() {
     echo -n "'$ANTIVIRUS_SYSTEM','$ANTISPAM_SYSTEM','$DB_SYSTEM',"
     echo -n "'$DNS_SYSTEM','$DNS_CLUSTER','$STATS_SYSTEM','$BACKUP_SYSTEM',"
     echo -n "'$CRON_SYSTEM','$DISK_QUOTA','$FIREWALL_SYSTEM','$REPOSITORY',"
-    echo -n "'$FIREWALL_EXTENSION','$VERSION','$RELEASE_BRANCH','$LANGUAGE','$BACKUP_GZIP',"
+    echo -n "'$FIREWALL_EXTENSION','$VERSION','$RELEASE_BRANCH','$THEME','$LANGUAGE','$BACKUP_GZIP',"
     echo -n "'$BACKUP','$WEBMAIL_ALIAS','$DB_PMA_URL','$DB_PGA_URL'"
     echo
 }

+ 102 - 0
bin/v-list-sys-themes

@@ -0,0 +1,102 @@
+#!/bin/bash
+# info: list web templates
+# options: USER [FORMAT]
+#
+# The function for obtaining the list of themes in the theme
+# library and displaying them in the backend or user interface.
+
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+# Argument definition
+format=${1-shell}
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+# JSON list function
+json_list() {
+    object1=$(echo "$themes" |wc -w)
+    object2=$(echo "$themes_custom" |wc -w)
+    i=1
+    echo '['
+    for theme in $themes; do
+        if [ "$i" -lt "$object1" ]; then
+            echo -e  "\t\"$theme\","
+        else
+            echo -e  "\t\"$theme\""
+        fi
+        (( ++i))
+    done
+    for custom_theme in $themes_custom; do
+        if [ "$i" -lt "$object2" ]; then
+            echo -e  "\t\"$custom_theme\","
+        else
+            echo -e  "\t\"$custom_theme\""
+        fi
+        (( ++i))
+    done
+    echo "]"
+}
+
+# SHELL list function
+shell_list() {
+    echo "THEME"
+    echo "------"
+    for theme in $themes; do
+        echo "$theme"
+    done
+    for custom_theme in $themes_custom; do
+        echo "$custom_theme"
+    done
+}
+
+# PLAIN list function
+plain_list() {
+    for theme in $themes; do
+        echo "$theme"
+    done
+    for custom_theme in $themes_custom; do
+        echo "$custom_theme"
+    done
+}
+
+# CSV list function
+csv_list() {
+    echo "THEME"
+    for theme in $themes; do
+        echo "$theme"
+    done
+    for custom_theme in $themes_custom; do
+        echo "$custom_theme"
+    done
+}
+
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Parsing templates
+themes=$(ls -v $HESTIA_THEMES/)
+themes_custom=$(ls -v $HESTIA_THEMES_CUSTOM/)
+themes=$(echo "$themes" |grep '\.css' |sed 's/\.css$//')
+themes_custom=$(echo "$themes_custom" |grep '\.css' |sed 's/\.css$//')
+
+# Listing data
+case $format in
+    json)   json_list ;;
+    plain)  plain_list ;;
+    csv)    csv_list ;;
+    shell)  shell_list ;;
+esac
+
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+exit

+ 1 - 1
bin/v-update-sys-hestia-git

@@ -348,7 +348,7 @@ if [ "$HESTIA_B" = true ] ; then
 
     # Move needed directories
     cd $BUILD_DIR/hestiacp-$branch
-    mv bin func install web ../hestia_$HESTIA_V/usr/local/hestia/
+    mv bin func install themes web ../hestia_$HESTIA_V/usr/local/hestia/
 
     # Set permission
     cd ../hestia_$HESTIA_V/usr/local/hestia/bin

+ 43 - 0
bin/v-update-sys-themes

@@ -0,0 +1,43 @@
+#!/bin/bash
+# info: Updates system theme CSS files from Hestia Control 
+#       Panel GitHub repository using the $HESTIA_GIT_REPO
+#       varible set in $HESTIA/func/main.sh.
+#
+# Usage: v-update-sys-themes
+#
+
+#----------------------------------------------------------#
+#                    Variable&Function                     #
+#----------------------------------------------------------#
+
+# Includes
+source $HESTIA/func/main.sh
+source $HESTIA/conf/hestia.conf
+
+# Set temporary directory
+HESTIA_THEMES_TEMP="/tmp/hestia-themes"
+
+#----------------------------------------------------------#
+#                       Action                             #
+#----------------------------------------------------------#
+
+# Initialize download directory
+mkdir $HESTIA_THEMES_TEMP
+
+# Update system themes
+for theme in `ls $HESTIA_THEMES/`; do
+    echo "Downloading theme: $theme"
+    wget $HESTIA_GIT_REPO/$RELEASE_BRANCH/themes/$theme -O \ 
+        $HESTIA_THEMES_TEMP/$theme > /dev/null 2>&1
+    rm -f $HESTIA_THEMES/$theme
+    mv $HESTIA_THEMES_TEMP/$theme $HESTIA_THEMES/$theme
+done
+
+# Remove any temporary files and directories
+rm -rf $HESTIA_THEMES_TEMP
+
+#----------------------------------------------------------#
+#                       Hestia                             #
+#----------------------------------------------------------#
+
+exit

+ 3 - 0
func/main.sh

@@ -13,6 +13,9 @@ MAILTPL=$HESTIA/data/templates/mail
 DNSTPL=$HESTIA/data/templates/dns
 RRD=$HESTIA/web/rrd
 SENDMAIL="$HESTIA/web/inc/mail-wrapper.php"
+HESTIA_GIT_REPO="https://raw.githubusercontent.com/hestiacp/hestiacp"
+HESTIA_THEMES="$HESTIA/themes"
+HESTIA_THEMES_CUSTOM="$HESTIA/data/templates/themes"
 
 # Return codes
 OK=0

+ 7 - 0
install/hst-install-debian.sh

@@ -1113,6 +1113,10 @@ cp -rf $hestiacp/templates $HESTIA/data/
 mkdir -p /var/www/html
 mkdir -p /var/www/document_errors
 
+# Installing default themes
+mkdir -p $HESTIA/themes
+cp -rf $hestiacp/themes $HESTIA/themes/
+
 # Install default success page
 cp -rf $hestiacp/templates/web/unassigned/index.html /var/www/html/
 cp -rf $hestiacp/templates/web/skel/document_errors/* /var/www/document_errors/
@@ -1805,6 +1809,9 @@ fi
 # Set backend port
 $HESTIA/bin/v-change-sys-port $port
 
+# Set default theme
+$HESTIA/bin/v-change-sys-theme default
+
 # Starting Hestia service
 update-rc.d hestia defaults
 service hestia start

+ 7 - 0
install/hst-install-ubuntu.sh

@@ -1076,6 +1076,10 @@ cp -rf $hestiacp/templates $HESTIA/data/
 mkdir -p /var/www/html
 mkdir -p /var/www/document_errors
 
+# Installing default themes
+mkdir -p $HESTIA/themes
+cp -rf $hestiacp/themes $HESTIA/themes/
+
 # Install default success page
 cp -rf $hestiacp/templates/web/unassigned/index.html /var/www/html/
 cp -rf $hestiacp/templates/web/skel/document_errors/* /var/www/document_errors/
@@ -1700,6 +1704,9 @@ fi
 # Set backend port
 $HESTIA/bin/v-change-sys-port $port
 
+# Set default theme
+$HESTIA/bin/v-change-sys-theme default
+
 # Starting Hestia service
 update-rc.d hestia defaults
 systemctl start hestia

+ 5 - 0
install/upgrade/versions/1.0.2.sh

@@ -6,4 +6,9 @@
 #######                      Place additional commands below.                   #######
 #######################################################################################
 
+# Set default theme
 
+if [ -z $THEME ]; then
+    echo "(*) Enabling support for customizable themes and configuring default..."
+    $BIN/v-change-sys-theme default
+fi

+ 1 - 1
src/hst_autocompile.sh

@@ -453,7 +453,7 @@ if [ "$HESTIA_B" = true ] ; then
 
     # Move needed directories
     cd $BUILD_DIR/hestiacp-$branch
-    mv bin func install web ../hestia_$HESTIA_V/usr/local/hestia/
+    mv bin func install themes web ../hestia_$HESTIA_V/usr/local/hestia/
 
     # Set permission
     cd ../hestia_$HESTIA_V/usr/local/hestia/bin

+ 1 - 0
themes/default.css

@@ -0,0 +1 @@
+/* This file left intentionally blank -- no overrides */

+ 19 - 0
web/edit/server/index.php

@@ -38,6 +38,11 @@ exec (HESTIA_CMD."v-list-sys-languages json", $output, $return_var);
 $languages = json_decode(implode('', $output), true);
 unset($output);
 
+// List themes
+exec (HESTIA_CMD."v-list-sys-themes json", $output, $return_var);
+$theme = json_decode(implode('', $output), true);
+unset($output);
+
 // List dns cluster hosts
 exec (HESTIA_CMD."v-list-remote-dns-hosts json", $output, $return_var);
 $dns_cluster = json_decode(implode('', $output), true);
@@ -140,10 +145,24 @@ if (!empty($_POST['save'])) {
     // Change default language
     if (empty($_SESSION['error_msg'])) {
         if ((!empty($_POST['v_language'])) && ($_SESSION['LANGUAGE'] != $_POST['v_language'])) {
+            if (isset($_POST['v_language_update'])) {
+                exec (HESTIA_CMD."v-change-sys-language ".escapeshellarg($_POST['v_language'])." yes", $output, $return_var);
+                if (empty($_SESSION['error_msg'])) $_SESSION['LANGUAGE'] = $_POST['v_language'];
+            }
             exec (HESTIA_CMD."v-change-sys-language ".escapeshellarg($_POST['v_language']), $output, $return_var);
             check_return_code($return_var,$output);
             unset($output);
             if (empty($_SESSION['error_msg'])) $_SESSION['LANGUAGE'] = $_POST['v_language'];
+
+        }
+    }
+
+    // Update theme
+    if (empty($_SESSION['error_msg'])) {
+        if ($_POST['v_theme'] != $_SESSION['THEME']) {
+            exec (HESTIA_CMD."v-change-sys-theme ".escapeshellarg($_POST['v_theme']), $output, $return_var);
+            check_return_code($return_var,$output);
+            unset($output);
         }
     }
 

+ 58 - 0
web/edit/server/theme/index.php

@@ -0,0 +1,58 @@
+<?php
+error_reporting(NULL);
+$TAB = 'SERVER';
+
+// Main include
+include($_SERVER['DOCUMENT_ROOT']."/inc/main.php");
+
+// Check user
+if ($_SESSION['user'] != 'admin') {
+    header("Location: /list/user");
+    exit;
+}
+
+// Check POST request
+if (!empty($_POST['save'])) {
+
+    // Check token
+    if ((!isset($_POST['token'])) || ($_SESSION['token'] != $_POST['token'])) {
+        header('location: /login/');
+        exit();
+    }
+
+    // Set restart flag
+    $v_restart = 'yes';
+    if (empty($_POST['v_restart'])) $v_restart = 'no';
+
+    // Update config
+    if (!empty($_POST['v_config'])) {
+        exec ('mktemp', $mktemp_output, $return_var);
+        $new_conf = $mktemp_output[0];
+        $fp = fopen($new_conf, 'w');
+        fwrite($fp, str_replace("\r\n", "\n",  $_POST['v_config']));
+        fclose($fp);
+        exec (HESTIA_CMD."v-restart-service "."hestia ", $output, $return_var);
+        check_return_code($return_var,$output);
+        unset($output);
+        unlink($new_conf);
+    }
+
+    // Set success message
+    if (empty($_SESSION['error_msg'])) {
+        $_SESSION['ok_msg'] = __('Changes has been saved.');
+    }
+
+}
+
+$v_config_path = '/usr/local/hestia/web/css/active-theme.css';
+$v_service_name = strtoupper('theme');
+
+// Read config
+$v_config = shell_exec(HESTIA_CMD."v-open-fs-config ".$v_config_path);
+
+// Render page
+render_page($user, $TAB, 'edit_server_service');
+
+// Flush session messages
+unset($_SESSION['error_msg']);
+unset($_SESSION['ok_msg']);

+ 2 - 0
web/inc/i18n/ar.php

@@ -755,6 +755,8 @@ $LANG['ar'] = array(
 
     'Email Credentials' => 'Email Credentials',
 
+    'Theme' => 'مظهر',
+    
 // Texts below doesn't exist in en.php
     '%s rule' => 'قواعد %s',
     'MainDomain' => 'النطاق الرئيسي',

+ 2 - 0
web/inc/i18n/bg.php

@@ -748,5 +748,7 @@ $LANG['bg'] = array(
     'maximum characters length, including prefix' => 'максималната дължина е %s символа, включително префикса',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Външния вид',
 );
 

+ 2 - 0
web/inc/i18n/bs.php

@@ -753,4 +753,6 @@ $LANG['bs'] = array(
     'maximum characters length, including prefix' => 'maksimalna dužina sme biti %s karaktera, uključujući i prefix',
 
     'Email Credentials' => 'Email lozinka i podešavanja',
+
+    'Theme' => 'Izgled',
 );

+ 2 - 0
web/inc/i18n/cn.php

@@ -762,4 +762,6 @@ $LANG['cn'] = array(
     'maximum characters length, including prefix' => '包含前缀最多 %s 个字符',
 
     'Email Credentials' => '电子邮件证书',
+
+    'Theme' => '外觀',
 );

+ 3 - 0
web/inc/i18n/cz.php

@@ -754,4 +754,7 @@ $LANG['cz'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Vzhled',
+    
 );

+ 2 - 0
web/inc/i18n/da.php

@@ -755,4 +755,6 @@ $LANG['da'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Udseende',
 );

+ 2 - 0
web/inc/i18n/de.php

@@ -753,4 +753,6 @@ $LANG['de'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'aussehen',
 );

+ 2 - 0
web/inc/i18n/el.php

@@ -755,4 +755,6 @@ $LANG['el'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Εμφάνιση',
 );

+ 1 - 0
web/inc/i18n/en.php

@@ -763,4 +763,5 @@ $LANG['en'] = array(
     'Please scan the code below in your 2FA application:' => 'To finish setting up two-factor authentication, scan the QR code below<br />using an authentication app (such as <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a>):',
     '2FA Reset Code:' => 'Account Recovery Code:',
 
+    'Theme' => 'Appearance',
 );

+ 1 - 0
web/inc/i18n/es.php

@@ -762,4 +762,5 @@ $LANG['es'] = array(
 
     'Email Credentials' => 'Datos de acceso a la cuenta de correo',
 
+    'Theme' => 'Aspecto',
 );

+ 1 - 0
web/inc/i18n/fa.php

@@ -725,4 +725,5 @@ $LANG['fa'] = array(
     'Do not use encryption' => 'از رمزگذاری استفاده نشود',
     'maximum characters length, including prefix' => 'بیشینه %s کاراکتر طول, شامل پیشوند',
     'Email Credentials' => 'اعتبار پست الکترونیکی',
+    'Theme' => 'ظاهر',
 );

+ 2 - 0
web/inc/i18n/fi.php

@@ -756,6 +756,8 @@ $LANG['fi'] = array(
 
     'Email Credentials' => 'Email Credentials',
 
+    'Theme' => 'Ulkonäkö',
+    
 // Texts below doesn't exist in en.php
     'traffic' => 'tiedonsiirto',
     'disk' => 'levytila',

+ 1 - 0
web/inc/i18n/fr.php

@@ -754,6 +754,7 @@ $LANG['fr'] = array(
 
     'Email Credentials' => 'Email Credentials',
 
+    'Theme' => 'Apparence',
 
 // Texts below doesn't exist in en.php
     'disk' => 'disque',

+ 2 - 0
web/inc/i18n/hu.php

@@ -757,4 +757,6 @@ $LANG['hu'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Megjelenése',
 );

+ 2 - 0
web/inc/i18n/id.php

@@ -754,4 +754,6 @@ $LANG['id'] = array(
     'maximum characters length, including prefix' => 'maksimal jumlah karakter %s, termasuk prefix',
 
     'Email Credentials' => 'Kredensial surel',
+
+    'Theme' => 'Penampilan',
 );

+ 2 - 0
web/inc/i18n/it.php

@@ -754,4 +754,6 @@ $LANG['it'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Apparenza',
 );

+ 2 - 0
web/inc/i18n/ja.php

@@ -753,4 +753,6 @@ $LANG['ja'] = array(
     'maximum characters length, including prefix' => '接頭辞込みで最大 %s 文字',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => '外観',
 );

+ 1 - 0
web/inc/i18n/ko.php

@@ -754,4 +754,5 @@ $LANG['ko'] = array(
 
     'Email Credentials' => '이메일 자격증명',
 
+    'Theme' => '모양을',
 );

+ 3 - 0
web/inc/i18n/no.php

@@ -754,4 +754,7 @@ $LANG['no'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Utseende',
+    
 );

+ 2 - 0
web/inc/i18n/pt.php

@@ -753,4 +753,6 @@ $LANG['pt'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Aparência',
 );

+ 2 - 0
web/inc/i18n/ro.php

@@ -755,4 +755,6 @@ $LANG['ro'] = array(
     'maximum characters length, including prefix' => 'maximum %s caractere, incluzând prefixul',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Aspectul',
 );

+ 3 - 1
web/inc/i18n/ru.php

@@ -760,5 +760,7 @@ $LANG['ru'] = array(
     'Enable 2FA' => 'Включить двухфакторную аутентификацию',
     'Please scan the code below in your 2FA application:' => 'Чтобы закончить настройку 2FA, сканируйте QR-Code ниже <br /> в приложении на Вашем устройстве (например, <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a>):',
     '2FA Reset Code:' => 'Код восстановления доступа к аккаунту:',
-	'Force SSL/HTTPS' => 'Принудительный редирект HTTP-на-HTTPS',
+    'Force SSL/HTTPS' => 'Принудительный редирект HTTP-на-HTTPS',
+    
+    'Theme' => 'Внешний вид',
 );

+ 3 - 0
web/inc/i18n/sr.php

@@ -753,4 +753,7 @@ $LANG['sr'] = array(
     'maximum characters length, including prefix' => 'maksimalna dužina sme biti %s karaktera, uključujući i prefix',
 
     'Email Credentials' => 'Email lozinka i podešavanja',
+
+    'Theme' => 'Izgled',
+    
 );

+ 2 - 0
web/inc/i18n/th.php

@@ -756,4 +756,6 @@ $LANG['th'] = array(
     'maximum characters length, including prefix' => 'ความยาว charset สูงสุด %s ตัว, รวมถึงคำนำหน้า',
 
     'Email Credentials' => 'ข้อมูลการใช้อีเมล',
+
+    'Theme' => 'ลักษณะ',
 );

+ 3 - 0
web/inc/i18n/tr.php

@@ -754,4 +754,7 @@ $LANG['tr'] = array(
     'maximum characters length, including prefix' => 'maximum %s characters length, including prefix',
 
     'Email Credentials' => 'Email Credentials',
+
+    'Theme' => 'Görünüm',
+    
 );

+ 3 - 0
web/inc/i18n/ua.php

@@ -727,3 +727,6 @@ $LANG['ua'] = array(
     'Please scan the code below in your 2FA application:' => 'Щоб закінчити налаштування 2FA, скануйте QR-Code нижче <br /> за допомогою додатку на Вашому пристрої (наприклад, <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">Google Authenticator</a>):',
     '2FA Reset Code:' => 'Код відновлення доступу до акаунту:',
 	'Force SSL/HTTPS' => 'Примусовий редирект HTTP-на-HTTPS',
+    'Theme' => 'Зовнішній вигляд',
+    
+);

+ 1 - 0
web/inc/i18n/ur.php

@@ -734,4 +734,5 @@ $LANG['ur'] = array(
     'MainDomain' => 'اہم ڈومین',
     'SubDomain' => 'ذیلی ڈومین',
     'Add Sub Domain' => 'ذیلی ڈومین کا اضافہ کریں',	
+    'Theme' => 'ظہور',
 );

+ 2 - 0
web/inc/i18n/vi.php

@@ -755,4 +755,6 @@ $LANG['vi'] = array(
     'maximum characters length, including prefix' => 'tối đa %s ký tự, bao gồm tiền tố',
 
     'Email Credentials' => 'Chứng nhận email',
+    
+    'Theme' => 'Xuất hiện',
 );

+ 29 - 0
web/templates/admin/edit_server.html

@@ -115,6 +115,35 @@
                                                         }
                                                     ?>
                                                 </select>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                        <td class="vst-text input-label">
+                                                <label><input type="checkbox" size="20" class="vst-checkbox" name="v_language_update"><?php print __('Update');?> <?php print __('Users');?> <?php print __('Default Languages');?></label>
+                                                <br><br>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td class="vst-text input-label">
+                                                <?php print __('Theme') ?>
+                                            </td>
+                                        </tr>
+                                        <tr>
+                                            <td>
+                                                <select class="vst-list" name="v_theme">
+                                                    <?php
+                                                        foreach ($theme as $key => $value) {
+                                                            echo "\t\t\t\t<option value=\"".$value."\"";
+                                                            if ((!empty($_SESSION['THEME'])) && ( $value == $_SESSION['THEME'])){
+                                                                echo ' selected' ;
+                                                            }
+                                                            if ((!empty($_SESSION['THEME'])) && ( $value == $_POST['v_theme'])){
+                                                                echo ' selected' ;
+                                                            }
+                                                            echo ">".$value."</option>\n";
+                                                        }
+                                                    ?>
+                                                </select>
                                                 <br><br>
                                             </td>
                                         </tr>

+ 1 - 0
web/templates/admin/list_server_info.html

@@ -5,6 +5,7 @@
   <link rel="icon" href="/images/favicon.ico" type="image/x-icon">
   <title>Hestia Task Viewer - <?=__($TAB)?></title>
   <link rel="stylesheet" href="/css/styles.min.css?1446554103">
+  <link rel="stylesheet" href="/css/active-theme.css?1446554103">
   <link type="text/css" href="/css/animate.css?1446554103" rel="stylesheet" />
   <link type="text/css" href="/css/jquery-custom-dialogs.css?1446554103" rel="stylesheet" />
   <link type="text/css" href="/css/all.min.css?1446554103" rel="stylesheet" />

+ 1 - 0
web/templates/admin/panel.html

@@ -31,6 +31,7 @@
 				</a>
 			</div>
 			<div class="l-menu__item"><a href="https://docs.hestiacp.com/" alt="<?=__('Help')?>" class="l-profile__help" target="_blank"><i class="fas fa-question-circle"></i></a></div>
+			<div class="l-menu__item"><a href="https://github.com/hestiacp/hestiacp/issues/new" target="_new" alt="Submit a bug report" class="l-profile__help"><i class="fas fa-comment-dots"></i></a></div>
 			<div class="l-menu__item"><a href="/edit/user/?user=<?php echo $user; ?>" alt="<?=$user?>" class="l-profile__username"><i class="fas fa-user-circle"></i></a></div>
 			<div class="l-menu__item"><a href="/logout/" alt="<?=__('Log out')?>" class="l-profile__logout"><i class="fas fa-sign-out-alt"></i></a></div>
 		</div>

+ 1 - 0
web/templates/header.html

@@ -5,6 +5,7 @@
   <link rel="icon" href="/images/favicon.ico" type="image/x-icon">
   <title>Hestia Control Panel - <?=__($TAB)?></title>
   <link rel="stylesheet" href="/css/styles.min.css?1446554103">
+  <link rel="stylesheet" href="/css/active-theme.css?1446554103">
   <link type="text/css" href="/css/animate.css?1446554103" rel="stylesheet" />
   <link type="text/css" href="/css/jquery-custom-dialogs.css?1446554103" rel="stylesheet" />
   <link type="text/css" href="/css/all.min.css?1446554103" rel="stylesheet" />

+ 1 - 0
web/templates/user/panel.html

@@ -26,6 +26,7 @@
 			</div>
 			<div class="l-menu__item"><a alt="<?=__('Notifications')?>" class="l-profile__notifications <? if($panel[$user]['NOTIFICATIONS'] == 'yes') echo " updates"; ?>"><i class="fas fa-bell <?if($panel[$user]['NOTIFICATIONS'] == 'yes') echo " status-icon orange"; ?>"></i></a></div>
 			<div class="l-menu__item"><a href="https://docs.hestiacp.com/" alt="<?=__('Help')?>" class="l-profile__help" target="_blank"><i class="fas fa-question-circle"></i></a></div>
+			<div class="l-menu__item"><a href="https://github.com/hestiacp/hestiacp/issues/new" target="_new" alt="Submit a bug report" class="l-profile__help"><i class="fas fa-comment-dots"></i></a></div>
 			<div class="l-menu__item"><a href="/edit/user/?user=<?php echo $user; ?>" alt="<?=$user?>" class="l-profile__username"><i class="fas fa-user-circle"></i></a></div>
 			<div class="l-menu__item"><a href="/logout/" alt="<?=__('Log out')?>" class="l-profile__logout"><i class="fas fa-sign-out-alt"></i></a></div>
 		</div>