Add FirstTimeSetupLicenseForm
authorTim Düsterhus <duesterhus@woltlab.com>
Mon, 6 Mar 2023 14:16:33 +0000 (15:16 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 17 Apr 2023 16:45:10 +0000 (18:45 +0200)
wcfsetup/install/files/acp/templates/firstTimeSetupLicense.tpl [new file with mode: 0644]
wcfsetup/install/files/acp/templates/firstTimeSetupOptions.tpl
wcfsetup/install/files/lib/acp/action/FirstTimeSetupAction.class.php
wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/acp/form/FirstTimeSetupOptionsForm.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

diff --git a/wcfsetup/install/files/acp/templates/firstTimeSetupLicense.tpl b/wcfsetup/install/files/acp/templates/firstTimeSetupLicense.tpl
new file mode 100644 (file)
index 0000000..e3ae8d2
--- /dev/null
@@ -0,0 +1,11 @@
+{include file='header' pageTitle='wcf.acp.firstTimeSetup.license'}
+
+<header class="contentHeader">
+       <div class="contentHeaderTitle">
+               <h1 class="contentTitle">{lang}wcf.acp.firstTimeSetup.license{/lang}</h1>
+       </div>
+</header>
+
+{@$form->getHtml()}
+
+{include file='footer'}
index 77246f121560ec01193ee07dabdc0ceaf1228cd9..7cd371d8dd41fca57f74140a1f01657d138010d1 100644 (file)
@@ -1,4 +1,4 @@
-{include file='header' pageTitle='wcf.acp.option.firstTimeSetup'}
+{include file='header' pageTitle='wcf.acp.firstTimeSetup.options'}
 
 {event name='javascriptInclude'}
 
@@ -12,8 +12,8 @@
 
 <header class="contentHeader">
        <div class="contentHeaderTitle">
-               <h1 class="contentTitle">{lang}wcf.acp.option.firstTimeSetup{/lang}</h1>
-               <p class="contentHeaderDescription">{lang}wcf.acp.option.firstTimeSetup.description{/lang}</p>
+               <h1 class="contentTitle">{lang}wcf.acp.firstTimeSetup.options{/lang}</h1>
+               <p class="contentHeaderDescription">{lang}wcf.acp.firstTimeSetup.options.description{/lang}</p>
        </div>
 </header>
 
index b2fbb1bf4921f40a1fa65f4b8fa277d909092ae1..6a4467dded0a7316b03d9d5b1ec4e3ebbe1967d0 100644 (file)
@@ -6,6 +6,7 @@ use Laminas\Diactoros\Response\RedirectResponse;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\RequestHandlerInterface;
+use wcf\acp\form\FirstTimeSetupLicenseForm;
 use wcf\acp\form\FirstTimeSetupOptionsForm;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
@@ -26,7 +27,8 @@ final class FirstTimeSetupAction implements RequestHandlerInterface
         ]);
 
         $controller = match (\FIRST_TIME_SETUP_STATE) {
-            0 => FirstTimeSetupOptionsForm::class,
+            0 => FirstTimeSetupLicenseForm::class,
+            1 => FirstTimeSetupOptionsForm::class,
             default => FirstTimeSetupCompletedPage::class
         };
 
