+ + \ No newline at end of file diff --git a/App/Package/Core/Views/Theme/Editor/template.php b/App/Package/Core/Views/Theme/Editor/template.php new file mode 100644 index 00000000..ea1cb9a8 --- /dev/null +++ b/App/Package/Core/Views/Theme/Editor/template.php @@ -0,0 +1,78 @@ + +getValue('DIR') . 'Admin/Resources/Views/Includes/head.inc.php'); + +/* INCLUDE SCRIPTS / STYLES */ +/* @var $includes */ +/* @var $content */ +View::loadInclude($includes, 'beforeScript'); +View::loadInclude($includes, 'styles'); + +include_once (EnvManager::getInstance()->getValue('DIR') . 'App/Package/Core/Views/Theme/Editor/Includes/header.inc.php'); + +echo $content; + +include_once (EnvManager::getInstance()->getValue('DIR') . 'App/Package/Core/Views/Theme/Editor/Includes/footer.inc.php'); +/* INCLUDE SCRIPTS */ +View::loadInclude($includes, 'afterScript'); +?> + + + + \ No newline at end of file diff --git a/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php b/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php new file mode 100644 index 00000000..930b007d --- /dev/null +++ b/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php @@ -0,0 +1,452 @@ + ThemeManager::getInstance()->getCurrentTheme()->name()])); +Website::setDescription(LangManager::translate('core.theme.manage.description')); + +//TODO Gérer les element visible ou non en js peut être un commentaire : jusqu'a +//TODO Gérer les images (en se basant sur le type de value) +//TODO Ajouter des truc colle comme le fa picker etc ... +?> + + +
+ +
+ + + + \ No newline at end of file diff --git a/App/Package/Core/Views/Theme/themeManage.admin.view.php b/App/Package/Core/Views/Theme/themeManage.admin.view.php deleted file mode 100644 index e1a0c036..00000000 --- a/App/Package/Core/Views/Theme/themeManage.admin.view.php +++ /dev/null @@ -1,159 +0,0 @@ - ThemeManager::getInstance()->getCurrentTheme()->name()])); -Website::setDescription(LangManager::translate('core.theme.manage.description')); -?> - -
-

getCurrentTheme()->name() ?>

