ddd173e7aee2e4d5bb90e73b2067193a661adfb6
[GitHub/WoltLab/woltlab.github.io.git] / docs / tutorial_tutorial-series_part-3-person-page-and-comments.md
1 # Tutorial Series Part 3: Person Page and Comments
2
3 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.
4 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.
5
6
7 ## Package Functionality
8
9 In addition to the existing functions from [part 1](tutorial_tutorial-series_part-1-base-structure.md), the package will provide the following possibilities/functions after this part of the tutorial:
10
11 - Details page for each person linked in the front end person list
12 - Comment on people on their respective page (can be disabled per person)
13 - User online location for person details page with name and link to person details page
14 - Create menu items linking to specific person details pages
15
16
17 ## Used Components
18
19 In addition to the components used in [part 1](tutorial_tutorial-series_part-1-base-structure.md), we will use the [objectType package installation plugin](package_pip_object-type.md), use the [comment API](php_api_comments.md), create a [runtime cache](php_api_caches_runtime-caches.md), and create a page handler.
20
21
22 ## Package Structure
23
24 The complete package will have the following file structure (including the files from [part 1](tutorial_tutorial-series_part-1-base-structure.md)):
25
26 ```
27 ├── acpMenu.xml
28 ├── acptemplates
29 │ ├── personAdd.tpl
30 │ └── personList.tpl
31 ├── files
32 │ └── lib
33 │ ├── acp
34 │ │ ├── form
35 │ │ │ ├── PersonAddForm.class.php
36 │ │ │ └── PersonEditForm.class.php
37 │ │ └── page
38 │ │ └── PersonListPage.class.php
39 │ ├── data
40 │ │ └── person
41 │ │ ├── Person.class.php
42 │ │ ├── PersonAction.class.php
43 │ │ ├── PersonEditor.class.php
44 │ │ └── PersonList.class.php
45 │ ├── page
46 │ │ ├── PersonListPage.class.php
47 │ │ └── PersonPage.class.php
48 │ └── system
49 │ ├── cache
50 │ │ └── runtime
51 │ │ └── PersonRuntimeCache.class.php
52 │ ├── comment
53 │ │ └── manager
54 │ │ └── PersonCommentManager.class.php
55 │ └── page
56 │ └── handler
57 │ └── PersonPageHandler.class.php
58 ├── install.sql
59 ├── language
60 │ ├── de.xml
61 │ └── en.xml
62 ├── menuItem.xml
63 ├── objectType.xml
64 ├── package.xml
65 ├── page.xml
66 ├── templates
67 │ ├── person.tpl
68 │ └── personList.tpl
69 └── userGroupOption.xml
70 ```
71
72 !!! warning "We will not mention every code change between the first part and this part, as we only want to focus on the important, new parts of the code. For example, there is a new `Person::getLink()` method and new language items have been added. For all changes, please refer to the [source code on GitHub](https://github.com/WoltLab/woltlab.github.io/tree/master/_includes/tutorial/tutorial-series/part-3)."
73
74
75 ## Runtime Cache
76
77 To reduce the number of database queries when different APIs require person objects, we implement a [runtime cache](php_api_caches_runtime-caches.md) for people:
78
79 {% highlight php %}
80 {% include tutorial/tutorial-series/part-3/files/lib/system/cache/runtime/PersonRuntimeCache.class.php %}
81 {% endhighlight %}
82
83
84 ## Comments
85
86 To allow users to comment on people, we need to tell the system that people support comments.
87 This is done by registering a `com.woltlab.wcf.comment.commentableContent` object type whose processor implements [ICommentManager](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/comment/manager/ICommentManager.class.php):
88
89 {% highlight xml %}
90 {% include tutorial/tutorial-series/part-3/objectType.xml %}
91 {% endhighlight %}
92
93 The `PersonCommentManager` class extended `ICommentManager`’s default implementation [AbstractCommentManager](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/comment/manager/AbstractCommentManager.class.php):
94
95 {% highlight php %}
96 {% include tutorial/tutorial-series/part-3/files/lib/system/comment/manager/PersonCommentManager.class.php %}
97 {% endhighlight %}
98
99 - First, the system is told the names of the permissions via the `$permission*` properties.
100 More information about comment permissions can be found [here](php_api_comments.md#user-group-options).
101 - The `getLink()` method returns the link to the person with the passed comment id.
102 As in `isAccessible()`, `PersonRuntimeCache` is used to potentially save database queries.
103 - The `isAccessible()` method checks if the active user can access the relevant person.
104 As we do not have any special restrictions for accessing people, we only need to check if the person exists.
105 - The `getTitle()` method returns the title used for comments and responses, which is just a generic language item in this case.
106 - The `updateCounter()` updates the comments’ counter of the person.
107 We have added a new `comments` database table column to the `wcf1_person` database table in order to keep track on the number of comments.
108
109 Additionally, we have added a new `enableComments` database table column to the `wcf1_person` database table whose value can be set when creating or editing a person in the ACP.
110 With this option, comments on individual people can be disabled.
111
112 !!! info "Liking comments is already built-in and only requires some extra code in the `PersonPage` class for showing the likes of pre-loaded comments."
113
114
115 ## Person Page
116
117 ### `PersonPage`
118
119 {% highlight php %}
120 {% include tutorial/tutorial-series/part-3/files/lib/page/PersonPage.class.php %}
121 {% endhighlight %}
122
123 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()`.
124 The rest of the code only handles fetching the list of comments on the requested person.
125 In `readData()`, this list is fetched using `CommentHandler::getCommentList()` if comments are enabled for the person.
126 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.
127
128 ### `person.tpl`
129
130 {% highlight tpl %}
131 {% include tutorial/tutorial-series/part-3/templates/person.tpl %}
132 {% endhighlight %}
133
134 For now, the `person` template is still very empty and only shows the comments in the content area.
135 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.
136 The `ul#personCommentList` elements has five additional `data-` attributes required by the JavaScript API for comments for loading more comments or creating new ones.
137 The `commentListAddComment` template adds the WYSIWYG support.
138 The attribute `wysiwygSelector` should be the id of the comment list `personCommentList` with an additional `AddComment` suffix.
139
140 ### `page.xml`
141
142 {% highlight xml %}
143 {% include tutorial/tutorial-series/part-3/page.xml %}
144 {% endhighlight %}
145
146 The `page.xml` file has been extended for the new person page with identifier `com.woltlab.wcf.people.Person`.
147 Compared to the pre-existing `com.woltlab.wcf.people.PersonList` page, there are four differences:
148
149 1. It has a `<handler>` element with a class name as value.
150 This aspect will be discussed in more detail in the next section.
151 1. There are no `<content>` elements because, both, the title and the content of the page are dynamically generated in the template.
152 1. The `<requireObjectID>` tells the system that this page requires an object id to properly work, in this case a valid person id.
153 1. This page has a `<parent>` page, the person list page.
154 In general, the details page for any type of object that is listed on a different page has the list page as its parent.
155
156 ### `PersonPageHandler`
157
158 {% highlight php %}
159 {% include tutorial/tutorial-series/part-3/files/lib/system/page/handler/PersonPageHandler.class.php %}
160 {% endhighlight %}
161
162 Like any page handler, the `PersonPageHandler` class has to implement the [IMenuPageHandler](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/page/handler/IMenuPageHandler.class.php) interface, which should be done by extending the [AbstractMenuPageHandler](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/page/handler/AbstractMenuPageHandler.class.php) class.
163 As we want administrators to link to specific people in menus, for example, we have to also implement the [ILookupPageHandler](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/page/handler/ILookupPageHandler.class.php) interface by extending the [AbstractLookupPageHandler](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/page/handler/AbstractLookupPageHandler.class.php) class.
164
165 For the `ILookupPageHandler` interface, we need to implement three methods:
166
167 1. `getLink($objectID)` returns the link to the person page with the given id.
168 In this case, we simply delegate this method call to the `Person` object returned by `PersonRuntimeCache::getObject()`.
169 1. `isValid($objectID)` returns `true` if the person with the given id exists, otherwise `false`.
170 Here, we use `PersonRuntimeCache::getObject()` again and check if the return value is `null`, which is the case for non-existing people.
171 1. `lookup($searchString)` is used when setting up an internal link and when searching for the linked person.
172 This method simply searches the first and last name of the people and returns an array with the person data.
173 While the `link`, the `objectID`, and the `title` element are self-explanatory, the `image` element can either contain an HTML `<img>` tag, which is displayed next to the search result (WoltLab Suite uses an image tag for users showing their avatar, for example), or a FontAwesome icon class (starting with `fa-`).
174
175 Additionally, the class also implements [IOnlineLocationPageHandler](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/page/handler/IOnlineLocationPageHandler.class.php) which is used to determine the online location of users.
176 To ensure upwards-compatibility if the `IOnlineLocationPageHandler` interface changes, the [TOnlineLocationPageHandler](https://github.com/WoltLab/WCF/blob/master/wcfsetup/install/files/lib/system/page/handler/TOnlineLocationPageHandler.class.php) trait is used.
177 The `IOnlineLocationPageHandler` interface requires two methods to be implemented:
178
179 1. `getOnlineLocation(Page $page, UserOnline $user)` returns the textual description of the online location.
180 The language item for the user online locations should use the pattern `wcf.page.onlineLocation.{page identifier}`.
181 1. `prepareOnlineLocation(Page $page, UserOnline $user)` is called for each user online before the `getOnlineLocation()` calls.
182 In this case, calling `prepareOnlineLocation()` first enables us to add all relevant person ids to the person runtime cache so that for all `getOnlineLocation()` calls combined, only one database query is necessary to fetch all person objects.
183
184 ---
185
186 This concludes the third part of our tutorial series after which each person has a dedicated page on which people can comment on the person.
187
188 The complete source code of this part can be found on [GitHub](https://github.com/WoltLab/woltlab.github.io/tree/master/_includes/tutorial/tutorial-series/part-3).
189