Commit | Line | Data |
---|---|---|
376b2543 | 1 | # Part 3: Person Page and Comments |
88aa7dc5 MS |
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 | ||
7e276cf9 | 9 | In addition to the existing functions from [part 1](part_1.md), the package will provide the following possibilities/functions after this part of the tutorial: |
88aa7dc5 MS |
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 | ||
7e276cf9 | 19 | In addition to the components used in [part 1](part_1.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. |
88aa7dc5 MS |
20 | |
21 | ||
22 | ## Package Structure | |
23 | ||
7e276cf9 | 24 | The complete package will have the following file structure (including the files from [part 1](part_1.md)): |
88aa7dc5 MS |
25 | |
26 | ``` | |
27 | ├── acpMenu.xml | |
28 | ├── acptemplates | |
60ed94fa MS |
29 | │ ├── personAdd.tpl |
30 | │ └── personList.tpl | |
88aa7dc5 | 31 | ├── files |
21f91348 MS |
32 | │ ├── acp |
33 | │ │ └── database | |
34 | │ │ └── install_com.woltlab.wcf.people.php | |
60ed94fa MS |
35 | │ └── lib |
36 | │ ├── acp | |
37 | │ │ ├── form | |
38 | │ │ │ ├── PersonAddForm.class.php | |
39 | │ │ │ └── PersonEditForm.class.php | |
40 | │ │ └── page | |
41 | │ │ └── PersonListPage.class.php | |
42 | │ ├── data | |
43 | │ │ └── person | |
44 | │ │ ├── Person.class.php | |
45 | │ │ ├── PersonAction.class.php | |
46 | │ │ ├── PersonEditor.class.php | |
47 | │ │ └── PersonList.class.php | |
48 | │ ├── page | |
49 | │ │ ├── PersonListPage.class.php | |
50 | │ │ └── PersonPage.class.php | |
51 | │ └── system | |
52 | │ ├── cache | |
53 | │ │ └── runtime | |
54 | │ │ └── PersonRuntimeCache.class.php | |
55 | │ ├── comment | |
56 | │ │ └── manager | |
57 | │ │ └── PersonCommentManager.class.php | |
58 | │ └── page | |
59 | │ └── handler | |
60 | │ └── PersonPageHandler.class.php | |
88aa7dc5 | 61 | ├── language |
60ed94fa MS |
62 | │ ├── de.xml |
63 | │ └── en.xml | |
88aa7dc5 MS |
64 | ├── menuItem.xml |
65 | ├── objectType.xml | |
66 | ├── package.xml | |
67 | ├── page.xml | |
68 | ├── templates | |
60ed94fa MS |
69 | │ ├── person.tpl |
70 | │ └── personList.tpl | |
88aa7dc5 MS |
71 | └── userGroupOption.xml |
72 | ``` | |
73 | ||
086a42fa | 74 | !!! 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]({jinja{ config.repo_url }}tree/{jinja{ config.edit_uri.split("/")[1] }}/snippets/tutorial/tutorial-series/part-3)." |
88aa7dc5 MS |
75 | |
76 | ||
77 | ## Runtime Cache | |
78 | ||
21609ed2 | 79 | 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: |
88aa7dc5 | 80 | |
9a3f5fa3 | 81 | {jinja{ codebox( |
f778fce2 MS |
82 | title="files/lib/system/cache/runtime/PersonRuntimeCache.class.php", |
83 | language="php", | |
84 | filepath="tutorial/tutorial-series/part-3/files/lib/system/cache/runtime/PersonRuntimeCache.class.php" | |
9a3f5fa3 | 85 | ) }} |
88aa7dc5 MS |
86 | |
87 | ||
88 | ## Comments | |
89 | ||
90 | To allow users to comment on people, we need to tell the system that people support comments. | |
91 | 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): | |
92 | ||
9a3f5fa3 | 93 | {jinja{ codebox( |
f778fce2 MS |
94 | title="objectType.xml", |
95 | language="xml", | |
96 | filepath="tutorial/tutorial-series/part-3/objectType.xml" | |
9a3f5fa3 | 97 | ) }} |
88aa7dc5 MS |
98 | |
99 | 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): | |
100 | ||
9a3f5fa3 | 101 | {jinja{ codebox( |
f778fce2 MS |
102 | title="files/lib/system/comment/manager/PersonCommentManager.class.php", |
103 | language="php", | |
104 | filepath="tutorial/tutorial-series/part-3/files/lib/system/comment/manager/PersonCommentManager.class.php" | |
9a3f5fa3 | 105 | ) }} |
88aa7dc5 MS |
106 | |
107 | - First, the system is told the names of the permissions via the `$permission*` properties. | |
21609ed2 | 108 | More information about comment permissions can be found [here](../../php/api/comments.md#user-group-options). |
88aa7dc5 MS |
109 | - The `getLink()` method returns the link to the person with the passed comment id. |
110 | As in `isAccessible()`, `PersonRuntimeCache` is used to potentially save database queries. | |
111 | - The `isAccessible()` method checks if the active user can access the relevant person. | |
112 | As we do not have any special restrictions for accessing people, we only need to check if the person exists. | |
113 | - The `getTitle()` method returns the title used for comments and responses, which is just a generic language item in this case. | |
114 | - The `updateCounter()` updates the comments’ counter of the person. | |
115 | 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. | |
116 | ||
abb300ed | 117 | 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. |
88aa7dc5 MS |
118 | With this option, comments on individual people can be disabled. |
119 | ||
9003992d | 120 | !!! 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." |
88aa7dc5 MS |
121 | |
122 | ||
123 | ## Person Page | |
124 | ||
125 | ### `PersonPage` | |
126 | ||
9a3f5fa3 | 127 | {jinja{ codebox( |
f778fce2 MS |
128 | title="files/lib/page/PersonPage.class.php", |
129 | language="php", | |
130 | filepath="tutorial/tutorial-series/part-3/files/lib/page/PersonPage.class.php" | |
9a3f5fa3 | 131 | ) }} |
88aa7dc5 MS |
132 | |
133 | 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()`. | |
134 | The rest of the code only handles fetching the list of comments on the requested person. | |
135 | In `readData()`, this list is fetched using `CommentHandler::getCommentList()` if comments are enabled for the person. | |
136 | 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. | |
137 | ||
138 | ### `person.tpl` | |
139 | ||
9a3f5fa3 | 140 | {jinja{ codebox( |
f778fce2 MS |
141 | title="templates/person.tpl", |
142 | language="tpl", | |
143 | filepath="tutorial/tutorial-series/part-3/templates/person.tpl" | |
9a3f5fa3 | 144 | ) }} |
88aa7dc5 MS |
145 | |
146 | For now, the `person` template is still very empty and only shows the comments in the content area. | |
eb0aecac | 147 | 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. |
88aa7dc5 | 148 | The `ul#personCommentList` elements has five additional `data-` attributes required by the JavaScript API for comments for loading more comments or creating new ones. |
eb0aecac MS |
149 | The `commentListAddComment` template adds the WYSIWYG support. |
150 | The attribute `wysiwygSelector` should be the id of the comment list `personCommentList` with an additional `AddComment` suffix. | |
88aa7dc5 MS |
151 | |
152 | ### `page.xml` | |
153 | ||
9a3f5fa3 | 154 | {jinja{ codebox( |
f778fce2 MS |
155 | title="page.xml", |
156 | language="xml", | |
157 | filepath="tutorial/tutorial-series/part-3/page.xml" | |
9a3f5fa3 | 158 | ) }} |
88aa7dc5 MS |
159 | |
160 | The `page.xml` file has been extended for the new person page with identifier `com.woltlab.wcf.people.Person`. | |
161 | Compared to the pre-existing `com.woltlab.wcf.people.PersonList` page, there are four differences: | |
162 | ||
163 | 1. It has a `<handler>` element with a class name as value. | |
164 | This aspect will be discussed in more detail in the next section. | |
165 | 1. There are no `<content>` elements because, both, the title and the content of the page are dynamically generated in the template. | |
166 | 1. The `<requireObjectID>` tells the system that this page requires an object id to properly work, in this case a valid person id. | |
167 | 1. This page has a `<parent>` page, the person list page. | |
168 | In general, the details page for any type of object that is listed on a different page has the list page as its parent. | |
169 | ||
170 | ### `PersonPageHandler` | |
171 | ||
9a3f5fa3 | 172 | {jinja{ codebox( |
f778fce2 MS |
173 | title="files/lib/system/page/handler/PersonPageHandler.class.php", |
174 | language="php", | |
175 | filepath="tutorial/tutorial-series/part-3/files/lib/system/page/handler/PersonPageHandler.class.php" | |
9a3f5fa3 | 176 | ) }} |
88aa7dc5 MS |
177 | |
178 | 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. | |
179 | 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. | |
180 | ||
181 | For the `ILookupPageHandler` interface, we need to implement three methods: | |
182 | ||
183 | 1. `getLink($objectID)` returns the link to the person page with the given id. | |
184 | In this case, we simply delegate this method call to the `Person` object returned by `PersonRuntimeCache::getObject()`. | |
185 | 1. `isValid($objectID)` returns `true` if the person with the given id exists, otherwise `false`. | |
186 | Here, we use `PersonRuntimeCache::getObject()` again and check if the return value is `null`, which is the case for non-existing people. | |
187 | 1. `lookup($searchString)` is used when setting up an internal link and when searching for the linked person. | |
188 | This method simply searches the first and last name of the people and returns an array with the person data. | |
189 | 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-`). | |
190 | ||
191 | 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. | |
192 | 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. | |
193 | The `IOnlineLocationPageHandler` interface requires two methods to be implemented: | |
194 | ||
195 | 1. `getOnlineLocation(Page $page, UserOnline $user)` returns the textual description of the online location. | |
196 | The language item for the user online locations should use the pattern `wcf.page.onlineLocation.{page identifier}`. | |
197 | 1. `prepareOnlineLocation(Page $page, UserOnline $user)` is called for each user online before the `getOnlineLocation()` calls. | |
198 | 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. | |
199 | ||
200 | --- | |
201 | ||
202 | 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. | |
203 | ||
086a42fa | 204 | The complete source code of this part can be found on [GitHub]({jinja{ config.repo_url }}tree/{jinja{ config.edit_uri.split("/")[1] }}/snippets/tutorial/tutorial-series/part-3). |
88aa7dc5 | 205 |