Skip to content

Commit 356b27a

Browse files
committed
Migrate UserActivityEventAction::load() to an RPC endpoint
1 parent 3caf9ad commit 356b27a

7 files changed

Lines changed: 236 additions & 27 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Loads a paginated list of recent user activity events.
3+
*
4+
* @author Marcel Werk
5+
* @copyright 2001-2026 WoltLab GmbH
6+
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7+
* @since 6.3
8+
*/
9+
10+
import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
11+
import { ApiResult, apiResultFromError, apiResultFromValue } from "../../Result";
12+
13+
type Response = {
14+
lastEventID: number;
15+
lastEventTime: number;
16+
template: string;
17+
};
18+
19+
export async function renderUserActivityEvents(
20+
lastEventTime: number,
21+
lastEventID: number = 0,
22+
userID: number = 0,
23+
boxID: number = 0,
24+
filteredByFollowedUsers: boolean = false,
25+
): Promise<ApiResult<Response | Record<string, never>>> {
26+
const url = new URL(`${window.WSC_RPC_API_URL}core/users/activity-events/render`);
27+
url.searchParams.set("lastEventTime", lastEventTime.toString());
28+
url.searchParams.set("lastEventID", lastEventID.toString());
29+
url.searchParams.set("userID", userID.toString());
30+
url.searchParams.set("boxID", boxID.toString());
31+
url.searchParams.set("filteredByFollowedUsers", filteredByFollowedUsers ? "1" : "0");
32+
33+
let response: Response | Record<string, never>;
34+
try {
35+
response = (await prepareRequest(url).get().fetchAsJson()) as Response | Record<string, never>;
36+
} catch (e) {
37+
return apiResultFromError(e);
38+
}
39+
40+
return apiResultFromValue(response);
41+
}

ts/WoltLabSuite/Core/Component/User/RecentActivity/Loader.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,23 @@
88
*/
99

1010
import { dboAction } from "WoltLabSuite/Core/Ajax";
11+
import { renderUserActivityEvents } from "WoltLabSuite/Core/Api/Users/ActivityEvents/RenderUserActivityEvents";
1112
import { stringToBool } from "WoltLabSuite/Core/Core";
1213
import DomUtil from "WoltLabSuite/Core/Dom/Util";
1314
import { promiseMutex } from "WoltLabSuite/Core/Helper/PromiseMutex";
1415
import { getPhrase } from "WoltLabSuite/Core/Language";
1516