-
- -
- -
-
-
- -
-
- insertHiddenToken() ?> -
- getCurrentThemeConfigFile(); ?> -
-
-
- - - - - - \ No newline at end of file From f50b4361c67e8756b1ce998c4356c4f0fc13173c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= <69589034+Zomblard@users.noreply.github.com> Date: Mon, 31 Mar 2025 14:35:06 +0200 Subject: [PATCH 03/70] [IMPROVE] add method + template compilation --- App/Manager/Theme/ThemeManager.php | 32 +++++++++++++++++ App/Manager/Views/View.php | 11 ++++-- .../Core/Controllers/ThemeController.php | 15 +++++--- App/Package/Core/Models/ThemeModel.php | 1 - .../Theme/Editor/Includes/header.inc.php | 2 +- .../Theme/Editor/themeManage.admin.view.php | 34 ++++++++----------- 6 files changed, 65 insertions(+), 30 deletions(-) diff --git a/App/Manager/Theme/ThemeManager.php b/App/Manager/Theme/ThemeManager.php index 217972b9..412624ea 100644 --- a/App/Manager/Theme/ThemeManager.php +++ b/App/Manager/Theme/ThemeManager.php @@ -2,6 +2,7 @@ namespace CMW\Manager\Theme; +use CMW\Controller\Core\PackageController; use CMW\Manager\Api\PublicAPI; use CMW\Manager\Env\EnvManager; use CMW\Manager\Manager\AbstractManager; @@ -108,6 +109,7 @@ public function getCurrentThemeConfigFile(): void /** * @return array + * @deprecated Sera supprimé en alpha-10 */ public function getCurrentThemeConfigSettings(): array { @@ -126,6 +128,36 @@ public function getCurrentThemeConfigSettings(): array return $content; } + /** + * @return array + */ + public function getFlattenedThemeConfigSettings(): array + { + $themeName = $this->getCurrentTheme()->name(); + $configPath = EnvManager::getInstance()->getValue('DIR') . "Public/Themes/{$themeName}/Config/config.settings.php"; + + if (!file_exists($configPath)) { + return []; + } + + $menus = include $configPath; + $flat = []; + + foreach ($menus as $menu) { + if (isset($menu->requiredPackage) && !PackageController::isInstalled($menu->requiredPackage)) { + continue; + } + + foreach ($menu->values as $value) { + $key = $menu->key . '_' . $value->themeKey; + $flat[$key] = $value->defaultValue; + } + } + + return $flat; + } + + /** * @param string $setting * @return ?string diff --git a/App/Manager/Views/View.php b/App/Manager/Views/View.php index 3440eb77..99de872f 100644 --- a/App/Manager/Views/View.php +++ b/App/Manager/Views/View.php @@ -423,16 +423,21 @@ public function view(): void $content = ob_get_clean(); $editorMode = isset($_GET['editor']) && $_GET['editor'] == '1'; $content = $this->replaceThemeValues($content, $editorMode); + + // bufferisation de template pour la gestion de replaceThemeValue + ob_start(); + require_once($this->getTemplateFile()); + $templateContent = ob_get_clean(); + $templateContent = $this->replaceThemeValues($templateContent, $editorMode); + echo $templateContent; } else { $content = ob_get_clean(); + require_once($this->getTemplateFile()); } - - require_once($this->getTemplateFile()); } private function replaceThemeValues(string $html, bool $editorMode = false): string { - //TODO : Ne fonctionne pas pour les classes !!! $html = preg_replace_callback( '/\/\* CMW:([\w-]+):([\w-]+) \*\//', function ($matches) use ($editorMode) { diff --git a/App/Package/Core/Controllers/ThemeController.php b/App/Package/Core/Controllers/ThemeController.php index d564a600..1ff9b995 100644 --- a/App/Package/Core/Controllers/ThemeController.php +++ b/App/Package/Core/Controllers/ThemeController.php @@ -138,6 +138,7 @@ private function adminThemeInstallation(int $id): void } // Install Theme settings + //TODO : On init les defaut value prendre en compte le menu_key et gérer le cache manager ! ThemeManager::getInstance()->installThemeSettings($theme['name']); CoreModel::getInstance()->updateOption('theme', $theme['name']); @@ -161,6 +162,8 @@ private function adminThemeManage(): void $themeConfigs = ThemeModel::getInstance()->fetchThemeConfigs($currentTheme); $configNames = array_column($themeConfigs, 'theme_config_name'); + //TODO : il serais bien de verifier si dans un menu, plusieur theme key sont egal, si c'est le cas on warning ! dans le menu X vous avez plusieru clé egal (themeKey) + foreach ($themeMenus as $themeMenu) { $menuKey = $themeMenu->getMenuKey(); @@ -222,6 +225,7 @@ private function adminThemeManagePost(): void $aresFiles = []; + //TODO : manage correctly images with db key foreach ($_FILES as $conf => $file) { $aresFiles['__images__'][$conf] = true; @@ -238,18 +242,19 @@ private function adminThemeManagePost(): void } } - foreach (ThemeManager::getInstance()->getCurrentThemeConfigSettings() as $conf => $value) { + foreach (ThemeManager::getInstance()->getFlattenedThemeConfigSettings() as $conf => $defaultValue) { if (isset($aresFiles['__images__'][$conf])) { continue; } - if (!isset($_POST[$conf]) || !empty($_POST[$conf])) { - ThemeModel::getInstance()->getInstance()->updateThemeConfig($conf, $_POST[$conf] ?? '0', ThemeManager::getInstance()->getCurrentTheme()->name()); + if (isset($_POST[$conf])) { + ThemeModel::getInstance()->updateThemeConfig($conf, $_POST[$conf], ThemeManager::getInstance()->getCurrentTheme()->name()); } } - $themeConfigs = ThemeModel::getInstance()->getInstance()->fetchThemeConfigs(ThemeManager::getInstance()->getCurrentTheme()->name()); - SimpleCacheManager::storeCache($themeConfigs, 'config', 'Themes/' . ThemeManager::getInstance()->getCurrentTheme()->name()); + + /*$themeConfigs = ThemeModel::getInstance()->getInstance()->fetchThemeConfigs(ThemeManager::getInstance()->getCurrentTheme()->name()); + SimpleCacheManager::storeCache($themeConfigs, 'config', 'Themes/' . ThemeManager::getInstance()->getCurrentTheme()->name());*/ echo json_encode([ 'success' => true, diff --git a/App/Package/Core/Models/ThemeModel.php b/App/Package/Core/Models/ThemeModel.php index 5c2409d0..2d09e45e 100644 --- a/App/Package/Core/Models/ThemeModel.php +++ b/App/Package/Core/Models/ThemeModel.php @@ -28,7 +28,6 @@ class ThemeModel extends AbstractModel */ public function fetchConfigValue(string $MenuKey, string $themeKey, string $themeName = null): ?string { - //TODO gére le menukey pour en db selectionner les bonne choses ! et eviter les doublon //TODO Gérer les images ici ! if ($themeName === null) { $themeName = ThemeManager::getInstance()->getCurrentTheme()->name(); diff --git a/App/Package/Core/Views/Theme/Editor/Includes/header.inc.php b/App/Package/Core/Views/Theme/Editor/Includes/header.inc.php index c4904271..70fdc9fc 100644 --- a/App/Package/Core/Views/Theme/Editor/Includes/header.inc.php +++ b/App/Package/Core/Views/Theme/Editor/Includes/header.inc.php @@ -317,7 +317,7 @@ function toggleSubMenu(button) { -
+

*

From b203642fe042d9588024b86ab281b5da1d7fc0f1 Mon Sep 17 00:00:00 2001 From: Teyir Date: Mon, 5 May 2025 12:10:38 +0200 Subject: [PATCH 41/70] [IMPROVES] CoreModel fetchOption execute checks --- App/Package/Core/Models/CoreModel.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/App/Package/Core/Models/CoreModel.php b/App/Package/Core/Models/CoreModel.php index 1856870a..74601258 100644 --- a/App/Package/Core/Models/CoreModel.php +++ b/App/Package/Core/Models/CoreModel.php @@ -16,7 +16,7 @@ */ class CoreModel extends AbstractModel { - public function fetchOption(string $option): string + public function fetchOption(string $option): ?string { // TODO Le cache ne fonctionne pas et du coup ralenti le chargement des page /*if (SimpleCacheManager::cacheExist('options', "Options")){ @@ -31,9 +31,17 @@ public function fetchOption(string $option): string $db = DatabaseManager::getInstance(); $req = $db->prepare('SELECT option_value FROM cmw_core_options WHERE option_name = ?'); - $req->execute([$option]); + + if (!$req->execute([$option])){ + return null; + } + $option = $req->fetch(); + if (!$option){ + return null; + } + return $option['option_value']; } From 09b23103362429cce1373f5d02dfba947fa5b937 Mon Sep 17 00:00:00 2001 From: Teyir Date: Mon, 5 May 2025 12:57:38 +0200 Subject: [PATCH 42/70] [IMPROVES] Add packages /Classes/ autoload --- App/Manager/Loader/AutoLoad.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/App/Manager/Loader/AutoLoad.php b/App/Manager/Loader/AutoLoad.php index 43137014..284f34d1 100644 --- a/App/Manager/Loader/AutoLoad.php +++ b/App/Manager/Loader/AutoLoad.php @@ -105,7 +105,7 @@ private static function getPackageElements(array $namespace, string $elementName $startDir = static function ($elementName) use ($namespace) { //Don't remove use $namespace try { return match ($elementName) { - 'Controller', 'Model', 'Mapper', 'Entity', 'Implementation', 'Interface', 'Event', 'Exception', 'Type', 'Component', 'PackageInfo', 'Package', 'Permissions' => 'App/Package/', + 'Controller', 'Model', 'Mapper', 'Entity', 'Implementation', 'Interface', 'Event', 'Exception', 'Type', 'Component', 'Classes', 'PackageInfo', 'Package', 'Permissions' => 'App/Package/', 'Manager' => 'App/Manager/', 'Utils' => 'App/Utils/', 'Theme' => 'Public/Themes/', @@ -134,6 +134,7 @@ private static function getPackageElements(array $namespace, string $elementName 'Exception' => 'Exception/', 'Type' => 'Type/', 'Component' => 'Components/', + 'Classes' => 'Classes/', 'PackageInfo', 'Manager' => '', 'Package', 'Theme' => '/', 'Permissions' => 'Init/', From 741c90e75908da7bd42d122e9a6b6a125d9f0272 Mon Sep 17 00:00:00 2001 From: Teyir Date: Tue, 13 May 2025 16:38:24 +0200 Subject: [PATCH 43/70] [FIX] Theme reset Translation --- App/Package/Core/Views/Theme/themes.admin.view.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/App/Package/Core/Views/Theme/themes.admin.view.php b/App/Package/Core/Views/Theme/themes.admin.view.php index bb6287f3..33efc291 100644 --- a/App/Package/Core/Views/Theme/themes.admin.view.php +++ b/App/Package/Core/Views/Theme/themes.admin.view.php @@ -71,7 +71,7 @@ class="btn-primary-sm"> + class="btn btn-sm btn-warning"> $theme->name()]) ?> From a30be44a959f3d884f0eb552f7fdbb2068d183a5 Mon Sep 17 00:00:00 2001 From: Teyir Date: Tue, 13 May 2025 16:48:28 +0200 Subject: [PATCH 44/70] [IMPROVES] Add some Utils and Managers... --- .npmrc | 1 + App/Manager/Http/HttpManager.php | 335 ++++++++++++++++++ App/Manager/Http/Url.php | 61 ++++ App/Manager/Requests/Request.php | 10 +- .../BaseRouterImplementation.php | 6 +- App/Manager/Router/Request.php | 64 ++++ App/Utils/Arr.php | 51 +++ App/Utils/ArrayFormatter.php | 2 - App/Utils/ArrayMerger.php | 60 ++++ App/Utils/File.php | 14 + App/Utils/Json.php | 51 +++ App/Utils/LazyValue.php | 26 ++ App/Utils/Str.php | 95 +++++ App/Utils/Utils.php | 2 + package.json | 6 +- 15 files changed, 773 insertions(+), 11 deletions(-) create mode 100644 .npmrc create mode 100644 App/Manager/Http/HttpManager.php create mode 100644 App/Manager/Http/Url.php create mode 100644 App/Manager/Router/Request.php create mode 100644 App/Utils/Arr.php create mode 100644 App/Utils/ArrayMerger.php create mode 100644 App/Utils/Json.php create mode 100644 App/Utils/LazyValue.php create mode 100644 App/Utils/Str.php diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..5660f81a --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npmjs.org/ \ No newline at end of file diff --git a/App/Manager/Http/HttpManager.php b/App/Manager/Http/HttpManager.php new file mode 100644 index 00000000..7fabaa88 --- /dev/null +++ b/App/Manager/Http/HttpManager.php @@ -0,0 +1,335 @@ + 'GET', + 'headers' => [], + 'timeout' => 30, + 'encoding' => 'utf-8', + 'body' => true, + 'follow' => true, + ]; + + public string|null $content = null; + public CurlHandle|false $curl; + public array $curlOpt = []; + public int $errorCode; + public string $errorMessage; + public array $headers = []; + public array $info = []; + public array $options = []; + + public function __construct(string $url, array $options = []) + { + $defaults = static::$defaultsOptions; + + $this->options = array_merge($defaults, $options); + $this->options['url'] = $url; + + $this->fetch(); + } + + public function __call(string $method, array $arguments = []) + { + $method = str_replace('-', '_', Str::kebab($method)); + return $this->info[$method] ?? null; + } + + public static function __callStatic(string $method, array $arguments = []): static + { + return new static( + url: $arguments[0], + options: array_merge( + ['method' => strtoupper($method)], + $arguments[1] ?? [] + ) + ); + } + + /** + *

Fetch the request

+ * @return $this + */ + public function fetch(): static + { + // curl options + $this->curlOpt = [ + CURLOPT_URL => $this->options['url'], + CURLOPT_ENCODING => $this->options['encoding'], + CURLOPT_CONNECTTIMEOUT => $this->options['timeout'], + CURLOPT_TIMEOUT => $this->options['timeout'], + CURLOPT_AUTOREFERER => true, + CURLOPT_RETURNTRANSFER => $this->options['body'], + CURLOPT_FOLLOWLOCATION => $this->options['follow'] ?? true, + CURLOPT_MAXREDIRS => 10, + CURLOPT_HEADER => false, + CURLOPT_HEADERFUNCTION => $this->prepareHeaders(), + ]; + + // Set Progress + if (is_callable($this->options['progress']) === true) { + $this->curlOpt[CURLOPT_NOPROGRESS] = false; + $this->curlOpt[CURLOPT_PROGRESSFUNCTION] = $this->options['progress']; + } + + // Add headers + if (empty($this->options['headers']) === false) { + $headers = []; + foreach ($this->options['headers'] as $key => $value) { + if (is_string($key) === true) { + $value = $key . ': ' . $value; + } + + $headers[] = $value; + } + + $this->curlOpt[CURLOPT_HTTPHEADER] = $headers; + } + + // Set agent + if (empty($this->options['agent']) === false) { + $this->curlOpt[CURLOPT_USERAGENT] = $this->options['agent']; + } + + // Prepare for specific methods + $this->prepareCurlOptions(); + + if ($this->options['test'] === true) { + return $this; + } + + // Start curl request + $this->curl = curl_init(); + + curl_setopt_array($this->curl, $this->curlOpt); + + $this->content = curl_exec($this->curl); + $this->info = curl_getinfo($this->curl); + $this->errorCode = curl_errno($this->curl); + $this->errorMessage = curl_error($this->curl); + + if ($this->errorCode) { + throw new RuntimeException($this->errorMessage, $this->errorCode); + } + + curl_close($this->curl); + + return $this; + } + + /** + * @param string $url + * @param array $params + * @return static + */ + public static function request(string $url, array $params = []): static + { + return new static($url, $params); + } + + /** + *

Send a simple GET request

+ * @param string $url + * @param array $params + * @return static + */ + public static function get(string $url, array $params = []): static + { + $options = array_merge(['method' => 'GET', 'data' => []], $params); + $query = http_build_query($options['data']); + + if (!empty($query)) { + $url .= Url::hasQuery($url) ? '&' . $query : '?' . $query; + } + + unset($options['data']); + return new static($url, $options); + } + + /** + *

Decode the json response content

+ * @param bool $array + * @return array|stdClass|null + */ + public function json(bool $array = true): array|stdClass|null + { + try { + return json_decode($this->getContent(), $array, 512, JSON_THROW_ON_ERROR); + } catch (JsonException) { + return null; + } + } + + /** + * @return Closure + */ + public function prepareHeaders(): Closure + { + return function ($curl, $header): int { + $parts = Str::split($header, ':'); + + if (empty($parts[0]) === false && empty($parts[1]) === false) { + $key = array_shift($parts); + $this->headers[$key] = implode(':', $parts); + } + + return strlen($header); + }; + } + + /** + *

Prepare data for post fields

+ * @param array|string $data + * @return string + */ + protected function preparePostFields(mixed $data): string + { + if (is_object($data) || is_array($data)) { + return http_build_query($data); + } + + return $data; + } + + /** + *

Return the http status code

+ * @return int|null + */ + public function getStatusCode(): ?int + { + return $this->info['http_code'] ?? null; + } + + /** + *

Return the response content

+ * @return string|null + */ + public function getContent(): ?string + { + return $this->content; + } + + /** + *

Return the response headers

+ * @return array + */ + public function getHeaders(): array + { + return $this->headers; + } + + /** + *

Return the response info

+ * @return array + */ + public function getInfo(): array + { + return $this->info; + } + + /** + *

Return the error code

+ * @return int + */ + public function getErrorCode(): int + { + return $this->errorCode; + } + + /** + *

Return the error message

+ * @return string + */ + public function getErrorMessage(): string + { + return $this->errorMessage; + } + + /** + *

Return the request method

+ * @return string + */ + public function getMethod(): string + { + return $this->options['method']; + } + + /** + *

Return the request URL

+ * @return string + */ + public function getUrl(): string + { + return $this->options['url']; + } + + /** + *

Return the request options

+ * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * @return void + */ + private function prepareCurlOptions(): void + { + switch (HttpMethodsType::fromName(Str::upper($this->options['method']))) { + case HttpMethodsType::POST: + $this->curlOpt[CURLOPT_POST] = true; + $this->curlOpt[CURLOPT_CUSTOMREQUEST] = 'POST'; + $this->curlOpt[CURLOPT_POSTFIELDS] = $this->preparePostFields($this->options['data']); + break; + case HttpMethodsType::PUT: + $this->curlOpt[CURLOPT_CUSTOMREQUEST] = 'PUT'; + $this->curlOpt[CURLOPT_POSTFIELDS] = $this->preparePostFields($this->options['data']); + + if ($this->options['file']) { + $this->curlOpt[CURLOPT_INFILE] = fopen($this->options['file'], 'rb'); + $this->curlOpt[CURLOPT_INFILESIZE] = File::size($this->options['file']) ?? 0; + } + break; + case HttpMethodsType::PATCH: + $this->curlOpt[CURLOPT_CUSTOMREQUEST] = 'PATCH'; + $this->curlOpt[CURLOPT_POSTFIELDS] = $this->preparePostFields($this->options['data']); + break; + case HttpMethodsType::DELETE: + $this->curlOpt[CURLOPT_CUSTOMREQUEST] = 'DELETE'; + $this->curlOpt[CURLOPT_POSTFIELDS] = $this->preparePostFields($this->options['data']); + break; + case HttpMethodsType::HEAD: + $this->curlOpt[CURLOPT_CUSTOMREQUEST] = 'HEAD'; + $this->curlOpt[CURLOPT_POSTFIELDS] = $this->preparePostFields($this->options['data']); + $this->curlOpt[CURLOPT_NOBODY] = true; + break; + case HttpMethodsType::GET: + $this->curlOpt[CURLOPT_CUSTOMREQUEST] = 'GET'; + break; + } + } +} \ No newline at end of file diff --git a/App/Manager/Http/Url.php b/App/Manager/Http/Url.php new file mode 100644 index 00000000..a7e91f48 --- /dev/null +++ b/App/Manager/Http/Url.php @@ -0,0 +1,61 @@ +url = $url; @@ -38,9 +38,9 @@ public function getUrl(): string } /** - * @return string + * @return HttpMethodsType */ - public function getMethod(): string + public function getMethod(): HttpMethodsType { return $this->method; } diff --git a/App/Manager/Router/Implementations/BaseRouterImplementation.php b/App/Manager/Router/Implementations/BaseRouterImplementation.php index 8fef2ee9..744f6094 100644 --- a/App/Manager/Router/Implementations/BaseRouterImplementation.php +++ b/App/Manager/Router/Implementations/BaseRouterImplementation.php @@ -63,7 +63,9 @@ public function weight(): int private function registerGetRoute(Link $link, ReflectionMethod $method): Route { return $this->get($link->getPath(), function (...$values) use ($method) { - $request = new Request(url: $this->url, method: 'GET', + $request = new Request( + url: $this->url, + method: HttpMethodsType::GET, params: $this->getRouteByUrl($this->url)?->getParams() ?? [], data: $_GET ?? [], emitUrl: $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); @@ -88,7 +90,7 @@ private function registerPostRoute(Link $link, ReflectionMethod $method): Route $request = new Request( url: $this->url, - method: 'POST', + method: HttpMethodsType::POST, params: $this->getRouteByUrl($this->url)?->getParams() ?? [], data: $_POST ?? [], emitUrl: $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], diff --git a/App/Manager/Router/Request.php b/App/Manager/Router/Request.php new file mode 100644 index 00000000..c4cea77b --- /dev/null +++ b/App/Manager/Router/Request.php @@ -0,0 +1,64 @@ +method = HttpMethodsType::fromName($_SERVER['REQUEST_METHOD']); + $this->url = Website::getUrl(); + } + + /** + * @return HttpMethodsType + */ + public function getMethod(): HttpMethodsType + { + return $this->method; + } + + /** + * @return string + */ + public function getUrl(): string + { + return $this->url; + } + + /** + * @return array + */ + public function getHeaders(): array + { + if (empty($this->headers)) { + $this->headers = getallheaders(); + } + + return $this->headers; + } + + /** + * @param string $name + * @param string|null $defaultValue + * @return string|null + */ + public function getHeader(string $name, ?string $defaultValue = null): ?string + { + $headers = $this->getHeaders(); + $name = str_replace('-', '_', strtoupper($name)); + + return $headers[$name] ?? $defaultValue; + } + + +} \ No newline at end of file diff --git a/App/Utils/Arr.php b/App/Utils/Arr.php new file mode 100644 index 00000000..ad147951 --- /dev/null +++ b/App/Utils/Arr.php @@ -0,0 +1,51 @@ +Check if the array is associative, ex:

+ * + * $array = ['a' => 1, 'b' => 2]; + * $array2 = [1 => 1, 2 => 2]; + * + * echo ArrayFormatter::isAssociative($array); // true + * echo ArrayFormatter::isAssociative($array2); // false + * + * @param array $array + * @return bool + */ + public static function isAssociative(array $array): bool + { + return ctype_digit(implode('', array_keys($array))) === false; + } + + /** + * @param array $array + * @param callable $callback + * @param int $mode + * @return array + */ + public static function filter(array $array, callable $callback, int $mode = ARRAY_FILTER_USE_BOTH): array + { + return array_filter($array, $callback, $mode); + } + + /** + * @param array $array + * @param mixed $value + * @param bool $strict + * @return bool + */ + public static function contains(array $array, mixed $value, bool $strict = true): bool + { + return in_array($value, $array, $strict); + } +} \ No newline at end of file diff --git a/App/Utils/ArrayFormatter.php b/App/Utils/ArrayFormatter.php index 12098837..a09bb80e 100644 --- a/App/Utils/ArrayFormatter.php +++ b/App/Utils/ArrayFormatter.php @@ -78,6 +78,4 @@ public static function convertToArray($data) return $data; } - - } \ No newline at end of file diff --git a/App/Utils/ArrayMerger.php b/App/Utils/ArrayMerger.php new file mode 100644 index 00000000..52a32dfc --- /dev/null +++ b/App/Utils/ArrayMerger.php @@ -0,0 +1,60 @@ + $value) { + if (is_int($key) === true && $mode === static::MERGE_APPEND) { + $merged[] = $value; + + } elseif (is_array($value) === true && isset($merged[$key]) === true && is_array($merged[$key]) === true) { + $merged[$key] = static::merge($merged[$key], $value, $mode); + } else { + $merged[$key] = $value; + } + } + + if ($mode === static::MERGE_APPEND) { + $merged = array_merge($merged, []); + } + } + + if (count($arrays) > 0) { + array_unshift($arrays, $merged); + $arrays[] = $mode; + return static::merge(...$arrays); + } + + return $merged; + } +} \ No newline at end of file diff --git a/App/Utils/File.php b/App/Utils/File.php index e3347698..a43abffc 100644 --- a/App/Utils/File.php +++ b/App/Utils/File.php @@ -55,4 +55,18 @@ public static function readArray(string $path): array|false return file($path); } + + /** + *

Get the file size

+ * @param string $path + * @return int|false + */ + public static function size(string $path): int|false + { + if (!is_readable($path)) { + return false; + } + + return filesize($path); + } } diff --git a/App/Utils/Json.php b/App/Utils/Json.php new file mode 100644 index 00000000..0915e4ab --- /dev/null +++ b/App/Utils/Json.php @@ -0,0 +1,51 @@ +value)(...$args); + } + + public static function unwrap(mixed $data, mixed ...$args): mixed + { + if (is_array($data)) { + return array_map(static fn($value) => self::unwrap($value, ...$args), $data); + } + + return $data instanceof self ? $data->resolve(...$args) : $data; + } +} \ No newline at end of file diff --git a/App/Utils/Str.php b/App/Utils/Str.php new file mode 100644 index 00000000..06667eed --- /dev/null +++ b/App/Utils/Str.php @@ -0,0 +1,95 @@ + trim($s), explode($separator, $string)); + + return array_filter($parts, static fn($p) => static::length($p) >= $length); + } + + /** + * @param string $data + * @return string + * @desc Replace CamelCase to snake_case. Ex: blaBla => bla_bla + */ + public static function camelToSnakeCase(string $data): string + { + return strtolower(preg_replace('/(? blaBla + */ + public static function snakeToCamelCase(string $data): string + { + return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $data)))); + } + + /** + * @param string|null $value + * @return string + */ + public static function kebab(string|null $value = null): string + { + return static::snake($value, '-'); + } + + /** + * @param string|null $value + * @param string $delimiter + * @return string + */ + public static function snake(string|null $value = null, string $delimiter = '_'): string + { + if (ctype_lower($value) === false) { + $value = preg_replace('/\s+/u', '', ucwords($value)); + $value = preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value); + $value = static::lower($value); + } + return $value; + } + + /** + * @param string|null $string + * @return string + */ + public static function upper(string|null $string = null): string + { + return mb_strtoupper($string ?? '', 'UTF-8'); + } +} \ No newline at end of file diff --git a/App/Utils/Utils.php b/App/Utils/Utils.php index 65535689..353f82cf 100644 --- a/App/Utils/Utils.php +++ b/App/Utils/Utils.php @@ -114,6 +114,7 @@ public static function generateRandomNumber(int $length): string * @param string $data * @return string * @desc Replace CamelCase to snake_case. Ex: blaBla => bla_bla + * @deprecated use Str::camelToSnakeCase() */ public static function camelToSnakeCase(string $data): string { @@ -124,6 +125,7 @@ public static function camelToSnakeCase(string $data): string * @param string $data * @return string * @desc Replace snake_case to CamelCase. Ex: bla_bla => blaBla + * @deprecated use Str::snakeToCamelCase() */ public static function snakeToCamelCase(string $data): string { diff --git a/package.json b/package.json index 6a20af01..4ee535bc 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Compilation de TailwindCss pou le CORE - npm run tw-core", "main": "index.js", "scripts": { - "tw-core": "npx tailwindcss -i Admin/Tailwind/tailwindInput.css -o Admin/Resources/Assets/Css/style.css --config ./Admin/Tailwind/tailwind-dashboard.config.js --watch --minify" + "tw-core": "npx tailwindcss -i Admin/Tailwind/tailwindInput.css -o Admin/Resources/Assets/Css/style.css --config ./Admin/Tailwind/tailwind-dashboard.config.js --watch --minify", + "vite": "vite" }, "repository": { "type": "git", @@ -23,6 +24,7 @@ "tailwindcss": "^3.2.2" }, "dependencies": { + "@inertiajs/vue3": "^2.0.8", "flowbite": "^1.6.3" } -} \ No newline at end of file +} From 939aed52c7ec29522aed2fc6ed4f110bf02e5021 Mon Sep 17 00:00:00 2001 From: Teyir Date: Tue, 13 May 2025 16:48:50 +0200 Subject: [PATCH 45/70] [REFACTOR] Add Views Interface --- App/Manager/Views/IView.php | 50 ++ .../BaseViewImplementation.php | 434 +++++++++++++++++ App/Manager/Views/View.php | 441 ++++-------------- 3 files changed, 566 insertions(+), 359 deletions(-) create mode 100644 App/Manager/Views/IView.php create mode 100644 App/Manager/Views/Implementations/BaseViewImplementation.php diff --git a/App/Manager/Views/IView.php b/App/Manager/Views/IView.php new file mode 100644 index 00000000..c772d882 --- /dev/null +++ b/App/Manager/Views/IView.php @@ -0,0 +1,50 @@ +package = $package; + $this->viewFile = $viewFile; + $this->includes = $this->generateInclude(); + $this->variables = []; + $this->needAdminControl = false; + $this->isAdminFile = $isAdminFile; + $this->isPublicView = false; + $this->themeName = ThemeLoader::getInstance()->getCurrentTheme()->name(); + $this->overrideBackendMode = false; + } + + public function weight(): int + { + return 1; + } + + public static function getInstance(): self + { + if (!isset(self::$_instance)) { + self::$_instance = new self(); + } + + return self::$_instance; + } + + + public function setPackage(string $package): static + { + $this->package = $package; + return $this; + } + + public function setViewFile(string $viewFile): static + { + $this->viewFile = $viewFile; + return $this; + } + + public function needAdminControl(bool $needAdminControl = true): static + { + $this->needAdminControl = $needAdminControl; + return $this; + } + + public function setAdminView(bool $isAdminFile = true): static + { + $this->isAdminFile = $isAdminFile; + return $this; + } + + public function addVariable(string $variableName, mixed $variable): static + { + $this->variables[$variableName] = $variable; + return $this; + } + + public function addVariableList(array $variableList): static + { + foreach ($variableList as $key => $value) { + $this->addVariable($key, $value); + } + return $this; + } + + public function addScriptBefore(string ...$script): static + { + foreach ($script as $s) { + $this->includes['scripts']['before'][] = $s; + } + return $this; + } + + public function addScriptAfter(string ...$script): static + { + foreach ($script as $s) { + $this->includes['scripts']['after'][] = $s; + } + return $this; + } + + public function addPhpBefore(string ...$php): static + { + foreach ($php as $p) { + $this->includes['php']['before'][] = $p; + } + return $this; + } + + public function addPhpAfter(string ...$php): static + { + foreach ($php as $p) { + $this->includes['php']['after'][] = $p; + } + return $this; + } + + public function addStyle(string ...$style): static + { + foreach ($style as $s) { + $this->includes['styles'][] = $s; + } + return $this; + } + + public function setCustomPath(string $path): static + { + $this->customPath = $path; + return $this; + } + + public function setCustomTemplate(string $path): static + { + $this->customTemplate = $path; + return $this; + } + + public function setOverrideBackendMode(bool $overrideBackendMode): static + { + $this->overrideBackendMode = $overrideBackendMode; + return $this; + } + + /** + * @param array $includes + * @param string ...$files + * @return void + */ + public static function loadInclude(array $includes, #[ExpectedValues(flags: ['beforeScript', 'afterScript', 'beforePhp', 'afterPhp', 'styles'])] string ...$files): void + { + foreach ($files as $file) { + self::loadIncludeFile($includes, $file); + } + } + + /** + * @param string $package + * @param string $viewFile + * @return void + * @throws RouterException + */ + public function basicPublicView(string $package, string $viewFile): void + { + $view = new self($package, $viewFile); + $view->view(); + } + + /** + * @param string $package + * @param string $viewFile + * @return self + */ + public function createPublicView(string $package, string $viewFile): self + { + $view = new self($package, $viewFile); + + $view->isPublicView = true; + + return $view; + } + + /** + * @param string $package + * @param string $viewFile + * @return self + */ + public static function createAdminView(string $package, string $viewFile): self + { + $view = new self($package, $viewFile); + + $view->setAdminView()->needAdminControl(); + + return $view; + } + + /** + * @throws RouterException + */ + public function view(): void + { + // Check admin permissions + if ($this->needAdminControl) { + UsersController::redirectIfNotHavePermissions('core.dashboard'); + } + + extract($this->variables); + $includes = $this->includes; + + //Backend mode view logic + $this->backendView(); + + if (is_null($this->customPath) && Utils::containsNullValue($this->package, $this->viewFile)) { + throw new RouterException('Invalid View usage. Please set a correct path.', 404); + } + + $path = $this->getViewPath(); + + if (!is_file($path)) { + throw new RouterException(null, 404); + } + + //Load Elements + ComponentsManager::getInstance()->loadThemeComponents($this->themeName); + + ob_start(); + require_once $path; + echo $this->callAlerts(); + if ($this->isPublicView) { + $content = ob_get_clean(); + $editorMode = isset($_GET['editor']) && $_GET['editor'] == '1'; + $content = ThemeEditorProcessor::getInstance()->replaceThemeValues($content, $editorMode); + + // bufferisation de template pour la gestion de replaceThemeValue + ob_start(); + require_once($this->getTemplateFile()); + $templateContent = ob_get_clean(); + $templateContent = ThemeEditorProcessor::getInstance()->replaceThemeValues($templateContent, $editorMode); + echo $templateContent; + } else { + $content = ob_get_clean(); + require_once($this->getTemplateFile()); + } + } + + /** + * @param array $includes + * @param string $fileType + * @return void + */ + private static function loadIncludeFile(array $includes, #[ExpectedValues(['beforeScript', 'afterScript', 'beforePhp', 'afterPhp', 'styles'])] string $fileType): void + { + if (!in_array($fileType, ['beforeScript', 'afterScript', 'beforePhp', 'afterPhp', 'styles'])) { + return; + } + + // STYLES + if ($fileType === 'styles') { + foreach ($includes['styles'] as $style) { + $styleLink = EnvManager::getInstance()->getValue('PATH_SUBFOLDER') . $style; + echo << + HTML; + } + } + + // SCRIPTS + if (in_array($fileType, ['beforeScript', 'afterScript'])) { + $arrayAccessJs = $fileType === 'beforeScript' ? 'before' : 'after'; + foreach ($includes['scripts'][$arrayAccessJs] as $script) { + $scriptLink = EnvManager::getInstance()->getValue('PATH_SUBFOLDER') . $script; + echo << + HTML; + } + } + + // PHP + if (in_array($fileType, ['beforePhp', 'afterPhp'])) { + $arrayAccessPhp = $fileType === 'beforePhp' ? 'before' : 'after'; + foreach ($includes['php'][$arrayAccessPhp] as $php) { + $phpLink = EnvManager::getInstance()->getValue('DIR') . $php; + include_once $phpLink; + } + } + } + + /** + * @return array|array[] + */ + #[ArrayShape(['styles' => 'array', 'scripts' => 'array', 'php' => 'array'])] + private function generateInclude(): array + { + $array = ['styles' => [], 'scripts' => [], 'array' => []]; + + $array['scripts']['before'] = []; + $array['scripts']['after'] = []; + + $array['php']['before'] = []; + $array['php']['after'] = []; + + return $array; + } + + /** + * @return string + */ + private function getTemplateFile(): string + { + if ($this->customTemplate !== null) { + return $this->customTemplate; + } + + return ($this->isAdminFile) + ? EnvManager::getInstance()->getValue('PATH_ADMIN_VIEW') . 'template.php' + : "Public/Themes/$this->themeName/Views/template.php"; + } + + /** + * @throws RouterException + */ + private function callAlerts(): string + { + $alerts = Flash::load(); + $alertContent = ''; + foreach ($alerts as $alert) { + if (!$alert->isAdmin()) { + $view = new self('Core', 'Alerts/' . $alert->getType()); + } else { + $view = new self('Core', 'Alerts/' . $alert->getType(), true); + } + $view->addVariable('alert', $alert); + $alertContent .= $view->loadFile(); + } + Flash::clear(); + return $alertContent; + } + + /** + * @throws RouterException + */ + public function loadFile(): string + { + $path = $this->getViewPath(); + + if (!is_file($path)) { + throw new RouterException(null, 404); + } + + extract($this->variables); + $includes = $this->includes; + + ob_start(); + require($path); + return ob_get_clean(); + } + + /** + * @return string + */ + private function getViewPath(): string + { + if ($this->customPath !== null) { + return $this->customPath; + } + + if ($this->isAdminFile) { + return "App/Package/$this->package/Views/$this->viewFile.admin.view.php"; + } + + $publicPath = "Public/Themes/$this->themeName/Views/$this->package/$this->viewFile.view.php"; + + if (is_file($publicPath)) { + return $publicPath; + } + + $this->isPublicView = true; + return "App/Package/$this->package/Public/$this->viewFile.view.php"; + } + + + /** + * @return void + * @desc Return the view data if the backend mode is enabled + */ + private function backendView(): void + { + $isBackendModeEnabled = EnvManager::getInstance()->getValue('ENABLE_BACKEND_MODE') === 'true'; + + if ($this->overrideBackendMode) { + return; + } + + if ($isBackendModeEnabled && !$this->needAdminControl) { + print_r( + APIManager::createResponse( + data: [ + 'package' => $this->package, + 'viewFile' => $this->viewFile, + 'viewFilePath' => $this->getViewPath(), + 'variables' => $this->variables, + 'includes' => $this->includes, + ], + ) + ); + die(); + } + } +} \ No newline at end of file diff --git a/App/Manager/Views/View.php b/App/Manager/Views/View.php index 99a2ea26..6df94cd5 100644 --- a/App/Manager/Views/View.php +++ b/App/Manager/Views/View.php @@ -2,39 +2,14 @@ namespace CMW\Manager\Views; -use CMW\Controller\Users\UsersController; -use CMW\Manager\Api\APIManager; -use CMW\Manager\Components\ComponentsManager; -use CMW\Manager\Env\EnvManager; -use CMW\Manager\Flash\Flash; -use CMW\Manager\Router\RouterException; -use CMW\Manager\Theme\Editor\ThemeEditorProcessor; -use CMW\Manager\Theme\Loader\ThemeLoader; -use CMW\Manager\Theme\ThemeManager; -use CMW\Utils\Utils; -use JetBrains\PhpStorm\ArrayShape; +use CMW\Manager\Loader\Loader; +use CMW\Manager\Views\Implementations\BaseViewImplementation; use JetBrains\PhpStorm\ExpectedValues; -use function extract; -use function in_array; -use function is_file; -use function is_null; -use function ob_get_clean; -use function ob_start; -use function print_r; +use function array_reduce; class View { - private ?string $package; - private ?string $viewFile; - private ?string $customPath = null; - private ?string $customTemplate = null; - private array $includes; - private array $variables; - private bool $needAdminControl; - private bool $isAdminFile; - private bool $isPublicView; - private string $themeName; - private bool $overrideBackendMode; + private static ?IView $_instance = null; /** * @param string|null $package @@ -43,402 +18,200 @@ class View */ public function __construct(?string $package = null, ?string $viewFile = null, ?bool $isAdminFile = false) { - $this->package = $package; - $this->viewFile = $viewFile; - $this->includes = $this->generateInclude(); - $this->variables = []; - $this->needAdminControl = false; - $this->isAdminFile = $isAdminFile; - $this->isPublicView = false; - $this->themeName = ThemeLoader::getInstance()->getCurrentTheme()->name(); - $this->overrideBackendMode = false; - } + $instance = self::getInstance(); - /** - * @param string $package - * @param string $viewFile - * @return void - * @throws RouterException - */ - public static function basicPublicView(string $package, string $viewFile): void - { - $view = new self($package, $viewFile); - $view->view(); + $instance->setPackage($package); + $instance->setViewFile($viewFile); + $instance->setAdminView($isAdminFile); } - /** - * @param string $package - * @param string $viewFile - * @return View - */ - public static function createPublicView(string $package, string $viewFile): View + public static function getInstance(): IView { - $view = new self($package, $viewFile); - - $view->isPublicView = true; + if (self::$_instance === null) { + self::$_instance = self::loadViewInstance(); + } - return $view; + return self::$_instance; } - /** - * @param string $package - * @param string $viewFile - * @return View - */ - public static function createAdminView(string $package, string $viewFile): View + private static function loadViewInstance(): IView { - $view = new self($package, $viewFile); + return self::getHighestImplementation() ?? BaseViewImplementation::getInstance(); + } - $view->setAdminView()->needAdminControl(); + private static function getHighestImplementation(): ?IView + { + $implementations = Loader::loadManagerImplementations(IView::class, 'Views'); - return $view; + return array_reduce($implementations, static function (?IView $highest, IView $current) { + return ($highest === null || $current->weight() > $highest->weight()) ? $current : $highest; + }); } + /** - * @return array|array[] + * @param string $package + * @param string $viewFile + * @return void */ - #[ArrayShape(['styles' => 'array', 'scripts' => 'array', 'php' => 'array'])] - private function generateInclude(): array + public static function basicPublicView(string $package, string $viewFile): void { - $array = ['styles' => [], 'scripts' => [], 'array' => []]; - - $array['scripts']['before'] = []; - $array['scripts']['after'] = []; - - $array['php']['before'] = []; - $array['php']['after'] = []; - - return $array; + self::getInstance()->basicPublicView($package, $viewFile); } /** - * @param string $position - * @param string $fileName - * @return void + * @param string $package + * @param string $viewFile + * @return IView */ - private function addScript(#[ExpectedValues(['after', 'before'])] string $position, string $fileName): void + public static function createPublicView(string $package, string $viewFile): IView { - $this->includes['scripts'][$position][] = $fileName; + return self::getInstance()->createPublicView($package, $viewFile); } /** - * @param string $position - * @param string $fileName - * @return void + * @param string $package + * @param string $viewFile + * @return IView */ - private function addPhp(#[ExpectedValues(['after', 'before'])] string $position, string $fileName): void + public static function createAdminView(string $package, string $viewFile): IView { - $this->includes['php'][$position][] = $fileName; + return self::getInstance()->createAdminView($package, $viewFile); } /** * @param string $package - * @return $this + * @return IView */ - public function setPackage(string $package): self + public function setPackage(string $package): IView { - $this->package = $package; - return $this; + return self::getInstance()->setPackage($package); } /** * @param string $viewFile - * @return $this + * @return IView */ - public function setViewFile(string $viewFile): self + public function setViewFile(string $viewFile): IView { - $this->viewFile = $viewFile; - return $this; + return self::getInstance()->setViewFile($viewFile); } /** * @param bool $needAdminControl - * @return $this + * @return IView */ - public function needAdminControl(bool $needAdminControl = true): self + public function needAdminControl(bool $needAdminControl = true): IView { - $this->needAdminControl = $needAdminControl; - return $this; + return self::getInstance()->needAdminControl($needAdminControl); } /** * @param bool $isAdminFile - * @return $this + * @return IView */ - public function setAdminView(bool $isAdminFile = true): self + public function setAdminView(bool $isAdminFile = true): IView { - $this->isAdminFile = $isAdminFile; - return $this; + return self::getInstance()->setAdminView($isAdminFile); } /** * @param string $variableName * @param mixed $variable - * @return $this + * @return IView */ - public function addVariable(string $variableName, mixed $variable): self + public function addVariable(string $variableName, mixed $variable): IView { - $this->variables[$variableName] ??= $variable; - return $this; + return self::getInstance()->addVariable($variableName, $variable); } /** * @param array $variableList - * @return $this + * @return IView */ - public function addVariableList(array $variableList): self + public function addVariableList(array $variableList): IView { - foreach ($variableList as $key => $value) { - $this->addVariable($key, $value); - } - - return $this; + return self::getInstance()->addVariableList($variableList); } /** * @param string ...$script - * @return $this + * @return IView */ - public function addScriptBefore(string ...$script): self + public function addScriptBefore(string ...$script): IView { - foreach ($script as $scriptFile) { - $this->addScript('before', $scriptFile); - } - - return $this; + return self::getInstance()->addScriptBefore(...$script); } /** * @param string ...$script - * @return $this + * @return IView */ - public function addScriptAfter(string ...$script): self + public function addScriptAfter(string ...$script): IView { - foreach ($script as $scriptFile) { - $this->addScript('after', $scriptFile); - } - - return $this; + return self::getInstance()->addScriptAfter(...$script); } /** * @param string ...$php - * @return $this + * @return IView */ - public function addPhpBefore(string ...$php): self + public function addPhpBefore(string ...$php): IView { - foreach ($php as $scriptFile) { - $this->addPhp('before', $scriptFile); - } - - return $this; + return self::getInstance()->addPhpBefore(...$php); } /** * @param string ...$php - * @return $this + * @return IView */ - public function addPhpAfter(string ...$php): self + public function addPhpAfter(string ...$php): IView { - foreach ($php as $scriptFile) { - $this->addPhp('after', $scriptFile); - } - - return $this; + return self::getInstance()->addPhpAfter(...$php); } /** * @param string ...$style - * @return $this + * @return IView */ - public function addStyle(string ...$style): self + public function addStyle(string ...$style): IView { - foreach ($style as $styleFile) { - $this->includes['styles'][] = $styleFile; - } - - return $this; + return self::getInstance()->addStyle(...$style); } /** * @param string $path - * @return $this + * @return IView */ - public function setCustomPath(string $path): self + public function setCustomPath(string $path): IView { - $this->customPath = $path; - return $this; + return self::getInstance()->setCustomPath($path); } /** * @param string $path - * @return $this + * @return IView */ - public function setCustomTemplate(string $path): self + public function setCustomTemplate(string $path): IView { - $this->customTemplate = $path; - return $this; + return self::getInstance()->setCustomTemplate($path); } /** * @param bool $overrideBackendMode - * @return View + * @return IView * If true, the view will be displayed even if the backend mode is enabled */ - public function setOverrideBackendMode(bool $overrideBackendMode): self - { - $this->overrideBackendMode = $overrideBackendMode; - return $this; - } - - /** - * @return string - */ - private function getViewPath(): string - { - if ($this->customPath !== null) { - return $this->customPath; - } - - if ($this->isAdminFile) { - return "App/Package/$this->package/Views/$this->viewFile.admin.view.php"; - } - - $publicPath = "Public/Themes/$this->themeName/Views/$this->package/$this->viewFile.view.php"; - - if (is_file($publicPath)) { - return $publicPath; - } - - $this->isPublicView = true; - return "App/Package/$this->package/Public/$this->viewFile.view.php"; - } - - /** - * @return string - */ - private function getTemplateFile(): string - { - if ($this->customTemplate !== null) { - return $this->customTemplate; - } - - return ($this->isAdminFile) - ? EnvManager::getInstance()->getValue('PATH_ADMIN_VIEW') . 'template.php' - : "Public/Themes/$this->themeName/Views/template.php"; - } - - /** - * @param array $includes - * @param string $fileType - * @return void - */ - private static function loadIncludeFile(array $includes, #[ExpectedValues(['beforeScript', 'afterScript', 'beforePhp', 'afterPhp', 'styles'])] string $fileType): void - { - if (!in_array($fileType, ['beforeScript', 'afterScript', 'beforePhp', 'afterPhp', 'styles'])) { - return; - } - - // STYLES - if ($fileType === 'styles') { - foreach ($includes['styles'] as $style) { - $styleLink = EnvManager::getInstance()->getValue('PATH_SUBFOLDER') . $style; - echo << - HTML; - } - } - - // SCRIPTS - if (in_array($fileType, ['beforeScript', 'afterScript'])) { - $arrayAccessJs = $fileType === 'beforeScript' ? 'before' : 'after'; - foreach ($includes['scripts'][$arrayAccessJs] as $script) { - $scriptLink = EnvManager::getInstance()->getValue('PATH_SUBFOLDER') . $script; - echo << - HTML; - } - } - - // PHP - if (in_array($fileType, ['beforePhp', 'afterPhp'])) { - $arrayAccessPhp = $fileType === 'beforePhp' ? 'before' : 'after'; - foreach ($includes['php'][$arrayAccessPhp] as $php) { - $phpLink = EnvManager::getInstance()->getValue('DIR') . $php; - include_once $phpLink; - } - } - } - - /** - * @throws RouterException - */ - public function loadFile(): string + public function setOverrideBackendMode(bool $overrideBackendMode): IView { - $path = $this->getViewPath(); - - if (!is_file($path)) { - throw new RouterException(null, 404); - } - - extract($this->variables); - $includes = $this->includes; - - ob_start(); - require($path); - return ob_get_clean(); + return self::getInstance()->setOverrideBackendMode($overrideBackendMode); } - /** - * @throws RouterException - */ public function view(): void { - // Check admin permissions - if ($this->needAdminControl) { - UsersController::redirectIfNotHavePermissions('core.dashboard'); - } - - extract($this->variables); - $includes = $this->includes; - - //Backend mode view logic - $this->backendView(); - - if (is_null($this->customPath) && Utils::containsNullValue($this->package, $this->viewFile)) { - throw new RouterException("Invalid View usage. Please set a correct path.", 404); - } - - $path = $this->getViewPath(); - - if (!is_file($path)) { - throw new RouterException(null, 404); - } - - //Load Elements - ComponentsManager::getInstance()->loadThemeComponents($this->themeName); - - ob_start(); - require_once $path; - echo $this->callAlerts(); - if ($this->isPublicView) { - $content = ob_get_clean(); - $editorMode = isset($_GET['editor']) && $_GET['editor'] == '1'; - $content = ThemeEditorProcessor::getInstance()->replaceThemeValues($content, $editorMode); - - // bufferisation de template pour la gestion de replaceThemeValue - ob_start(); - require_once($this->getTemplateFile()); - $templateContent = ob_get_clean(); - $templateContent = ThemeEditorProcessor::getInstance()->replaceThemeValues($templateContent, $editorMode); - echo $templateContent; - } else { - $content = ob_get_clean(); - require_once($this->getTemplateFile()); - } + self::getInstance()->view(); } - /** * @param array $includes * @param string ...$files @@ -446,56 +219,6 @@ public function view(): void */ public static function loadInclude(array $includes, #[ExpectedValues(flags: ['beforeScript', 'afterScript', 'beforePhp', 'afterPhp', 'styles'])] string ...$files): void { - foreach ($files as $file) { - self::loadIncludeFile($includes, $file); - } - } - - /** - * @throws RouterException - */ - private function callAlerts(): string - { - $alerts = Flash::load(); - $alertContent = ''; - foreach ($alerts as $alert) { - if (!$alert->isAdmin()) { - $view = new View('Core', 'Alerts/' . $alert->getType()); - } else { - $view = new View('Core', 'Alerts/' . $alert->getType(), true); - } - $view->addVariable('alert', $alert); - $alertContent .= $view->loadFile(); - } - Flash::clear(); - return $alertContent; - } - - /** - * @return void - * @desc Return the view data if the backend mode is enabled - */ - private function backendView(): void - { - $isBackendModeEnabled = EnvManager::getInstance()->getValue('ENABLE_BACKEND_MODE') === 'true'; - - if ($this->overrideBackendMode) { - return; - } - - if ($isBackendModeEnabled && !$this->needAdminControl) { - print_r( - APIManager::createResponse( - data: [ - 'package' => $this->package, - 'viewFile' => $this->viewFile, - 'viewFilePath' => $this->getViewPath(), - 'variables' => $this->variables, - 'includes' => $this->includes, - ], - ) - ); - die(); - } + self::getInstance()->loadInclude($includes, ...$files); } } From df40d2f86f78837eba80c1769220d109bf9b5a9d Mon Sep 17 00:00:00 2001 From: Teyir Date: Tue, 13 May 2025 16:48:59 +0200 Subject: [PATCH 46/70] [FIX] Mail config NPE --- .../Core/Views/Mail/mailConfig.admin.view.php | 226 ++++++++++-------- 1 file changed, 131 insertions(+), 95 deletions(-) diff --git a/App/Package/Core/Views/Mail/mailConfig.admin.view.php b/App/Package/Core/Views/Mail/mailConfig.admin.view.php index f3c2d1b9..17487fce 100644 --- a/App/Package/Core/Views/Mail/mailConfig.admin.view.php +++ b/App/Package/Core/Views/Mail/mailConfig.admin.view.php @@ -1,15 +1,16 @@ - - -cmwWarn() ?> \ No newline at end of file + + + +cmwWarn(); +?> \ No newline at end of file diff --git a/Public/Themes/Sampler/Views/Includes/header.inc.php b/Public/Themes/Sampler/Views/Includes/header.inc.php index 80b6100e..5f99ce48 100644 --- a/Public/Themes/Sampler/Views/Includes/header.inc.php +++ b/Public/Themes/Sampler/Views/Includes/header.inc.php @@ -1,5 +1,8 @@ - - - \ No newline at end of file + + diff --git a/Public/Themes/Sampler/Views/News/individual.view.php b/Public/Themes/Sampler/Views/News/individual.view.php deleted file mode 100644 index ce3673d9..00000000 --- a/Public/Themes/Sampler/Views/News/individual.view.php +++ /dev/null @@ -1,68 +0,0 @@ -getTitle()); -Website::setDescription('Affichage de la news ' . $news->getTitle()); -?> - - -
-

NEWS : getTitle() ?>

-
- << Revenir aux news -
-

- -
-

Contenue : getContent() ?>

- getLikes()->getTotal() ?> - getLikes()->userCanLike()): ?> - - - You will like - - Par : getAuthor()->getPseudo() ?> le getDateCreated() ?> -
- -

Espace commentaire

- getComments() as $comment): ?> -
-
-
- ... -
-
-

getUser()->getPseudo() ?> : - getContent() ?>

- getDate() ?> -
-
-
- -
-
- insertHiddenToken() ?> -

Votre commentaire :

- -
- - - - Commenter - -
-
-
- -
-
\ No newline at end of file diff --git a/Public/Themes/Sampler/Views/News/list.view.php b/Public/Themes/Sampler/Views/News/list.view.php deleted file mode 100644 index 9a028a59..00000000 --- a/Public/Themes/Sampler/Views/News/list.view.php +++ /dev/null @@ -1,43 +0,0 @@ - $newsModel->getSomeNews(3, 'DESC') */ -?> - -
-

NEWS

-
- -
-
... -
-

getTitle() ?> -

- -

Contenue : getDescription() ?>

- - getLikes()->getTotal() ?> - - - getLikes()->userCanLike()): ?> - - - You will like - - Par : getAuthor()->getPseudo() ?> le getDateCreated() ?> -
- -
-
diff --git a/Public/Themes/Sampler/Views/Pages/main.view.php b/Public/Themes/Sampler/Views/Pages/main.view.php index 86f9a489..a7c9d5be 100644 --- a/Public/Themes/Sampler/Views/Pages/main.view.php +++ b/Public/Themes/Sampler/Views/Pages/main.view.php @@ -8,11 +8,16 @@ Website::setTitle(ucfirst($page->getTitle())); Website::setDescription($page->getContentPreview()); ?> - +
+
+
+

