diff --git a/docs/tutorial/series/overview.md b/docs/tutorial/series/overview.md
index f8d5138c1..057981c26 100644
--- a/docs/tutorial/series/overview.md
+++ b/docs/tutorial/series/overview.md
@@ -9,6 +9,8 @@ Note that in the context of this example, not every added feature might make per
- [Part 1: Base Structure](part_1.md)
- [Part 2: Event and Template Listeners](part_2.md)
- [Part 3: Person Page and Comments](part_3.md)
+
diff --git a/docs/tutorial/series/part_1.md b/docs/tutorial/series/part_1.md
index 74ce02a75..2f4623aae 100644
--- a/docs/tutorial/series/part_1.md
+++ b/docs/tutorial/series/part_1.md
@@ -18,7 +18,6 @@ The package should provide the following possibilities/functions:
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),
@@ -27,7 +26,13 @@ We will use the following package installation plugins:
- [template package installation plugin](../../package/pip/template.md),
- [userGroupOption package installation plugin](../../package/pip/user-group-option.md),
-use [database objects](../../php/database-objects.md), create [pages](../../php/pages.md) and use [templates](../../view/templates.md).
+In addition, we use the following features:
+
+- [ACP menu items](../../package/acp-menu-items.md)
+- [bootstrap scripts](../../package/bootstrap-scripts.md)
+- [database objects](../../php/database-objects.md)
+- [pages](../../php/pages.md)
+- [templates](../../view/templates.md)
## Package Structure
@@ -35,37 +40,54 @@ use [database objects](../../php/database-objects.md), create [pages](../../php/
The package will have the following file structure:
```
-├── acpMenu.xml
├── acptemplates
-│ ├── personAdd.tpl
-│ └── personList.tpl
+│ ├── personAdd.tpl
+│ └── personList.tpl
├── files
-│ ├── acp
-│ │ └── database
-│ │ └── install_com.woltlab.wcf.people.php
-│ └── lib
-│ ├── acp
-│ │ ├── form
-│ │ │ ├── PersonAddForm.class.php
-│ │ │ └── PersonEditForm.class.php
-│ │ └── page
-│ │ └── PersonListPage.class.php
-│ ├── data
-│ │ └── person
-│ │ ├── Person.class.php
-│ │ ├── PersonAction.class.php
-│ │ ├── PersonEditor.class.php
-│ │ └── PersonList.class.php
-│ └── page
-│ └── PersonListPage.class.php
+│ ├── acp
+│ │ └── database
+│ │ └── install_com.woltlab.wcf.people.php
+│ └── lib
+│ ├── acp
+│ │ ├── form
+│ │ │ ├── PersonAddForm.class.php
+│ │ │ └── PersonEditForm.class.php
+│ │ └── page
+│ │ └── PersonListPage.class.php
+│ ├── bootstrap
+│ │ └── com.woltlab.wcf.people.php
+│ ├── data
+│ │ └── person
+│ │ ├── Person.class.php
+│ │ ├── PersonAction.class.php
+│ │ ├── PersonEditor.class.php
+│ │ └── PersonList.class.php
+│ ├── event
+│ │ └── gridView
+│ │ └── admin
+│ │ └── PersonGridViewInitialized.class.php
+│ ├── page
+│ │ └── PersonListPage.class.php
+│ └── system
+│ ├── endpoint
+│ │ └── controller
+│ │ └── core
+│ │ └── persons
+│ │ └── DeletePerson.class.php
+│ ├── gridView
+│ │ └── admin
+│ │ └── PersonGridView.class.php
+│ └── interaction
+│ └── admin
+│ └── PersonInteractions.class.php
├── language
-│ ├── de.xml
-│ └── en.xml
+│ ├── de.xml
+│ └── en.xml
├── menuItem.xml
├── package.xml
├── page.xml
├── templates
-│ └── personList.tpl
+│ └── personList.tpl
└── userGroupOption.xml
```
@@ -163,10 +185,12 @@ We need to create three menu items:
1. a third level menu item for the people list page, and
1. a fourth level menu item for the form to add new people.
+We create the [menu entries](../../package/acp-menu-items.md) using a [bootstrap script](../../package/bootstrap-scripts.md):
+
{jinja{ codebox(
- title="acpMenu.xml",
- language="xml",
- filepath="tutorial/tutorial-series/part-1/acpMenu.xml"
+ title="files/lib/bootstrap/com.woltlab.wcf.people.php",
+ language="php",
+ filepath="tutorial/tutorial-series/part-1/files/lib/bootstrap/com.woltlab.wcf.people.php"
) }}
We choose `wcf.acp.menu.link.content` as the parent menu item for the first menu item `wcf.acp.menu.link.person` because the people we are managing is just one form of content.
@@ -174,7 +198,7 @@ The fourth level menu item `wcf.acp.menu.link.person.add` will only be shown as
### People List
-To list the people in the ACP, we need a `PersonListPage` class and a `personList` template.
+To list the people in the ACP, we need the classes `PersonListPage`, `PersonGridView`, and a `personList` template.
#### `PersonListPage`
@@ -184,13 +208,26 @@ To list the people in the ACP, we need a `PersonListPage` class and a `personLis
filepath="tutorial/tutorial-series/part-1/files/lib/acp/page/PersonListPage.class.php"
) }}
-As WoltLab Suite Core already provides a powerful default implementation of a sortable page, our work here is minimal:
+`PersonListPage` uses a [grid view](../../php/api/grid_views.md) to display the list of people, our work here is minimal:
1. We need to set the active ACP menu item via the `$activeMenuItem`.
1. `$neededPermissions` contains a list of permissions of which the user needs to have at least one in order to see the person list.
We use the same permission for both the menu item and the page.
-1. The database object list class whose name is provided via `$objectListClassName` and that handles fetching the people from database is the `PersonList` class, which we have already created.
-1. To validate the sort field passed with the request, we set `$validSortFields` to the available database table columns.
+1. Implement the method `createGridView()` and return the grid view that should be used to render the list of people.
+
+#### `PersonGridView`
+
+{jinja{ codebox(
+ title="files/lib/system/gridView/admin/PersonGridView.class.php",
+ language="php",
+ filepath="tutorial/tutorial-series/part-1/files/lib/system/gridView/admin/PersonGridView.class.php"
+) }}
+
+The following features are defined in the grid view:
+
+1. The columns to be displayed and their order.
+1. Which columns can be sorted and what their default sorting is.
+1. What interaction options the user has with the displayed items.
#### `personList.tpl`
@@ -207,17 +244,7 @@ We will go piece by piece through the template code:
1. We set the content header and additional provide a button to create a new person in the content header navigation.
1. As not all people are listed on the same page if many people have been created, we need a pagination for which we use the `pages` template plugin.
The `{hascontent}{content}{/content}{/hascontent}` construct ensures the `.paginationTop` element is only shown if the `pages` template plugin has a return value, thus if a pagination is necessary.
-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 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. For the main part of the page we only need to call the `render()` method of the grid view.
1. Lastly, the `footer` template is included that terminates the page.
You also have to include this template for every page!
diff --git a/docs/tutorial/series/part_2.md b/docs/tutorial/series/part_2.md
index f260cfd5f..d15f99fcd 100644
--- a/docs/tutorial/series/part_2.md
+++ b/docs/tutorial/series/part_2.md
@@ -40,23 +40,24 @@ The package will have the following file structure:
```
├── eventListener.xml
├── files
-│ ├── acp
-│ │ └── database
-│ │ └── install_com.woltlab.wcf.people.birthday.php
-│ └── lib
-│ └── system
-│ └── event
-│ └── listener
-│ ├── BirthdayPersonAddFormListener.class.php
-│ └── BirthdaySortFieldPersonListPageListener.class.php
+│ ├── acp
+│ │ └── database
+│ │ └── install_com.woltlab.wcf.people.birthday.php
+│ └── lib
+│ ├── bootstrap
+│ │ └── com.woltlab.wcf.people.birthday.php
+│ └── system
+│ └── event
+│ └── listener
+│ └── BirthdayPersonAddFormListener.class.php
├── language
-│ ├── de.xml
-│ └── en.xml
+│ ├── de.xml
+│ └── en.xml
├── package.xml
├── templateListener.xml
-└── templates
- ├── __personListBirthday.tpl
- └── __personListBirthdaySortField.tpl
+├── templates
+│ ├── __personListBirthday.tpl
+│ └── __personListBirthdaySortField.tpl
```
@@ -118,35 +119,14 @@ The language item `wcf.person.birthday` used in the label is the only new one fo
## Adding Birthday Table Column in ACP
-To add a birthday column to the person list page in the ACP, we need three parts:
-
-1. an event listener that makes the `birthday` database table column a valid sort field,
-1. a template listener that adds the birthday column to the table’s head, and
-1. a template listener that adds the birthday column to the table’s rows.
-
-The first part is a very simple class:
+To add a birthday column to the person list page in the ACP, we need to listen to the `PersonGridViewInitialized` event and add the additional column:
{jinja{ codebox(
- title="files/lib/system/event/listener/BirthdaySortFieldPersonListPageListener.class.php",
+ title="files/lib/bootstrap/com.woltlab.wcf.people.birthday.php",
language="php",
- filepath="tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdaySortFieldPersonListPageListener.class.php"
+ filepath="tutorial/tutorial-series/part-2/files/lib/bootstrap/com.woltlab.wcf.people.birthday.php"
) }}
-!!! info "We use `SortablePage` as a type hint instead of `wcf\acp\page\PersonListPage` because we will be using the same event listener class in the front end to also allow sorting that list by birthday."
-
-As the relevant template codes are only one line each, we will simply put them directly in the `templateListener.xml` file that will be shown [later on](#templatelistenerxml).
-The code for the table head is similar to the other `th` elements:
-
-```smarty
-
{lang}wcf.person.birthday{/lang} |
-```
-
-For the table body’s column, we need to make sure that the birthday is only show if it is actually set:
-
-```smarty
-{if $person->birthday}{$person->birthday}{/if} |
-```
-
## Adding Birthday in Front End
diff --git a/docs/tutorial/series/part_3.md b/docs/tutorial/series/part_3.md
index 6170c3371..6e372279d 100644
--- a/docs/tutorial/series/part_3.md
+++ b/docs/tutorial/series/part_3.md
@@ -24,50 +24,66 @@ In addition to the components used in [part 1](part_1.md), we will use the [obje
The complete package will have the following file structure (including the files from [part 1](part_1.md)):
```
-├── acpMenu.xml
├── acptemplates
-│ ├── personAdd.tpl
-│ └── personList.tpl
+│ ├── personAdd.tpl
+│ └── personList.tpl
├── files
-│ ├── acp
-│ │ └── database
-│ │ └── install_com.woltlab.wcf.people.php
-│ └── lib
-│ ├── acp
-│ │ ├── form
-│ │ │ ├── PersonAddForm.class.php
-│ │ │ └── PersonEditForm.class.php
-│ │ └── page
-│ │ └── PersonListPage.class.php
-│ ├── data
-│ │ └── person
-│ │ ├── Person.class.php
-│ │ ├── PersonAction.class.php
-│ │ ├── PersonEditor.class.php
-│ │ └── PersonList.class.php
-│ ├── page
-│ │ ├── PersonListPage.class.php
-│ │ └── PersonPage.class.php
-│ └── system
-│ ├── cache
-│ │ └── runtime
-│ │ └── PersonRuntimeCache.class.php
-│ ├── comment
-│ │ └── manager
-│ │ └── PersonCommentManager.class.php
-│ └── page
-│ └── handler
-│ └── PersonPageHandler.class.php
+│ ├── acp
+│ │ └── database
+│ │ └── install_com.woltlab.wcf.people.php
+│ └── lib
+│ ├── acp
+│ │ ├── form
+│ │ │ ├── PersonAddForm.class.php
+│ │ │ └── PersonEditForm.class.php
+│ │ └── page
+│ │ └── PersonListPage.class.php
+│ ├── bootstrap
+│ │ └── com.woltlab.wcf.people.php
+│ ├── data
+│ │ └── person
+│ │ ├── Person.class.php
+│ │ ├── PersonAction.class.php
+│ │ ├── PersonEditor.class.php
+│ │ └── PersonList.class.php
+│ ├── event
+│ │ └── gridView
+│ │ └── admin
+│ │ └── PersonGridViewInitialized.class.php
+│ ├── page
+│ │ ├── PersonListPage.class.php
+│ │ └── PersonPage.class.php
+│ └── system
+│ ├── cache
+│ │ └── runtime
+│ │ └── PersonRuntimeCache.class.php
+│ ├── comment
+│ │ └── manager
+│ │ └── PersonCommentManager.class.php
+│ ├── endpoint
+│ │ └── controller
+│ │ └── core
+│ │ └── persons
+│ │ └── DeletePerson.class.php
+│ ├── gridView
+│ │ └── admin
+│ │ └── PersonGridView.class.php
+│ ├── interaction
+│ │ └── admin
+│ │ └── PersonInteractions.class.php
+│ └── page
+│ └── handler
+│ └── PersonPageHandler.class.php
├── language
-│ ├── de.xml
-│ └── en.xml
+│ ├── de.xml
+│ └── en.xml
├── menuItem.xml
├── objectType.xml
├── package.xml
├── page.xml
├── templates
-│ ├── person.tpl
-│ └── personList.tpl
+│ ├── person.tpl
+│ └── personList.tpl
└── userGroupOption.xml
```
@@ -132,8 +148,8 @@ With this option, comments on individual people can be disabled.
The `PersonPage` class is similar to the `PersonEditForm` in the ACP in that it reads the id of the requested person from the request data and validates the id in `readParameters()`.
The rest of the code only handles fetching the list of comments on the requested person.
-In `readData()`, this list is fetched using `CommentHandler::getCommentList()` if comments are enabled for the person.
-The `assignVariables()` method assigns some additional template variables like `$commentCanAdd`, which is `1` if the active person can add comments and is `0` otherwise, `$lastCommentTime`, which contains the UNIX timestamp of the last comment, and `$likeData`, which contains data related to the likes for the disabled comments.
+In `readData()`, this list is fetched using a `CommentsView` if comments are enabled for the person.
+The `assignVariables()` method assigns this view as a template variable.
### `person.tpl`
@@ -144,10 +160,7 @@ The `assignVariables()` method assigns some additional template variables like `
) }}
For now, the `person` template is still very empty and only shows the comments in the content area.
-The template code shown for comments is very generic and used in this form in many locations as it only sets the header of the comment list and the container `ul#personCommentList` element for the comments shown by `commentList` template.
-The `ul#personCommentList` elements has five additional `data-` attributes required by the JavaScript API for comments for loading more comments or creating new ones.
-The `commentListAddComment` template adds the WYSIWYG support.
-The attribute `wysiwygSelector` should be the id of the comment list `personCommentList` with an additional `AddComment` suffix.
+For the main part of the page we only need to call the `render()` method of the `CommentsView`.
### `page.xml`
diff --git a/mkdocs.yml b/mkdocs.yml
index 80b92cf9f..e6ac965f0 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -6,18 +6,18 @@ theme:
logo: assets/logo.png
favicon: assets/default.favicon.ico
palette:
- - media: "(prefers-color-scheme)"
+ - media: '(prefers-color-scheme)'
toggle:
icon: material/link
name: Switch to light mode
- - media: "(prefers-color-scheme: light)"
+ - media: '(prefers-color-scheme: light)'
scheme: default
primary: teal
accent: indigo
toggle:
icon: material/toggle-switch
name: Switch to dark mode
- - media: "(prefers-color-scheme: dark)"
+ - media: '(prefers-color-scheme: dark)'
scheme: slate
primary: teal
accent: indigo
@@ -206,9 +206,9 @@ nav:
- 'Part 1': 'tutorial/series/part_1.md'
- 'Part 2': 'tutorial/series/part_2.md'
- 'Part 3': 'tutorial/series/part_3.md'
- - 'Part 4': 'tutorial/series/part_4.md'
- - 'Part 5': 'tutorial/series/part_5.md'
- - 'Part 6': 'tutorial/series/part_6.md'
+# - 'Part 4': 'tutorial/series/part_4.md'
+# - 'Part 5': 'tutorial/series/part_5.md'
+# - 'Part 6': 'tutorial/series/part_6.md'
plugins:
- git-revision-date
diff --git a/snippets/tutorial/tutorial-series/part-1/acpMenu.xml b/snippets/tutorial/tutorial-series/part-1/acpMenu.xml
deleted file mode 100644
index 18bc0d945..000000000
--- a/snippets/tutorial/tutorial-series/part-1/acpMenu.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
- wcf.acp.menu.link.content
-
-
- wcf\acp\page\PersonListPage
- wcf.acp.menu.link.person
- admin.content.canManagePeople
-
-
- wcf\acp\form\PersonAddForm
- wcf.acp.menu.link.person.list
- admin.content.canManagePeople
- plus
-
-
-
diff --git a/snippets/tutorial/tutorial-series/part-1/acptemplates/personList.tpl b/snippets/tutorial/tutorial-series/part-1/acptemplates/personList.tpl
index befd40e57..f36730ff3 100644
--- a/snippets/tutorial/tutorial-series/part-1/acptemplates/personList.tpl
+++ b/snippets/tutorial/tutorial-series/part-1/acptemplates/personList.tpl
@@ -14,70 +14,8 @@
-{if $pages > 1}
-
-{/if}
-
-{if $objects|count}
-
-
-
-{else}
- {lang}wcf.global.noItems{/lang}
-{/if}
+
+ {unsafe:$gridView->render()}
+
{include file='footer'}
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonAddForm.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonAddForm.class.php
index 2d5490332..81d68040f 100644
--- a/snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonAddForm.class.php
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonAddForm.class.php
@@ -13,7 +13,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Acp\Form
*/
class PersonAddForm extends AbstractFormBuilderForm
{
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonEditForm.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonEditForm.class.php
index 47c2b7650..dc11fdc01 100644
--- a/snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonEditForm.class.php
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/acp/form/PersonEditForm.class.php
@@ -11,7 +11,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Acp\Form
*/
class PersonEditForm extends PersonAddForm
{
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/acp/page/PersonListPage.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/acp/page/PersonListPage.class.php
index 9d57855b7..99affb2fc 100644
--- a/snippets/tutorial/tutorial-series/part-1/files/lib/acp/page/PersonListPage.class.php
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/acp/page/PersonListPage.class.php
@@ -2,18 +2,19 @@
namespace wcf\acp\page;
-use wcf\data\person\PersonList;
-use wcf\page\SortablePage;
+use wcf\page\AbstractGridViewPage;
+use wcf\system\gridView\admin\PersonGridView;
/**
* Shows the list of people.
*
- * @author Matthias Schmidt
- * @copyright 2001-2021 WoltLab GmbH
- * @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Acp\Page
+ * @author Matthias Schmidt
+ * @copyright 2001-2025 WoltLab GmbH
+ * @license GNU Lesser General Public License
+ *
+ * @extends AbstractGridViewPage
*/
-class PersonListPage extends SortablePage
+class PersonListPage extends AbstractGridViewPage
{
/**
* @inheritDoc
@@ -25,13 +26,9 @@ class PersonListPage extends SortablePage
*/
public $neededPermissions = ['admin.content.canManagePeople'];
- /**
- * @inheritDoc
- */
- public $objectListClassName = PersonList::class;
-
- /**
- * @inheritDoc
- */
- public $validSortFields = ['personID', 'firstName', 'lastName'];
+ #[\Override]
+ protected function createGridView(): PersonGridView
+ {
+ return new PersonGridView();
+ }
}
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/bootstrap/com.woltlab.wcf.people.php b/snippets/tutorial/tutorial-series/part-1/files/lib/bootstrap/com.woltlab.wcf.people.php
new file mode 100644
index 000000000..829d95fc9
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/bootstrap/com.woltlab.wcf.people.php
@@ -0,0 +1,45 @@
+register(
+ ItemCollecting::class,
+ static function (ItemCollecting $event) {
+ if (!WCF::getSession()->getPermission('admin.content.canManagePeople')) {
+ return;
+ }
+
+ $event->register(new AcpMenuItem(
+ 'wcf.acp.menu.link.person',
+ parentMenuItem: 'wcf.acp.menu.link.content'
+ ));
+ $event->register(new AcpMenuItem(
+ 'wcf.acp.menu.link.person.list',
+ parentMenuItem: 'wcf.acp.menu.link.person',
+ link: LinkHandler::getInstance()->getControllerLink(PersonListPage::class),
+ ));
+ $event->register(new AcpMenuItem(
+ 'wcf.acp.menu.link.person.add',
+ parentMenuItem: 'wcf.acp.menu.link.person.list',
+ link: LinkHandler::getInstance()->getControllerLink(PersonAddForm::class),
+ icon: FontAwesomeIcon::fromValues('plus')
+ ));
+ }
+ );
+
+ EventHandler::getInstance()->register(
+ ControllerCollecting::class,
+ static function (ControllerCollecting $event) {
+ $event->register(new \wcf\system\endpoint\controller\core\persons\DeletePerson());
+ }
+ );
+};
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/Person.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/Person.class.php
index 24a502859..e6f49001f 100644
--- a/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/Person.class.php
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/Person.class.php
@@ -11,7 +11,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Data\Person
*
* @property-read int $personID unique id of the person
* @property-read string $firstName first name of the person
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonAction.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonAction.class.php
index 3f3465561..ee1b67fa1 100644
--- a/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonAction.class.php
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonAction.class.php
@@ -10,7 +10,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Data\Person
*
* @method Person create()
* @method PersonEditor[] getObjects()
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonEditor.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonEditor.class.php
index b8d5a3ce7..e89eb82a5 100644
--- a/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonEditor.class.php
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonEditor.class.php
@@ -10,7 +10,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Data\Person
*
* @method static Person create(array $parameters = [])
* @method Person getDecoratedObject()
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonList.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonList.class.php
index 4e16a0d0e..340e3b3ef 100644
--- a/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonList.class.php
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/data/person/PersonList.class.php
@@ -10,13 +10,10 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @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 {}
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/event/gridView/admin/PersonGridViewInitialized.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/event/gridView/admin/PersonGridViewInitialized.class.php
new file mode 100644
index 000000000..484f057aa
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/event/gridView/admin/PersonGridViewInitialized.class.php
@@ -0,0 +1,18 @@
+
+ */
+final class PersonGridViewInitialized implements IPsr14Event
+{
+ public function __construct(public readonly PersonGridView $gridView) {}
+}
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/page/PersonListPage.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/page/PersonListPage.class.php
index 5fbacd441..ca478353f 100644
--- a/snippets/tutorial/tutorial-series/part-1/files/lib/page/PersonListPage.class.php
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/page/PersonListPage.class.php
@@ -10,7 +10,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Page
*/
class PersonListPage extends SortablePage
{
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/system/endpoint/controller/core/persons/DeletePerson.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/system/endpoint/controller/core/persons/DeletePerson.class.php
new file mode 100644
index 000000000..88d63cc76
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/system/endpoint/controller/core/persons/DeletePerson.class.php
@@ -0,0 +1,39 @@
+
+ */
+#[DeleteRequest('/core/persons/{id:\d+}')]
+final class DeletePerson implements IController
+{
+ #[\Override]
+ public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
+ {
+ if (!WCF::getSession()->getPermission('admin.content.canManagePeople')) {
+ throw new PermissionDeniedException();
+ }
+
+ $person = Helper::fetchObjectFromRequestParameter($variables['id'], Person::class);
+ $action = new PersonAction([$person], 'delete');
+ $action->executeAction();
+
+ return new JsonResponse([]);
+ }
+}
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/system/gridView/admin/PersonGridView.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/system/gridView/admin/PersonGridView.class.php
new file mode 100644
index 000000000..f8173c907
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/system/gridView/admin/PersonGridView.class.php
@@ -0,0 +1,72 @@
+
+ *
+ * @extends AbstractGridView
+ */
+final class PersonGridView extends AbstractGridView
+{
+ public function __construct()
+ {
+ $this->addColumns([
+ GridViewColumn::for('personID')
+ ->label('wcf.global.objectID')
+ ->renderer(new ObjectIdColumnRenderer())
+ ->sortable(),
+ GridViewColumn::for('firstName')
+ ->label('wcf.person.firstName')
+ ->sortable(),
+ GridViewColumn::for('lastName')
+ ->label('wcf.person.lastName')
+ ->titleColumn()
+ ->sortable(),
+ ]);
+
+ $provider = new PersonInteractions();
+ $provider->addInteractions([
+ new Divider(),
+ new EditInteraction(PersonEditForm::class)
+ ]);
+ $this->setInteractionProvider($provider);
+
+ $this->setDefaultSortField('personID');
+ $this->addRowLink(new GridViewRowLink(PersonEditForm::class));
+ }
+
+ #[\Override]
+ public function isAccessible(): bool
+ {
+ return WCF::getSession()->getPermission('admin.content.canManagePeople');
+ }
+
+ #[\Override]
+ protected function createObjectList(): PersonList
+ {
+ return new PersonList();
+ }
+
+ #[\Override]
+ protected function getInitializedEvent(): PersonGridViewInitialized
+ {
+ return new PersonGridViewInitialized($this);
+ }
+}
diff --git a/snippets/tutorial/tutorial-series/part-1/files/lib/system/interaction/admin/PersonInteractions.class.php b/snippets/tutorial/tutorial-series/part-1/files/lib/system/interaction/admin/PersonInteractions.class.php
new file mode 100644
index 000000000..38b7fda4e
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-1/files/lib/system/interaction/admin/PersonInteractions.class.php
@@ -0,0 +1,30 @@
+
+ */
+final class PersonInteractions extends AbstractInteractionProvider
+{
+ public function __construct()
+ {
+ $this->addInteractions([
+ new DeleteInteraction('core/persons/%s'),
+ ]);
+ }
+
+ #[\Override]
+ public function getObjectClassName(): string
+ {
+ return Person::class;
+ }
+}
diff --git a/snippets/tutorial/tutorial-series/part-1/package.xml b/snippets/tutorial/tutorial-series/part-1/package.xml
index f6eb532f6..55fdea256 100644
--- a/snippets/tutorial/tutorial-series/part-1/package.xml
+++ b/snippets/tutorial/tutorial-series/part-1/package.xml
@@ -3,8 +3,8 @@
WoltLab Suite Core Tutorial: People
Adds a simple management system for people as part of a tutorial to create packages.
- 5.4.0
- 2022-01-17
+ 6.2.0
+ 2025-10-19
@@ -13,11 +13,11 @@
- com.woltlab.wcf
+ com.woltlab.wcf
- com.woltlab.wcf
+ com.woltlab.wcf
@@ -27,7 +27,6 @@
-
diff --git a/snippets/tutorial/tutorial-series/part-2/eventListener.xml b/snippets/tutorial/tutorial-series/part-2/eventListener.xml
index 54d0c62f7..443f56cab 100644
--- a/snippets/tutorial/tutorial-series/part-2/eventListener.xml
+++ b/snippets/tutorial/tutorial-series/part-2/eventListener.xml
@@ -2,12 +2,6 @@
-
- admin
- wcf\acp\page\PersonListPage
- validateSortField
- wcf\system\event\listener\BirthdaySortFieldPersonListPageListener
-
admin
wcf\acp\form\PersonAddForm
diff --git a/snippets/tutorial/tutorial-series/part-2/files/lib/bootstrap/com.woltlab.wcf.people.birthday.php b/snippets/tutorial/tutorial-series/part-2/files/lib/bootstrap/com.woltlab.wcf.people.birthday.php
new file mode 100644
index 000000000..e6cecf58b
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-2/files/lib/bootstrap/com.woltlab.wcf.people.birthday.php
@@ -0,0 +1,18 @@
+register(
+ PersonGridViewInitialized::class,
+ static function (PersonGridViewInitialized $event) {
+ $event->gridView->addColumn(
+ GridViewColumn::for('birthday')
+ ->label('wcf.person.birthday')
+ ->sortable()
+ );
+ }
+ );
+};
diff --git a/snippets/tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdayPersonAddFormListener.class.php b/snippets/tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdayPersonAddFormListener.class.php
index 7250d1626..df15a871a 100644
--- a/snippets/tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdayPersonAddFormListener.class.php
+++ b/snippets/tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdayPersonAddFormListener.class.php
@@ -13,7 +13,6 @@
* @author Matthias Schmidt
* @copyright 2001-2022 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\System\Event\Listener
*/
final class BirthdayPersonAddFormListener extends AbstractEventListener
{
diff --git a/snippets/tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdaySortFieldPersonListPageListener.class.php b/snippets/tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdaySortFieldPersonListPageListener.class.php
deleted file mode 100644
index c7b9b0af9..000000000
--- a/snippets/tutorial/tutorial-series/part-2/files/lib/system/event/listener/BirthdaySortFieldPersonListPageListener.class.php
+++ /dev/null
@@ -1,24 +0,0 @@
-
- * @package WoltLabSuite\Core\System\Event\Listener
- */
-final class BirthdaySortFieldPersonListPageListener extends AbstractEventListener
-{
- /**
- * @see SortablePage::validateSortField()
- */
- public function onValidateSortField(SortablePage $page): void
- {
- $page->validSortFields[] = 'birthday';
- }
-}
diff --git a/snippets/tutorial/tutorial-series/part-2/package.xml b/snippets/tutorial/tutorial-series/part-2/package.xml
index bc6a5c603..d1cf88380 100644
--- a/snippets/tutorial/tutorial-series/part-2/package.xml
+++ b/snippets/tutorial/tutorial-series/part-2/package.xml
@@ -3,8 +3,8 @@
WoltLab Suite Core Tutorial: People (Birthday)
Adds a birthday field to the people management system as part of a tutorial to create packages.
- 5.4.0
- 2022-01-17
+ 6.2.0
+ 2025-10-19
@@ -13,12 +13,12 @@
- com.woltlab.wcf
- com.woltlab.wcf.people
+ com.woltlab.wcf
+ com.woltlab.wcf.people
- com.woltlab.wcf
+ com.woltlab.wcf
diff --git a/snippets/tutorial/tutorial-series/part-2/templateListener.xml b/snippets/tutorial/tutorial-series/part-2/templateListener.xml
index 8499b04eb..a220907bf 100644
--- a/snippets/tutorial/tutorial-series/part-2/templateListener.xml
+++ b/snippets/tutorial/tutorial-series/part-2/templateListener.xml
@@ -1,21 +1,6 @@
-
-
- columnHeads
- admin
- {lang}wcf.person.birthday{/lang}]]>
- personList
-
-
- columns
- admin
- {if $person->birthday}{$person->birthday}{/if}]]>
- personList
-
-
-
personStatistics
diff --git a/snippets/tutorial/tutorial-series/part-3/acpMenu.xml b/snippets/tutorial/tutorial-series/part-3/acpMenu.xml
deleted file mode 100644
index 18bc0d945..000000000
--- a/snippets/tutorial/tutorial-series/part-3/acpMenu.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
- wcf.acp.menu.link.content
-
-
- wcf\acp\page\PersonListPage
- wcf.acp.menu.link.person
- admin.content.canManagePeople
-
-
- wcf\acp\form\PersonAddForm
- wcf.acp.menu.link.person.list
- admin.content.canManagePeople
- plus
-
-
-
diff --git a/snippets/tutorial/tutorial-series/part-3/acptemplates/personList.tpl b/snippets/tutorial/tutorial-series/part-3/acptemplates/personList.tpl
index befd40e57..f36730ff3 100644
--- a/snippets/tutorial/tutorial-series/part-3/acptemplates/personList.tpl
+++ b/snippets/tutorial/tutorial-series/part-3/acptemplates/personList.tpl
@@ -14,70 +14,8 @@
-{if $pages > 1}
-
-{/if}
-
-{if $objects|count}
-
-
-
-{else}
- {lang}wcf.global.noItems{/lang}
-{/if}
+
+ {unsafe:$gridView->render()}
+
{include file='footer'}
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/acp/form/PersonAddForm.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/acp/form/PersonAddForm.class.php
index 7ecf6f878..12ee4d4f8 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/acp/form/PersonAddForm.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/acp/form/PersonAddForm.class.php
@@ -14,7 +14,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Acp\Form
*/
class PersonAddForm extends AbstractFormBuilderForm
{
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/acp/form/PersonEditForm.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/acp/form/PersonEditForm.class.php
index 47c2b7650..dc11fdc01 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/acp/form/PersonEditForm.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/acp/form/PersonEditForm.class.php
@@ -11,7 +11,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Acp\Form
*/
class PersonEditForm extends PersonAddForm
{
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/acp/page/PersonListPage.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/acp/page/PersonListPage.class.php
index 9d57855b7..99affb2fc 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/acp/page/PersonListPage.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/acp/page/PersonListPage.class.php
@@ -2,18 +2,19 @@
namespace wcf\acp\page;
-use wcf\data\person\PersonList;
-use wcf\page\SortablePage;
+use wcf\page\AbstractGridViewPage;
+use wcf\system\gridView\admin\PersonGridView;
/**
* Shows the list of people.
*
- * @author Matthias Schmidt
- * @copyright 2001-2021 WoltLab GmbH
- * @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Acp\Page
+ * @author Matthias Schmidt
+ * @copyright 2001-2025 WoltLab GmbH
+ * @license GNU Lesser General Public License
+ *
+ * @extends AbstractGridViewPage
*/
-class PersonListPage extends SortablePage
+class PersonListPage extends AbstractGridViewPage
{
/**
* @inheritDoc
@@ -25,13 +26,9 @@ class PersonListPage extends SortablePage
*/
public $neededPermissions = ['admin.content.canManagePeople'];
- /**
- * @inheritDoc
- */
- public $objectListClassName = PersonList::class;
-
- /**
- * @inheritDoc
- */
- public $validSortFields = ['personID', 'firstName', 'lastName'];
+ #[\Override]
+ protected function createGridView(): PersonGridView
+ {
+ return new PersonGridView();
+ }
}
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/bootstrap/com.woltlab.wcf.people.php b/snippets/tutorial/tutorial-series/part-3/files/lib/bootstrap/com.woltlab.wcf.people.php
new file mode 100644
index 000000000..829d95fc9
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/bootstrap/com.woltlab.wcf.people.php
@@ -0,0 +1,45 @@
+register(
+ ItemCollecting::class,
+ static function (ItemCollecting $event) {
+ if (!WCF::getSession()->getPermission('admin.content.canManagePeople')) {
+ return;
+ }
+
+ $event->register(new AcpMenuItem(
+ 'wcf.acp.menu.link.person',
+ parentMenuItem: 'wcf.acp.menu.link.content'
+ ));
+ $event->register(new AcpMenuItem(
+ 'wcf.acp.menu.link.person.list',
+ parentMenuItem: 'wcf.acp.menu.link.person',
+ link: LinkHandler::getInstance()->getControllerLink(PersonListPage::class),
+ ));
+ $event->register(new AcpMenuItem(
+ 'wcf.acp.menu.link.person.add',
+ parentMenuItem: 'wcf.acp.menu.link.person.list',
+ link: LinkHandler::getInstance()->getControllerLink(PersonAddForm::class),
+ icon: FontAwesomeIcon::fromValues('plus')
+ ));
+ }
+ );
+
+ EventHandler::getInstance()->register(
+ ControllerCollecting::class,
+ static function (ControllerCollecting $event) {
+ $event->register(new \wcf\system\endpoint\controller\core\persons\DeletePerson());
+ }
+ );
+};
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/Person.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/Person.class.php
index c663871a8..2aa46251d 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/Person.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/Person.class.php
@@ -13,7 +13,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Data\Person
*
* @property-read int $personID unique id of the person
* @property-read string $firstName first name of the person
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonAction.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonAction.class.php
index 3f3465561..ee1b67fa1 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonAction.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonAction.class.php
@@ -10,7 +10,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Data\Person
*
* @method Person create()
* @method PersonEditor[] getObjects()
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonEditor.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonEditor.class.php
index b8d5a3ce7..e89eb82a5 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonEditor.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonEditor.class.php
@@ -10,7 +10,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Data\Person
*
* @method static Person create(array $parameters = [])
* @method Person getDecoratedObject()
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonList.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonList.class.php
index 4e16a0d0e..340e3b3ef 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonList.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/data/person/PersonList.class.php
@@ -10,13 +10,10 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @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 {}
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/event/gridView/admin/PersonGridViewInitialized.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/event/gridView/admin/PersonGridViewInitialized.class.php
new file mode 100644
index 000000000..484f057aa
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/event/gridView/admin/PersonGridViewInitialized.class.php
@@ -0,0 +1,18 @@
+
+ */
+final class PersonGridViewInitialized implements IPsr14Event
+{
+ public function __construct(public readonly PersonGridView $gridView) {}
+}
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/page/PersonListPage.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/page/PersonListPage.class.php
index 5fbacd441..ca478353f 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/page/PersonListPage.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/page/PersonListPage.class.php
@@ -10,7 +10,6 @@
* @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Page
*/
class PersonListPage extends SortablePage
{
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/page/PersonPage.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/page/PersonPage.class.php
index d70feda1b..30a6231f0 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/page/PersonPage.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/page/PersonPage.class.php
@@ -2,52 +2,23 @@
namespace wcf\page;
-use wcf\data\comment\StructuredCommentList;
use wcf\data\person\Person;
-use wcf\system\comment\CommentHandler;
-use wcf\system\comment\manager\PersonCommentManager;
use wcf\system\exception\IllegalLinkException;
+use wcf\system\view\CommentsView;
use wcf\system\WCF;
/**
* Shows the details of a certain person.
*
- * @author Matthias Schmidt
+ * @author Matthias Schmidt
* @copyright 2001-2021 WoltLab GmbH
- * @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\Page
+ * @license GNU Lesser General Public License
*/
class PersonPage extends AbstractPage
{
- /**
- * list of comments
- * @var StructuredCommentList
- */
- public $commentList;
-
- /**
- * person comment manager object
- * @var PersonCommentManager
- */
- public $commentManager;
-
- /**
- * id of the person comment object type
- * @var int
- */
- public $commentObjectTypeID = 0;
-
- /**
- * shown person
- * @var Person
- */
- public $person;
-
- /**
- * id of the shown person
- * @var int
- */
- public $personID = 0;
+ public Person $person;
+ public int $personID = 0;
+ public ?CommentsView $commentsView = null;
/**
* @inheritDoc
@@ -57,11 +28,7 @@ public function assignVariables()
parent::assignVariables();
WCF::getTPL()->assign([
- 'commentCanAdd' => WCF::getSession()->getPermission('user.person.canAddComment'),
- 'commentList' => $this->commentList,
- 'commentObjectTypeID' => $this->commentObjectTypeID,
- 'lastCommentTime' => $this->commentList ? $this->commentList->getMinCommentTime() : 0,
- 'likeData' => MODULE_LIKE && $this->commentList ? $this->commentList->getLikeData() : [],
+ 'commentsView' => $this->commentsView,
'person' => $this->person,
]);
}
@@ -74,16 +41,11 @@ public function readData()
parent::readData();
if ($this->person->enableComments) {
- $this->commentObjectTypeID = CommentHandler::getInstance()->getObjectTypeID(
- 'com.woltlab.wcf.person.personComment'
- );
- $this->commentManager = CommentHandler::getInstance()->getObjectType(
- $this->commentObjectTypeID
- )->getProcessor();
- $this->commentList = CommentHandler::getInstance()->getCommentList(
- $this->commentManager,
- $this->commentObjectTypeID,
- $this->person->personID
+ $this->commentsView = new CommentsView(
+ 'com.woltlab.wcf.person.personComment',
+ $this->person->personID,
+ 'personCommentList',
+ WCF::getSession()->getPermission('user.person.canAddComment')
);
}
}
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/system/cache/runtime/PersonRuntimeCache.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/system/cache/runtime/PersonRuntimeCache.class.php
index b81bcc367..70783bba4 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/system/cache/runtime/PersonRuntimeCache.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/system/cache/runtime/PersonRuntimeCache.class.php
@@ -11,7 +11,6 @@
* @author Matthias Schmidt
* @copyright 2001-2022 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\System\Cache\Runtime
*
* @method Person[] getCachedObjects()
* @method Person getObject($objectID)
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/system/comment/manager/PersonCommentManager.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/system/comment/manager/PersonCommentManager.class.php
index d658d2d3b..bf3ce29e9 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/system/comment/manager/PersonCommentManager.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/system/comment/manager/PersonCommentManager.class.php
@@ -13,7 +13,6 @@
* @author Matthias Schmidt
* @copyright 2001-2022 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\System\Comment\Manager
*/
final class PersonCommentManager extends AbstractCommentManager
{
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/system/endpoint/controller/core/persons/DeletePerson.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/system/endpoint/controller/core/persons/DeletePerson.class.php
new file mode 100644
index 000000000..88d63cc76
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/system/endpoint/controller/core/persons/DeletePerson.class.php
@@ -0,0 +1,39 @@
+
+ */
+#[DeleteRequest('/core/persons/{id:\d+}')]
+final class DeletePerson implements IController
+{
+ #[\Override]
+ public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
+ {
+ if (!WCF::getSession()->getPermission('admin.content.canManagePeople')) {
+ throw new PermissionDeniedException();
+ }
+
+ $person = Helper::fetchObjectFromRequestParameter($variables['id'], Person::class);
+ $action = new PersonAction([$person], 'delete');
+ $action->executeAction();
+
+ return new JsonResponse([]);
+ }
+}
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/system/gridView/admin/PersonGridView.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/system/gridView/admin/PersonGridView.class.php
new file mode 100644
index 000000000..f8173c907
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/system/gridView/admin/PersonGridView.class.php
@@ -0,0 +1,72 @@
+
+ *
+ * @extends AbstractGridView
+ */
+final class PersonGridView extends AbstractGridView
+{
+ public function __construct()
+ {
+ $this->addColumns([
+ GridViewColumn::for('personID')
+ ->label('wcf.global.objectID')
+ ->renderer(new ObjectIdColumnRenderer())
+ ->sortable(),
+ GridViewColumn::for('firstName')
+ ->label('wcf.person.firstName')
+ ->sortable(),
+ GridViewColumn::for('lastName')
+ ->label('wcf.person.lastName')
+ ->titleColumn()
+ ->sortable(),
+ ]);
+
+ $provider = new PersonInteractions();
+ $provider->addInteractions([
+ new Divider(),
+ new EditInteraction(PersonEditForm::class)
+ ]);
+ $this->setInteractionProvider($provider);
+
+ $this->setDefaultSortField('personID');
+ $this->addRowLink(new GridViewRowLink(PersonEditForm::class));
+ }
+
+ #[\Override]
+ public function isAccessible(): bool
+ {
+ return WCF::getSession()->getPermission('admin.content.canManagePeople');
+ }
+
+ #[\Override]
+ protected function createObjectList(): PersonList
+ {
+ return new PersonList();
+ }
+
+ #[\Override]
+ protected function getInitializedEvent(): PersonGridViewInitialized
+ {
+ return new PersonGridViewInitialized($this);
+ }
+}
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/system/interaction/admin/PersonInteractions.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/system/interaction/admin/PersonInteractions.class.php
new file mode 100644
index 000000000..38b7fda4e
--- /dev/null
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/system/interaction/admin/PersonInteractions.class.php
@@ -0,0 +1,30 @@
+
+ */
+final class PersonInteractions extends AbstractInteractionProvider
+{
+ public function __construct()
+ {
+ $this->addInteractions([
+ new DeleteInteraction('core/persons/%s'),
+ ]);
+ }
+
+ #[\Override]
+ public function getObjectClassName(): string
+ {
+ return Person::class;
+ }
+}
diff --git a/snippets/tutorial/tutorial-series/part-3/files/lib/system/page/handler/PersonPageHandler.class.php b/snippets/tutorial/tutorial-series/part-3/files/lib/system/page/handler/PersonPageHandler.class.php
index 67b15d67f..d432e1401 100644
--- a/snippets/tutorial/tutorial-series/part-3/files/lib/system/page/handler/PersonPageHandler.class.php
+++ b/snippets/tutorial/tutorial-series/part-3/files/lib/system/page/handler/PersonPageHandler.class.php
@@ -15,7 +15,6 @@
* @author Matthias Schmidt
* @copyright 2001-2022 WoltLab GmbH
* @license GNU Lesser General Public License
- * @package WoltLabSuite\Core\System\Page\Handler
*/
final class PersonPageHandler extends AbstractLookupPageHandler implements IOnlineLocationPageHandler
{
diff --git a/snippets/tutorial/tutorial-series/part-3/package.xml b/snippets/tutorial/tutorial-series/part-3/package.xml
index 8fa077ef0..a0ce8145e 100644
--- a/snippets/tutorial/tutorial-series/part-3/package.xml
+++ b/snippets/tutorial/tutorial-series/part-3/package.xml
@@ -3,8 +3,8 @@
WoltLab Suite Core Tutorial: People
Adds a simple management system for people as part of a tutorial to create packages.
- 5.4.0
- 2022-01-17
+ 6.2.0
+ 2025-10-19
@@ -13,11 +13,11 @@
- com.woltlab.wcf
+ com.woltlab.wcf
- com.woltlab.wcf
+ com.woltlab.wcf
@@ -27,7 +27,6 @@
-
diff --git a/snippets/tutorial/tutorial-series/part-3/templates/person.tpl b/snippets/tutorial/tutorial-series/part-3/templates/person.tpl
index 977127f90..bc6ff6100 100644
--- a/snippets/tutorial/tutorial-series/part-3/templates/person.tpl
+++ b/snippets/tutorial/tutorial-series/part-3/templates/person.tpl
@@ -4,32 +4,8 @@
{include file='header'}
-{if $person->enableComments}
- {if $commentList|count || $commentCanAdd}
-
- {/if}
+{if $commentsView}
+ {unsafe:$commentsView->render()}
{/if}
- {lang}wcf.person.comments{/lang} - {if $person->comments}{#$person->comments}{/if} -
-personID}" {* - *}data-object-type-id="{$commentObjectTypeID}" {* - *}data-comments="{if $person->comments}{$commentList->countObjects()}{else}0{/if}" {* - *}data-last-comment-time="{$lastCommentTime}" {* - *}> - {include file='commentListAddComment' wysiwygSelector='personCommentListAddComment'} - {include file='commentList'} -
-