Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
248bc71
Add the option to define the query selector for the header that shoul…
Cyperghost Jul 28, 2025
78ac201
Use `$view->user` instead of `$user`
Cyperghost Jul 28, 2025
26f53bb
Adding functions for ban/unban users via RPC endpoints
Cyperghost Jul 28, 2025
e5921d1
Adding functions for enable/disable users avtar via RPC endpoints
Cyperghost Jul 28, 2025
48092ea
Adding functions for enable/disable users signature via RPC endpoints
Cyperghost Jul 28, 2025
bfb3eb4
Adding functions for enable/disable users cover photo via RPC endpoints
Cyperghost Jul 28, 2025
15c6950
Adding functions for enable/disable users via RPC endpoints
Cyperghost Jul 28, 2025
4427cb1
Remove unused `Ui/User/Editor`
Cyperghost Jul 28, 2025
6051f14
Use `LinkInteraction` instead of an abstract Interaction
Cyperghost Jul 28, 2025
8d8e636
Remove data attributes that are no longer needed
Cyperghost Jul 28, 2025
b65c31a
Simplify the action for enabling/disabling a user's avatar/signature/…
Cyperghost Jul 28, 2025
726d7c0
Use `UserAction` directly so that the events continue to function.
Cyperghost Jul 28, 2025
21f2a8a
Reload context menu if `this.#reloadContextMenu` is `true`
Cyperghost Jul 29, 2025
30b594b
Rename `Ban` to `BanUser`
Cyperghost Jul 29, 2025
c899ba3
Apply suggestions from code review
Cyperghost Jul 29, 2025
04249a3
Check whether the user is null directly in `handle()` and expect a no…
Cyperghost Jul 29, 2025
610308d
Deprecated UserAction methods to ban a user and disable avatar, cover…
Cyperghost Jul 29, 2025
2742ab5
Deprecated UserAction methods to enable/unban a user and enable avata…
Cyperghost Jul 30, 2025
0b44359
Move user commands to `wcf\command\user`
Cyperghost Aug 15, 2025
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
1 change: 1 addition & 0 deletions com.woltlab.wcf/fileDelete.xml
Original file line number Diff line number Diff line change
Expand Up @@ -572,5 +572,6 @@
<file>icon/font-awesome/v6/brands/yoast.svg</file>
<file>icon/font-awesome/v6/brands/youtube.svg</file>
<file>icon/font-awesome/v6/brands/zhihu.svg</file>
<file>js/WoltLabSuite/Core/Ui/User/Editor.js</file>
</delete>
</data>
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
'{unsafe:$providerClassName|encodeJS}',
'{unsafe:$objectID|encodeJS}',
'{unsafe:$redirectUrl|encodeJS}',
'{unsafe:$reloadHeaderEndpoint|encodeJS}'
'{unsafe:$reloadHeaderEndpoint|encodeJS}',
'{unsafe:$headerCssClassName|encodeJS}',
{if $reloadContextMenu}true{else}false{/if}
);
});
</script>
Expand Down
40 changes: 0 additions & 40 deletions com.woltlab.wcf/templates/user.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,6 @@
{capture assign='headContent'}
{event name='javascriptInclude'}
<script data-relocate="true">
{if $__wcf->getUser()->userID && $__wcf->getUser()->userID != $user->userID}
require(['Language', 'WoltLabSuite/Core/Ui/User/Editor'], function(Language, UiUserEditor) {
Language.addObject({
'wcf.acp.user.disable': '{jslang}wcf.acp.user.disable{/jslang}',
'wcf.acp.user.enable': '{jslang}wcf.acp.user.enable{/jslang}',
'wcf.user.ban': '{jslang}wcf.user.ban{/jslang}',
'wcf.user.banned': '{jslang}wcf.user.banned{/jslang}',
'wcf.user.ban.confirmMessage': '{jslang}wcf.user.ban.confirmMessage{/jslang}',
'wcf.user.ban.expires': '{jslang}wcf.user.ban.expires{/jslang}',
'wcf.user.ban.expires.description': '{jslang}wcf.user.ban.expires.description{/jslang}',
'wcf.user.ban.neverExpires': '{jslang}wcf.user.ban.neverExpires{/jslang}',
'wcf.user.ban.reason.description': '{jslang}wcf.user.ban.reason.description{/jslang}',
'wcf.user.disableAvatar': '{jslang}wcf.user.disableAvatar{/jslang}',
'wcf.user.disableAvatar.confirmMessage': '{jslang}wcf.user.disableAvatar.confirmMessage{/jslang}',
'wcf.user.disableAvatar.expires': '{jslang}wcf.user.disableAvatar.expires{/jslang}',
'wcf.user.disableAvatar.expires.description': '{jslang}wcf.user.disableAvatar.expires.description{/jslang}',
'wcf.user.disableAvatar.neverExpires': '{jslang}wcf.user.disableAvatar.neverExpires{/jslang}',
'wcf.user.disableCoverPhoto': '{jslang}wcf.user.disableCoverPhoto{/jslang}',
'wcf.user.disableCoverPhoto.confirmMessage': '{jslang}wcf.user.disableCoverPhoto.confirmMessage{/jslang}',
'wcf.user.disableCoverPhoto.expires': '{jslang}wcf.user.disableCoverPhoto.expires{/jslang}',
'wcf.user.disableCoverPhoto.expires.description': '{jslang}wcf.user.disableCoverPhoto.expires.description{/jslang}',
'wcf.user.disableCoverPhoto.neverExpires': '{jslang}wcf.user.disableCoverPhoto.neverExpires{/jslang}',
'wcf.user.disableSignature': '{jslang}wcf.user.disableSignature{/jslang}',
'wcf.user.disableSignature.confirmMessage': '{jslang}wcf.user.disableSignature.confirmMessage{/jslang}',
'wcf.user.disableSignature.expires': '{jslang}wcf.user.disableSignature.expires{/jslang}',
'wcf.user.disableSignature.expires.description': '{jslang}wcf.user.disableSignature.expires.description{/jslang}',
'wcf.user.disableSignature.neverExpires': '{jslang}wcf.user.disableSignature.neverExpires{/jslang}',
'wcf.user.edit': '{jslang}wcf.user.edit{/jslang}',
'wcf.user.enableAvatar': '{jslang}wcf.user.enableAvatar{/jslang}',
'wcf.user.enableCoverPhoto': '{jslang}wcf.user.enableCoverPhoto{/jslang}',
'wcf.user.enableSignature': '{jslang}wcf.user.enableSignature{/jslang}',
'wcf.user.unban': '{jslang}wcf.user.unban{/jslang}'
});

{if $isAccessible}
UiUserEditor.init();
{/if}
});
{/if}

