Skip to content

Commit

Permalink
Merge pull request #306 from WonderCMS/343
Browse files Browse the repository at this point in the history
Releasing 3.4.3, all notes can be found in the release notes.
  • Loading branch information
robiso committed Nov 5, 2023
2 parents 65ad814 + aa80550 commit ee41c13
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 27 deletions.
117 changes: 93 additions & 24 deletions index.php
@@ -1,13 +1,13 @@
<?php
/**
* @package WonderCMS
* @author Robert Isoski
* @author Robert Isoski (https://robert.si)
* @see https://www.wondercms.com
* @license MIT
*/

session_start();
define('VERSION', '3.4.2');
define('VERSION', '3.4.3');
mb_internal_encoding('UTF-8');

if (defined('PHPUNIT_TESTING') === false) {
Expand Down Expand Up @@ -50,6 +50,9 @@ class Wcms
/** @var array $currentPageTree - Tree hierarchy of the current page */
public $currentPageTree = [];

/** @var array $installedPlugins - Currently installed plugins */
public $installedPlugins = [];

/** @var bool $currentPageExists - check if current page exists */
public $currentPageExists = false;

Expand Down Expand Up @@ -135,6 +138,7 @@ public function init(): void
{
$this->forceSSL();
$this->loginStatus();
$this->getSiteLanguage();
$this->pageStatus();
$this->logoutAction();
$this->loginAction();
Expand All @@ -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
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -872,7 +900,7 @@ public function css(): string
{
if ($this->loggedIn) {
$styles = <<<'EOT'
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/robiso/wondercms-cdn-files@3.2.25/wcms-admin.min.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WonderCMS/wondercms-cdn-files@3.2.25/wcms-admin.min.css" crossorigin="anonymous">
EOT;
return $this->hook('css', $styles)[0];
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -1609,10 +1663,14 @@ public function js(): string
$scripts = <<<EOT
<script src="https://cdn.jsdelivr.net/npm/autosize@4.0.2/dist/autosize.min.js" integrity="sha384-gqYjRLBp7SeF6PCEz2XeqqNyvtxuzI3DuEepcrNHbrO+KG3woVNa/ISn/i8gGtW8" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/taboverride@4.0.3/build/output/taboverride.min.js" integrity="sha384-fYHyZra+saKYZN+7O59tPxgkgfujmYExoI6zUvvvrKVT1b7krdcdEpTLVJoF/ap1" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/robiso/wondercms-cdn-files@3.2.26/wcms-admin.min.js" integrity="sha384-lwdbkm/17hWy+Y4iBnY0iEp0FlaKvjdeTBZaRYM1DGPshGgxKoPaB87Xue26Wv1W" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/WonderCMS/wondercms-cdn-files@3.2.26/wcms-admin.min.js" integrity="sha384-lwdbkm/17hWy+Y4iBnY0iEp0FlaKvjdeTBZaRYM1DGPshGgxKoPaB87Xue26Wv1W" crossorigin="anonymous"></script>
EOT;
$scripts .= '<script>const token = "' . $this->getToken() . '";</script>';
$scripts .= '<script>const rootURL = "' . $this->url() . '";</script>';
$scripts .= '<script>function recheckPasswordPrompt(e) {
e.target.elements["password_recheck"].value = prompt("Please confirm your admin password.");
return true;
}</script>';

return $this->hook('js', $scripts)[0];
}
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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');
Expand All @@ -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);
Expand Down Expand Up @@ -2324,11 +2386,18 @@ public function settings(): string
$output .= $this->renderModuleTab('plugins');
$output .= ' <div role="tabpanel" class="tab-pane" id="security">
<p class="subTitle">Admin login URL</p>
<p class="change marginTop5 small danger">Important: save your login URL to log in to your website next time:<br/><b><span class="normalFont">' . self::url($this->get('config',
'login')) . '</b></span>
<p class="change marginTop5 small danger">Important: save your login URL to log in to your website next time:<br/><b><span class="normalFont">' . self::url($this->get('config', 'login')) . '</b></span></p>
<div class="change">
<div data-target="config" id="login" class="editText">' . $this->get('config', 'login') . '</div>
</div>
<p class="subTitle">Site language config</p>
<p class="change marginTop5 small">HTML lang value for admin.</p>
<div class="change">
<div data-target="config" id="adminLang" class="editText">' . $this->get('config', 'adminLang') . '</div>
</div>
<p class="change marginTop5 small">HTML lang value for visitors.</p>
<div class="change">
<div data-target="config" id="login" class="editText">' . $this->get('config',
'login') . '</div>
<div data-target="config" id="siteLang" class="editText">' . $this->get('config', 'siteLang') . '</div>
</div>
<p class="subTitle">Password</p>
<div class="change">
Expand All @@ -2348,7 +2417,7 @@ public function settings(): string
<button type="submit" class="wbtn wbtn-block wbtn-info" name="backup"><i class="installIcon"></i> Backup website</button><input type="hidden" name="token" value="' . $this->getToken() . '">
</form>
</div>
<p class="text-right marginTop5"><a href="https://github.com/robiso/wondercms/wiki/Restore-backup#how-to-restore-a-backup-in-3-steps" target="_blank"><i class="linkIcon"></i> How to restore backup</a></p>
<p class="text-right marginTop5"><a href="https://github.com/WonderCMS/wondercms/wiki/Restore-backup#how-to-restore-a-backup-in-3-steps" target="_blank"><i class="linkIcon"></i> How to restore backup</a></p>
<p class="subTitle">Save confirmation popup</p>
<p class="change small">If this is turned "ON", WonderCMS will always ask you to confirm any changes you make.</p>
Expand Down Expand Up @@ -2385,7 +2454,7 @@ public function settings(): string
<input type="hidden" name="token" value="' . $this->getToken() . '">
</form>
</div>
<p class="text-right marginTop5"><a href="https://github.com/robiso/wondercms/wiki/Better-security-mode-(HTTPS-and-other-features)#important-read-before-turning-this-feature-on" target="_blank"><i class="linkIcon"></i> Read more before enabling</a></p>';
<p class="text-right marginTop5"><a href="https://github.com/WonderCMS/wondercms/wiki/Better-security-mode-(HTTPS-and-other-features)#important-read-before-turning-this-feature-on" target="_blank"><i class="linkIcon"></i> Read more before enabling</a></p>';
$output .= $this->renderAdminLoginIPs();
$output .= '
</div>
Expand All @@ -2396,7 +2465,7 @@ public function settings(): string
<a href="https://wondercms.com" target="_blank">WonderCMS ' . VERSION . '</a> &nbsp;
<b><a href="https://wondercms.com/news" target="_blank">News</a> &nbsp;
<a href="https://wondercms.com/community" target="_blank">Community</a> &nbsp;
<a href="https://github.com/robiso/wondercms/wiki#wondercms-documentation" target="_blank">Docs</a> &nbsp;
<a href="https://github.com/WonderCMS/wondercms/wiki#wondercms-documentation" target="_blank">Docs</a> &nbsp;
<a href="https://wondercms.com/donate" target="_blank">Donate</a> &nbsp;
<a href="https://swag.wondercms.com" target="_blank">Shop/Merch</a></b>
</p>
Expand Down Expand Up @@ -2594,8 +2663,8 @@ private function renderModuleTab(string $type = 'themes'): string
$isThemeSelected = $this->get('config', 'theme') === $directoryName;