diff --git a/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php b/wcfsetup/install/files/lib/acp/form/FirstTimeSetupLicenseForm.class.php
new file mode 100644 (file)
index 0000000..5d94e78
--- /dev/null
@@ -0,0 +1,211 @@
+<?php
+
+namespace wcf\acp\form;
+
+use CuyZ\Valinor\Mapper\MappingError;
+use CuyZ\Valinor\Mapper\Source\Source;
+use CuyZ\Valinor\MapperBuilder;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Psr7\Request;
+use Psr\Http\Client\ClientExceptionInterface;
+use wcf\data\option\Option;
+use wcf\data\option\OptionAction;
+use wcf\data\package\update\server\PackageUpdateServerAction;
+use wcf\data\package\update\server\PackageUpdateServerList;
+use wcf\form\AbstractForm;
+use wcf\form\AbstractFormBuilderForm;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\form\builder\container\FormContainer;
+use wcf\system\form\builder\field\CheckboxFormField;
+use wcf\system\form\builder\field\dependency\EmptyFormFieldDependency;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\field\validation\FormFieldValidationError;
+use wcf\system\form\builder\field\validation\FormFieldValidator;
+use wcf\system\form\builder\LanguageItemFormNode;
+use wcf\system\io\HttpFactory;
+use wcf\system\request\LinkHandler;
+use wcf\util\HeaderUtil;
+
+/**
+ * Sets up license data during first time setup.
+ *
+ * @author      Tim Duesterhus
+ * @copyright   2001-2023 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+final class FirstTimeSetupLicenseForm extends AbstractFormBuilderForm
+{
+    /**
+     * @inheritDoc
+     */
+    public $neededPermissions = ['admin.configuration.package.canEditServer'];
+
+    private array $apiResponse;
+
+    /**
+     * @inheritDoc
+     */
+    public function readParameters()
+    {
+        parent::readParameters();
+
+        if (\FIRST_TIME_SETUP_STATE == -1) {
+            throw new PermissionDeniedException();
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function createForm()
+    {
+        parent::createForm();
+
+        $this->form->appendChildren([
+            LanguageItemFormNode::create('explanation')
+                ->languageItem('wcf.acp.firstTimeSetup.license.explanation'),
+            $credentialsContainer = FormContainer::create('credentials')
+                ->label('wcf.acp.firstTimeSetup.license.credentials')
+                ->appendChildren([
+                    TextFormField::create('licenseNo')
+                        ->label('wcf.acp.package.update.licenseNo')
+                        ->required()
+                        ->maximumLength(255)
+                        ->placeholder('123456'),
+                    TextFormField::create('serialNo')
+                        ->label('wcf.acp.package.update.serialNo')
+                        ->required()
+                        ->maximumLength(255)
+                        ->placeholder('AB12-34CD-E56F-12AB-7890')
+                        ->addValidator(new FormFieldValidator('serialNo', function (TextFormField $serialNo) {
+                            $licenseNo = $serialNo->getDocument()->getNodeById('licenseNo');
+                            \assert($licenseNo instanceof TextFormField);
+
+                            try {
+                                $this->apiResponse = $this->getLicenseData($licenseNo->getValue(), $serialNo->getValue());
+                            } catch (ConnectException) {
+                                $serialNo->addValidationError(new FormFieldValidationError(
+                                    'failedConnect',
+                                    'wcf.acp.firstTimeSetup.license.credentials.error.failedConnect'
+                                ));
+                            } catch (ClientExceptionInterface | MappingError) {
+                                $serialNo->addValidationError(new FormFieldValidationError(
+                                    'failedValidation',
+                                    'wcf.acp.firstTimeSetup.license.credentials.error.failedValidation'
+                                ));
+                            }
+                        })),
+                ]),
+            FormContainer::create('noCredentials')
+                ->label('wcf.acp.firstTimeSetup.license.noCredentials')
+                ->appendChildren([
+                    CheckboxFormField::create('noCredentialsConfirm')
+                        ->label('wcf.acp.firstTimeSetup.license.noCredentialsConfirm')
+                        ->description('wcf.acp.firstTimeSetup.license.noCredentialsConfirm.description'),
+                ]),
+        ]);
+
+        $credentialsContainer->addDependency(
+            EmptyFormFieldDependency::create('noCredentialsConfirm')
+                ->fieldId('noCredentialsConfirm')
+        );
+    }
+
+    private function getLicenseData(string $licenseNo, string $serialNo): array
+    {
+        $request = new Request(
+            'POST',
+            'https://api.woltlab.com/2.0/customer/license/list.json',
+            [
+                'content-type' => 'application/x-www-form-urlencoded',
+            ],
+            \http_build_query([
+                'licenseNo' => $licenseNo,
+                'serialNo' => $serialNo,
+                'instanceId' => \hash_hmac('sha256', 'api.woltlab.com', \WCF_UUID),
+            ], '', '&', \PHP_QUERY_RFC1738)
+        );
+
+        $response = HttpFactory::makeClientWithTimeout(5)->send($request);
+        return (new MapperBuilder())
+            ->allowSuperfluousKeys()
+            ->mapper()
+            ->map(
+                <<<'EOT'
+                    array {
+                        status: 200,
+                        license: array {
+                            authCode: string,
+                            type: string,
+                            expiryDates?: array<string, int>,
+                        },
+                        pluginstore: array<string, string>,
+                        woltlab: array<string, string>,
+                    }
+                    EOT,
+                Source::json($response->getBody())
+            );
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function save()
+    {
+        AbstractForm::save();
+
+        $data = $this->form->getData();
+
+        if (!$data['data']['noCredentialsConfirm']) {
+            $packageServerList = new PackageUpdateServerList();
+            $packageServerList->readObjects();
+
+            foreach ($packageServerList as $packageServer) {
+                if (
+                    !$packageServer->isWoltLabUpdateServer()
+                    && !$packageServer->isWoltLabStoreServer()
+                ) {
+                    continue;
+                }
+
+                $objectAction = new PackageUpdateServerAction(
+                    [$packageServer],
+                    'update',
+                    [
+                        'data' => [
+                            'loginUsername' => $data['data']['licenseNo'],
+                            'loginPassword' => $data['data']['serialNo'],
+                        ],
+                    ]
+                );
+                $objectAction->executeAction();
+            }
+        }
+
+        $optionData = [
+            Option::getOptionByName('first_time_setup_state')->optionID => 1,
+        ];
+
+        if (isset($this->apiResponse)) {
+            $optionData[Option::getOptionByName('package_server_auth_code')->optionID] = $this->apiResponse['license']['authCode'];
+        }
+
+        $objectAction = new OptionAction(
+            [],
+            'updateAll',
+            [
+                'data' => $optionData,
+            ]
+        );
+        $objectAction->executeAction();
+
+        $this->saved();
+
+        \http_response_code(303);
+        HeaderUtil::redirect(LinkHandler::getInstance()->getControllerLink(
+            FirstTimeSetupAction::class,
+        ));
+
+        exit;
+    }
+}
index c9fe17dd4e7a850dd0102155507d17e96eb27cf1..0d5231de97556f9694df82736624d380afe6b65a 100644 (file)
@@ -42,7 +42,6 @@ final class FirstTimeSetupOptionsForm extends AbstractOptionListForm
         'mail_from_address',
         'mail_admin_address',
         'module_contact_form',
-        'package_server_auth_code',
     ];
 
     /**
@@ -87,7 +86,7 @@ final class FirstTimeSetupOptionsForm extends AbstractOptionListForm
         parent::save();
 
         $saveOptions = $this->optionHandler->save('wcf.acp.option', 'wcf.acp.option.option');
-        $saveOptions[Option::getOptionByName('first_time_setup_state')->optionID] = 1;
+        $saveOptions[Option::getOptionByName('first_time_setup_state')->optionID] = 2;
         $this->objectAction = new OptionAction([], 'updateAll', ['data' => $saveOptions]);
         $this->objectAction->executeAction();
         $this->saved();
index 767afefb3aaf7b00165edd1c645432c22cb419c5..cec1d62836017da02f447e718f9b23c900f514dd 100644 (file)
@@ -685,6 +685,19 @@ Das Fehlerprotokoll ist sehr groß. Bitte prüfen Sie das Protokoll händisch: {
 ACHTUNG: Die oben genannten Meldungen sind stark gekürzt. Sie können Details zu einzelnen Fehlermeldungen in der Administrationsoberfläche unter „Verwaltung » Protokoll » Fehler“ einsehen.
 Sie erreichen das Fehlerprotokoll unter: {link controller='ExceptionLogView' isEmail=true isACP=true}{/link}]]></item>
        </category>
+       <category name="wcf.acp.firstTimeSetup">
+               <item name="wcf.acp.firstTimeSetup.license"><![CDATA[Erstmalige Einrichtung: Lizenz]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.credentials"><![CDATA[Lizenzdaten]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.credentials.error.failedConnect"><![CDATA[Es konnte keine ausgehende Verbindung zur Überprüfung der Lizenzdaten aufgebaut werden.]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.credentials.error.failedValidation"><![CDATA[Die Gültigkeit der Lizenzdaten konnte nicht bestätigt werden.]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.noCredentials"><![CDATA[Jetzt nicht?]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.noCredentialsConfirm"><![CDATA[Ohne Eingabe der Lizenzdaten fortfahren]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.explanation"><![CDATA[<p>Die Eingabe von Lizenzdaten ist zur Installation und zur Aktualisierung der gekauften WoltLab Suite™-Apps erforderlich.</p>]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.noCredentialsConfirm.description"><![CDATA[Die Eingabe der Lizenzdaten wird empfohlen. Falls noch keine Internetverbindung zu unseren Paket-Servern zur Verfügung steht oder Sie noch keine Lizenzdaten besitzen, können Sie die Eingabe überspringen. Lizenzdaten können jederzeit hinterlegt werden, um die gekauften WoltLab Suite™-Apps zu installieren.]]></item>
+               <item name="wcf.acp.firstTimeSetup.options"><![CDATA[Erstmalige Einrichtung: Einstellungen]]></item>
+               <item name="wcf.acp.firstTimeSetup.options.description"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Konfiguriere{else}Konfigurieren Sie{/if} die wichtigsten Einstellungen, diese können auch später über die Optionen verändert werden.]]></item>
+               <item name="wcf.acp.firstTimeSetup.completed"><![CDATA[Erstmalige Einrichtung abgeschlossen]]></item>
+       </category>
        <category name="wcf.acp.group">
                <item name="wcf.acp.group.add"><![CDATA[Benutzergruppe hinzufügen]]></item>
                <item name="wcf.acp.group.edit"><![CDATA[Benutzergruppe bearbeiten]]></item>
@@ -1676,8 +1689,6 @@ Als Benachrichtigungs-URL in der Konfiguration der sofortigen Zahlungsbestätigu
                <item name="wcf.acp.option.contact_form_enable_attachments"><![CDATA[Dateianhänge im Kontaktformular aktivieren]]></item>
                <item name="wcf.acp.option.contact_form_prune_attachments"><![CDATA[Dateianhänge automatisch entfernen]]></item>
                <item name="wcf.acp.option.contact_form_prune_attachments.description"><![CDATA[Alte Dateianhänge werden nach Ablauf der Frist automatisch gelöscht. [0, um die Löschung zu deaktivieren]]]></item>
-               <item name="wcf.acp.option.firstTimeSetup"><![CDATA[Erstmalige Einrichtung]]></item>
-               <item name="wcf.acp.option.firstTimeSetup.description"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Konfiguriere{else}Konfigurieren Sie{/if} die wichtigsten Einstellungen, diese können auch später über die Optionen verändert werden.]]></item>
                <item name="wcf.acp.option.category.security.blacklist.sfs"><![CDATA[Stop Forum Spam]]></item>
                <item name="wcf.acp.option.category.security.blacklist.sfs.description"><![CDATA[Datenschutzkonforme Überprüfung auf Basis einer lokalen Kopie der StopForumSpam.com-Datenbestände.]]></item>
                <item name="wcf.acp.option.category.security.blacklist.custom"><![CDATA[Eigene Sperrlisten]]></item>
index c74e80b35fec327f8dbcd0f02aa08b81a66b58b1..6c0a91c2cd11b908c18bf47439cffd03cb200c53 100644 (file)
@@ -663,6 +663,19 @@ This protocol file is very large. Please check the protocol manually: {'WCF_DIR'
 ATTENTION: The messages listed above are greatly shortened. You can view details of individual error messages in the admin panel under “Management » Logs » Errors”.
 You can access the error log at: {link controller='ExceptionLogView' isEmail=true isACP=true}{/link}]]></item>
        </category>
+       <category name="wcf.acp.firstTimeSetup">
+               <item name="wcf.acp.firstTimeSetup.license"><![CDATA[First Time Setup: License]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.credentials"><![CDATA[License Data]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.credentials.error.failedConnect"><![CDATA[Failed to establish a connection to confirm the license data.]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.credentials.error.failedValidation"><![CDATA[The license data could not be confirmed as valid.]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.noCredentials"><![CDATA[Not now?]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.noCredentialsConfirm"><![CDATA[Continue without entering license information]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.explanation"><![CDATA[<p>Entering license credentials is required to install and update the purchased WoltLab Suite™ Apps.</p>]]></item>
+               <item name="wcf.acp.firstTimeSetup.license.noCredentialsConfirm.description"><![CDATA[Entering license information is recommended. If no network connection to our package servers is available yet, or if you do not yet have license information, you can skip this step. License information can be entered at any time to install purchased WoltLab Suite™ Apps.]]></item>
+               <item name="wcf.acp.firstTimeSetup.options"><![CDATA[First Time Setup: Settings]]></item>
+               <item name="wcf.acp.firstTimeSetup.options.description"><![CDATA[Configure the most important settings now, you can change them later at any time.]]></item>
+               <item name="wcf.acp.firstTimeSetup.completed"><![CDATA[First Time Setup Completed]]></item>
+       </category>
        <category name="wcf.acp.group">
                <item name="wcf.acp.group.add"><![CDATA[Add User Group]]></item>
                <item name="wcf.acp.group.edit"><![CDATA[Edit User Group]]></item>
@@ -1662,8 +1675,6 @@ When prompted for the notification URL for the instant payment notifications, pl
                <item name="wcf.acp.option.contact_form_enable_attachments"><![CDATA[Enable attachments for contact messages]]></item>
                <item name="wcf.acp.option.contact_form_prune_attachments"><![CDATA[Prune old attachments]]></item>
                <item name="wcf.acp.option.contact_form_prune_attachments.description"><![CDATA[Older attachments are automatically removed to recover disk space. Use 0 to disable.]]></item>
-               <item name="wcf.acp.option.firstTimeSetup"><![CDATA[First Time Setup]]></item>
-               <item name="wcf.acp.option.firstTimeSetup.description"><![CDATA[Configure the most important settings now, you can change them later at any time.]]></item>
                <item name="wcf.acp.option.category.security.blacklist.sfs"><![CDATA[Stop Forum Spam]]></item>
                <item name="wcf.acp.option.category.security.blacklist.sfs.description"><![CDATA[Privacy compliant verification based on a local copy of the StopForumSpam.com database.]]></item>
                <item name="wcf.acp.option.category.security.blacklist.custom"><![CDATA[Own Blacklists]]></item>