diff --git a/index.php b/index.php index 38c13b1..1fa5736 100644 --- a/index.php +++ b/index.php @@ -1,13 +1,13 @@ forceSSL(); $this->loginStatus(); + $this->getSiteLanguage(); $this->pageStatus(); $this->logoutAction(); $this->loginAction(); @@ -159,6 +163,28 @@ public function init(): void } } + /** + * Set site language based on logged-in user + * @return string + * @throws Exception + */ + public function getSiteLanguage(): string + { + if ($this->loggedIn) { + $lang = $this->get('config', 'adminLang'); + } else { + $lang = $this->get('config', 'siteLang'); + } + + if (gettype($lang) === 'object' && empty(get_object_vars($lang))) { + $lang = 'en'; + $this->set('config', 'siteLang', $lang); + $this->set('config', 'adminLang', $lang); + } + + return $lang; + } + /** * Display the HTML. Called after init() * @return void @@ -442,6 +468,8 @@ public function createDb(): void $this->db = (object)[ self::DB_CONFIG => [ 'siteTitle' => 'Website title', + 'siteLang' => 'en', + 'adminLang' => 'en', 'theme' => 'sky', 'defaultPage' => 'home', 'login' => 'loginURL', @@ -872,7 +900,7 @@ public function css(): string { if ($this->loggedIn) { $styles = <<<'EOT' - + EOT; return $this->hook('css', $styles)[0]; } @@ -1244,6 +1272,19 @@ public function getModulesCachedData(string $type = self::THEMES_DIR): array return $data !== null && array_key_exists($type, $data) ? $data[$type] : []; } + /** + * Retrieve cached single Theme/Plugin data + * @param string $moduleKey + * @param string $type + * @return array|null + * @throws Exception + */ + public function getSingleModuleCachedData(string $moduleKey, string $type = self::THEMES_DIR): array + { + $data = $this->getModulesCachedData($type); + return $data !== null && array_key_exists($moduleKey, $data) ? $data[$moduleKey] : []; + } + /** * Force cache refresh for updates * @throws Exception @@ -1469,9 +1510,15 @@ private function validateWcmsModuleStructure(object $wcmsModule): bool { */ public function addCustomModule(): void { - if (!isset($_POST['pluginThemeUrl'], $_POST['pluginThemeType']) || !$this->verifyFormActions()) { + if (!isset($_POST['pluginThemeUrl'], $_POST['pluginThemeType'], $_POST['password_recheck']) || !$this->verifyFormActions()) { return; } + + if (!password_verify($_POST['password_recheck'], $this->get('config', 'password'))) { + $this->alert('danger', 'Invalid password.'); + $this->redirect(); + } + $type = $_POST['pluginThemeType']; $url = rtrim(trim($_POST['pluginThemeUrl']), '/'); $customModules = (array)$this->get('config', 'customModules', $type); @@ -1531,12 +1578,19 @@ public function getModuleVersion(string $type, string $name): ?string */ public function installUpdateModuleAction(): void { - if (!isset($_REQUEST['installModule'], $_REQUEST['directoryName'], $_REQUEST['type']) || !$this->verifyFormActions(true)) { + if (!isset($_REQUEST['installModule'], $_REQUEST['type']) || !$this->verifyFormActions(true)) { return; } - $url = $_REQUEST['installModule']; - $folderName = $_REQUEST['directoryName']; + + $folderName = trim(htmlspecialchars($_REQUEST['installModule'])); $type = $_REQUEST['type']; + $cached = $this->getSingleModuleCachedData($folderName, $type); + $url = !empty($cached) ? $cached['zip'] : null; + + if (empty($url)) { + $this->alert('danger', 'Unable to find theme or plugin.'); + return; + } $path = sprintf('%s/%s/', $this->rootDir, $type); @@ -1609,10 +1663,14 @@ public function js(): string $scripts = << - + EOT; $scripts .= ''; $scripts .= ''; + $scripts .= ''; return $this->hook('js', $scripts)[0]; } @@ -1625,6 +1683,7 @@ public function js(): string */ public function loadPlugins(): void { + $this->installedPlugins = []; $plugins = $this->rootDir . '/plugins'; if (!is_dir($plugins) && !mkdir($plugins) && !is_dir($plugins)) { return; @@ -1633,14 +1692,16 @@ public function loadPlugins(): void return; } foreach (glob($plugins . '/*', GLOB_ONLYDIR) as $dir) { - if (file_exists($dir . '/' . basename($dir) . '.php')) { - include $dir . '/' . basename($dir) . '.php'; + $pluginName = basename($dir); + if (file_exists($dir . '/' . $pluginName . '.php')) { + include $dir . '/' . $pluginName . '.php'; + $this->installedPlugins[] = $pluginName; } } } /** - * Loads theme files and functions.php file (if they exists) + * Loads theme files and functions.php file (if they exist) * @return void */ public function loadThemeAndFunctions(): void @@ -2037,6 +2098,7 @@ public function pageStatus(): void public function parseUrl(): string { $page = $_GET['page'] ?? null; + $page = !empty($page) ? trim(htmlspecialchars($page, ENT_QUOTES)) : null; if (!isset($page) || !$page) { $defaultPage = $this->get('config', 'defaultPage'); @@ -2046,7 +2108,7 @@ public function parseUrl(): string $this->currentPageTree = explode('/', rtrim($page, '/')); if ($page === $this->get('config', 'login')) { - return htmlspecialchars($page, ENT_QUOTES); + return $page; } $currentPage = end($this->currentPageTree); @@ -2324,11 +2386,18 @@ public function settings(): string $output .= $this->renderModuleTab('plugins'); $output .= '

Admin login URL

-

Important: save your login URL to log in to your website next time:
' . self::url($this->get('config', - 'login')) . ' +

Important: save your login URL to log in to your website next time:
' . self::url($this->get('config', 'login')) . '

+
+
' . $this->get('config', 'login') . '
+
+

Site language config

+

HTML lang value for admin.

+
+
' . $this->get('config', 'adminLang') . '
+
+

HTML lang value for visitors.

-
' . $this->get('config', - 'login') . '
+
' . $this->get('config', 'siteLang') . '

Password

@@ -2348,7 +2417,7 @@ public function settings(): string
-

How to restore backup

+

How to restore backup

Save confirmation popup

If this is turned "ON", WonderCMS will always ask you to confirm any changes you make.

@@ -2385,7 +2454,7 @@ public function settings(): string
-

Read more before enabling

'; +

Read more before enabling

'; $output .= $this->renderAdminLoginIPs(); $output .= ' @@ -2396,7 +2465,7 @@ public function settings(): string WonderCMS ' . VERSION . '   News   Community   - Docs   + Docs   Donate   Shop/Merch

@@ -2594,8 +2663,8 @@ private function renderModuleTab(string $type = 'themes'): string $isThemeSelected = $this->get('config', 'theme') === $directoryName; $image = $addon['image'] !== null ? '' . $name . '' : $defaultImage; - $installButton = $addon['install'] ? ' Install' : ''; - $updateButton = !$addon['install'] && $addon['update'] ? ' Update to ' . $addon['version'] . '' : ''; + $installButton = $addon['install'] ? ' Install' : ''; + $updateButton = !$addon['install'] && $addon['update'] ? ' Update to ' . $addon['version'] . '' : ''; $removeButton = !$addon['install'] ? '' : ''; $inactiveThemeButton = $type === 'themes' && !$addon['install'] && !$isThemeSelected ? ' Activate' : ''; $activeThemeButton = $type === 'themes' && !$addon['install'] && $isThemeSelected ? 'Active' : ''; @@ -2631,15 +2700,15 @@ private function renderModuleTab(string $type = 'themes'): string $output .= $installs; $output .= '

Custom module

-
+
- +
-

Read more about custom modules

+

Read more about custom modules

'; return $output; } diff --git a/themes/sky/theme.php b/themes/sky/theme.php index 1e620e0..b670182 100644 --- a/themes/sky/theme.php +++ b/themes/sky/theme.php @@ -1,7 +1,7 @@ - + diff --git a/themes/sky/wcms-modules.json b/themes/sky/wcms-modules.json index b3bfdd3..bc6ae6b 100644 --- a/themes/sky/wcms-modules.json +++ b/themes/sky/wcms-modules.json @@ -6,7 +6,7 @@ "repo": "https://github.com/robiso/sky/tree/master", "zip": "https://github.com/robiso/sky/archive/master.zip", "summary": "Default WonderCMS theme (2022). Theme works without Bootstrap and jQuery.", - "version": "3.2.3", + "version": "3.2.4", "image": "https://raw.githubusercontent.com/robiso/sky/master/preview.jpg" } } diff --git a/version b/version index 4d9d11c..6cb9d3d 100644 --- a/version +++ b/version @@ -1 +1 @@ -3.4.2 +3.4.3