diff --git a/wire/core/Notices.php b/wire/core/Notices.php index 68ef7d71..b8f1f8c3 100644 --- a/wire/core/Notices.php +++ b/wire/core/Notices.php @@ -58,6 +58,14 @@ abstract class Notice extends WireData { * */ const allowMarkup = 32; + + /** + * Flag indicates notice should prepend (rather than append) to any existing notices + * + * @since 3.0.135 + * + */ + const prepend = 64; /** * Create the Notice @@ -212,7 +220,7 @@ class Notices extends WireArray { $item->flags = $item->flags | Notice::allowMarkup; } else if(is_object($item->text) && $item->text instanceof Wire) { $item->text = "
" . $this->wire('sanitizer')->entities(print_r($item->text, true)) . ""; - $item->flags = $item->flag | Notice::allowMarkup; + $item->flags = $item->flags | Notice::allowMarkup; } else if(is_object($item->text)) { $item->text = (string) $item->text; } @@ -240,7 +248,11 @@ class Notices extends WireArray { if($item->flags & Notice::logOnly) return $this; } - return parent::add($item); + if($item->flags & Notice::prepend) { + return parent::prepend($item); + } else { + return parent::add($item); + } } protected function addLog($item) { diff --git a/wire/core/User.php b/wire/core/User.php index 8a07b97f..22997ff0 100644 --- a/wire/core/User.php +++ b/wire/core/User.php @@ -16,6 +16,7 @@ * @property string|Password $pass Set the user’s password. * @property PageArray $roles Get the roles this user has. * @property Language $language User language, applicable only if LanguageSupport installed. + * @property string $admin_theme Admin theme class name * * @method bool hasPagePermission($name, Page $page = null) #pw-internal * @method bool hasTemplatePermission($name, $template) #pw-internal diff --git a/wire/modules/Process/ProcessLogin/ProcessLogin.module b/wire/modules/Process/ProcessLogin/ProcessLogin.module index e145067d..0ef132e9 100644 --- a/wire/modules/Process/ProcessLogin/ProcessLogin.module +++ b/wire/modules/Process/ProcessLogin/ProcessLogin.module @@ -382,98 +382,27 @@ class ProcessLogin extends Process implements ConfigurableModule { * */ protected function ___afterLogin() { - if(!$this->user->isSuperuser()) return; - - $indexVersion = ProcessWire::indexVersion; - $htaccessVersion = ProcessWire::htaccessVersion; - - if(PROCESSWIRE < $indexVersion) { - $this->warning( - "Not urgent, but note that your root index.php file is not up-to-date with this ProcessWire version - please update it when possible. " . - "
setlocale(LC_ALL,'$example');
";
- $this->warning($msg, Notice::allowMarkup);
}
}
}
@@ -699,7 +628,7 @@ class ProcessLogin extends Process implements ConfigurableModule {
/** @var Session $session */
$session = $this->wire('session');
- $session->message($user->name . ' - ' . $this->_("Successful login"));
+ // $session->message($user->name . ' - ' . $this->_("Successful login"));
if($this->isAdmin) {
$copyVars = $session->getFor($this, 'copyVars');
diff --git a/wire/modules/System/SystemUpdater/SystemUpdater.module b/wire/modules/System/SystemUpdater/SystemUpdater.module
index a0ef1cf8..3178ac4f 100644
--- a/wire/modules/System/SystemUpdater/SystemUpdater.module
+++ b/wire/modules/System/SystemUpdater/SystemUpdater.module
@@ -43,6 +43,14 @@ class SystemUpdater extends WireData implements Module, ConfigurableModule {
*/
protected $numUpdatesApplied = 0;
+ /**
+ * Get array of updates that were applied
+ *
+ * @var array Array of update version numbers
+ *
+ */
+ protected $updatesApplied = array();
+
/**
* Is an update being applied manually?
*
@@ -96,6 +104,7 @@ class SystemUpdater extends WireData implements Module, ConfigurableModule {
// then already applied updates won't be applied again
$this->saveSystemVersion($systemVersion);
$this->numUpdatesApplied++;
+ $this->updatesApplied[] = ($systemVersion-1);
}
if($this->numUpdatesApplied > 0) {
@@ -216,6 +225,7 @@ class SystemUpdater extends WireData implements Module, ConfigurableModule {
*
* @param int $version Update version number
* @return null|SystemUpdate Returns SystemUpdate instance of available or null if not
+ * @since 3.0.135
*
*/
public function getUpdate($version) {
@@ -246,6 +256,7 @@ class SystemUpdater extends WireData implements Module, ConfigurableModule {
*
* @param int|SystemUpdate $version Update version number or instance of SystemUpdate you want to apply
* @return bool True on success, false on fail
+ * @since 3.0.135
*
*/
public function apply($version) {
@@ -273,6 +284,33 @@ class SystemUpdater extends WireData implements Module, ConfigurableModule {
return $success;
}
+ /**
+ * Get instance of SystemUpdaterChecks for performing system checks
+ *
+ * #pw-internal
+ *
+ * @return SystemUpdaterChecks
+ * @since 3.0.135
+ *
+ */
+ public function getChecks() {
+ require_once(dirname(__FILE__) . '/SystemUpdaterChecks.php');
+ $checks = new SystemUpdaterChecks();
+ $this->wire($checks);
+ return $checks;
+ }
+
+ /**
+ * Get array of updates (update version numbers) that were automatically applied during this request
+ *
+ * @return array
+ * @since 3.0.135
+ *
+ */
+ public function getUpdatesApplied() {
+ return $this->updatesApplied;
+ }
+
/**
* Message notice
*
diff --git a/wire/modules/System/SystemUpdater/SystemUpdaterChecks.php b/wire/modules/System/SystemUpdater/SystemUpdaterChecks.php
new file mode 100644
index 00000000..15befcef
--- /dev/null
+++ b/wire/modules/System/SystemUpdater/SystemUpdaterChecks.php
@@ -0,0 +1,441 @@
+showNotices = $showNotices;
+ }
+
+ /**
+ * Set whether or not to test all checks (as if all checks failed)
+ *
+ * @param bool $testAll
+ *
+ */
+ public function setTestAll($testAll = true) {
+ $this->testAll = $testAll;
+ }
+
+ /**
+ * Run all system checks and return array of results
+ *
+ * @return array
+ *
+ */
+ public function execute() {
+
+ $this->checkAll = true;
+ $results = array();
+ $checks = array(
+ 'checkWelcome',
+ 'checkIndexFile',
+ 'checkHtaccessFile',
+ //'checkOtherHtaccessFiles',
+ 'checkInstallerFiles',
+ 'checkFilePermissions',
+ 'checkPublishedField',
+ 'checkLocale',
+ 'checkDebugMode',
+ );
+
+ foreach($checks as $method) {
+ try {
+ $results[$method] = $this->$method();
+ } catch(\Exception $e) {
+ if($this->showNotices) $this->warning("$method: " . $e->getMessage());
+ $results[$method] = false;
+ }
+ }
+
+ $this->checkAll = false;
+
+ $numWarnings = count($this->warnings);
+ if($this->showNotices && $numWarnings) {
+ if($numWarnings > 1) $this->warning($this->_('Multiple issues detected, please review:'));
+ foreach($this->warnings as $warning) {
+ $this->warning($warning[0], $warning[1]);
+ }
+ }
+
+ $this->warnings = array();
+
+ return $results;
+ }
+
+ /**
+ * Check that index.php file is the correct version
+ *
+ * @return bool
+ *
+ */
+ public function checkIndexFile() {
+
+ $requiredVersion = ProcessWire::indexVersion;
+ $actualVersion = PROCESSWIRE;
+
+ if(PROCESSWIRE < $requiredVersion || $this->testAll) {
+ if($this->showNotices) {
+ $warning = sprintf(
+ $this->_('Please note that your root %s file is not up-to-date with this ProcessWire version, please update it when possible.'),
+ $this->location('index.php')
+ );
+ $details = $this->versionsLabel($requiredVersion, $actualVersion);
+ $this->warning($warning . $this->small($details), Notice::log | Notice::allowMarkup);
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check that main htaccess file is the correct version
+ *
+ * @return bool
+ * @throws WireException
+ *
+ */
+ public function checkHtaccessFile() {
+
+ $requiredVersion = ProcessWire::htaccessVersion;
+ $htaccessFile = $this->wire('config')->paths->root . '.htaccess';
+
+ if(is_readable($htaccessFile)) {
+ $data = file_get_contents($htaccessFile);
+ if(!preg_match('/@(?:htaccess|index)Version\s+(\d+)\b/', $data, $matches) || ((int) $matches[1]) < $requiredVersion || $this->testAll) {
+ if($this->showNotices) {
+ $foundVersion = isset($matches[1]) ? (int) $matches[1] : '?';
+ $warning = sprintf(
+ $this->_('Please note that your root %s file is not up-to-date with this ProcessWire version, please update it when possible.'),
+ $this->location('.htaccess')
+ );
+ $details = $this->small(
+ $this->versionsLabel($requiredVersion, $foundVersion) . ' ' .
+ $this->_('To suppress this warning, replace or add the following in the top of your existing .htaccess file:') .
+ $this->code("# @htaccessVersion $requiredVersion")
+ );
+ $this->warning("$warning$details", Notice::log | Notice::allowMarkup);
+ }
+ return false;
+ }
+ } else {
+ if($this->showNotices) $this->warning($this->fileNotFoundLabel($htaccessFile));
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check that other useful htaccess files are present
+ *
+ * @return bool
+ *
+ */
+ public function checkOtherHtaccessFiles() {
+ /** @var SystemUpdater $systemUpdater */
+ $systemUpdater = $this->wire('modules')->get('SystemUpdater');
+ if(!$systemUpdater) return false;
+ $result = true;
+
+ /** @var SystemUpdate17 $update */
+ // update 17 verifies that fallback .htaccess files are in place for 2nd layer protections
+ $update = $systemUpdater->getUpdate(17);
+ if($update) {
+ if($update->isUseful() || $this->testAll) $result = $update->update();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Check if this is the first call to checkWelcome and show a welcome message and add an active.php file if so
+ *
+ * @return bool Returns false if active.php does not yet exist or true if it does
+ *
+ */
+ public function checkWelcome() {
+
+ $activeFile = $this->wire('config')->paths->assets . 'active.php';
+ $exists = is_file($activeFile);
+
+ if($this->showNotices && ((!$exists && !$this->wire('config')->debug) || $this->testAll)) {
+ $this->message(
+ $this->strong($this->_('Welcome to ProcessWire!')) . ' ' .
+ $this->_('If this installation is currently being used for development or testing, we recommend enabling debug mode.') . ' ' .
+ $this->_('Debug mode ensures all errors are visible, which can be helpful during development or troubleshooting.') . ' ' .
+ $this->_('It also enables additional developer information to appear here in the admin.') . ' ' .
+ sprintf($this->_('You can enable debug mode by editing your %s file and adding the following:'), $this->location('/site/config.php')) .
+ $this->code('$config->debug = true;') .
+ $this->small($this->_('Please note: this notification will not be shown again. Remember to disable debug mode before going live.')),
+ Notice::allowMarkup | Notice::prepend
+ );
+ }
+
+ if(!$exists) {
+ $data =
+ "config->paths->root}]";
+ $this->wire('files')->filePutContents($activeFile, $data);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if unnecessary installer files are present
+ *
+ * @return bool
+ *
+ */
+ public function checkInstallerFiles() {
+ if(is_file($this->wire('config')->paths->root . "install.php") || $this->testAll) {
+ if($this->showNotices) {
+ $warning = $this->_("Security Warning: file '%s' exists and should be deleted as soon as possible.");
+ $this->warning(sprintf($warning, '/install.php'), Notice::log);
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Check for insecure file permissions
+ *
+ * @return bool
+ *
+ */
+ public function checkFilePermissions() {
+
+ // warnings about 0666/0777 file permissions
+ if($this->config->chmodDir != '0777' && $this->config->chmodFile != '0666' && !$this->testAll) return true;
+ if(!$this->config->chmodWarn || !$this->showNotices) return false;
+
+ $warning = sprintf(
+ $this->_('Warning, your %s file specifies file permissions that are too loose for many environments:'),
+ $this->location('/site/config.php')
+ );
+
+ $code =
+ $this->code("\$config->chmodFile = '{$this->config->chmodFile}';") .
+ $this->code("\$config->chmodDir = '{$this->config->chmodDir}';");
+
+ $link = $this->link(
+ 'https://processwire.com/docs/security/file-permissions/',
+ $this->_('Read "Securing file permissions" for more details')
+ );
+
+ $details = $this->small(
+ sprintf($this->_('To suppress this warning, add the following to your %s file:'), $this->location('/site/config.php')) . ' ' .
+ $this->code('$config->chmodWarn = false;')
+ );
+
+ $code = str_replace(array('0666', '0777'), array('0666', '0777'), $code);
+
+ $this->warning("$warning$code$link$details", Notice::allowMarkup | Notice::log);
+
+ return false;
+ }
+
+ /**
+ * Check if there is a field named 'published' that should not be present
+ *
+ * @return bool
+ *
+ */
+ public function checkPublishedField() {
+ if(!$this->wire('fields')->get('published') && !$this->testAll) return true;
+ if($this->showNotices) $this->warning(
+ $this->_('Warning: you have a field named “published” that conflicts with the page “published” property.') . ' ' .
+ $this->_('Please rename your field field to something else and update any templates referencing it.')
+ );
+ return false;
+ }
+
+ /**
+ * Check locale setting
+ *
+ * Warning about servers with locales that break UTF-8 strings called by basename
+ * and other file functions, due to a long running PHP bug
+ *
+ * @return bool
+ *
+ */
+ public function checkLocale() {
+
+ if(basename("§") !== "" && !$this->testAll) return true;
+ if(!$this->showNotices) return false;
+
+ $example = stripos(PHP_OS, 'WIN') === 0 ? 'en-US' : 'en_US.UTF-8';
+ $localeLabel = $this->_('Your current locale setting is “%s”.') . ' ';
+ $warning = $this->_('Note: your current server locale setting isn’t working as expected with the UTF-8 charset and may cause minor issues.');
+ $msg = '';
+
+ if($this->wire('modules')->isInstalled('LanguageSupport')) {
+ // language support installed
+ $textdomain = 'wire--modules--languagesupport--languagesupport-module';
+ $locale = __('C', $textdomain);
+ if(empty($locale)) $locale = setlocale(LC_CTYPE, 0);
+ $msg .= ' ' .
+ sprintf($localeLabel, $locale) . ' ' .
+ sprintf(
+ $this->_('Please translate the “C” locale setting for each language to the compatible locale in %s'),
+ $this->location('/wire/modules/LanguageSupport/LanguageSupport.module') . ':'
+ );
+
+ foreach($this->wire('languages') as $language) {
+ $url = $this->wire('config')->urls->admin .
+ "setup/language-translator/edit/?" .
+ "language_id=$language->id&" .
+ "textdomain=$textdomain&" .
+ "filename=wire/modules/LanguageSupport/LanguageSupport.module";
+ $msg .= "