getTitle() ?>

+
+
+
-

getTitle() ?>

-
+
getConverted() ?>
diff --git a/Public/Themes/Sampler/Views/Users/2fa.view.php b/Public/Themes/Sampler/Views/Users/2fa.view.php index dc76123b..981ece07 100644 --- a/Public/Themes/Sampler/Views/Users/2fa.view.php +++ b/Public/Themes/Sampler/Views/Users/2fa.view.php @@ -8,32 +8,37 @@ Website::setTitle('Connexion - 2FA'); Website::setDescription('Double authentification'); ?> +
+
+
+

Double facteur

+
+
+
+ + +
+
+

Double facteur

+
-
-
-
-
-

Double facteur

-
+
+ insertHiddenToken() ?> + +
+ +
-
-
-
- - insertHiddenToken() ?> -
- - -
-
- -
- + +
+
-
+
-
\ No newline at end of file +
diff --git a/Public/Themes/Sampler/Views/Users/enforce2fa.view.php b/Public/Themes/Sampler/Views/Users/enforce2fa.view.php index b3d8b2e5..c07f0e74 100644 --- a/Public/Themes/Sampler/Views/Users/enforce2fa.view.php +++ b/Public/Themes/Sampler/Views/Users/enforce2fa.view.php @@ -10,36 +10,41 @@ Website::setTitle('Double facteur obligatoire'); Website::setDescription("Merci d'activer le 2fa !"); ?> +
+
+
+

