diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ea0f13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.* +!.gitignore diff --git a/Module.php b/Module.php index 14cff4e..46e0f3a 100644 --- a/Module.php +++ b/Module.php @@ -25,7 +25,7 @@ public function getDescription() // Link to configuration page public function getConfigUrl() { - return Url::to(['/flex-theme/config']); + return Url::to(['/flex-theme/admin']); } // Module Activation diff --git a/controllers/ConfigController.php b/controllers/AdminController.php similarity index 91% rename from controllers/ConfigController.php rename to controllers/AdminController.php index dbbf3ba..5b0d4d7 100644 --- a/controllers/ConfigController.php +++ b/controllers/AdminController.php @@ -8,7 +8,7 @@ use humhub\modules\flexTheme\models\AdvancedSettings; use Yii; -class ConfigController extends \humhub\modules\admin\components\Controller +class AdminController extends \humhub\modules\admin\components\Controller { public $subLayout = '@flex-theme/views/layouts/admin'; @@ -19,7 +19,7 @@ public function actionIndex() if ($form->load(Yii::$app->request->post()) && $form->save()) { $this->view->saved(); // Redirect instead of render to make browser reload CSS - return $this->redirect(['/flex-theme/config']); + return $this->redirect(['/flex-theme/admin']); } return $this->render('index', ['model' => $form]); @@ -36,7 +36,7 @@ public function actionColors() $this->view->saved(); } // Redirect instead of render to make browser reload CSS - return $this->redirect(['/flex-theme/config/colors']); + return $this->redirect(['/flex-theme/admin/colors']); } return $this->render('colors', ['model' => $form]); @@ -53,7 +53,7 @@ public function actionDarkColors() $this->view->saved(); } // Redirect instead of render to make browser reload CSS - return $this->redirect(['/flex-theme/config/dark-colors']); + return $this->redirect(['/flex-theme/admin/dark-colors']); } return $this->render('dark-colors', ['model' => $form]); @@ -90,7 +90,7 @@ public function actionAdvanced() if ($config->save() && $colorSettings->save() && $darkColorSettings->save()) { $this->view->saved(); // Redirect instead of render to make browser reload CSS - return $this->redirect(['/flex-theme/config/advanced']); + return $this->redirect(['/flex-theme/admin/advanced']); } } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1c12074..ca44b2b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,4 +1,5 @@ ## TBA +- Fix #39: rounding issue in ColorHelper - thanks to @marc-farre - Fix #42: Division by zero (when trying to lighten white color) - Fix #43: Invalid faded color calculated from 3 digits base color - Enh #45: Add option to reset all colors of the current form diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md deleted file mode 100644 index 745f0bd..0000000 --- a/docs/INSTALLATION.md +++ /dev/null @@ -1,6 +0,0 @@ -# Installation - -1. Upload the module to /protected/modules (or another module loader path) OR use `git clone https://github.com/felixhahnweilheim/humhub-flex-theme.git` -2. Enable the module in Administration > Modules - -That's it! The theme is automatically activated. diff --git a/helpers/ColorHelper.php b/helpers/ColorHelper.php index 19d7083..3eddc95 100644 --- a/helpers/ColorHelper.php +++ b/helpers/ColorHelper.php @@ -2,11 +2,18 @@ namespace humhub\modules\flexTheme\helpers; +/** + * helper class for color manipulations, imitating the LESS functions lighten, darken, fade + */ class ColorHelper { /* - * This function imitates the LESS function lighten() - * But it does not convert the color into HSL and back because this is not necessary to achieve the same result. + * lighten() imitates the LESS function lighten() + * It does not convert the color into HSL and back because this is not necessary to achieve the same result. + * @param string $color RGB hexadecimal color code including '#' + * @param int $amount between 0 and 100 + * @param bool $relative wether to lighten relatively to ligthness, default: false + * @return string RGB hexadecimal color code including '#' */ public static function lighten(string $color, int $amount, bool $relative = false): string { @@ -16,16 +23,11 @@ public static function lighten(string $color, int $amount, bool $relative = fals */ $color_parts = ColorHelper::getColorComponents($color); - // $amount is expected to be a number between 0 an 100 $percentage = $amount / 100; // By default the LESS lighten() function adds the $amount absolutely to L, not relatively if (!$relative) { - - /* - * Converting a RGB color to HSL, the Lightness would be calculated by L = [max(R,G,B) + min(R,G,B)] / (2 * 255) - * So we need $max and $min - */ + //Converting a RGB color to HSL, the Lightness would be calculated by L = [max(R,G,B) + min(R,G,B)] / (2 * 255) $max = hexdec(max($color_parts)); $min = hexdec(min($color_parts)); @@ -39,65 +41,78 @@ public static function lighten(string $color, int $amount, bool $relative = fals $percentage = $percentage / (1 - ($max + $min) / (2 * 255)); } - $return = '#'; + $result = '#'; foreach ($color_parts as $color) { $color = hexdec($color); // Convert to decimal $color = round($color + (255 - $color) * $percentage); // Adjust color $color = max(min($color, 255), 0); // keep between 0 and 255 - $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code + $result .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code } - return $return; - + return $result; } /* - * Documentation, see lighten() + * see lighten() + * @param string $color RGB hexadecimal color code including '#' + * @param int $amount between 0 and 100 + * @param bool $relative wether to darken relatively to ligthness, default: false + * @return string RGB hexadecimal color code including '#' */ public static function darken(string $color, int $amount, bool $relative = false): string { - - $percentage = $amount / 100; + // split color into its components $color_parts = ColorHelper::getColorComponents($color); - $max = hexdec(max($color_parts)); - $min = hexdec(min($color_parts)); - - if ($max == 0) { - return '#000000'; - } - + + $percentage = $amount / 100; + + // By default the LESS darken() function substracts the $amount absolutely to L, not relatively if (!$relative) { - $percentage = 2 * 255 * $percentage / ($max + $min); + //Converting a RGB color to HSL, the Lightness would be calculated by L = [max(R,G,B) + min(R,G,B)] / (2 * 255) + $max = hexdec(max($color_parts)); + $min = hexdec(min($color_parts)); + if ($max !== 0) { + $percentage = 2 * 255 * $percentage / ($max + $min); + } } - $return = '#'; + $result = '#'; foreach ($color_parts as $color) { $color = hexdec($color); // Convert to decimal $color = round($color * (1 - $percentage)); // Adjust color $color = max(min($color, 255), 0); // keep between 0 and 255 - $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code + $result .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code } - return $return; - + return $result; } + /* + * fade() imitates the LESS function fade() which sets the opacity defined by $amount + * @param string $color RGB hexadecimal color code including '#' + * @param int $amount between 0 and 100 + * @return string RGBA hexadecimal color code including '#' + */ public static function fade(string $color, int $amount): string { // make sure we have 6 letters code not 3 $color = '#' . self::getSixDigitsColor($color); // $amount is expected to be between 0 and 100 - $opacity = ($amount / 100) * 255; + $opacity = round(($amount / 100) * 255); $opacity = max(min($opacity, 255), 0); // keep between 0 and 255 $opacity = str_pad(dechex($opacity), 2, '0', STR_PAD_LEFT); // make 2 char hex code - // return RGBA as hex code return $color . $opacity; } - + + /* + * Split color into its components (R, G, B) + * @param string $color RGB hexadecimal color code + * @return array color components as 2 character hexadecimal code + */ protected static function getColorComponents(string $color): array { $hexstr = self::getSixDigitsColor($color); diff --git a/helpers/FileHelper.php b/helpers/FileHelper.php index 172d960..9fa3d3a 100644 --- a/helpers/FileHelper.php +++ b/helpers/FileHelper.php @@ -5,30 +5,18 @@ use Yii; use yii\base\ErrorException; +/* + * helper class for safely accessing and modifying files + */ class FileHelper { public const THEME_PATH = '@flex-theme/themes/FlexTheme'; - private static function getVarsFile(string $prefix) - { - return Yii::getAlias(self::THEME_PATH . '/css/' . $prefix . 'variables.css'); - } - - private static function getThemeFile(string $prefix) - { - if ($prefix === 'dark_') { - $fileName = 'dark'; - } else { - $fileName = 'theme'; - } - return Yii::getAlias(self::THEME_PATH . '/css/' . $fileName . '.css'); - } - - private static function getThemeBaseFile(string $prefix) - { - return Yii::getAlias(self::THEME_PATH . '/css/' . $prefix . 'theme_base.css'); - } - + /* + * Update the color variables CSS file with given content + * @param string $content + * @param string $prefix, empty for default file or 'dark_' + */ public static function updateVarsFile(string $content, string $prefix): bool { try { @@ -40,16 +28,16 @@ public static function updateVarsFile(string $content, string $prefix): bool return true; } + /* + * Reload the CSS and update the main theme CSS file + * @param string $prefix, empty for default file or 'dark_' + */ public static function updateThemeFile(string $prefix): bool { // Base Theme - $theme_base = file_get_contents(self::getThemeBaseFile($prefix)); - + $content = file_get_contents(self::getThemeBaseFile($prefix)); // CSS Variables - $vars = file_get_contents(self::getVarsFile($prefix)); - - // Create/Update theme.css - $content = $theme_base . $vars; + $content .= file_get_contents(self::getVarsFile($prefix)); try { file_put_contents(self::getThemeFile($prefix), $content); @@ -67,4 +55,24 @@ public static function updateThemeFile(string $prefix): bool } return true; } + + private static function getVarsFile(string $prefix): string + { + return Yii::getAlias(self::THEME_PATH . '/css/' . $prefix . 'variables.css'); + } + + private static function getThemeFile(string $prefix): string + { + if ($prefix === 'dark_') { + $fileName = 'dark'; + } else { + $fileName = 'theme'; + } + return Yii::getAlias(self::THEME_PATH . '/css/' . $fileName . '.css'); + } + + private static function getThemeBaseFile(string $prefix): string + { + return Yii::getAlias(self::THEME_PATH . '/css/' . $prefix . 'theme_base.css'); + } } diff --git a/models/Config.php b/models/Config.php index 85cefb5..a8f39a9 100644 --- a/models/Config.php +++ b/models/Config.php @@ -26,7 +26,7 @@ class Config extends \yii\base\Model public $showTopicMenu; public $showUploadAsButtons; - public static function getSetting(string $setting_name): string + public static function getSetting(string $setting_name): ?string { // Note: return can be empty return Yii::$app->getModule('flex-theme')->settings->get($setting_name); diff --git a/tests/codeception/acceptance/SettingsCest.php b/tests/codeception/acceptance/SettingsCest.php index 03eee30..8ab873e 100644 --- a/tests/codeception/acceptance/SettingsCest.php +++ b/tests/codeception/acceptance/SettingsCest.php @@ -10,7 +10,7 @@ class SettingsCest public function testSettings(AcceptanceTester $I) { $I->amAdmin(); - $I->amOnRoute(['/flex-theme/config']); + $I->amOnRoute(['/flex-theme/admin']); $I->waitForText('Flex Theme'); $I->selectOption('#config-commentlink', 'icon'); $I->selectOption('#config-likelink', 'icon'); @@ -22,7 +22,7 @@ public function testSettings(AcceptanceTester $I) public function testSettingsColor(AcceptanceTester $I) { $I->amAdmin(); - $I->amOnRoute(['/flex-theme/config/colors']); + $I->amOnRoute(['/flex-theme/admin/colors']); $I->waitForText('Flex Theme'); $I->jsClick('.form-collapsible-fields:nth-of-type(3) > div'); $I->see('Background Color Page'); diff --git a/views/config/advanced.php b/views/admin/advanced.php similarity index 100% rename from views/config/advanced.php rename to views/admin/advanced.php diff --git a/views/config/colors.php b/views/admin/colors.php similarity index 100% rename from views/config/colors.php rename to views/admin/colors.php diff --git a/views/config/dark-colors.php b/views/admin/dark-colors.php similarity index 100% rename from views/config/dark-colors.php rename to views/admin/dark-colors.php diff --git a/views/config/index.php b/views/admin/index.php similarity index 100% rename from views/config/index.php rename to views/admin/index.php diff --git a/widgets/AdminMenu.php b/widgets/AdminMenu.php index c91ba10..429d301 100644 --- a/widgets/AdminMenu.php +++ b/widgets/AdminMenu.php @@ -15,8 +15,6 @@ /** * User Administration Menu - * - * @author Basti */ class AdminMenu extends TabMenu { @@ -27,9 +25,9 @@ public function init() { $this->addEntry(new MenuLink([ 'label' => Yii::t('FlexThemeModule.admin', 'General Settings'), - 'url' => ['/flex-theme/config/index'], + 'url' => ['/flex-theme/admin/index'], 'sortOrder' => 100, - 'isActive' => MenuLink::isActiveState('flex-theme', 'config', 'index'), + 'isActive' => MenuLink::isActiveState('flex-theme', 'admin', 'index'), 'isVisible' => Yii::$app->user->can([ ManageSettings::class ]) @@ -37,9 +35,9 @@ public function init() $this->addEntry(new MenuLink([ 'label' => Yii::t('FlexThemeModule.admin', 'Colors'), - 'url' => ['/flex-theme/config/colors'], + 'url' => ['/flex-theme/admin/colors'], 'sortOrder' => 200, - 'isActive' => MenuLink::isActiveState('flex-theme', 'config', 'colors'), + 'isActive' => MenuLink::isActiveState('flex-theme', 'admin', 'colors'), 'isVisible' => Yii::$app->user->can([ ManageSettings::class ]) @@ -47,9 +45,9 @@ public function init() $this->addEntry(new MenuLink([ 'label' => Yii::t('FlexThemeModule.admin', 'Dark Mode'), - 'url' => ['/flex-theme/config/dark-colors'], + 'url' => ['/flex-theme/admin/dark-colors'], 'sortOrder' => 300, - 'isActive' => MenuLink::isActiveState('flex-theme', 'config', 'dark-colors'), + 'isActive' => MenuLink::isActiveState('flex-theme', 'admin', 'dark-colors'), 'isVisible' => Yii::$app->user->can([ ManageSettings::class ]) @@ -57,9 +55,9 @@ public function init() $this->addEntry(new MenuLink([ 'label' => Yii::t('FlexThemeModule.admin', 'Advanced'), - 'url' => ['/flex-theme/config/advanced'], + 'url' => ['/flex-theme/admin/advanced'], 'sortOrder' => 400, - 'isActive' => MenuLink::isActiveState('flex-theme', 'config', 'advanced'), + 'isActive' => MenuLink::isActiveState('flex-theme', 'admin', 'advanced'), 'isVisible' => Yii::$app->user->can([ ManageSettings::class ])