16-
type ResponseLoadMore = {
17-
lastEventID: number;
18-
lastEventTime: number;
19-
template: string;
20-
};
21-
2217
async function loadMore(container: HTMLElement): Promise<void> {
23-
const response = (await dboAction("load", "wcf\\data\\user\\activity\\event\\UserActivityEventAction")
24-
.payload({
25-
lastEventTime: container.dataset.lastEventTime,
26-
lastEventID: container.dataset.lastEventId || 0,
27-
userID: container.dataset.userId || 0,
28-
boxID: container.dataset.boxId || 0,
29-
filteredByFollowedUsers: stringToBool(container.dataset.filteredByFollowedUsers || ""),
30-
})
31-
.dispatch()) as ResponseLoadMore;
18+
const result = await renderUserActivityEvents(
19+
parseInt(container.dataset.lastEventTime || "0"),
20+
parseInt(container.dataset.lastEventId || "0"),
21+
parseInt(container.dataset.userId || "0"),
22+
parseInt(container.dataset.boxId || "0"),
23+
stringToBool(container.dataset.filteredByFollowedUsers || ""),
24+
);
25+
const response = result.unwrap();
3226

33-
if (response.template) {
27+
if ("template" in response) {
3428
container.dataset.lastEventTime = response.lastEventTime.toString();
3529
container.dataset.lastEventId = response.lastEventID.toString();
3630

wcfsetup/install/files/js/WoltLabSuite/Core/Api/Users/ActivityEvents/RenderUserActivityEvents.js

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wcfsetup/install/files/js/WoltLabSuite/Core/Component/User/RecentActivity/Loader.js

Lines changed: 4 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) {
238238
$event->register(new \wcf\system\endpoint\controller\core\users\groups\assignments\DeleteAssignment());
239239
$event->register(new \wcf\system\endpoint\controller\core\users\groups\assignments\EnableAssignment());
240240
$event->register(new \wcf\system\endpoint\controller\core\users\groups\assignments\DisableAssignment());
241+
$event->register(new \wcf\system\endpoint\controller\core\users\activityEvents\RenderUserActivityEvents());
241242
$event->register(new \wcf\system\endpoint\controller\core\users\groups\DeleteGroup());
242243
$event->register(new \wcf\system\endpoint\controller\core\menus\DeleteMenu());
243244
$event->register(new \wcf\system\endpoint\controller\core\trophies\EnableTrophy());

wcfsetup/install/files/lib/data/user/activity/event/UserActivityEventAction.class.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class UserActivityEventAction extends AbstractDatabaseObjectAction
3737
* Validates parameters to load recent activity entries.
3838
*
3939
* @return void
40+
* @deprecated 6.3
4041
*/
4142
public function validateLoad()
4243
{
@@ -70,6 +71,7 @@ public function validateLoad()
7071
* lastEventTime: int,
7172
* template: string,
7273
* }|array{}
74+
* @deprecated 6.3 Use `RenderUserActivityEvents` endpoint instead.
7375
*/
7476
public function load()
7577
{
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
3+
namespace wcf\system\endpoint\controller\core\users\activityEvents;
4+
5+
use Laminas\Diactoros\Response\JsonResponse;
6+
use Psr\Http\Message\ResponseInterface;
7+
use Psr\Http\Message\ServerRequestInterface;
8+
use wcf\data\box\Box;
9+
use wcf\data\user\activity\event\ViewableUserActivityEventList;
10+
use wcf\http\Helper;
11+
use wcf\system\box\RecentActivityListBoxController;
12+
use wcf\system\endpoint\GetRequest;
13+
use wcf\system\endpoint\IController;
14+
use wcf\system\exception\UserInputException;
15+
use wcf\system\user\activity\event\UserActivityEventHandler;
16+
use wcf\system\user\UserProfileHandler;
17+
use wcf\system\WCF;
18+
19+
/**
20+
* Retrieves the HTML code for the rendering of recent user activity events.
21+
*
22+
* @author Marcel Werk
23+
* @copyright 2001-2026 WoltLab GmbH
24+
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
25+
* @since 6.3
26+
*/
27+
#[GetRequest('/core/users/activity-events/render')]
28+
final class RenderUserActivityEvents implements IController
29+
{
30+
#[\Override]
31+
public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
32+
{
33+
$parameters = Helper::mapApiParameters($request, RenderUserActivityEventsParameters::class);
34+
35+
$boxController = null;
36+
if ($parameters->boxID !== 0) {
37+
$box = new Box($parameters->boxID);
38+
if (!$box->boxID) {
39+
throw new UserInputException('boxID');
40+
}
41+
42+
$controller = $box->getController();
43+
if (!$controller instanceof RecentActivityListBoxController) {
44+
throw new UserInputException('boxID');
45+
}
46+
47+
$boxController = $controller;
48+
}
49+
50+
return new JsonResponse($this->render(
51+
$parameters->lastEventTime,
52+
$parameters->lastEventID,
53+
$parameters->userID,
54+
$parameters->filteredByFollowedUsers,
55+
$boxController,
56+
));
57+
}
58+
59+
/**
60+
* @return array{
61+
* lastEventID: int,
62+
* lastEventTime: int,
63+
* template: string,
64+
* }|array{}
65+
*/
66+
private function render(
67+
int $lastEventTime,
68+
int $lastEventID = 0,
69+
int $userID = 0,
70+
bool $filteredByFollowedUsers = false,
71+
?RecentActivityListBoxController $boxController = null,
72+
): array {
73+
if ($boxController !== null) {
74+
$eventList = $boxController->getFilteredList();
75+
} else {
76+
$eventList = new ViewableUserActivityEventList();
77+
78+
if ($userID) {
79+
$eventList->getConditionBuilder()->add(
80+
"user_activity_event.userID = ?",
81+
[$userID]
82+
);
83+
} elseif (
84+
$filteredByFollowedUsers
85+
&& \count(UserProfileHandler::getInstance()->getFollowingUsers())
86+
) {
87+
$eventList->getConditionBuilder()->add(
88+
'user_activity_event.userID IN (?)',
89+
[UserProfileHandler::getInstance()->getFollowingUsers()]
90+
);
91+
}
92+
}
93+
94+
if ($lastEventID) {
95+
$eventList->getConditionBuilder()->add(
96+
"user_activity_event.time <= ?",
97+
[$lastEventTime]
98+
);
99+
$eventList->getConditionBuilder()->add(
100+
"user_activity_event.eventID < ?",
101+
[$lastEventID]
102+
);
103+
} else {
104+
$eventList->getConditionBuilder()->add(
105+
"user_activity_event.time < ?",
106+
[$lastEventTime]
107+
);
108+
}
109+
110+
$eventList->readObjects();
111+
$lastEventTime = $eventList->getLastEventTime();
112+
113+
if (!$lastEventTime) {
114+
return [];
115+
}
116+
117+
UserActivityEventHandler::validateEvents($eventList);
118+
119+
if ($boxController !== null) {
120+
$eventList->truncate($boxController->getBox()->limit);
121+
}
122+
123+
if (!\count($eventList)) {
124+
return [];
125+
}
126+
127+
$events = $eventList->getObjects();
128+
129+
return [
130+
'lastEventID' => \end($events)->eventID,
131+
'lastEventTime' => $lastEventTime,
132+
'template' => WCF::getTPL()->render('wcf', 'recentActivityListItem', [
133+
'eventList' => $eventList,
134+
]),
135+
];
136+
}
137+
}
138+
139+
/** @internal */
140+
final class RenderUserActivityEventsParameters
141+
{
142+
public function __construct(
143+
public readonly int $lastEventTime,
144+
public readonly int $lastEventID = 0,
145+
public readonly int $userID = 0,
146+
public readonly int $boxID = 0,
147+
public readonly bool $filteredByFollowedUsers = false,
148+
) {}
149+
}

0 commit comments

Comments
 (0)