Double facteur

+
+
+
-
-
-
-
-

Double facteur

-
-
+
+
+

Double authentification requise

+

+ Veuillez activer le double facteur pour pouvoir vous connecter +

+ +
+ QR Code double authentification +

get2Fa()->get2FaSecretDecoded() ?>

-
-
-

Veuillez activer le double facteur pour pouvoir vous connecter

-
- QR Code double authentification - get2Fa()->get2FaSecretDecoded() ?> -
-
- insertHiddenToken() ?> - -
- - -
- -
+ +
+ insertHiddenToken() ?> + + +
+ +
-
+ + +
diff --git a/Public/Themes/Sampler/Views/Users/forgot_password.view.php b/Public/Themes/Sampler/Views/Users/forgot_password.view.php index 949f81c0..699a4beb 100644 --- a/Public/Themes/Sampler/Views/Users/forgot_password.view.php +++ b/Public/Themes/Sampler/Views/Users/forgot_password.view.php @@ -7,26 +7,34 @@ Website::setTitle('Mot de passe oublié'); Website::setDescription('Retrouvez votre mot de passe'); ?> +
+
+
+

Mot de passe oublié

+
+
+
-
-
+
+
+

Mot de passe oublié

-

Mot de passe oublié

-
+ insertHiddenToken() ?> -
- +
+ +
-
-
- -
+
+
- -
+
\ No newline at end of file diff --git a/Public/Themes/Sampler/Views/Users/login.view.php b/Public/Themes/Sampler/Views/Users/login.view.php index 03d83b97..5af80894 100644 --- a/Public/Themes/Sampler/Views/Users/login.view.php +++ b/Public/Themes/Sampler/Views/Users/login.view.php @@ -13,61 +13,60 @@ Website::setDescription('Connectez-vous à votre compte ' . Website::getWebsiteName()); ?> +
+
+
+