$(function() {
{if $__wcf->getUser()->userID && $__wcf->getUser()->userID != $user->userID}
WCF.Language.addObject({
Expand Down
40 changes: 10 additions & 30 deletions com.woltlab.wcf/templates/userProfileHeader.tpl
Original file line number Diff line number Diff line change
@@ -1,24 +1,4 @@
<header
class="userProfileHeader"
data-object-id="{$view->user->userID}"
{if $view->isInAccessibleGroup()}
{if $__wcf->session->getPermission('admin.user.canBanUser')}
data-banned="{$view->user->banned}"
{/if}
{if $__wcf->session->getPermission('admin.user.canDisableAvatar')}
data-disable-avatar="{$view->user->disableAvatar}"
{/if}
{if $__wcf->session->getPermission('admin.user.canDisableSignature')}
data-disable-signature="{$view->user->disableSignature}"
{/if}
{if $__wcf->session->getPermission('admin.user.canDisableCoverPhoto')}
data-disable-cover-photo="{$view->user->disableCoverPhoto}"
{/if}
{if $__wcf->session->getPermission('admin.user.canEnableUser')}
data-is-disabled="{if $view->user->activationCode}true{else}false{/if}"
{/if}
{/if}
>
<header class="userProfileHeader" data-object-id="{$view->user->userID}">
<div class="userProfileHeader__coverPhotoContainer">
<div class="userProfileHeader__coverPhoto">
<img src="{$view->user->getCoverPhoto()->getURL()}" data-object-id="{$view->user->getCoverPhoto()->getObjectID()}" class="userProfileHeader__coverPhotoImage">
Expand All @@ -30,7 +10,7 @@
{if $view->canEditCoverPhoto()}
<ul class="userProfileManageCoverPhoto buttonGroup buttonList smallButtons">
<li>
<button type="button" data-edit-cover-photo="{link controller="UserCoverPhoto" id=$user->userID}{/link}" data-default-cover-photo="{$__wcf->styleHandler->getStyle()->getCoverPhotoUrl()}" class="button small">
<button type="button" data-edit-cover-photo="{link controller="UserCoverPhoto" id=$view->user->userID}{/link}" data-default-cover-photo="{$__wcf->styleHandler->getStyle()->getCoverPhotoUrl()}" class="button small">
{icon name='camera'}
<span>{lang}wcf.user.coverPhoto.edit{/lang}</span>
</button>
Expand Down Expand Up @@ -68,7 +48,7 @@
<h1 class="userProfileHeader__username">
<span class="userProfileUsername">{$view->user->username}</span>
{if $view->user->banned}
<span class="jsTooltip jsUserBanned" title="{lang}wcf.user.banned{/lang}">
<span class="jsTooltip jsUserBanned" title="{lang user=$view->user}wcf.user.banned{/lang}">
{icon name='lock'}
</span>
{/if}
Expand Down Expand Up @@ -108,21 +88,21 @@
<div class="userProfileHeader__buttons">
{event name='beforeButtons'}

{if $__wcf->user->userID && $user->userID != $__wcf->user->userID}
{if !$__wcf->getUserProfileHandler()->isIgnoredByUser($user->userID)}
{if $__wcf->getUserProfileHandler()->isFollowing($user->userID)}
{if $__wcf->user->userID && $view->user->userID != $__wcf->user->userID}
{if !$__wcf->getUserProfileHandler()->isIgnoredByUser($view->user->userID)}
{if $__wcf->getUserProfileHandler()->isFollowing($view->user->userID)}
<button
type="button"
data-following="1"
data-follow-user="{link controller='UserFollow' id=$user->userID}{/link}"
data-follow-user="{link controller='UserFollow' id=$view->user->userID}{/link}"
class="userProfileHeader__button button small jsTooltip"
title="{lang}wcf.user.button.unfollow{/lang}"
>{icon name='minus' type='solid'}</button>
{else}
<button
type="button"
data-following="0"
data-follow-user="{link controller='UserFollow' id=$user->userID}{/link}"
data-follow-user="{link controller='UserFollow' id=$view->user->userID}{/link}"
class="userProfileHeader__button button small jsTooltip"
title="{lang}wcf.user.button.follow{/lang}"
>{icon name='plus' type='solid'}</button>
Expand Down Expand Up @@ -164,9 +144,9 @@
{if $view->user->canViewOnlineStatus() && $view->user->getLastActivityTime()}
<dt>{icon name='clock'} {lang}wcf.user.usersOnline.lastActivity{/lang}</dt>
<dd>{time time=$view->user->getLastActivityTime()}</dd>
{if $user->getCurrentLocation()}
{if $view->user->getCurrentLocation()}
<dt>{icon name='location-arrow'} {lang}wcf.user.usersOnline.location{/lang}</dt>
<dd>{unsafe:$user->getCurrentLocation()}</dd>
<dd>{unsafe:$view->user->getCurrentLocation()}</dd>
{/if}
{/if}
{if $__wcf->session->getPermission('admin.user.canViewIpAddress') && $view->user->registrationIpAddress}
Expand Down
18 changes: 15 additions & 3 deletions ts/WoltLabSuite/Core/Component/Interaction/StandaloneButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { getObject } from "WoltLabSuite/Core/Api/GetObject";
import { getContextMenuOptions } from "WoltLabSuite/Core/Api/Interactions/GetContextMenuOptions";
import UiDropdownSimple from "WoltLabSuite/Core/Ui/Dropdown/Simple";
import { insertHtml } from "WoltLabSuite/Core/Dom/Util";

interface HeaderContent {
template: string;
Expand All @@ -23,25 +24,35 @@ export class StandaloneButton {
#objectId: string | number;
#redirectUrl: string;
#reloadHeaderEndpoint: string;
#headerCssClassName: string;
#reloadContextMenu: boolean;

constructor(
container: HTMLElement,
providerClassName: string,
objectId: string | number,
redirectUrl: string,
reloadHeaderEndpoint: string,
headerCssClassName: string,
reloadContextMenu: boolean,
) {
this.#container = container;
this.#providerClassName = providerClassName;
this.#objectId = objectId;
this.#redirectUrl = redirectUrl;
this.#reloadHeaderEndpoint = reloadHeaderEndpoint;
this.#headerCssClassName = headerCssClassName;
this.#reloadContextMenu = reloadContextMenu;

this.#initInteractions();
this.#initEventListeners();
}

async #refreshContextMenu(): Promise<void> {
if (!this.#reloadContextMenu) {
return;
}

const { template } = await getContextMenuOptions(this.#providerClassName, this.#objectId);

const dropdown = this.#getDropdownMenu();
Expand All @@ -55,11 +66,11 @@ export class StandaloneButton {
}

async #refreshHeader(): Promise<void> {
if (!this.#reloadHeaderEndpoint) {
if (!this.#reloadHeaderEndpoint || !this.#headerCssClassName) {
return;
}

const header = document.querySelector(".contentHeaderTitle");
const header = document.querySelector(`${this.#headerCssClassName}`);
if (!header) {
return;
}
Expand All @@ -69,7 +80,8 @@ export class StandaloneButton {
return;
}

header.outerHTML = result.value.template;
insertHtml(result.value.template, header, "before");
header.remove();
}

#getDropdownMenu(): HTMLElement | undefined {
Expand Down
Loading