$image = $addon['image'] !== null ? '<a class="text-center center-block" href="' . $addon['image'] . '" target="_blank"><img style="max-width: 100%; max-height: 250px;" src="' . $addon['image'] . '" alt="' . $name . '" /></a>' : $defaultImage;
$installButton = $addon['install'] ? '<a class="wbtn wbtn-success wbtn-block wbtn-sm" href="' . self::url('?installModule=' . $addon['zip'] . '&directoryName=' . $directoryName . '&type=' . $type . '&token=' . $this->getToken()) . '" title="Install"><i class="installIcon"></i> Install</a>' : '';
$updateButton = !$addon['install'] && $addon['update'] ? '<a class="wbtn wbtn-info wbtn-sm wbtn-block marginTop5" href="' . self::url('?installModule=' . $addon['zip'] . '&directoryName=' . $directoryName . '&type=' . $type . '&token=' . $this->getToken()) . '" title="Update"><i class="refreshIcon"></i> Update to ' . $addon['version'] . '</a>' : '';
$installButton = $addon['install'] ? '<a class="wbtn wbtn-success wbtn-block wbtn-sm" href="' . self::url('?installModule=' . $directoryName . '&type=' . $type . '&token=' . $this->getToken()) . '" title="Install"><i class="installIcon"></i> Install</a>' : '';
$updateButton = !$addon['install'] && $addon['update'] ? '<a class="wbtn wbtn-info wbtn-sm wbtn-block marginTop5" href="' . self::url('?installModule=' . $directoryName . '&type=' . $type . '&token=' . $this->getToken()) . '" title="Update"><i class="refreshIcon"></i> Update to ' . $addon['version'] . '</a>' : '';
$removeButton = !$addon['install'] ? '<a class="wbtn wbtn-danger wbtn-sm marginTop5" href="' . self::url('?deleteModule=' . $directoryName . '&type=' . $type . '&token=' . $this->getToken()) . '" onclick="return confirm(\'Remove ' . $name . '?\')" title="Remove"><i class="deleteIcon"></i></a>' : '';
$inactiveThemeButton = $type === 'themes' && !$addon['install'] && !$isThemeSelected ? '<a class="wbtn wbtn-primary wbtn-sm wbtn-block" href="' . self::url('?selectModule=' . $directoryName . '&type=' . $type . '&token=' . $this->getToken()) . '" onclick="return confirm(\'Activate ' . $name . ' theme?\')"><i class="checkmarkIcon"></i> Activate</a>' : '';
$activeThemeButton = $type === 'themes' && !$addon['install'] && $isThemeSelected ? '<a class="wbtn wbtn-primary wbtn-sm wbtn-block" disabled>Active</a>' : '';
Expand Down Expand Up @@ -2631,15 +2700,15 @@ private function renderModuleTab(string $type = 'themes'): string
$output .= $installs;
$output .= '</div>
<p class="subTitle">Custom module</p>
<form action="' . $this->getCurrentPageUrl() . '" method="post">
<form action="' . $this->getCurrentPageUrl() . '" method="post" onsubmit="recheckPasswordPrompt(event)">
<div class="wform-group">
<div class="change winput-group marginTop5"><input type="text" name="pluginThemeUrl" class="wform-control normalFont" placeholder="Enter full URL to wcms-modules.json file">
<span class="winput-group-btn"><button type="submit" class="wbtn wbtn-info" onclick="return confirm(\'Adding unknown modules can be VERY dangerous, are you sure you want to continue?\')"><i class="addNewIcon"></i> Add</button></span>
</div>
</div>
<input type="hidden" name="token" value="' . $this->getToken() . '" /><input type="hidden" name="pluginThemeType" value="' . $type . '" />
<input type="hidden" name="token" value="' . $this->getToken() . '" /><input type="hidden" name="pluginThemeType" value="' . $type . '" /><input type="hidden" name="password_recheck" />
</form>
<p class="text-right"><a href="https://github.com/robiso/wondercms/wiki/Custom-modules" target="_blank"><i class="linkIcon"></i> Read more about custom modules</a></p>
<p class="text-right"><a href="https://github.com/WonderCMS/wondercms/wiki/Custom-modules" target="_blank"><i class="linkIcon"></i> Read more about custom modules</a></p>
</div>';
return $output;
}
Expand Down
2 changes: 1 addition & 1 deletion themes/sky/theme.php
@@ -1,7 +1,7 @@
<?php global $Wcms ?>

<!DOCTYPE html>
<html lang="en">
<html lang="<?= $Wcms->getSiteLanguage() ?>">
<head>
<!-- Encoding, browser compatibility, viewport -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
Expand Down
2 changes: 1 addition & 1 deletion themes/sky/wcms-modules.json
Expand Up @@ -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"
}
}
Expand Down
2 changes: 1 addition & 1 deletion version
@@ -1 +1 @@
3.4.2
3.4.3

0 comments on commit ee41c13

Please sign in to comment.