Connexion

+
+
+
+ +
+
+ +
+ insertHiddenToken() ?> + -
-
-
-
-

Connexion

-
+
+ +
-
-
-
- - insertHiddenToken() ?> - -
- - -
-
- - -
-
-
-
- - -
-
- -
-
- - - <?= $oAuth->methodeName() ?> - - -
+
+ + +
- -
- -
- +
+ + + Mot de passe oublié +
-
+ +
+ + + <?= $oAuth->methodeName() ?> + + +
+ + + + + +

Pas encore de compte ? S'inscrire

+ +
-
\ No newline at end of file +
diff --git a/Public/Themes/Sampler/Views/Users/profile.view.php b/Public/Themes/Sampler/Views/Users/profile.view.php index 37a29a9c..c331c88d 100644 --- a/Public/Themes/Sampler/Views/Users/profile.view.php +++ b/Public/Themes/Sampler/Views/Users/profile.view.php @@ -11,121 +11,117 @@ ?> +
+
+
+

Bonjour getPseudo() ?>

+
+
+
-
-
-

getPseudo() ?>

-
-
-
-

Informations personnel

-
+
+
+ + +
+

Informations personnelles

+ insertHiddenToken() ?> +
-
-
- - +
+
+ +
-
- - +
+ +
-
-
- - +
+
+ +
-
- - +
+ +
-
-
- + +
+
-
-

