Merge pull request #152 from WoltLab/tutorial4
authorMatthias Schmidt <gravatronics@live.com>
Tue, 20 Apr 2021 08:06:08 +0000 (10:06 +0200)
committerGitHub <noreply@github.com>
Tue, 20 Apr 2021 08:06:08 +0000 (10:06 +0200)
 Add fourth part of tutorial series on boxes and box conditions

24 files changed:
docs/assets/boxPlaceholders.png [new file with mode: 0644]
docs/package/pip/box.md
docs/tutorial/series/part_1.md
docs/tutorial/series/part_2.md
docs/tutorial/series/part_3.md
snippets/tutorial/tutorial-series/part-1/acpMenu.xml
snippets/tutorial/tutorial-series/part-1/acptemplates/personAdd.tpl
snippets/tutorial/tutorial-series/part-1/acptemplates/personList.tpl
snippets/tutorial/tutorial-series/part-1/files/acp/database/install_com.woltlab.wcf.people.php [new file with mode: 0644]
snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonAddForm.class.php
snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonEditForm.class.php
snippets/tutorial/tutorial-series/part-1/files/lib/acp/page/PersonListPage.class.php
snippets/tutorial/tutorial-series/part-1/files/lib/data/person/Person.class.php
snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonAction.class.php
snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonEditor.class.php
snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonList.class.php
snippets/tutorial/tutorial-series/part-1/files/lib/page/PersonListPage.class.php
snippets/tutorial/tutorial-series/part-1/install.sql [deleted file]
snippets/tutorial/tutorial-series/part-1/language/de.xml
snippets/tutorial/tutorial-series/part-1/language/en.xml
snippets/tutorial/tutorial-series/part-1/menuItem.xml
snippets/tutorial/tutorial-series/part-1/package.xml
snippets/tutorial/tutorial-series/part-1/page.xml
snippets/tutorial/tutorial-series/part-1/userGroupOption.xml

diff --git a/docs/assets/boxPlaceholders.png b/docs/assets/boxPlaceholders.png
new file mode 100644 (file)
index 0000000..7e5245e
Binary files /dev/null and b/docs/assets/boxPlaceholders.png differ
index 0459a658ab8bcb004cfcbae1724a1a167edc7b75..fcf4d7209cb52d91cdd27c0d535a9eb9ea619e9e 100644 (file)
@@ -43,7 +43,7 @@ The default display position of this box, can be any of the following:
 
 #### Placeholder Positions
 
---8<-- "image.html file="boxPlaceholders.png" alt="Visual illustration of placeholder positions""
+![Visual illustration of placeholder positions](../../assets/boxPlaceholders.png)
 
 ### `<showHeader>`
 
index 5c2bfbd0780a1f65ec5e92a222b0ecf134a69f82..55e77c6810f21a25e2cf064ceea06e1193da588b 100644 (file)
@@ -19,11 +19,11 @@ We will use the following package installation plugins:
 
 - [acpTemplate package installation plugin](../../package/pip/acp-template.md),
 - [acpMenu package installation plugin](../../package/pip/acp-menu.md),
+- [database package installation plugin](../../package/pip/database.md),
 - [file package installation plugin](../../package/pip/file.md),
 - [language package installation plugin](../../package/pip/language.md),
 - [menuItem package installation plugin](../../package/pip/menu-item.md),
 - [page package installation plugin](../../package/pip/page.md),
-- [sql package installation plugin](../../package/pip/sql.md),
 - [template package installation plugin](../../package/pip/template.md),
 - [userGroupOption package installation plugin](../../package/pip/user-group-option.md),
 
@@ -40,6 +40,9 @@ The package will have the following file structure:
 │   ├── personAdd.tpl
 │   └── personList.tpl
 ├── files
+│   ├── acp
+│   │   └── database
+│   │       └── install_com.woltlab.wcf.people.php
 │   └── lib
 │       ├── acp
 │       │   ├── form
@@ -49,13 +52,12 @@ The package will have the following file structure:
 │       │       └── PersonListPage.class.php
 │       ├── data
 │       │   └── person
-│       │       ├── PersonAction.class.php
 │       │       ├── Person.class.php
+│       │       ├── PersonAction.class.php
 │       │       ├── PersonEditor.class.php
 │       │       └── PersonList.class.php
 │       └── page
 │           └── PersonListPage.class.php
-├── install.sql
 ├── language
 │   ├── de.xml
 │   └── en.xml
@@ -80,10 +82,10 @@ Thus, the database table we will store the people in only contains three columns
 1. `firstName` contains the first name of the person,
 1. `lastName` contains the last name of the person.
 
-The first file for our package is the `install.sql` file used to create such a database table during package installation:
+The first file for our package is the `install_com.woltlab.wcf.people.php` file used to create such a database table during package installation:
 
 ```sql
---8<-- "tutorial/tutorial-series/part-1/install.sql"
+--8<-- "tutorial/tutorial-series/part-1/files/acp/database/install_com.woltlab.wcf.people.php"
 ```
 
 ### Database Object
