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
65 changes: 35 additions & 30 deletions ts/WoltLabSuite/Core/Component/User/Avatar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,30 @@ import { dialogFactory } from "WoltLabSuite/Core/Component/Dialog";
import { registerCallback } from "WoltLabSuite/Core/Form/Builder/Field/Controller/FileProcessor";
import WoltlabCoreFile from "WoltLabSuite/Core/Component/File/woltlab-core-file";
import { showDefaultSuccessSnackbar } from "../Snackbar";
import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
import * as FormBuilderManager from "WoltLabSuite/Core/Form/Builder/Manager";

interface Result {
avatar: string;
}
type ResponseGetForm = {
dialog: string;
formId: string;
title: string;
};

let defaultAvatar = "";

async function editAvatar(button: HTMLElement): Promise<void> {
const { ok, result } = await dialogFactory().usingFormBuilder().fromEndpoint<Result>(button.dataset.editAvatar!);
defaultAvatar = button.dataset.defaultAvatar || "";

if (ok) {
const avatarForm = document.getElementById("avatarForm");
if (avatarForm) {
const img = avatarForm.querySelector<HTMLImageElement>("img.userAvatarImage")!;
if (img.src === result.avatar) {
return;
}
const json = (await prepareRequest(button.dataset.editAvatar!).get().fetchAsJson()) as ResponseGetForm;
const dialog = dialogFactory().fromHtml(json.dialog).withoutControls();

// In the ACP, the form should not be reloaded after changing the avatar.
img.src = result.avatar;
showDefaultSuccessSnackbar();
} else {
window.location.reload();
dialog.addEventListener("afterClose", () => {
if (FormBuilderManager.hasForm(json.formId)) {
FormBuilderManager.unregisterForm(json.formId);
}
}
});

dialog.show(json.title);
}

export function setup(): void {
Expand All @@ -51,28 +52,32 @@ export function setup(): void {
const avatarForm = document.getElementById("avatarForm");
if (avatarForm) {
registerCallback("wcf\\action\\UserAvatarAction_avatarFileID", (fileId: number | undefined) => {
if (!fileId) {
return;
let link = defaultAvatar;
if (fileId !== undefined) {
const file = document.querySelector<WoltlabCoreFile>(
`#wcf\\\\action\\\\UserAvatarAction_avatarFileIDContainer woltlab-core-file[file-id="${fileId}"]`,
)!;

link = file.link!;
}

const file = document.querySelector<WoltlabCoreFile>(
`#wcf\\\\action\\\\UserAvatarAction_avatarFileIDContainer woltlab-core-file[file-id="${fileId}"]`,
)!;
avatarForm.querySelector<HTMLImageElement>("img.userAvatarImage")!.src = link;

document
.getElementById("wcf\\action\\UserAvatarAction_avatarFileIDContainer")
?.closest("woltlab-core-dialog")
?.close();

avatarForm.querySelector<HTMLImageElement>("img.userAvatarImage")!.src = file.link!;
showDefaultSuccessSnackbar();
});
} else {
registerCallback("wcf\\action\\UserAvatarAction_avatarFileID", (fileId: number | undefined) => {
if (fileId === undefined) {
return;
}

registerCallback("wcf\\action\\UserAvatarAction_avatarFileID", () => {
document
.getElementById("wcf\\action\\UserAvatarAction_avatarFileIDContainer")
?.closest("woltlab-core-dialog")
?.querySelector<HTMLButtonElement>(".dialog__control__button--primary")
?.click();
?.close();

window.location.reload();
});
}

Expand Down
2 changes: 1 addition & 1 deletion wcfsetup/install/files/acp/templates/userAdd.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@
<dl>
<dt></dt>
<dd>
<button type="button" class="button userAvatarManagement" data-edit-avatar="{link controller="UserAvatar" forceFrontend=true id=$user->userID}{/link}">
<button type="button" class="button userAvatarManagement" data-edit-avatar="{link controller="UserAvatar" forceFrontend=true id=$user->userID}{/link}" data-default-avatar="{$__wcf->getPath()}images/avatars/avatar-default.svg">
{lang}wcf.user.avatar.edit{/lang}
</button>
</dd>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 1 addition & 63 deletions wcfsetup/install/files/lib/action/UserAvatarAction.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,16 @@

namespace wcf\action;

use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use wcf\data\IStorableObject;
use wcf\data\user\UserProfile;
use wcf\http\Helper;
use wcf\system\cache\runtime\UserProfileRuntimeCache;
use wcf\system\exception\NamedUserException;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\form\builder\data\processor\CustomFormDataProcessor;
use wcf\system\form\builder\field\dependency\ValueFormFieldDependency;
use wcf\system\form\builder\field\FileProcessorFormField;
use wcf\system\form\builder\field\RadioButtonFormField;
use wcf\system\form\builder\IFormDocument;
use wcf\system\form\builder\Psr15DialogForm;
use wcf\command\user\SetAvatar;
use wcf\system\user\UserProfileHandler;
use wcf\system\WCF;
use wcf\util\HtmlString;
Expand Down Expand Up @@ -69,31 +62,6 @@ public function handle(ServerRequestInterface $request): ResponseInterface

if ($request->getMethod() === 'GET') {
return $form->toResponse();
} elseif ($request->getMethod() === 'POST') {
$response = $form->validateRequest($request);
if ($response !== null) {
return $response;
}

$data = $form->getData()['data'];

// If the user has already uploaded and optionally cropped an image,
// this is already assigned to the `$user` and does not need to be saved again.
// However, if the user wants to delete their avatar and use a standard avatar,
// this must be saved and the cache reset
if ($data['avatarType'] === 'none') {
(new SetAvatar($user->getDecoratedObject()))();
}

// Reload the user object to get the updated avatar
UserProfileRuntimeCache::getInstance()->removeObject($user->userID);
$user = UserProfileRuntimeCache::getInstance()->getObject($user->userID);

return new JsonResponse([
'result' => [
'avatar' => $user->getAvatar()->getURL(),
],
]);
} else {
throw new \LogicException('Unreachable');
}
Expand All @@ -106,44 +74,14 @@ private function getForm(UserProfile $user): Psr15DialogForm
WCF::getLanguage()->get('wcf.user.avatar.edit')
);
$form->appendChildren([
RadioButtonFormField::create('avatarType')
->value("none")
->required()
->options([
"none" => WCF::getLanguage()->get('wcf.user.avatar.type.none'),
"custom" => WCF::getLanguage()->get('wcf.user.avatar.type.custom'),
]),
FileProcessorFormField::create('avatarFileID')
->objectType('com.woltlab.wcf.user.avatar')
->description('wcf.user.avatar.type.custom.description')
->required()
->singleFileUpload()
->bigPreview()
->simpleReplace()
->hideDeleteButton()
->thumbnailSize('128')
->addDependency(
ValueFormFieldDependency::create('avatarType')
->fieldId('avatarType')
->values(['custom'])
),
->thumbnailSize('128'),
]);
$form->getDataHandler()->addProcessor(
new CustomFormDataProcessor(
'avatarType',
null,
function (IFormDocument $document, array $data, IStorableObject $object) {
\assert($object instanceof UserProfile);
if ($object->avatarFileID === null) {
$data['avatarType'] = 'none';
} else {
$data['avatarType'] = 'custom';
}

return $data;
}
)
);

$form->markRequiredFields(false);
$form->updatedObject($user);
Expand Down
4 changes: 2 additions & 2 deletions wcfsetup/install/lang/de.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5257,9 +5257,7 @@ Sobald {if LANGUAGE_USE_INFORMAL_VARIANT}dein{else}Ihr{/if} Benutzerkonto freige
<item name="wcf.user.avatar"><![CDATA[Avatar]]></item>
<item name="wcf.user.avatar.edit"><![CDATA[Avatar bearbeiten]]></item>
<item name="wcf.user.avatar.error.disabled"><![CDATA[Der Administrator hat{if $__wcf->user->avatarID} {if LANGUAGE_USE_INFORMAL_VARIANT}deinen{else}Ihren{/if} derzeitigen Avatar gesperrt und{/if} {if LANGUAGE_USE_INFORMAL_VARIANT}dir{else}Ihnen{/if} die weitere Nutzungsberechtigung der Avatar-Funktion {if !$__wcf->user->disableAvatarReason}entzogen.{else} aus folgenden Gründen entzogen: {$__wcf->user->disableAvatarReason}{/if}]]></item>
<item name="wcf.user.avatar.type.custom"><![CDATA[Eigenen Avatar hochladen]]></item>
<item name="wcf.user.avatar.type.custom.description"><![CDATA[Eigene Avatare dürfen die Dateiendungen {"\n"|str_replace:', ':$__wcf->session->getPermission('user.profile.avatar.allowedFileExtensions')} besitzen.<br>Die Mindestgröße für Avatare liegt bei 128 × 128 Pixel.]]></item>
<item name="wcf.user.avatar.type.none"><![CDATA[Keinen Avatar verwenden]]></item>
</category>
<category name="wcf.user.condition">
<item name="wcf.user.condition.activityPoints"><![CDATA[Punkte]]></item>
Expand Down Expand Up @@ -5727,5 +5725,7 @@ Erlaubte Dateiendungen: {', '|implode:$allowedFileExtensions}]]></item>
<item name="wcf.user.option.skype"/>
<item name="wcf.user.profileHits"/>
<item name="wcf.user.profileHits.hitsPerDay"/>
<item name="wcf.user.avatar.type.none"/>
<item name="wcf.user.avatar.type.custom"/>
</delete>
</language>
4 changes: 2 additions & 2 deletions wcfsetup/install/lang/en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5256,9 +5256,7 @@ You also received a list of backup codes to use when your second factor becomes
<item name="wcf.user.avatar"><![CDATA[Avatar]]></item>
<item name="wcf.user.avatar.edit"><![CDATA[Edit Avatar]]></item>
<item name="wcf.user.avatar.error.disabled"><![CDATA[The administrators {if $__wcf->user->avatarID}have blocked your avatar and {/if}disallowed you from using an avatar{if $__wcf->user->disableAvatarReason}: {$__wcf->user->disableAvatarReason}{/if}.]]></item>
<item name="wcf.user.avatar.type.custom"><![CDATA[Upload Your Avatar]]></item>
<item name="wcf.user.avatar.type.custom.description"><![CDATA[You may use the following file extensions “{"\n"|str_replace:', ':$__wcf->session->getPermission('user.profile.avatar.allowedFileExtensions')}” for your avatar.<br>The minimum dimensions are 128 × 128 pixels.]]></item>
<item name="wcf.user.avatar.type.none"><![CDATA[Do Not Use Avatar]]></item>
</category>
<category name="wcf.user.condition">
<item name="wcf.user.condition.activityPoints"><![CDATA[Points]]></item>
Expand Down Expand Up @@ -5729,5 +5727,7 @@ Allowed extensions: {', '|implode:$allowedFileExtensions}]]></item>
<item name="wcf.user.option.skype"/>
<item name="wcf.user.profileHits"/>
<item name="wcf.user.profileHits.hitsPerDay"/>
<item name="wcf.user.avatar.type.none"/>
<item name="wcf.user.avatar.type.custom"/>
</delete>
</language>