Sécurité : - - get2Fa()->isEnabled()): ?> - Actif ! - - Inactif ! - - -

+ +
+

Sécurité :

+

+ get2Fa()->isEnabled()): ?> + Actif ✅ + + Inactif ❌ + +

+ get2Fa()->isEnabled()): ?> -

Pour activer l'authentification à double facteur scannez le QR code dans une application - d'authentification (GoogleAuthenticator, Aegis ...)

+

Scannez ce QR code dans Google Authenticator, Aegis, etc.

-
-
-
- QR Code double authentification - get2Fa()->get2FaSecretDecoded() ?> -
+ +
+
+ QR Code 2FA + get2Fa()->get2FaSecretDecoded() ?>
-
-
+
+ insertHiddenToken() ?> -
- - -
-
-
-
-
-

Identité visuel

+ + + +
+
+

Identité visuelle

+ getUserPicture()?->getImage())): ?> - - - Image de profil de <?= $user->getPseudo() ?> +
+ Image de profil de <?= $user->getPseudo() ?> +
-
+ + insertHiddenToken() ?> - -
- - +
+ +

PNG, JPG, JPEG, WEBP, GIF (MAX. 400x400px).

-

PNG, JPG, JPEG, WEBP, GIF (MAX. 400px400px).

+
-
- -