@@ -192,15 +194,14 @@ We will go piece by piece through the template code:
 1. Now comes the main part of the page, the list of the people, which will only be displayed if any people exist.
    Otherwise, an info box is displayed using the generic `wcf.global.noItems` language item.
    The `$objects` template variable is automatically assigned by `wcf\page\MultipleLinkPage` and contains the `PersonList` object used to read the people from database.
-   
    The table itself consists of a `thead` and a `tbody` element and is extendable with more columns using the template events `columnHeads` and `columns`.
    In general, every table should provide these events.
    The default structure of a table is used here so that the first column of the content rows contains icons to edit and to delete the row (and provides another standard event `rowButtons`) and that the second column contains the ID of the person.
    The table can be sorted by clicking on the head of each column.
    The used variables `$sortField` and `$sortOrder` are automatically assigned to the template by `SortablePage`.
 1. The `.contentFooter` element is only shown if people exist as it basically repeats the `.contentHeaderNavigation` and `.paginationTop` element.
-1. The JavaScript code here fulfills two duties:
-   Handling clicks on the delete icons and forwarding the requests via AJAX to the `PersonAction` class, and setting up some code that triggers if all people shown on the current page are deleted via JavaScript to either reload the page or show the `wcf.global.noItems` info box. 
+1. The delete button for each person shown in the `.columnIcon` element relies on the global [`WoltLabSuite/Core/Ui/Object/Action`](../../migration/wsc53/javascript.md#wcfactiondelete-and-wcfactiontoggle) module which only requires the `jsObjectActionContainer` CSS class in combination with the `data-object-action-class-name` attribute for the `table` element, the `jsObjectActionObject` CSS class for each person's `tr` element in combination with the `data-object-id` attribute, and lastly the delete button itself, which is created with the [`objectAction` template plugin](../../view/template-plugins.md#view/template-plugins/#54-objectaction).
+1. The [`.jsReloadPageWhenEmpty` CSS class](../../migration/wsc53/javascript.md#wcftableemptytablehandler) on the `tbody` element ensures that once all persons on the page have been deleted, the page is reloaded.
 1. Lastly, the `footer` template is included that terminates the page.
    You also have to include this template for every page!
 
@@ -216,22 +217,16 @@ Like the person list, the form to add new people requires a controller class and
 --8<-- "tutorial/tutorial-series/part-1/files/lib/acp/form/PersonAddForm.class.php"
 ```
 
-The properties here consist of two types:
-the “housekeeping” properties `$activeMenuItem` and `$neededPermissions`, which fulfill the same roles as for `PersonListPage`, and the “data” properties `$firstName` and `$lastName`, which will contain the data entered by the user of the person to be created.
+The properties here consist of three types:
+the “housekeeping” properties `$activeMenuItem` and `$neededPermissions`, which fulfill the same roles as for `PersonListPage`, and the [`$objectEditLinkController` property](../../migration/wsc52/php.md#addform), which is used to generate a link to edit the newly created person after submitting the form, and finally `$formAction` and `$objectActionClass` required by the [PHP form builder API](../../php/api/form_builder/overview.md) used to generate the form.
 
-Now, let's go through each method in execution order:
+Because of using form builder, we only have to set up the two form fields for entering the first and last name, respectively:
 
-1. `readFormParameters()` is called after the form has been submitted and reads the entered first and last name and sanitizes the values by calling `StringUtil::trim()`.
-1. `validate()` is called after the form has been submitted and is used to validate the input data.
-   In case of invalid data, the method is expected to throw a `UserInputException`.
-   Here, the validation for first and last name is the same and quite basic:
-   We check that any name has been entered and that it is not longer than the database table column permits.
-1. `save()` is called after the form has been submitted and the entered data has been validated and it creates the new person via `PersonAction`.
-   Please note that we do not just pass the first and last name to the action object but merge them with the `$this->additionalFields` array which can be used by event listeners of plugins to add additional data.
-   After creating the object, the `saved()` method is called which fires an event for plugins and the data properties are cleared so that the input fields on the page are empty so that another new person can be created.
-   Lastly, a `success` variable is assigned to the template which will show a message that the person has been successfully created.
-1. `assignVariables()` assigns the values of the “data” properties to the template and additionally assigns an `action` variable.
-   This `action` variable will be used in the template to distinguish between adding a new person and editing an existing person so that which minimal adjustments, we can use the template for both cases.
+1. Each field is a simple single-line text field, thus we use [`TextFormField`](../../php/api/form_builder/form_fields.md#textformfield).
+1. The parameter of the `create()` method expects the id of the field/name of the database object property, which is `firstName` and `lastName`, respectively, here.
+1. The language item of the label shown in the ouput above the input field is set via the `label()` method.
+1. As both fields have to be filled out, `required()` is called, and the maximum length is set via `maximumLength()`.
+1. Lastly, to make it easier to fill out the form more quickly, the first field is auto-focused by calling `autoFocus()`.
 
 #### `personAdd.tpl`
 
@@ -242,23 +237,7 @@ Now, let's go through each method in execution order:
 We will now only concentrate on the new parts compared to `personList.tpl`:
 
 1. We use the `$action` variable to distinguish between the languages items used for adding a person and for creating a person.
-1. Including the `formError` template automatically shows an error message if the validation failed.
-1. The `.success` element is shown after successful saving the data and, again, shows different a text depending on the executed action.
-1. The main part is the `form` element which has a common structure you will find in many forms in WoltLab Suite Core.
-   The notable parts here are:
-   - The `action` attribute of the `form` element is set depending on which controller will handle the request.
-     In the link for the edit controller, we can now simply pass the edited `Person` object directly as the `Person` class implements the `IRouteController` interface.
-   - The field that caused the validation error can be accessed via `$errorField`.
-   - The type of the validation error can be accessed via `$errorType`.
-     For an empty input field, we show the generic `wcf.global.form.error.empty` language item.
-     In all other cases, we use the error type to determine the object- and property-specific language item to show.
-     The approach used here allows plugins to easily add further validation error messages by simply using a different error type and providing the associated language item.
-   - Input fields can be grouped into different `.section` elements.
-     At the end of each `.section` element, there should be an template event whose name ends with `Fields`.
-     The first part of the event name should reflect the type of fields in the particular `.section` element.
-     Here, the input fields are just general “data” fields so that the event is called `dataFields`.
-   - After the last `.section` element, fire a `section` event so that plugins can add further sections.
-   - Lastly, the `.formSubmit` shows the submit button and `{csrfToken}` contains a CSRF token that is automatically validated after the form is submitted.
+1. Because of form builder, we only have to call `{@$form->getHtml()}` to generate all relevant output for the form.
 
 ### Person Edit Form
 
@@ -272,24 +251,9 @@ As mentioned before, for the form to edit existing people, we only need a new co
 
 In general, edit forms extend the associated add form so that the code to read and to validate the input data is simply inherited.
 
-After setting a different active menu item, we declare two new properties for the edited person:
-the id of the person passed in the URL is stored in `$personID` and based on this ID, a `Person` object is created that is stored in the `$person` property.
-
-Now let use go through the different methods in chronological order again:
+After setting a different active menu item, we have to change the value of `$formAction` because this form, in contrast to `PersonAddForm`, does not create but update existing persons.
 
-1. `readParameters()` reads the passed ID of the edited person and creates a `Person` object based on this ID.
-   If the ID is invalid, `$this->person->personID` is `null` and an `IllegalLinkException` is thrown.
-1. `readData()` only executes additional code in the case if `$_POST` is empty, thus only for the initial request before the form has been submitted.
-   The data properties of `PersonAddForm` are populated with the data of the edited person so that this data is shown in the form for the initial request.
-1. `save()` handles saving the changed data.
-   
-   !!! warning "Do not call `parent::save()` because that would cause `PersonAddForm::save()` to be executed and thus a new person would to be created! In order for the `save` event to be fired, call `AbstractForm::save()` instead!"
-   
-   The only differences compared to `PersonAddForm::save()` are that we pass the edited object to the `PersonAction` constructor, execute the `update` action instead of the `create` action and do not clear the input fields after saving the changes.
-1. In `assignVariables()`, we assign the edited `Person` object to the template, which is required to create the link in the form’s action property.
-   Furthermore, we assign the template variable `$action` `edit` as value.
-   
-   !!! info "After calling `parent::assignVariables()`, the template variable `$action` actually has the value `add` so that here, we are overwriting this already assigned value."
+As we rely on form builder, the only thing necessary in this controller is to read and validate the edit object, i.e. the edited person, which is done in `readParameters()`.
 
 
 ## Frontend
@@ -382,9 +346,8 @@ For more information about this kind of file, please refer to [the `package.xml`
 ```
 
 As this is a package for WoltLab Suite Core 3, we need to require it using `<requiredpackage>`.
-We require the latest version (when writing this tutorial) `3.0.0 RC 4`.
-Additionally, we disallow installation of the package in the next major version `3.1` by excluding the `3.1.0 Alpha 1` version.
-This ensures that if changes from WoltLab Suite Core 3.0 to 3.1 require changing some parts of the package, it will not break the instance in which the package is installed.
+We require the latest version (when writing this tutorial) `5.4.0 Alpha 1`.
+Additionally, we disallow installation of the package in the next major version `6.0` by excluding the `6.0.0 Alpha 1` version.
 
 The most important part are to installation instructions.
 First, we install the ACP templates, files and templates, create the database table and import the language item.
index 03f9a24df1e176dd1de6e0dca6a8548eca9c9073..409807776b98ad3d38195d156cca79543dc734be 100644 (file)
@@ -1,5 +1,7 @@
 # Part 2: Event Listeners and Template Listeners
 
+!!! warning "This part of the tutorial series is currently outdated while the tutorial series is updated."
+
 In the [first part](part_1.md) of this tutorial series, we have created the base structure of our people management package.
 In further parts, we will use the package of the first part as a basis to directly add new features.
 In order to explain how event listeners and template works, however, we will not directly adding a new feature to the package by altering it in this part, but we will assume that somebody else created the package and that we want to extend it the “correct” way by creating a plugin.
index 5f650a5133c805e0fa3d93b1d851cf054669af15..7ab4b2529478b2226c996f8ecac23e4d06bae75f 100644 (file)
@@ -1,4 +1,6 @@
-# Tutorial Series Part 3: Person Page and Comments
+# Part 3: Person Page and Comments
+
+!!! warning "This part of the tutorial series is currently outdated while the tutorial series is updated."
 
 In this part of our tutorial series, we will add a new front end page to our package that is dedicated to each person and shows their personal details.
 To make good use of this new page and introduce a new API of WoltLab Suite, we will add the opportunity for users to comment on the person using WoltLab Suite’s reusable comment functionality.
index 8ad2af2cf7af4569a9fc03bb76857ebe7ca5dc0c..6ccf64fdbe0f830d0f6c16503e0c6642144944dd 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/tornado/acpMenu.xsd">
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/5.4/acpMenu.xsd">
        <import>
                <acpmenuitem name="wcf.acp.menu.link.person">
                        <parent>wcf.acp.menu.link.content</parent>
index a19603abf461a10f0926d09f80e69d1e0aab60f1..4cf2ec4a7d77e24bf8eb2af81cb5af637792a5d3 100644 (file)
        </nav>
 </header>
 
-{include file='formNotice'}
-
-<form method="post" action="{if $action == 'add'}{link controller='PersonAdd'}{/link}{else}{link controller='PersonEdit' object=$person}{/link}{/if}">
-       <div class="section">
-               <dl{if $errorField == 'firstName'} class="formError"{/if}>
-                       <dt><label for="firstName">{lang}wcf.person.firstName{/lang}</label></dt>
-                       <dd>
-                               <input type="text" id="firstName" name="firstName" value="{$firstName}" required autofocus maxlength="255" class="long">
-                               {if $errorField == 'firstName'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.acp.person.firstName.error.{$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
-               <dl{if $errorField == 'lastName'} class="formError"{/if}>
-                       <dt><label for="lastName">{lang}wcf.person.lastName{/lang}</label></dt>
-                       <dd>
-                               <input type="text" id="lastName" name="lastName" value="{$lastName}" required maxlength="255" class="long">
-                               {if $errorField == 'lastName'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.acp.person.lastName.error.{$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
-               {event name='dataFields'}
-       </div>
-       
-       {event name='sections'}
-       
-       <div class="formSubmit">
-               <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
-               {csrfToken}
-       </div>
-</form>
+{@$form->getHtml()}
 
 {include file='footer'}
index fc7dc36e24fbbf7b71312c34ea0ad251cd83fd2b..71766eff5a05d7043891dc14be4a018476b5a3a1 100644 (file)
@@ -21,8 +21,8 @@
 {/hascontent}
 
 {if $objects|count}
-       <div class="section tabularBox" id="personTableContainer">
-               <table class="table">
+       <div class="section tabularBox">
+               <table class="table jsObjectActionContainer" data-object-action-class-name="wcf\data\person\PersonAction">
                        <thead>
                                <tr>
                                        <th class="columnID columnPersonID{if $sortField == 'personID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='PersonList'}pageNo={@$pageNo}&sortField=personID&sortOrder={if $sortField == 'personID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
                        
                        <tbody class="jsReloadPageWhenEmpty">
                                {foreach from=$objects item=person}
-                                       <tr class="jsPersonRow">
+                                       <tr class="jsObjectActionObject" data-object-id="{@$person->getObjectID()}">
                                                <td class="columnIcon">
                                                        <a href="{link controller='PersonEdit' object=$person}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 fa-pencil"></span></a>
-                                                       <span class="icon icon16 fa-times jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$person->personID}" data-confirm-message-html="{lang __encode=true}wcf.acp.person.delete.confirmMessage{/lang}"></span>
+                                                       {objectAction action="delete" objectTitle=$person->getTitle()}
                                                        
                                                        {event name='rowButtons'}
                                                </td>
        <p class="info">{lang}wcf.global.noItems{/lang}</p>
 {/if}
 
-<script data-relocate="true">
-       $(function() {
-               new WCF.Action.Delete('wcf\\data\\person\\PersonAction', '.jsPersonRow');
-       });
-</script>
-
 {include file='footer'}
diff --git a/snippets/tutorial/tutorial-series/part-1/files/acp/database/install_com.woltlab.wcf.people.php b/snippets/tutorial/tutorial-series/part-1/files/acp/database/install_com.woltlab.wcf.people.php
new file mode 100644 (file)
index 0000000..57b341b
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+use wcf\system\database\table\column\NotNullVarchar255DatabaseTableColumn;
+use wcf\system\database\table\column\ObjectIdDatabaseTableColumn;
+use wcf\system\database\table\DatabaseTable;
+
+return [
+    DatabaseTable::create('wcf1_person')
+        ->columns([
+            ObjectIdDatabaseTableColumn::create('personID'),
+            NotNullVarchar255DatabaseTableColumn::create('firstName'),
+            NotNullVarchar255DatabaseTableColumn::create('lastName'),
+        ]),
+];
index fd3beeb800ddd2eb4197ccfecb5b72389422847a..212155208a5998b3b05896cc327c3c5d6791da68 100644 (file)
 <?php
+
 namespace wcf\acp\form;
+
 use wcf\data\person\PersonAction;
-use wcf\form\AbstractForm;
-use wcf\system\exception\UserInputException;
-use wcf\system\request\LinkHandler;
-use wcf\system\WCF;
-use wcf\util\StringUtil;
+use wcf\form\AbstractFormBuilderForm;
+use wcf\system\form\builder\container\FormContainer;
+use wcf\system\form\builder\field\TextFormField;
 
 /**
  * Shows the form to create a new person.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package    WoltLabSuite\Core\Acp\Form
+ *
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Acp\Form
  */
-class PersonAddForm extends AbstractForm {
-       /**
-        * @inheritDoc
-        */
-       public $activeMenuItem = 'wcf.acp.menu.link.person.add';
-       
-       /**
-        * first name of the person
-        * @var string
-        */
-       public $firstName = '';
-       
-       /**
-        * last name of the person
-        * @var string
-        */
-       public $lastName = '';
-       
-       /**
-        * @inheritDoc
-        */
-       public $neededPermissions = ['admin.content.canManagePeople'];
-       
-       /**
-        * @inheritDoc
-        */
-       public function assignVariables() {
-               parent::assignVariables();
-               
-               WCF::getTPL()->assign([
-                       'action' => 'add',
-                       'firstName' => $this->firstName,
-                       'lastName' => $this->lastName
-               ]);
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function readFormParameters() {
-               parent::readFormParameters();
-               
-               if (isset($_POST['firstName'])) $this->firstName = StringUtil::trim($_POST['firstName']);
-               if (isset($_POST['lastName'])) $this->lastName = StringUtil::trim($_POST['lastName']);
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function save() {
-               parent::save();
-               
-               $this->objectAction = new PersonAction([], 'create', [
-                       'data' => array_merge($this->additionalFields, [
-                               'firstName' => $this->firstName,
-                               'lastName' => $this->lastName
-                       ])
-               ]);
-               $returnValues = $this->objectAction->executeAction();
-               
-               $this->saved();
-               
-               // reset values
-               $this->firstName = '';
-               $this->lastName = '';
-               
-               // show success message
-               WCF::getTPL()->assign([
-                       'success' => true,
-                       'objectEditLink' => LinkHandler::getInstance()->getControllerLink(PersonEditForm::class, ['id' => $returnValues['returnValues']->personID]),
-               ]);
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function validate() {
-               parent::validate();
-               
-               // validate first name
-               if (empty($this->firstName)) {
-                       throw new UserInputException('firstName');
-               }
-               if (mb_strlen($this->firstName) > 255) {
-                       throw new UserInputException('firstName', 'tooLong');
-               }
-               
-               // validate last name
-               if (empty($this->lastName)) {
-                       throw new UserInputException('lastName');
-               }
-               if (mb_strlen($this->lastName) > 255) {
-                       throw new UserInputException('lastName', 'tooLong');
-               }
-       }
+class PersonAddForm extends AbstractFormBuilderForm
+{
+    /**
+     * @inheritDoc
+     */
+    public $activeMenuItem = 'wcf.acp.menu.link.person.add';
+
+    /**
+     * @inheritDoc
+     */
+    public $formAction = 'create';
+
+    /**
+     * @inheritDoc
+     */
+    public $neededPermissions = ['admin.content.canManagePeople'];
+
+    /**
+     * @inheritDoc
+     */
+    public $objectActionClass = PersonAction::class;
+
+    /**
+     * @inheritDoc
+     */
+    public $objectEditLinkController = PersonEditForm::class;
+
+    /**
+     * @inheritDoc
+     */
+    public function createForm()
+    {
+        parent::createForm();
+
+        $this->form->appendChild(
+            FormContainer::create('data')
+                ->label('wcf.global.form.data')
+                ->appendChildren([
+                    TextFormField::create('firstName')
+                        ->label('wcf.person.firstName')
+                        ->required()
+                        ->autoFocus()
+                        ->maximumLength(255),
+
+                    TextFormField::create('lastName')
+                        ->label('wcf.person.lastName')
+                        ->required()
+                        ->maximumLength(255),
+                ])
+        );
+    }
 }
index ad8f86a81a4b88149129acc2adf88dbb75ae2ec9..47c2b7650089156233252d2fe666353f7d5bdf1c 100644 (file)
@@ -1,91 +1,43 @@
 <?php
+
 namespace wcf\acp\form;
+
 use wcf\data\person\Person;
-use wcf\data\person\PersonAction;
-use wcf\form\AbstractForm;
 use wcf\system\exception\IllegalLinkException;
-use wcf\system\WCF;
 
 /**
  * Shows the form to edit an existing person.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package    WoltLabSuite\Core\Acp\Form
+ *
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Acp\Form
  */
-class PersonEditForm extends PersonAddForm {
-       /**
-        * @inheritDoc
-        */
-       public $activeMenuItem = 'wcf.acp.menu.link.person';
-       
-       /**
-        * edited person object
-        * @var Person
-        */
-       public $person = null;
-       
-       /**
-        * id of the edited person
-        * @var integer
-        */
-       public $personID = 0;
-       
-       /**
-        * @inheritDoc
-        */
-       public function assignVariables() {
-               parent::assignVariables();
-               
-               WCF::getTPL()->assign([
-                       'action' => 'edit',
-                       'person' => $this->person
-               ]);
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function readData() {
-               parent::readData();
-               
-               if (empty($_POST)) {
-                       $this->firstName = $this->person->firstName;
-                       $this->lastName = $this->person->lastName;
-               }
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function readParameters() {
-               parent::readParameters();
-               
-               if (isset($_REQUEST['id'])) $this->personID = intval($_REQUEST['id']);
-               $this->person = new Person($this->personID);
-               if (!$this->person->personID) {
-                       throw new IllegalLinkException();
-               }
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function save() {
-               AbstractForm::save();
-               
-               $this->objectAction = new PersonAction([$this->person], 'update', [
-                       'data' => array_merge($this->additionalFields, [
-                               'firstName' => $this->firstName,
-                               'lastName' => $this->lastName
-                       ])
-               ]);
-               $this->objectAction->executeAction();
-               
-               $this->saved();
-               
-               // show success message
-               WCF::getTPL()->assign('success', true);
-       }
+class PersonEditForm extends PersonAddForm
+{
+    /**
+     * @inheritDoc
+     */
+    public $activeMenuItem = 'wcf.acp.menu.link.person';
+
+    /**
+     * @inheritDoc
+     */
+    public $formAction = 'update';
+
+    /**
+     * @inheritDoc
+     */
+    public function readParameters()
+    {
+        parent::readParameters();
+
+        if (isset($_REQUEST['id'])) {
+            $this->formObject = new Person($_REQUEST['id']);
+
+            if (!$this->formObject->getObjectID()) {
+                throw new IllegalLinkException();
+            }
+        }
+    }
 }
index a16f28dd5e90e599528c0a0f3e1c9c6a628faa03..9d57855b7eb116875efb5950063536c381bf7430 100644 (file)
@@ -1,34 +1,37 @@
 <?php
+
 namespace wcf\acp\page;
+
 use wcf\data\person\PersonList;
 use wcf\page\SortablePage;
 
 /**
  * Shows the list of people.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package    WoltLabSuite\Core\Acp\Page
+ *
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Acp\Page
  */
-class PersonListPage extends SortablePage {
-       /**
-        * @inheritDoc
-        */
-       public $activeMenuItem = 'wcf.acp.menu.link.person.list';
-       
-       /**
-        * @inheritDoc
-        */
-       public $neededPermissions = ['admin.content.canManagePeople'];
-       
-       /**
-        * @inheritDoc
-        */
-       public $objectListClassName = PersonList::class;
-       
-       /**
-        * @inheritDoc
-        */
-       public $validSortFields = ['personID', 'firstName', 'lastName'];
+class PersonListPage extends SortablePage
+{
+    /**
+     * @inheritDoc
+     */
+    public $activeMenuItem = 'wcf.acp.menu.link.person.list';
+
+    /**
+     * @inheritDoc
+     */
+    public $neededPermissions = ['admin.content.canManagePeople'];
+
+    /**
+     * @inheritDoc
+     */
+    public $objectListClassName = PersonList::class;
+
+    /**
+     * @inheritDoc
+     */
+    public $validSortFields = ['personID', 'firstName', 'lastName'];
 }
index fe30af443b23d14aa9c5db0243c5044636b0ea75..00ff194fe17136f8fc1826a37d41a282b4e32c3b 100644 (file)
@@ -1,34 +1,39 @@
 <?php
+
 namespace wcf\data\person;
+
 use wcf\data\DatabaseObject;
 use wcf\system\request\IRouteController;
 
 /**
  * Represents a person.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package    WoltLabSuite\Core\Data\Person
- * 
- * @property-read      integer         $personID       unique id of the person
- * @property-read      string          $firstName      first name of the person
- * @property-read      string          $lastName       last name of the person
+ *
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\Person
+ *
+ * @property-read   integer     $personID   unique id of the person
+ * @property-read   string      $firstName  first name of the person
+ * @property-read   string      $lastName   last name of the person
  */
-class Person extends DatabaseObject implements IRouteController {
-       /**
-        * Returns the first and last name of the person if a person object is treated as a string.
-        * 
-        * @return      string
-        */
-       public function __toString() {
-               return $this->getTitle();
-       }
-       
-       /**
-        * @inheritDoc
-        */
-       public function getTitle() {
-               return $this->firstName . ' ' . $this->lastName;
-       }
+class Person extends DatabaseObject implements IRouteController
+{
+    /**
+     * Returns the first and last name of the person if a person object is treated as a string.
+     *
+     * @return  string
+     */
+    public function __toString()
+    {
+        return $this->getTitle();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getTitle()
+    {
+        return $this->firstName . ' ' . $this->lastName;
+    }
 }
index d76b926758de1e05a360206fc4efefd752602148..3f34655613dc06c9cf574eaa630b9b636f54648a 100644 (file)
@@ -1,27 +1,30 @@
 <?php
+
 namespace wcf\data\person;
+
 use wcf\data\AbstractDatabaseObjectAction;
 
 /**
  * Executes person-related actions.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package    WoltLabSuite\Core\Data\Person
  *
- * @method     Person          create()
- * @method     PersonEditor[]  getObjects()
- * @method     PersonEditor    getSingleObject()
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\Person
+ *
+ * @method  Person      create()
+ * @method  PersonEditor[]  getObjects()
+ * @method  PersonEditor    getSingleObject()
  */
-class PersonAction extends AbstractDatabaseObjectAction {
-       /**
-        * @inheritDoc
-        */
-       protected $permissionsDelete = ['admin.content.canManagePeople'];
-       
-       /**
-        * @inheritDoc
-        */
-       protected $requireACP = ['delete'];
+class PersonAction extends AbstractDatabaseObjectAction
+{
+    /**
+     * @inheritDoc
+     */
+    protected $permissionsDelete = ['admin.content.canManagePeople'];
+
+    /**
+     * @inheritDoc
+     */
+    protected $requireACP = ['delete'];
 }
index 3890bc1009c8b88fb087f1bb31bf063436f48198..b8d5a3ce73cbcaccf6eb04a7e87258fd7dadce3d 100644 (file)
@@ -1,22 +1,25 @@
 <?php
+
 namespace wcf\data\person;
+
 use wcf\data\DatabaseObjectEditor;
 
 /**
  * Provides functions to edit people.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package    WoltLabSuite\Core\Data\Person
- * 
- * @method static      Person  create(array $parameters = [])
- * @method             Person  getDecoratedObject()
- * @mixin              Person
+ *
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\Person
+ *
+ * @method static   Person  create(array $parameters = [])
+ * @method      Person  getDecoratedObject()
+ * @mixin       Person
  */
-class PersonEditor extends DatabaseObjectEditor {
-       /**
-        * @inheritDoc
-        */
-       protected static $baseClass = Person::class;
+class PersonEditor extends DatabaseObjectEditor
+{
+    /**
+     * @inheritDoc
+     */
+    protected static $baseClass = Person::class;
 }
index bedf543c5f51e666dec76f3937908e93e2e8a384..4e16a0d0e2ae4bcdcb4e41d166b3adc731a74ede 100644 (file)
@@ -1,18 +1,22 @@
 <?php
+
 namespace wcf\data\person;
+
 use wcf\data\DatabaseObjectList;
 
 /**
  * Represents a list of people.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package    WoltLabSuite\Core\Data\Person
- * 
- * @method     Person          current()
- * @method     Person[]        getObjects()
- * @method     Person|null     search($objectID)
- * @property   Person[]        $objects
+ *
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Data\Person
+ *
+ * @method  Person      current()
+ * @method  Person[]    getObjects()
+ * @method  Person|null search($objectID)
+ * @property    Person[]    $objects
  */
-class PersonList extends DatabaseObjectList {}
+class PersonList extends DatabaseObjectList
+{
+}
index 839b3e2a035f8ac8cb4517159fed7eb39f448b14..5fbacd4412ad607ff29998c7160b3d30028d74fd 100644 (file)
@@ -1,28 +1,31 @@
 <?php
+
 namespace wcf\page;
+
 use wcf\data\person\PersonList;
 
 /**
  * Shows the list of people.
- * 
- * @author     Matthias Schmidt
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package    WoltLabSuite\Core\Page
+ *
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\Page
  */
-class PersonListPage extends SortablePage {
-       /**
-        * @inheritDoc
-        */
-       public $defaultSortField = 'lastName';
-       
-       /**
-        * @inheritDoc
-        */
-       public $objectListClassName = PersonList::class;
-       
-       /**
-        * @inheritDoc
-        */
-       public $validSortFields = ['personID', 'firstName', 'lastName'];
+class PersonListPage extends SortablePage
+{
+    /**
+     * @inheritDoc
+     */
+    public $defaultSortField = 'lastName';
+
+    /**
+     * @inheritDoc
+     */
+    public $objectListClassName = PersonList::class;
+
+    /**
+     * @inheritDoc
+     */
+    public $validSortFields = ['personID', 'firstName', 'lastName'];
 }
diff --git a/snippets/tutorial/tutorial-series/part-1/install.sql b/snippets/tutorial/tutorial-series/part-1/install.sql
deleted file mode 100644 (file)
index 111a0f8..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-DROP TABLE IF EXISTS wcf1_person;
-CREATE TABLE wcf1_person (
-       personID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
-       firstName VARCHAR(255) NOT NULL,
-       lastName VARCHAR(255) NOT NULL
-);
index c046856287daed1358439c7a0f2e6d50fe5d6e63..cb217919b451f8e78d6fa16e90d32f01123c83db 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/tornado/language.xsd" languagecode="de">
+<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/5.4/language.xsd" languagecode="de">
        <category name="wcf.acp.group">
                <item name="wcf.acp.group.option.admin.content.canManagePeople"><![CDATA[Kann Personen verwalten]]></item>
        </category>
        
        <category name="wcf.acp.person">
                <item name="wcf.acp.person.add"><![CDATA[Person hinzufügen]]></item>
-               <item name="wcf.acp.person.delete.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} die Person <span class="confirmationObject">{$person}</span> wirklich löschen?]]></item>
                <item name="wcf.acp.person.edit"><![CDATA[Person bearbeiten]]></item>
-               <item name="wcf.acp.person.firstName.error.tooLong"><![CDATA[Der Vorname darf nicht länger als 255 Zeichen sein.]]></item>
-               <item name="wcf.acp.person.lastName.error.tooLong"><![CDATA[Der Nachname darf nicht länger als 255 Zeichen sein.]]></item>
                <item name="wcf.acp.person.list"><![CDATA[Personen]]></item>
        </category>
        
index bff83cfff089979dbd9d61ab8bea13f5c3c85bda..a76387ca8fa3d8b91d53ad1f76be4bd153074130 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/tornado/language.xsd" languagecode="en">
+<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/5.4/language.xsd" languagecode="en">
        <category name="wcf.acp.group">
                <item name="wcf.acp.group.option.admin.content.canManagePeople"><![CDATA[Can manage people]]></item>
        </category>
        
        <category name="wcf.acp.person">
                <item name="wcf.acp.person.add"><![CDATA[Add Person]]></item>
-               <item name="wcf.acp.person.delete.confirmMessage"><![CDATA[Do you really want to delete the person <span class="confirmationObject">{$person}</span>?]]></item>
                <item name="wcf.acp.person.edit"><![CDATA[Edit Person]]></item>
-               <item name="wcf.acp.person.firstName.error.tooLong"><![CDATA[The first name must not be longer than 255 characters.]]></item>
-               <item name="wcf.acp.person.lastName.error.tooLong"><![CDATA[The last name must not be longer than 255 characters.]]></item>
                <item name="wcf.acp.person.list"><![CDATA[People]]></item>
        </category>
        
index 378a2973eff701f20d840e6081ae3626bfb16ead..bcf3e04d0c93db6f35dde00bea2a81200ac7a78a 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/tornado/menuItem.xsd">
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/5.4/menuItem.xsd">
        <import>
                <item identifier="com.woltlab.wcf.people.PersonList">
                        <menu>com.woltlab.wcf.MainMenu</menu>
index 5beddeda944d2c33f9bfc8da5e5e439ebb0844fb..ecc46fd173a228bec1beb1d71621af52bb217fcf 100644 (file)
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<package name="com.woltlab.wcf.people" xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/tornado/package.xsd">
+<package name="com.woltlab.wcf.people" xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/5.4/package.xsd">
        <packageinformation>
                <packagename>WoltLab Suite Core Tutorial: People</packagename>
                <packagedescription>Adds a simple management system for people as part of a tutorial to create packages.</packagedescription>
-               <version>3.1.0</version>
-               <date>2018-03-30</date>
+               <version>5.4.0</version>
+               <date>2021-04-16</date>
        </packageinformation>
        
        <authorinformation>
        </authorinformation>
        
        <requiredpackages>
-               <requiredpackage minversion="3.1.0">com.woltlab.wcf</requiredpackage>
+               <requiredpackage minversion="5.4.0 Alpha 1">com.woltlab.wcf</requiredpackage>
        </requiredpackages>
        
        <excludedpackages>
-               <excludedpackage version="3.2.0 Alpha 1">com.woltlab.wcf</excludedpackage>
+               <excludedpackage version="6.0.0 Alpha 1">com.woltlab.wcf</excludedpackage>
        </excludedpackages>
-
-       <compatibility>
-               <api version="2018" />
-       </compatibility>
        
        <instructions type="install">
                <instruction type="acpTemplate" />
                <instruction type="file" />
-               <instruction type="sql" />
+               <instruction type="database">acp/database/install_com.woltlab.wcf.people.php</instruction>
                <instruction type="template" />
                <instruction type="language" />
                
index 3f80ef8bb288f9886625072e198f11ff781a22d5..72cf47b7d79d2f77deddd964fca21a6924be3201 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/tornado/page.xsd">
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/5.4/page.xsd">
        <import>
                <page identifier="com.woltlab.wcf.people.PersonList">
                        <pageType>system</pageType>
index 2e041d387f42a8ffe0f4844416ddb14e3d66a094..15397b9d3c2bef62f1025035bae87140308dc042 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/tornado/userGroupOption.xsd">
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/5.4/userGroupOption.xsd">
        <import>
                <options>
                        <option name="admin.content.canManagePeople">