{/hascontent}
</header>
-<div class="section tabularBox">
- <table class="table">
- <thead>
- <tr>
- <th class="columnID columnPackageID" colspan="2">{lang}wcf.global.objectID{/lang}</th>
- <th class="columnText columnPackageName">{lang}wcf.acp.package.name{/lang}</th>
- <th class="columnText columnDomainName">{lang}wcf.acp.application.domainName{/lang}</th>
- <th class="columnText columnDomainPath">{lang}wcf.acp.application.domainPath{/lang}</th>
- <th class="columnText columnLandingPageID">{lang}wcf.acp.application.landingPage{/lang}</th>
+{if $isMultiDomainSetup}
+ <div class="warning">{lang}wcf.acp.application.multiDomain{/lang}</div>
+{/if}
+
+<form method="post" action="{link controller='ApplicationManagement'}{/link}">
+ {if !$isMultiDomainSetup}
+ <section class="section">
+ <h2 class="sectionTitle">{lang}wcf.acp.application.management.domain{/lang}</h2>
+
+ <dl{if $errorField == 'domainName'} class="formError"{/if}>
+ <dt><label for="domainName">{lang}wcf.acp.application.management.domainName{/lang}</label></dt>
+ <dd>
+ <div class="inputAddon">
+ <span class="inputPrefix">http(s)://</span>
+ <input type="text" name="domainName" id="domainName" value="{$domainName}" class="long">
+ </div>
+ {if $errorField == 'domainName'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.application.management.domainName.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ <small>{lang}wcf.acp.application.management.domainName.description{/lang}</small>
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'cookieDomain'} class="formError"{/if}>
+ <dt><label for="cookieDomain">{lang}wcf.acp.application.management.cookieDomain{/lang}</label></dt>
+ <dd>
+ <input type="text" name="cookieDomain" id="cookieDomain" value="{$cookieDomain}" class="long">
+ {if $errorField == 'cookieDomain'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.application.management.cookieDomain.error.{$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ <small>{lang}wcf.acp.application.management.cookieDomain.description{/lang}</small>
+ </dd>
+ </dl>
+ </section>
+
+ <script>
+ (() => {
+ const domainName = document.getElementById("domainName");
+ const cookieDomain = document.getElementById("cookieDomain");
+
+ // Keep the cookie domain in sync if it was previously identical.
+ if (domainName.value === cookieDomain.value) {
+ domainName.addEventListener("input", () => {
+ cookieDomain.value = domainName.value;
+ });
+ }
+ })();
+ </script>
+ {/if}
+
+ <section class="section">
+ <h2 class="sectionTitle">{lang}wcf.acp.application.landingPage{/lang}</h2>
+
+ <div class="tabularBox">
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnID columnPackageID">{lang}wcf.global.objectID{/lang}</th>
+ <th class="columnText columnPackageName">{lang}wcf.acp.package.name{/lang}</th>
+ <th class="columnText columnLandingPageID">{lang}wcf.acp.application.landingPage{/lang}</th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
- {event name='columnHeads'}
- </tr>
- </thead>
-
- <tbody>
- {foreach from=$applicationList item=application}
- <tr>
- <td class="columnIcon"><a href="{link controller='ApplicationEdit' id=$application->packageID}{/link}" class="jsTooltip" title="{lang}wcf.global.button.edit{/lang}"><span class="icon icon16 fa-pencil"></span></a></td>
- <td class="columnID columnPackageID">{#$application->packageID}</td>
- <td class="columnTitle columnPackageName"><a href="{link controller='ApplicationEdit' id=$application->packageID}{/link}">{$application->getPackage()}</a></td>
- <td class="columnText columnDomainName">{$application->domainName}</td>
- <td class="columnText columnDomainPath">{$application->domainPath}</td>
- <td class="columnText columnLandingPageID">
- {if $application->landingPageID && $pageList[$application->landingPageID]|isset}
- {$pageList[$application->landingPageID]}
- {else}
- {lang}wcf.global.noSelection{/lang}
- {/if}
- </td>
-
- {event name='columns'}
- </tr>
- {/foreach}
- </tbody>
- </table>
-</div>
+ <tbody>
+ {foreach from=$applicationList item=application}
+ <tr>
+ <td class="columnID columnPackageID">{#$application->packageID}</td>
+ <td class="columnTitle columnPackageName">
+ <p><strong>{$application->getPackage()}</strong></p>
+ <small>{$application->getPageURL()}</small>
+ </td>
+ <td class="columnText columnLandingPageID">
+ <select name="landingPageID[{$application->packageID}]">
+ <option value="0">{lang}wcf.acp.application.landingPage.default{/lang}</option>
+
+ {foreach from=$pageNodeList item=pageNode}
+ {if !$pageNode->isDisabled && !$pageNode->requireObjectID && !$pageNode->excludeFromLandingPage}
+ <option value="{@$pageNode->pageID}"{if $pageNode->pageID == $application->landingPageID} selected{/if} data-identifier="{@$pageNode->identifier}">{if $pageNode->getDepth() > 1}{@" "|str_repeat:($pageNode->getDepth() - 1)}{/if}{$pageNode->name}</option>
+ {/if}
+ {/foreach}
+ </select>
+ </td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ </div>
+ </section>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
+ {csrfToken}
+ </div>
+</form>
<footer class="contentFooter">
{hascontent}
use wcf\system\Regex;
use wcf\system\WCF;
use wcf\util\FileUtil;
+use wcf\util\HeaderUtil;
use wcf\util\StringUtil;
/**
{
parent::readParameters();
+ if (!ApplicationHandler::getInstance()->isMultiDomainSetup()) {
+ throw new IllegalLinkException();
+ }
+
if (isset($_REQUEST['id'])) {
$this->packageID = \intval($_REQUEST['id']);
}
--- /dev/null
+<?php
+
+namespace wcf\acp\form;
+
+use Stripe\Exception\PermissionException;
+use wcf\data\application\ViewableApplicationList;
+use wcf\data\page\Page;
+use wcf\data\page\PageList;
+use wcf\data\page\PageNodeTree;
+use wcf\form\AbstractForm;
+use wcf\system\application\ApplicationHandler;
+use wcf\system\cache\builder\ApplicationCacheBuilder;
+use wcf\system\cache\builder\RoutingCacheBuilder;
+use wcf\system\exception\UserInputException;
+use wcf\system\Regex;
+use wcf\system\WCF;
+use wcf\util\ArrayUtil;
+use wcf\util\FileUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Shows the application management form.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Acp\Form
+ */
+class ApplicationManagementForm extends AbstractForm
+{
+ /**
+ * @inheritDoc
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.application.management';
+
+ /**
+ * list of applications
+ * @var ViewableApplicationList
+ */
+ public $applicationList;
+
+ /**
+ * @var string
+ */
+ public $cookieDomain = '';
+
+ /**
+ * @var string
+ */
+ public $domainName = '';
+
+ /**
+ * @var int[]
+ */
+ public $landingPageID = [];
+
+ /**
+ * @inheritDoc
+ */
+ public $neededPermissions = ['admin.configuration.canManageApplication'];
+
+ /**
+ * nested list of page nodes
+ * @var \RecursiveIteratorIterator
+ */
+ public $pageNodeList;
+
+ /**
+ * @inheritDoc
+ */
+ public function readParameters()
+ {
+ parent::readParameters();
+
+ $this->pageNodeList = (new PageNodeTree())->getNodeList();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readFormParameters()
+ {
+ parent::readFormParameters();
+
+ if (isset($_POST['cookieDomain'])) $this->cookieDomain = StringUtil::trim($_POST['cookieDomain']);
+ if (isset($_POST['domainName'])) $this->domainName = StringUtil::trim($_POST['domainName']);
+ if (isset($_POST['landingPageID']) && \is_array($_POST['landingPageID'])) $this->landingPageID = ArrayUtil::toIntegerArray($_POST['landingPageID']);
+ }
+
+ public function validate()
+ {
+ parent::validate();
+
+ if (ApplicationHandler::getInstance()->isMultiDomainSetup()) {
+ // Changes to the domain for all apps are only possible for setups using the same domain.
+ if (!empty($this->cookieDomain) || !empty($this->domainName)) {
+ throw new PermissionException();
+ }
+ }
+
+ if (empty($this->domainName)) {
+ throw new UserInputException('domainName');
+ } else {
+ $regex = new Regex('^https?\://');
+ $this->domainName = FileUtil::removeTrailingSlash($regex->replace($this->domainName, ''));
+ $this->cookieDomain = FileUtil::removeTrailingSlash($regex->replace($this->cookieDomain, ''));
+
+ // domain may not contain path components
+ $regex = new Regex('[/#\?&]');
+ if ($regex->match($this->domainName)) {
+ throw new UserInputException('domainName', 'containsPath');
+ } elseif ($regex->match($this->cookieDomain)) {
+ throw new UserInputException('cookieDomain', 'containsPath');
+ }
+
+ // strip port from cookie domain
+ $regex = new Regex(':[0-9]+$');
+ $this->cookieDomain = $regex->replace($this->cookieDomain, '');
+
+ // check if cookie domain shares the same domain (may exclude subdomains)
+ if (!StringUtil::endsWith($regex->replace($this->domainName, ''), $this->cookieDomain)) {
+ throw new UserInputException('cookieDomain', 'invalid');
+ }
+ }
+
+ foreach ($this->landingPageID as $packageID => $landingPageID) {
+ if (!$landingPageID) {
+ continue;
+ }
+
+ $page = new Page($landingPageID);
+ if (!$page->pageID) {
+ throw new UserInputException('landingPageID');
+ } elseif ($page->requireObjectID || $page->excludeFromLandingPage || $page->isDisabled) {
+ throw new UserInputException('landingPageID', 'invalid');
+ }
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function readData()
+ {
+ parent::readData();
+
+ $this->applicationList = new ViewableApplicationList();
+ $this->applicationList->readObjects();
+
+ if (!ApplicationHandler::getInstance()->isMultiDomainSetup()) {
+ $core = ApplicationHandler::getInstance()->getApplicationByID(1);
+ $this->domainName = $core->domainName;
+ $this->cookieDomain = $core->cookieDomain;
+ }
+ }
+
+ public function save()
+ {
+ parent::save();
+
+ if (!ApplicationHandler::getInstance()->isMultiDomainSetup()) {
+ $sql = "UPDATE wcf" . WCF_N . "_application
+ SET domainName = ?,
+ cookieDomain = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([
+ $this->domainName,
+ $this->cookieDomain,
+ ]);
+ }
+
+ $sql = "UPDATE wcf" . WCF_N . "_application
+ SET landingPageID = ?
+ WHERE packageID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ foreach ($this->landingPageID as $packageID => $landingPageID) {
+ $statement->execute([
+ $landingPageID ?: null,
+ $packageID,
+ ]);
+ }
+
+ $this->saved();
+
+ if (!empty($this->landingPageID[1])) {
+ (new Page($this->landingPageID[1]))->setAsLandingPage();
+ } else {
+ $sql = "UPDATE wcf" . WCF_N . "_page
+ SET isLandingPage = ?
+ WHERE isLandingPage = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([
+ 0,
+ 1,
+ ]);
+ }
+
+ ApplicationHandler::rebuild();
+
+ // Reset caches to reflect the new landing pages.
+ ApplicationCacheBuilder::getInstance()->reset();
+ RoutingCacheBuilder::getInstance()->reset();
+
+ // Reload the applications to update the selected landing page id.
+ $this->applicationList = new ViewableApplicationList();
+ $this->applicationList->readObjects();
+
+ // show success message
+ WCF::getTPL()->assign('success', true);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function assignVariables()
+ {
+ parent::assignVariables();
+
+ $pageList = new PageList();
+ $pageList->readObjects();
+
+ WCF::getTPL()->assign([
+ 'applicationList' => $this->applicationList,
+ 'cookieDomain' => $this->cookieDomain,
+ 'domainName' => $this->domainName,
+ 'isMultiDomainSetup' => ApplicationHandler::getInstance()->isMultiDomainSetup(),
+ 'pageNodeList' => $this->pageNodeList,
+ 'pageList' => $pageList->getObjects(),
+ ]);
+ }
+}
+++ /dev/null
-<?php
-
-namespace wcf\acp\page;
-
-use wcf\data\application\ViewableApplicationList;
-use wcf\data\page\PageList;
-use wcf\page\AbstractPage;
-use wcf\system\WCF;
-
-/**
- * Shows the application management 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
- */
-class ApplicationManagementPage extends AbstractPage
-{
- /**
- * @inheritDoc
- */
- public $activeMenuItem = 'wcf.acp.menu.link.application.management';
-
- /**
- * list of applications
- * @var ViewableApplicationList
- */
- public $applicationList;
-
- /**
- * @inheritDoc
- */
- public $neededPermissions = ['admin.configuration.canManageApplication'];
-
- /**
- * @inheritDoc
- */
- public function readData()
- {
- parent::readData();
-
- $this->applicationList = new ViewableApplicationList();
- $this->applicationList->readObjects();
- }
-
- /**
- * @inheritDoc
- */
- public function assignVariables()
- {
- parent::assignVariables();
-
- $pageList = new PageList();
- $pageList->readObjects();
-
- WCF::getTPL()->assign([
- 'applicationList' => $this->applicationList,
- 'pageList' => $pageList->getObjects(),
- ]);
- }
-}
*
* @return bool
* @since 3.1
+ * @deprecated 5.4
*/
public function isMultiDomainSetup()
{
<item name="wcf.acp.application.edit"><![CDATA[App bearbeiten]]></item>
<item name="wcf.acp.application.edit.title"><![CDATA[App bearbeiten: „<a href="{link controller='Package' id=$application->packageID}{/link}">{$application->getPackage()->getName()}</a>“]]></item>
<item name="wcf.acp.application.landingPage"><![CDATA[Einstiegsseite]]></item>
+ <item name="wcf.acp.application.landingPage.default"><![CDATA[(Standard)]]></item>
<item name="wcf.acp.application.landingPage.description"><![CDATA[Optional: {if LANGUAGE_USE_INFORMAL_VARIANT}Gib{else}Geben Sie{/if} die Seite an, die angezeigt wird, wenn diese App direkt aufgerufen wird.]]></item>
<item name="wcf.acp.application.list"><![CDATA[Installierte Apps]]></item>
+ <item name="wcf.acp.application.multiDomain"><![CDATA[<p style="font-size: 18px">Die Unterstützung für Installationen mit mehreren Domains wird eingestellt.</p>
+<p><br></p>
+<p>Es wird dringend empfohlen die Apps auf eine gemeinsame Domain zu verschieben, bitte passen Sie dazu die Domain- und Pfadkonfiguration der installierten Apps an:</p>
+<ul class="nativeList">
+ {foreach from=$applicationList item=application}
+ <li><a href="{link controller='ApplicationEdit' id=$application->packageID}{/link}">{$application->getPackage()->getName()}</a></li>
+ {/foreach}
+</ul>]]></item>
+ <item name="wcf.acp.application.management.domain"><![CDATA[Domain-Konfiguration]]></item>
+ <item name="wcf.acp.application.management.domainName"><![CDATA[Domain]]></item>
+ <item name="wcf.acp.application.management.domainName.description"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Gib{else}Geben Sie{/if} den Domain-Namen ohne Protokoll und Pfad ein, beispielsweise „www.example.com“.]]></item>
+ <item name="wcf.acp.application.management.cookieDomain"><![CDATA[Cookie-Domain]]></item>
+ <item name="wcf.acp.application.management.cookieDomain.description"><![CDATA[Die Cookie-Domain muss zur eingegebenen Domain passen, die Eingabe erfolgt ohne Protokoll und Pfad. Sollten {if LANGUAGE_USE_INFORMAL_VARIANT}du dir{else}Sie sich{/if} unsicher sein, {if LANGUAGE_USE_INFORMAL_VARIANT}trage{else}tragen Sie{/if} hier bitte den identischen Wert wie für „Domain“ ein.]]></item>
</category>
<category name="wcf.acp.article">
<item name="wcf.acp.article.add"><![CDATA[Artikel hinzufügen]]></item>
<item name="wcf.acp.application.edit"><![CDATA[Edit Application]]></item>
<item name="wcf.acp.application.edit.title"><![CDATA[Edit Application: “<a href="{link controller='Package' id=$application->packageID}{/link}">{$application->getPackage()->getName()}</a>”]]></item>
<item name="wcf.acp.application.landingPage"><![CDATA[Entry Page]]></item>
+ <item name="wcf.acp.application.landingPage.default"><![CDATA[(Default)]]></item>
<item name="wcf.acp.application.landingPage.description"><![CDATA[Optional: This page will be shown when a user is navigating to this app.]]></item>
<item name="wcf.acp.application.list"><![CDATA[Installed Applications]]></item>
+ <item name="wcf.acp.application.multiDomain"><![CDATA[<p style="font-size: 18px">The support for setups with multiple domains is discontinued.</p>
+<p><br></p>
+<p>It is strongly recommended to consolidate all apps on a shared domain, please adjust the domain and path settings for these apps:</p>
+<ul class="nativeList">
+ {foreach from=$applicationList item=application}
+ <li><a href="{link controller='ApplicationEdit' id=$application->packageID}{/link}">{$application->getPackage()->getName()}</a></li>
+ {/foreach}
+</ul>]]></item>
+ <item name="wcf.acp.application.management.domain"><![CDATA[Domain Configuration]]></item>
+ <item name="wcf.acp.application.management.domainName"><![CDATA[Domain]]></item>
+ <item name="wcf.acp.application.management.domainName.description"><![CDATA[Provide the domain name excluding any protocol or path, for example “www.example.com”.]]></item>
+ <item name="wcf.acp.application.management.cookieDomain"><![CDATA[Cookie Domain]]></item>
+ <item name="wcf.acp.application.management.cookieDomain.description"><![CDATA[The cookie domain must match the provided domain. When in doubt simply provide the same value that you have entered for “Domain”.]]></item>
</category>
<category name="wcf.acp.article">
<item name="wcf.acp.article.add"><![CDATA[Add Article]]></item>