-
-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #306 from WonderCMS/343
Releasing 3.4.3, all notes can be found in the release notes.
- Loading branch information
Showing
4 changed files
with
96 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) { | ||
|
@@ -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; | ||
|
||
|
@@ -135,6 +138,7 @@ public function init(): void | |
{ | ||
$this->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' | ||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/robiso/[email protected]/wcms-admin.min.css" crossorigin="anonymous"> | ||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/WonderCMS/[email protected]/wcms-admin.min.css" crossorigin="anonymous"> | ||
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 | ||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/autosize.min.js" integrity="sha384-gqYjRLBp7SeF6PCEz2XeqqNyvtxuzI3DuEepcrNHbrO+KG3woVNa/ISn/i8gGtW8" crossorigin="anonymous"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/output/taboverride.min.js" integrity="sha384-fYHyZra+saKYZN+7O59tPxgkgfujmYExoI6zUvvvrKVT1b7krdcdEpTLVJoF/ap1" crossorigin="anonymous"></script> | ||
<script src="https://cdn.jsdelivr.net/gh/robiso/[email protected]/wcms-admin.min.js" integrity="sha384-lwdbkm/17hWy+Y4iBnY0iEp0FlaKvjdeTBZaRYM1DGPshGgxKoPaB87Xue26Wv1W" crossorigin="anonymous"></script> | ||
<script src="https://cdn.jsdelivr.net/gh/WonderCMS/[email protected]/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]; | ||
} | ||
|
@@ -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 .= ' <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"> | ||
|
@@ -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> | ||
|
@@ -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> | ||
|
@@ -2396,7 +2465,7 @@ public function settings(): string | |
<a href="https://wondercms.com" target="_blank">WonderCMS ' . VERSION . '</a> | ||
<b><a href="https://wondercms.com/news" target="_blank">News</a> | ||
<a href="https://wondercms.com/community" target="_blank">Community</a> | ||
<a href="https://github.com/robiso/wondercms/wiki#wondercms-documentation" target="_blank">Docs</a> | ||
<a href="https://github.com/WonderCMS/wondercms/wiki#wondercms-documentation" target="_blank">Docs</a> | ||
<a href="https://wondercms.com/donate" target="_blank">Donate</a> | ||
<a href="https://swag.wondercms.com" target="_blank">Shop/Merch</a></b> | ||
</p> | ||
|
@@ -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>' : ''; | ||
|
@@ -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; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
3.4.2 | ||
3.4.3 |