Implemented a basic system check
authorAlexander Ebert <ebert@woltlab.com>
Tue, 29 Jan 2019 18:14:00 +0000 (19:14 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 29 Jan 2019 18:14:00 +0000 (19:14 +0100)
See #2811

The original issue was going much further, but during development it showed that it overlapped way too much with the existing validation methods.

com.woltlab.wcf/acpMenu.xml
wcfsetup/install/files/acp/templates/systemCheck.tpl [new file with mode: 0644]
wcfsetup/install/files/lib/acp/page/SystemCheckPage.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/bbcode/GroupBBCode.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 5a1a504089f901b03fa6fbc33adbe05ccb5cfb3c..24c77f4e13a967a428272c114b8e1955914bd89c 100644 (file)
                        <parent>wcf.acp.menu.link.management</parent>
                        <showorder>1</showorder>
                </acpmenuitem>
+               <acpmenuitem name="wcf.acp.menu.link.systemCheck">
+                       <controller>wcf\acp\page\SystemCheckPage</controller>
+                       <parent>wcf.acp.menu.link.maintenance</parent>
+                       <permissions>admin.configuration.package.canInstallPackage</permissions>
+                       <showorder>1</showorder>
+               </acpmenuitem>
                <acpmenuitem name="wcf.acp.menu.link.cronjob.list">
                        <controller>wcf\acp\page\CronjobListPage</controller>
                        <parent>wcf.acp.menu.link.maintenance</parent>
                        <permissions>admin.management.canManageCronjob</permissions>
-                       <showorder>1</showorder>
+                       <showorder>2</showorder>
                </acpmenuitem>
                <acpmenuitem name="wcf.acp.menu.link.cronjob.add">
                        <controller>wcf\acp\form\CronjobAddForm</controller>
                        <parent>wcf.acp.menu.link.cronjob.list</parent>
                        <permissions>admin.management.canManageCronjob</permissions>
-                       <showorder>2</showorder>
+                       <showorder>3</showorder>
                        <icon>fa-plus</icon>
                </acpmenuitem>
                <acpmenuitem name="wcf.acp.menu.link.maintenance.cache">
diff --git a/wcfsetup/install/files/acp/templates/systemCheck.tpl b/wcfsetup/install/files/acp/templates/systemCheck.tpl
new file mode 100644 (file)
index 0000000..4a2109d
--- /dev/null
@@ -0,0 +1,154 @@
+{include file='header'}
+
+<header class="contentHeader">
+       <h1 class="contentTitle">{lang}wcf.acp.systemCheck{/lang}</h1>
+</header>
+
+{event name='userNotice'}
+
+{capture assign='statusOk'}<span class="icon icon16 fa-check green"></span>{/capture}
+{capture assign='statusSufficient'}<span class="icon icon16 fa-exclamation-triangle orange"></span>{/capture}
+{capture assign='statusInsufficient'}<span class="icon icon16 fa-exclamation-triangle red"></span>{/capture}
+
+<section class="section">
+       <h2 class="sectionTitle">{lang}wcf.acp.systemCheck.result{/lang}</h2>
+       
+       <dl{if !$results[status][php]} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.php{/lang}</dt>
+               <dd>
+                       {if $results[status][php]}
+                               {if $results[php][version][result] === 'sufficient'}
+                                       {@$statusSufficient} {lang}wcf.acp.systemCheck.sufficient{/lang}
+                               {else}
+                                       {@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
+                               {/if}
+                       {else}
+                               {@$statusInsufficient} {lang}wcf.acp.systemCheck.insufficient{/lang}
+                       {/if}
+               </dd>
+       </dl>
+       
+       <dl{if !$results[status][mysql]} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.mysql{/lang}</dt>
+               <dd>
+                       {if $results[status][mysql]}
+                               {@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
+                       {else}
+                               {@$statusInsufficient} {lang}wcf.acp.systemCheck.insufficient{/lang}
+                       {/if}
+               </dd>
+       </dl>
+       
+       <dl{if !$results[status][directories]} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.directories{/lang}</dt>
+               <dd>
+                       {if $results[status][directories]}
+                               {@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
+                       {else}
+                               {@$statusInsufficient} {lang}wcf.acp.systemCheck.insufficient{/lang}
+                       {/if}
+               </dd>
+       </dl>
+</section>
+
+<section class="section">
+       <h2 class="sectionTitle">{lang}wcf.acp.systemCheck.php{/lang}</h2>
+       
+       <dl{if $results[php][version][result] === 'unsupported'} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.php.version{/lang}</dt>
+               <dd>
+                       {if $results[php][version][result] === 'recommended'}
+                               {@$statusOk}
+                       {elseif $results[php][version][result] === 'sufficient'}
+                               {@$statusSufficient}
+                       {else}
+                               {@$statusInsufficient}
+                       {/if}
+                       {$results[php][version][value]}
+                       <small>{lang}wcf.acp.systemCheck.php.version.description{/lang}</small>
+               </dd>
+       </dl>
+       
+       <dl{if !$results[php][extension]|empty} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.php.extension{/lang}</dt>
+               <dd>
+                       {if $results[php][extension]|empty}
+                               {@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
+                       {else}
+                               <ul class="nativeList">
+                                       {foreach from=$results[php][extension] item=extension}
+                                               <li>{@$statusInsufficient} <kbd>{$extension}</kbd></li>
+                                       {/foreach}
+                               </ul>
+                       {/if}
+                       <small>{lang}wcf.acp.systemCheck.php.extension.description{/lang}</small>
+               </dd>
+       </dl>
+       
+       <dl{if !$results[php][memoryLimit][result]} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.php.memoryLimit{/lang}</dt>
+               <dd>
+                       {if $results[php][memoryLimit][result]}{@$statusOk}{else}{@$statusInsufficient}{/if} {$results[php][memoryLimit][value]}
+                       <small>{lang}wcf.acp.systemCheck.php.memoryLimit.description{/lang}</small>
+               </dd>
+       </dl>
+       
+       <dl{if !$results[php][sha256]} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.php.sha256{/lang}</dt>
+               <dd>
+                       {if $results[php][sha256]}
+                               {@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
+                       {else}
+                               {@$statusInsufficient} {lang}wcf.acp.systemCheck.notSupported{/lang}
+                       {/if}
+                       <small>{lang}wcf.acp.systemCheck.php.sha256.description{/lang}</small>
+               </dd>
+       </dl>
+</section>
+
+<section class="section">
+       <h2 class="sectionTitle">{lang}wcf.acp.systemCheck.mysql{/lang}</h2>
+       
+       <dl{if !$results[mysql][result]} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.mysql.version{/lang}</dt>
+               <dd>
+                       {if $results[mysql][result]}{@$statusOk}{else}{@$statusInsufficient}{/if}
+                       {if $results[mysql][mariadb]}MariaDB{else}MySQL{/if} {$results[mysql][version]}
+                       <small>{lang}wcf.acp.systemCheck.mysql.version.description{/lang}</small>
+               </dd>
+       </dl>
+       
+       <dl{if !$results[mysql][innodb]} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.mysql.innodb{/lang}</dt>
+               <dd>
+                       {if $results[mysql][innodb]}
+                               {@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
+                       {else}
+                               {@$statusInsufficient} {lang}wcf.acp.systemCheck.notSupported{/lang}
+                       {/if}
+                       <small>{lang}wcf.acp.systemCheck.mysql.innodb.description{/lang}</small>
+               </dd>
+       </dl>
+</section>
+
+<section class="section">
+       <h2 class="sectionTitle">{lang}wcf.acp.systemCheck.directories{/lang}</h2>
+       
+       <dl{if !$results[directories]|empty} class="formError"{/if}>
+               <dt>{lang}wcf.acp.systemCheck.directories.writable{/lang}</dt>
+               <dd>
+                       {if $results[directories]|empty}
+                               {@$statusOk} {lang}wcf.acp.systemCheck.pass{/lang}
+                       {else}
+                               <ul class="nativeList">
+                                       {foreach from=$results[directories] item=directory}
+                                               <li>{@$statusInsufficient} <kbd>{$directory}</kbd></li>
+                                       {/foreach}
+                               </ul>
+                       {/if}
+                       <small>{lang}wcf.acp.systemCheck.directories.writable.description{/lang}</small>
+               </dd>
+       </dl>
+</section>
+
+{include file='footer'}
diff --git a/wcfsetup/install/files/lib/acp/page/SystemCheckPage.class.php b/wcfsetup/install/files/lib/acp/page/SystemCheckPage.class.php
new file mode 100644 (file)
index 0000000..820e812
--- /dev/null
@@ -0,0 +1,328 @@
+<?php
+namespace wcf\acp\page;
+use wcf\data\application\Application;
+use wcf\page\AbstractPage;
+use wcf\system\exception\SystemException;
+use wcf\system\WCF;
+use wcf\util\FileUtil;
+
+/**
+ * Shows the style list page.
+ *
+ * @author      Alexander Ebert
+ * @copyright   2001-2019 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     WoltLabSuite\Core\Acp\Page
+ * @since       5.2
+ */
+class SystemCheckPage extends AbstractPage {
+       /**
+        * @inheritDoc
+        */
+       public $activeMenuItem = 'wcf.acp.menu.link.systemCheck';
+       
+       /**
+        * @inheritDoc
+        */
+       public $neededPermissions = ['admin.configuration.package.canInstallPackage'];
+       
+       /**
+        * A list of directories that need to be writable at all times, grouped by their application
+        * identifier. Only the directory itself is checked, unless the path ends with `/*`.
+        * @var string[][]
+        */
+       public $directories = [
+               'wcf' => [
+                       '/',
+                       '/acp/style',
+                       '/acp/templates/compiled',
+                       '/attachments',
+                       '/cache',
+                       '/images/*',
+                       '/language',
+                       '/log',
+                       '/media_files/*',
+                       '/sitemaps',
+                       '/style',
+                       '/templates/compiled',
+                       '/tmp',
+               ],
+       ];
+       
+       public $mysqlVersions = [
+               'mysql' => '5.5.35',
+               'mariadb' => [
+                       // MariaDB 5.5.47+ or 10.0.22+ are required
+                       // https://jira.mariadb.org/browse/MDEV-8756
+                       '5' => '5.5.47',
+                       '10' => '10.0.22',
+               ],
+       ];
+       
+       public $phpExtensions = [
+               'mbstring',
+               'libxml',
+               'dom',
+               'zlib',
+               'pdo',
+               'pdo_mysql',
+               'json',
+               'pcre',
+               'gd',
+               'hash',
+       ];
+       
+       public $phpMemoryLimit = 128;
+       
+       public $phpVersions = [
+               'minimum' => '7.0.22',
+               'sufficient' => ['7.0'],
+               'recommended' => ['7.1', '7.2', '7.3'],
+       ];
+       
+       public $results = [
+               'directories' => [],
+               'mysql' => [
+                       'innodb' => false,
+                       'mariadb' => false,
+                       'result' => false,
+                       'version' => '0.0.0',
+               ],
+               'php' => [
+                       'extension' => [],
+                       'memoryLimit' => [
+                               'required' => '0',
+                               'result' => false,
+                               'value' => '0',
+                       ],
+                       'sha256' => false,
+                       'version' => [
+                               'result' => 'unsupported',
+                               'value' => '0.0.0',
+                       ],
+               ],
+               'status' => [
+                       'directories' => false,
+                       'mysql' => false,
+                       'php' => false,
+               ],
+       ];
+       
+       /**
+        * @inheritDoc
+        */
+       public function readData() {
+               parent::readData();
+               
+               if (IMAGE_ADAPTER_TYPE === 'imagick' && !in_array('imagick', $this->phpExtensions)) {
+                       $this->phpExtensions[] = 'imagick';
+               }
+               
+               $this->validateMysql();
+               $this->validatePhpExtensions();
+               $this->validatePhpMemoryLimit();
+               $this->validatePhpVersion();
+               $this->validateWritableDirectories();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+               
+               WCF::getTPL()->assign([
+                       'mysqlVersions' => $this->mysqlVersions,
+                       'phpExtensions' => $this->phpExtensions,
+                       'phpMemoryLimit' => $this->phpMemoryLimit,
+                       'phpVersions' => $this->phpVersions,
+                       'results' => $this->results,
+               ]);
+       }
+       
+       protected function validateMysql() {
+               // check sql version
+               $sqlVersion = WCF::getDB()->getVersion();
+               $compareSQLVersion = preg_replace('/^(\d+\.\d+\.\d+).*$/', '\\1', $sqlVersion);
+               // Do not use the "raw" version, it usually contains a lot of noise.
+               $this->results['mysql']['version'] = $compareSQLVersion;
+               if (stripos($sqlVersion, 'MariaDB') !== false) {
+                       $this->results['mysql']['mariadb'] = true;
+                       
+                       // MariaDB has some legacy version that use the major version '5'.
+                       if ($compareSQLVersion[0] === '5') {
+                               $this->results['mysql']['result'] = (version_compare($compareSQLVersion, $this->mysqlVersions['mariadb']['5']) >= 0);
+                       }
+                       else {
+                               $this->results['mysql']['result'] = (version_compare($compareSQLVersion, $this->mysqlVersions['mariadb']['10']) >= 0);
+                       }
+               }
+               else if (version_compare($compareSQLVersion, $this->mysqlVersions['mysql']) >= 0) {
+                       $this->results['mysql']['result'] = true;
+               }
+               
+               // check innodb support
+               $sql = "SHOW ENGINES";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute();
+               while ($row = $statement->fetchArray()) {
+                       if ($row['Engine'] == 'InnoDB' && in_array($row['Support'], ['DEFAULT', 'YES'])) {
+                               $this->results['mysql']['innodb'] = true;
+                               break;
+                       }
+               }
+               
+               if ($this->results['mysql']['result'] && $this->results['mysql']['innodb']) {
+                       $this->results['status']['mysql'] = true;
+               }
+       }
+       
+       protected function validatePhpExtensions() {
+               foreach ($this->phpExtensions as $phpExtension) {
+                       $result = extension_loaded($phpExtension);
+                       if (!$result) {
+                               $this->results['php']['extension'][] = $phpExtension;
+                       }
+               }
+               
+               if (extension_loaded('hash')) {
+                       $this->results['php']['sha256'] = in_array('sha256', hash_algos());
+               }
+               
+               $this->results['status']['php'] = empty($this->results['php']['extension']) && $this->results['php']['sha256'];
+       }
+       
+       protected function validatePhpMemoryLimit() {
+               $this->results['php']['memoryLimit']['required'] = $this->phpMemoryLimit . 'M';
+               
+               $memoryLimit = ini_get('memory_limit');
+               
+               // Memory is not limited through PHP.
+               if ($memoryLimit == -1) {
+                       $this->results['php']['memoryLimit']['value'] = '∞';
+                       $this->results['php']['memoryLimit']['result'] = true;
+               }
+               else {
+                       // Completely numeric, PHP assumes this to be a value in bytes.
+                       if (is_numeric($memoryLimit)) {
+                               $memoryLimit = $memoryLimit / 1024 / 1024;
+                               
+                               $this->results['php']['memoryLimit']['value'] = $memoryLimit . 'M';
+                               $this->results['php']['memoryLimit']['result'] = ($memoryLimit >= $this->phpMemoryLimit);
+                       }
+                       else {
+                               // PHP supports the 'K', 'M' and 'G' shorthand notations.
+                               if (preg_match('~^(\d+)([KMG])$~', $memoryLimit, $matches)) {
+                                       switch ($matches[2]) {
+                                               case 'K':
+                                                       $memoryLimit = $matches[1] * 1024;
+                                                       
+                                                       $this->results['php']['memoryLimit']['value'] = $memoryLimit . 'M';
+                                                       $this->results['php']['memoryLimit']['result'] = ($memoryLimit >= $this->phpMemoryLimit);
+                                                       break;
+                                               
+                                               case 'M':
+                                                       $this->results['php']['memoryLimit']['value'] = $memoryLimit;
+                                                       $this->results['php']['memoryLimit']['result'] = ($matches[1] >= $this->phpMemoryLimit);
+                                                       break;
+                                               
+                                               case 'G':
+                                                       $this->results['php']['memoryLimit']['value'] = $memoryLimit;
+                                                       $this->results['php']['memoryLimit']['result'] = ($matches[1] * 1024 >= $this->phpMemoryLimit);
+                                                       break;
+                                                       
+                                               default:
+                                                       $this->results['php']['memoryLimit']['value'] = $memoryLimit;
+                                                       $this->results['php']['memoryLimit']['result'] = false;
+                                                       return;
+                                       }
+                               }
+                       }
+               }
+               
+               $this->results['status']['php'] = $this->results['status']['php'] && $this->results['php']['memoryLimit']['result'];
+       }
+       
+       protected function validatePhpVersion() {
+               $phpVersion = phpversion();
+               $comparePhpVersion = preg_replace('/^(\d+\.\d+\.\d+).*$/', '\\1', $phpVersion);
+               // Do not use the "raw" version, it usually contains a lot of noise.
+               $this->results['php']['version']['value'] = $comparePhpVersion;
+               if (version_compare($comparePhpVersion, $this->phpVersions['minimum']) >= 0) {
+                       $majorMinor = preg_replace('~^(\d+\.\d+).*$~', '\\1', $phpVersion);
+                       foreach (['recommended', 'sufficient'] as $type) {
+                               foreach ($this->phpVersions[$type] as $version) {
+                                       if ($majorMinor === $version) {
+                                               $this->results['php']['version']['result'] = $type;
+                                               break 2;
+                                       }
+                               }
+                       }
+               }
+               else {
+                       $this->results['php']['version']['result'] = 'unsupported';
+               }
+               
+               $this->results['status']['php'] = $this->results['status']['php'] && ($this->results['php']['version']['result'] !== 'unsupported');
+       }
+       
+       protected function validateWritableDirectories() {
+               foreach ($this->directories as $abbreviation => $directories) {
+                       $basePath = Application::getDirectory($abbreviation);
+                       foreach ($directories as $directory) {
+                               $recursive = false;
+                               if (preg_match('~(.*)/\*$~', $directory, $matches)) {
+                                       $recursive = true;
+                                       $directory = $matches[1];
+                               }
+                               
+                               $path = $basePath . FileUtil::removeLeadingSlash(FileUtil::addTrailingSlash($directory));
+                               if ($this->checkDirectory($path) && $recursive) {
+                                       $rdi = new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS);
+                                       $it = new \RecursiveIteratorIterator($rdi, \RecursiveIteratorIterator::SELF_FIRST);
+                                       /** @var \SplFileInfo $item */
+                                       foreach ($it as $item) {
+                                               if ($item->isDir()) {
+                                                       $this->makeDirectoryWritable($item->getPathname());
+                                               }
+                                       }
+                               }
+                       }
+               }
+               
+               $this->results['status']['directories'] = empty($this->results['directories']);
+       }
+       
+       protected function checkDirectory($path) {
+               if (!$this->createDirectoryIfNotExists($path)) {
+                       $this->results['directories'][] = FileUtil::unifyDirSeparator($path);
+                       return false;
+               }
+               
+               return $this->makeDirectoryWritable($path);
+       }
+       
+       protected function createDirectoryIfNotExists($path) {
+               if (!file_exists($path) && !FileUtil::makePath($path)) {
+                       // FileUtil::makePath() returns false if either the directory cannot be created
+                       // or if it cannot be made writable.
+                       if (!file_exists($path)) {
+                               return false;
+                       }
+               }
+               
+               return true;
+       }
+       
+       protected function makeDirectoryWritable($path) {
+               try {
+                       FileUtil::makeWritable($path);
+               }
+               catch (SystemException $e) {
+                       $this->results['directories'][] = FileUtil::unifyDirSeparator($path);
+                       return false;
+               }
+               
+               return true;
+       }
+}
index cbb82e13a901214050d558f5f852b4c9ae465f77..914dec1bc820a63788ef4a9ad44fd27478715a77 100644 (file)
@@ -4,13 +4,13 @@ use wcf\data\user\group\UserGroup;
 use wcf\system\WCF;
 
 /**
- * Parses the [user] bbcode tag.
+ * Parses the [group] bbcode tag.
  *
  * @author      Alexander Ebert
  * @copyright   2001-2019 WoltLab GmbH
  * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @package     WoltLabSuite\Core\System\Bbcode
- * @since       5.0
+ * @since       5.2
  */
 class GroupBBCode extends AbstractBBCode {
        /**
index 0774c2557235036c2ac58ec7a7409d26a776bba5..017ad5735f32a49c53ccf0ca4155d45589dfec8a 100644 (file)
                <item name="wcf.acp.menu.link.reactionType.add"><![CDATA[Reaktions-Typ hinzufügen]]></item>
                <item name="wcf.acp.menu.link.reactionType.edit"><![CDATA[Reaktions-Typ bearbeiten]]></item>
                <item name="wcf.acp.menu.link.language.item.add"><![CDATA[Text hinzufügen]]></item>
+               <item name="wcf.acp.menu.link.systemCheck"><![CDATA[Systemüberprüfung]]></item>
        </category>
        <category name="wcf.acp.modificationLog">
                <item name="wcf.acp.modificationLog.list"><![CDATA[Globales Veränderungsprotokoll]]></item>
@@ -2600,6 +2601,31 @@ Kein Abschnitt darf leer sein und alle Abschnitten dürfen nur folgende Zeichen
                <item name="wcf.acp.stat.category.com.woltlab.wcf.user"><![CDATA[Benutzer-Daten]]></item>
                <item name="wcf.acp.stat.noData"><![CDATA[Es liegen keine Daten für den ausgewählten Zeitraum vor.]]></item>
        </category>
+       <category name="wcf.acp.systemCheck">
+               <item name="wcf.acp.systemCheck"><![CDATA[Systemüberprüfung]]></item>
+               <item name="wcf.acp.systemCheck.result"><![CDATA[Resultat]]></item>
+               <item name="wcf.acp.systemCheck.pass"><![CDATA[Test bestanden]]></item>
+               <item name="wcf.acp.systemCheck.sufficient"><![CDATA[Ausreichend]]></item>
+               <item name="wcf.acp.systemCheck.insufficient"><![CDATA[Anforderungen nicht erfüllt]]></item>
+               <item name="wcf.acp.systemCheck.notSupported"><![CDATA[Nicht unterstützt]]></item>
+               <item name="wcf.acp.systemCheck.php"><![CDATA[PHP]]></item>
+               <item name="wcf.acp.systemCheck.mysql"><![CDATA[MySQL]]></item>
+               <item name="wcf.acp.systemCheck.directories"><![CDATA[Dateisystem]]></item>
+               <item name="wcf.acp.systemCheck.php.version"><![CDATA[Version]]></item>
+               <item name="wcf.acp.systemCheck.php.version.description"><![CDATA[Es wird mindestens PHP {$phpVersions[minimum]} benötigt, empfohlen werden die folgenden Versionsreihen von PHP: {implode from=$phpVersions[recommended] item=$recommended}{$recommended}{/implode}.]]></item>
+               <item name="wcf.acp.systemCheck.mysql.version"><![CDATA[MySQL]]></item>
+               <item name="wcf.acp.systemCheck.mysql.version.description"><![CDATA[Es wird mindestens MySQL {$mysqlVersions[mysql]} bzw. MariaDB {$mysqlVersions[mariadb][5]} oder MariaDB {$mysqlVersions[mariadb][10]} benötigt, grundsätzlich wird der Einsatz neuerer Version ausdrücklich empfohlen.]]></item>
+               <item name="wcf.acp.systemCheck.php.extension"><![CDATA[Erweiterungen]]></item>
+               <item name="wcf.acp.systemCheck.php.extension.description"><![CDATA[Die offiziellen Erweiterungen von PHP stellen zusätzliche Funktionen bereit, die für den Betrieb der Software notwendig sind.]]></item>
+               <item name="wcf.acp.systemCheck.php.memoryLimit"><![CDATA[Verfügbarer Arbeitsspeicher („memory_limit“)]]></item>
+               <item name="wcf.acp.systemCheck.php.memoryLimit.description"><![CDATA[Einige Prozesse benötigen in Spitzenzeiten eine große Menge Arbeitsspeicher, für den einwandfreien Betrieb werden mindestens {$phpMemoryLimit} MB benötigt.]]></item>
+               <item name="wcf.acp.systemCheck.php.sha256"><![CDATA[Unterstützung für SHA-256]]></item>
+               <item name="wcf.acp.systemCheck.php.sha256.description"><![CDATA[Die kryptographische Funktion SHA-256 wird für den sicheren Betrieb der Software benötigt.]]></item>
+               <item name="wcf.acp.systemCheck.mysql.innodb"><![CDATA[InnoDB verfügbar]]></item>
+               <item name="wcf.acp.systemCheck.mysql.innodb.description"><![CDATA[InnoDB (XtraDB unter MariaDB) ist eine transaktionale Storage-Engine von MySQL und wird zwingend für den Betrieb benötigt. Dies ist eine Standardfunktion von MySQL.]]></item>
+               <item name="wcf.acp.systemCheck.directories.writable"><![CDATA[Beschreibbare Verzeichnisse]]></item>
+               <item name="wcf.acp.systemCheck.directories.writable.description"><![CDATA[Einige Verzeichnisse werden zur Laufzeit durch die Software beschrieben, der PHP-Benutzer muss Schreibrechte haben.]]></item>
+       </category>
        <category name="wcf.acp.updateServer">
                <item name="wcf.acp.updateServer.add"><![CDATA[Server hinzufügen]]></item>
                <item name="wcf.acp.updateServer.delete.sure"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} den Server <span class="confirmationObject">{$updateServer->serverURL}</span> wirklich löschen?]]></item>
index b12509be152fc45c66d1be305682505cef33e01e..6f4d92d0925b1ba9372f4403fd4e7e4ad18ac7f0 100644 (file)
                <item name="wcf.acp.menu.link.reactionType.add"><![CDATA[Add Reaction Type]]></item>
                <item name="wcf.acp.menu.link.reactionType.edit"><![CDATA[Edit Reaction Type]]></item>
                <item name="wcf.acp.menu.link.language.item.add"><![CDATA[Add Phrase]]></item>
+               <item name="wcf.acp.menu.link.systemCheck"><![CDATA[System Check]]></item>
        </category>
        <category name="wcf.acp.modificationLog">
                <item name="wcf.acp.modificationLog.list"><![CDATA[Global Modification Log]]></item>
@@ -2528,6 +2529,31 @@ If you have <strong>already bought the licenses for the listed apps</strong>, th
                <item name="wcf.acp.stat.category.com.woltlab.wcf.user"><![CDATA[User Data]]></item>
                <item name="wcf.acp.stat.noData"><![CDATA[There is no data available for the set period.]]></item>
        </category>
+       <category name="wcf.acp.systemCheck">
+               <item name="wcf.acp.systemCheck"><![CDATA[System Check]]></item>
+               <item name="wcf.acp.systemCheck.result"><![CDATA[Result]]></item>
+               <item name="wcf.acp.systemCheck.pass"><![CDATA[Test passed]]></item>
+               <item name="wcf.acp.systemCheck.sufficient"><![CDATA[Sufficient]]></item>
+               <item name="wcf.acp.systemCheck.insufficient"><![CDATA[Requirements are not met]]></item>
+               <item name="wcf.acp.systemCheck.notSupported"><![CDATA[Not supported]]></item>
+               <item name="wcf.acp.systemCheck.php"><![CDATA[PHP]]></item>
+               <item name="wcf.acp.systemCheck.mysql"><![CDATA[MySQL]]></item>
+               <item name="wcf.acp.systemCheck.directories"><![CDATA[Filesystem]]></item>
+               <item name="wcf.acp.systemCheck.php.version"><![CDATA[Version]]></item>
+               <item name="wcf.acp.systemCheck.php.version.description"><![CDATA[PHP {$phpVersions[minimum]} or newer is required, the following PHP series are strongly recommended: {implode from=$phpVersions[recommended] item=$recommended}{$recommended}{/implode}.]]></item>
+               <item name="wcf.acp.systemCheck.mysql.version"><![CDATA[MySQL]]></item>
+               <item name="wcf.acp.systemCheck.mysql.version.description"><![CDATA[MySQL {$mysqlVersions[mysql]} or MariaDB {$mysqlVersions[mariadb][5]} or MariaDB {$mysqlVersions[mariadb][10]} are required at minimum, but it’s strongly recommended to always use the latest available version.]]></item>
+               <item name="wcf.acp.systemCheck.php.extension"><![CDATA[Extensions]]></item>
+               <item name="wcf.acp.systemCheck.php.extension.description"><![CDATA[The official PHP extensions provide additional capabilities and features that are required for the software to work.]]></item>
+               <item name="wcf.acp.systemCheck.php.memoryLimit"><![CDATA[Available Memory (“memory_limit”)]]></item>
+               <item name="wcf.acp.systemCheck.php.memoryLimit.description"><![CDATA[Processes may require large amounts of memory at peak, requiring a limit of at least {$phpMemoryLimit} MB.]]></item>
+               <item name="wcf.acp.systemCheck.php.sha256"><![CDATA[Support for SHA-256]]></item>
+               <item name="wcf.acp.systemCheck.php.sha256.description"><![CDATA[The cryptographic function SHA-256 is required for a safe and secure operation of the software.]]></item>
+               <item name="wcf.acp.systemCheck.mysql.innodb"><![CDATA[InnoDB available]]></item>
+               <item name="wcf.acp.systemCheck.mysql.innodb.description"><![CDATA[InnoDB (XtraDB when using MariaDB) is a transaction storage engine for MySQL and is required for the operation of the software. This is a built-in feature of MySQL.]]></item>
+               <item name="wcf.acp.systemCheck.directories.writable"><![CDATA[Writable Directories]]></item>
+               <item name="wcf.acp.systemCheck.directories.writable.description"><![CDATA[Some directories are being written to during the normal operation. The user that runs the PHP process must have write access.]]></item>
+       </category>
        <category name="wcf.acp.updateServer">
                <item name="wcf.acp.updateServer.add"><![CDATA[Add Server]]></item>
                <item name="wcf.acp.updateServer.delete.sure"><![CDATA[Do you really want to delete the server <span class="confirmationObject">{$updateServer->serverURL}</span>?]]></item>