Vous nous quittez ?

- -

Nous somme triste de vous voir partir !

- Supprimer mon compte + +
+
+

Vous nous quittez ?

+

Nous sommes tristes de vous voir partir !

+ + Supprimer mon compte + +
-
- diff --git a/Public/Themes/Sampler/Views/Users/register.view.php b/Public/Themes/Sampler/Views/Users/register.view.php index 3e752f79..4ccefc69 100644 --- a/Public/Themes/Sampler/Views/Users/register.view.php +++ b/Public/Themes/Sampler/Views/Users/register.view.php @@ -2,6 +2,7 @@ use CMW\Controller\Core\SecurityController; use CMW\Interface\Users\IUsersOAuth; +use CMW\Manager\Env\EnvManager; use CMW\Manager\Lang\LangManager; use CMW\Manager\Security\SecurityManager; use CMW\Model\Core\ThemeModel; @@ -14,57 +15,64 @@ ?> -
-
-
-
-

Inscription

-
-
+
+
+
+

Inscription

-
-
-
- insertHiddenToken() ?> -
- - -
-
- - -
-
- - -
-
- - -
+
+
-
- - - <?= $oAuth->methodeName() ?> - - -
+
+
+ + insertHiddenToken() ?> - -
- -
- +
+ +
-
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + <?= $oAuth->methodeName() ?> + + +
+ + + +
+ +
+

Déjà un compte ? Se connecter

+
-
\ No newline at end of file +
diff --git a/Public/Themes/Sampler/Views/Votes/main.view.php b/Public/Themes/Sampler/Views/Votes/main.view.php deleted file mode 100644 index 305e28ed..00000000 --- a/Public/Themes/Sampler/Views/Votes/main.view.php +++ /dev/null @@ -1,101 +0,0 @@ - - - -
-

Votes

-
-
-
- getCurrentUser()?->getId() === -1): ?> - -
-

Connectez-vous

-

Pour pouvoir voter et donc récupérer vos récompenses vous devez être - connecté sur le site, alors n'attendez plus pour obtenir des récompenses - uniques! -
- Connectez-vous dès maintenant en cliquant ici -

-
- -
-

Votez

-

Les votes nous permettent de faire connaitre le serveur plus facilement, - en contre partie nous vous offrons entre 0 et 3 VotePoints par votes.
- Les VotePoints sont dépensables en jeux avec la commande /voteshop -

- -
-

getTitle() ?>

- 1 à 3 VotePoints - getTimeFormatted() ?> - - -
- -
- -
-

Classement

-

Top 10 du mois

- - - - - - - - - - - - - - - - - -
PositionPseudoVotes
#getUser()->getPseudo() ?>getVotes() ?>
-
-
-
-

Top 10 global

- - - - - - - - - - - - - - - - - -
PositionPseudoVotes
#getUser()->getPseudo() ?>getVotes() ?>
-
- -
-
diff --git a/Public/Themes/Sampler/Views/Wiki/main.view.php b/Public/Themes/Sampler/Views/Wiki/main.view.php deleted file mode 100644 index fe2d45a0..00000000 --- a/Public/Themes/Sampler/Views/Wiki/main.view.php +++ /dev/null @@ -1,64 +0,0 @@ -getTitle()); - Website::setDescription($article->getTitle()); -} else { - Website::setTitle('Wiki'); - Website::setDescription('Apprenez-en plus sur le site ' . Website::getWebsiteName() . ' grâce à notre wiki !'); -} -?> - -
-

Wiki

