Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/tutorial/series/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
<!---
- [Part 4: Box and Box Conditions](part_4.md)
- [Part 5: Person Information](part_5.md)
- [Part 6: Activity Points and Activity Events](part_6.md)
-->
115 changes: 71 additions & 44 deletions docs/tutorial/series/part_1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -27,45 +26,68 @@ 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

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
```

Expand Down Expand Up @@ -163,18 +185,20 @@ 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.
The fourth level menu item `wcf.acp.menu.link.person.add` will only be shown as an icon and thus needs an additional element `icon` which takes a FontAwesome icon class as value.

### 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`

Expand All @@ -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`

Expand All @@ -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!

Expand Down
56 changes: 18 additions & 38 deletions docs/tutorial/series/part_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```


Expand Down Expand Up @@ -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
<th class="columnDate columnBirthday{if $sortField == 'birthday'} active {$sortOrder}{/if}"><a href="{link controller='PersonList'}pageNo={$pageNo}&sortField=birthday&sortOrder={if $sortField == 'birthday' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.person.birthday{/lang}</a></th>
```

For the table body’s column, we need to make sure that the birthday is only show if it is actually set:

```smarty
<td class="columnDate columnBirthday">{if $person->birthday}{$person->birthday}{/if}</td>
```


## Adding Birthday in Front End

Expand Down
97 changes: 55 additions & 42 deletions docs/tutorial/series/part_3.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down Expand Up @@ -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`

Expand All @@ -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`

Expand Down
Loading