-
-
-
-
- -
getName() ?>
- - - -
- -
-
- - getTitle() ?> - getContent() ?> - getDateCreate())) ?> - getAuthor()->getPseudo() ?> - getDateUpdate())) ?> - - You haven't started creating your Wiki yet! - - getTitle() ?> - getContent() ?> - getDateCreate())) ?> - getAuthor()->getPseudo() ?> - getDateUpdate())) ?> - -
-
- - -
-
- diff --git a/Public/Themes/Sampler/Views/template.php b/Public/Themes/Sampler/Views/template.php index 54d29c14..737d2114 100644 --- a/Public/Themes/Sampler/Views/template.php +++ b/Public/Themes/Sampler/Views/template.php @@ -1,13 +1,28 @@ - + + +showCookieConsent(); +} +?> + + \ No newline at end of file diff --git a/Public/Themes/Sampler/package.json b/Public/Themes/Sampler/package.json new file mode 100644 index 00000000..a4b5a342 --- /dev/null +++ b/Public/Themes/Sampler/package.json @@ -0,0 +1,28 @@ +{ + "name": "theme-sampler", + "version": "1.0.0", + "description": "Compilation de TailwindCss pour votre theme - npm run Sampler", + "main": "index.js", + "scripts": { + "Sampler": "npx tailwindcss -i ../../../Public/Themes/Sampler/Resources/input.css -o ../../../Public/Themes/Sampler/Assets/Css/style.css --config ../../../Public/Themes/Sampler/Resources/tailwind.config.js --watch --minify" + }, + "repository": { + "type": "git", + "url": "git+" + }, + "keywords": [], + "author": "Zomb", + "license": "ISC", + "bugs": { + "url": "https://github.com/CraftMyWebsite/cmw-core/issues/new/choose" + }, + "homepage": "https://craftmywebsite.fr/", + "devDependencies": { + "autoprefixer": "^10.4.13", + "postcss-cli": "^10.0.0", + "tailwindcss": "^3.2.2" + }, + "dependencies": { + "flowbite": "^1.6.3" + } +} \ No newline at end of file diff --git a/Public/Themes/Sampler/router.php b/Public/Themes/Sampler/router.php index 14c857af..1e0ba198 100644 --- a/Public/Themes/Sampler/router.php +++ b/Public/Themes/Sampler/router.php @@ -11,4 +11,4 @@ use CMW\Manager\Loader\Loader; /** @desc Create a simple route "/hello" with the file "hello.view.php" in the package folder "Core" */ -Loader::createSimpleRoute('/hello', 'hello', 'Core'); +Loader::createSimpleRoute('/hello', 'hello', 'CustomRoutes'); From 597ee27d31ad9deda81baf21617acd8d40e4ac1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= <69589034+Zomblard@users.noreply.github.com> Date: Sun, 13 Jul 2025 19:39:11 +0200 Subject: [PATCH 62/70] [FIX] Flash : added a fallback to BaseFlashImplementation --- App/Manager/Flash/Flash.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/App/Manager/Flash/Flash.php b/App/Manager/Flash/Flash.php index e8f18a0f..ea8c82cf 100644 --- a/App/Manager/Flash/Flash.php +++ b/App/Manager/Flash/Flash.php @@ -33,11 +33,12 @@ private static function getHighestImplementation(): IFlash { $implementations = Loader::loadManagerImplementations(IFlash::class, 'Flash'); - return array_reduce($implementations, static function (?IFlash $highest, IFlash $current) { + $highest = array_reduce($implementations, static function (?IFlash $highest, IFlash $current) { return ($highest === null || $current->weight() > $highest->weight()) ? $current : $highest; }); - } + return $highest ?? new BaseFlashImplementation(); + } /** * @return Alert[] From 8c0596f97a937de930b851ed95d34cda3248495b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= <69589034+Zomblard@users.noreply.github.com> Date: Wed, 16 Jul 2025 09:32:10 +0200 Subject: [PATCH 63/70] [IMPROVE] color area visibility --- App/Manager/Theme/Editor/ThemeEditorProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/App/Manager/Theme/Editor/ThemeEditorProcessor.php b/App/Manager/Theme/Editor/ThemeEditorProcessor.php index c6d5ebeb..bd604476 100644 --- a/App/Manager/Theme/Editor/ThemeEditorProcessor.php +++ b/App/Manager/Theme/Editor/ThemeEditorProcessor.php @@ -239,7 +239,7 @@ public function renderInput($value, $menuKey, $val) :string case 'color': return <<{$label} - + HTML; case 'number': From dc6431ed1ad2fba1acae7ab0d3174cf305079f99 Mon Sep 17 00:00:00 2001 From: zomb Date: Thu, 24 Jul 2025 16:14:39 +0200 Subject: [PATCH 64/70] [ADD] advanced custom var for editor --- .../Theme/Editor/ThemeEditorProcessor.php | 18 ++++++++++++++++++ .../Core/Views/Theme/Editor/template.php | 2 +- .../Theme/Editor/themeManage.admin.view.php | 17 ++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/App/Manager/Theme/Editor/ThemeEditorProcessor.php b/App/Manager/Theme/Editor/ThemeEditorProcessor.php index bd604476..5899d740 100644 --- a/App/Manager/Theme/Editor/ThemeEditorProcessor.php +++ b/App/Manager/Theme/Editor/ThemeEditorProcessor.php @@ -38,6 +38,7 @@ public function replaceThemeValues(string $html, bool $editorMode = false): stri $this->processCmwStyle($xpath); $this->processCmwClass($xpath); $this->processCmwAttr($xpath); + $this->processCmwVar($xpath); return $dom->saveHTML(); } @@ -201,6 +202,23 @@ private function processCmwAttr(DOMXPath $xpath): void } } + private function processCmwVar(DOMXPath $xpath): void + { + $nodes = $xpath->query('//*[@data-cmw-var]'); + foreach ($nodes as $node) { + $defs = explode(' ', $node->getAttribute('data-cmw-var')); + + foreach ($defs as $def) { + [$varName, $menu, $key] = explode(':', $def); + $val = ThemeModel::getInstance()->fetchConfigValue($menu, $key); + $node->setAttribute('style', $node->getAttribute('style') . "; $varName: $val"); + } + + $node->removeAttribute('data-cmw-var'); + } + } + + /** * @return EditorMenu[] */ diff --git a/App/Package/Core/Views/Theme/Editor/template.php b/App/Package/Core/Views/Theme/Editor/template.php index 6918673e..5a32c194 100644 --- a/App/Package/Core/Views/Theme/Editor/template.php +++ b/App/Package/Core/Views/Theme/Editor/template.php @@ -89,7 +89,7 @@ function setIframeWidth(mode, btn) { iframe.style.height = '667px'; break; case 'desktop': - iframe.style.width = '98%'; + iframe.style.width = '100%'; iframe.style.height = '100%'; break; } diff --git a/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php b/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php index fad0b066..e6023b42 100644 --- a/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php +++ b/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php @@ -43,7 +43,7 @@ ?>
- +
@@ -284,6 +284,21 @@ classes.forEach(cls => el.classList.add(cls)); }); }); }); + + // 🔹 Variables CSS dynamiques :
+ iframeDoc.querySelectorAll('[data-cmw-var]').forEach(el => { + const defs = el.getAttribute("data-cmw-var").trim().split(/\s+/); + + defs.forEach(def => { + const [cssVar, menuKey, valueKey] = def.split(":"); + const fullKey = `${menuKey}_${valueKey}`; + if (keyToUpdate && fullKey !== keyToUpdate) return; + + const rawValue = configValues[fullKey] || ""; + el.style.setProperty(cssVar, rawValue); + }); + }); + } // Écoute les modifications des inputs et met à jour uniquement l'élément concerné From b705da296bcc9522ed410517500d94460a272e24 Mon Sep 17 00:00:00 2001 From: zomb Date: Fri, 25 Jul 2025 16:58:17 +0200 Subject: [PATCH 65/70] [ADD] CSS Editor processor --- .../Theme/Editor/ThemeEditorProcessor.php | 26 +++++++++++++++++++ .../Theme/Editor/themeManage.admin.view.php | 21 +++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/App/Manager/Theme/Editor/ThemeEditorProcessor.php b/App/Manager/Theme/Editor/ThemeEditorProcessor.php index 5899d740..7051b8e2 100644 --- a/App/Manager/Theme/Editor/ThemeEditorProcessor.php +++ b/App/Manager/Theme/Editor/ThemeEditorProcessor.php @@ -39,6 +39,7 @@ public function replaceThemeValues(string $html, bool $editorMode = false): stri $this->processCmwClass($xpath); $this->processCmwAttr($xpath); $this->processCmwVar($xpath); + $this->processCmwStyleComments($xpath); return $dom->saveHTML(); } @@ -218,6 +219,31 @@ private function processCmwVar(DOMXPath $xpath): void } } + private function processCmwStyleComments(DOMXPath $xpath): void + { + $styleTags = $xpath->query('//style'); + + foreach ($styleTags as $styleTag) { + $css = $styleTag->textContent; + + $css = preg_replace_callback('/\/\*cmw:([\w-]+):([\w-]+)\*\//', function ($matches) { + [$full, $menu, $key] = $matches; + $val = ThemeModel::getInstance()->fetchConfigValue($menu, $key); + + $editorType = ThemeConfigResolver::getInstance()->getEditorType($menu, $key); + + if ($editorType === EditorType::RANGE) { + $opts = ThemeConfigResolver::getInstance()->getEditorRangeOptions($menu, $key); + $val = $opts->getPrefix() . $val . $opts->getSuffix(); + } + + return $val . " /*cmw:$menu:$key*/"; + }, $css); + + $styleTag->textContent = $css; + } + } + /** * @return EditorMenu[] diff --git a/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php b/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php index e6023b42..316ad1ef 100644 --- a/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php +++ b/App/Package/Core/Views/Theme/Editor/themeManage.admin.view.php @@ -299,6 +299,27 @@ classes.forEach(cls => el.classList.add(cls)); }); }); + // 🔹 CSS - + -
+$packagesToUpdate = []; +$packagesUpToDate = []; + +foreach ($packagesList as $pkg) { + if (!PackageController::isInstalled($pkg['name'])) { + continue; + } + $local = PackageController::getPackage($pkg['name']); + if ($pkg['version_status'] === 0 && $local->version() !== $pkg['version_name']) { + $packagesToUpdate[] = $pkg; + } else { + $packagesUpToDate[] = $pkg; + } +} + + +function renderCard($name, $image, $description, $author = null, $version = null, $id = null, $notVerified = false, $updateBadge = false, $downloads = null, $versionCMW = null, $releaseDate = null) { + $uniqueId = $id ?? $name; + ?> +
+
+ img +
+
+
-

- -

+ +
+
+
+

+ +

+ + + +

+ +
+
+
+ +
+
+
-
+ +
-
- -