Skip to content

feat: add support for customizable user avatar drivers#4130

Open
datlechin wants to merge 16 commits intoflarum:2.xfrom
datlechin:feat/avatar-driver
Open

feat: add support for customizable user avatar drivers#4130
datlechin wants to merge 16 commits intoflarum:2.xfrom
datlechin:feat/avatar-driver

Conversation

@datlechin
Copy link
Contributor

@datlechin datlechin commented Nov 24, 2024

This pull request introduces a new avatar driver system. The changes include adding support for different avatar drivers, updating the user model to accommodate these changes, and modifying relevant components to utilize the new avatar driver system.

Fixes #0000

Changes proposed in this pull request:

Reviewers should focus on:

Screenshot

Necessity

  • Has the problem that is being solved here been clearly explained?
  • If applicable, have various options for solving this problem been considered?
  • For core PRs, does this need to be in core, or could it be in an extension?
  • Are we willing to maintain this for years / potentially forever?

Confirmed

  • Frontend changes: tested on a local Flarum installation.
  • Backend changes: tests are green (run composer test).
  • Core developer confirmed locally this works as intended.
  • Tests have been added, or are not appropriate here.

Required changes:

@datlechin datlechin requested a review from a team as a code owner November 24, 2024 09:41
@imorland
Copy link
Member

Hey @datlechin sorry this has been dormant for so long. Do you have any interest in continuing user avatar drivers? If so, please would you rebase and resolve the conflicts here?

I'd really like to include this before the 2.0 stable release if possible 🙏

Copilot AI review requested due to automatic review settings December 22, 2025 04:36
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces a customizable avatar driver system that allows extensions to provide alternative avatar sources (e.g., Gravatar) when users don't have uploaded avatars. The implementation mirrors the existing display name driver pattern.

Key Changes:

  • Added avatar driver interface and default implementation that returns null for users without custom avatars
  • Introduced originalAvatarUrl attribute to distinguish between uploaded avatars and driver-generated ones
  • Extended admin UI to allow configuring avatar drivers when multiple options are available

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
framework/core/src/User/UserServiceProvider.php Registers avatar driver system with container bindings and default driver
framework/core/src/User/User.php Adds avatar driver property, setter method, and modified getAvatarUrlAttribute to use driver as fallback
framework/core/src/User/Avatar/DriverInterface.php New interface defining the contract for avatar drivers
framework/core/src/User/Avatar/DefaultDriver.php Default implementation returning null when no custom avatar exists
framework/core/src/Extend/User.php Extends User extender with avatarDriver() method for registering custom drivers
framework/core/src/Api/Resource/UserResource.php Adds originalAvatarUrl field to API exposing the raw avatar_url attribute
framework/core/src/Admin/Content/AdminPayload.php Includes available avatar drivers in admin payload for UI configuration
framework/core/locale/core.yml Adds translation keys for avatar driver settings in admin basics page
framework/core/js/src/forum/components/AvatarEditor.js Updates to use originalAvatarUrl() for detecting uploaded avatars
framework/core/js/src/common/models/User.tsx Adds originalAvatarUrl() accessor method to User model
framework/core/js/src/admin/components/BasicsPage.tsx Adds avatar driver selection UI when multiple drivers available
framework/core/js/src/admin/AdminApplication.tsx Adds avatarDrivers to TypeScript interface for admin data

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

datlechin and others added 2 commits December 26, 2025 11:12
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@datlechin
Copy link
Contributor Author

@imorland, I’ve completed the work on this PR. Should I include the Gravatar driver along with the default driver in the core?

@imorland imorland added this to the 2.0.0-beta.6 milestone Dec 29, 2025
Copy link
Member

@imorland imorland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one @datlechin this looks good to me.

Yes, let's include gravatar support in core. I think it would be used by some communities, and admins can always choose to switch from 'classic' to gravatar as they wish. I don't think the overhead would be too much here.

I think it best to add that as a seperate PR tho, rather than in this one.

Before I merge this, would you please create an counterpart PR to https://github.com/flarum/docs so that developers can see how to use it?

datlechin added a commit to datlechin/flarum-docs that referenced this pull request Dec 30, 2025
Documents the new avatar driver system introduced in flarum/framework#4130.
Includes implementation guide, examples, and admin configuration.
datlechin added a commit to datlechin/flarum-docs that referenced this pull request Dec 30, 2025
Documents the new avatar driver system introduced in flarum/framework#4130.
Includes implementation guide, examples, and admin configuration.
@datlechin
Copy link
Contributor Author

@imorland I’ve opened the pull request in flarum. I’ll also open another pull request to add Gravatar support to the core when this PR was merged

@datlechin datlechin requested a review from imorland December 30, 2025 03:09
@imorland imorland modified the milestones: 2.0.0-beta.6, 2.0.0-beta.7 Jan 26, 2026
Copy link
Contributor

@DavideIadeluca DavideIadeluca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just jumping in here. Generally, I really like the idea of custom avatar drivers and would like to see it for 2.x. I implemented a Gravatar driver for testing purposes locally, and everything is working as expected.

One thing that stood out to me is that two values related to the avatar URL are now serialized (avatarUrl and originalAvatarUrl). I haven’t dug into the details yet as to why this was done, but I generally think we’d be better off serializing just one value to the frontend and letting the backend handle the rest.

Besides this, lgtm

Copy link
Member

@imorland imorland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a well-structured feature that follows established Flarum patterns (mirrors displayNameDriver closely). The architecture is sound and it would enable Gravatar, identicons, and similar drivers as first-class extensions. Several things need to be addressed before merge.

Design question: does originalAvatarUrl belong in the public API?
Exposing originalAvatarUrl as a serialised API field is a significant API surface addition. Its only purpose in this PR is to let AvatarEditor know whether the user has a real uploaded avatar (to show the pencil vs plus-circle icon). This is an implementation detail that leaks through to every API consumer. Consider instead returning a boolean field (e.g. hasCustomAvatar) or resolving this client-side by comparing avatarUrl to what a driver would produce. If originalAvatarUrl must be exposed, it should have the same visibility constraints as the existing avatarUrl field.

AvatarEditor.js should be .tsx
The three changed lines in AvatarEditor.js are the only changes to that file. Since this is a JS → TS migration project (#3533), this file could be converted at the same time. Not a blocker, but a missed opportunity.

driverLocale() is not extensible for third-party driver labels
BasicsPage.driverLocale() is a static method that hardcodes the known driver labels. When a Gravatar extension registers an avatarDriver('gravatar', GravatarDriver::class), it has no way to register a human-readable label — it will fall back to the raw identifier string ('gravatar'). The existing displayNameDriver pattern has the same gap, but this PR makes it worse by adding another driver type. Consider documenting this limitation or providing an extension point (e.g. a static avatarDriverLocale map that extensions can populate).

getOriginalAvatarUrlAttribute array access
$this->attributes['avatar_url'] will throw an ErrorException (undefined array key) if the key is not present in the attributes array, e.g. when the model is freshly instantiated without all columns loaded. The existing getAvatarUrlAttribute receives ?string $value = null from Eloquent (which handles absent keys safely). Consider using $this->attributes['avatar_url'] ?? null instead.

Fallback when avatar_driver setting holds an unregistered identifier
In UserServiceProvider, when Arr::get($drivers, $driverName) returns null (unknown driver name in settings), the code falls back to AvatarDefaultDriver. That's reasonable, but it silently ignores a misconfiguration. This matches displayNameDriver behaviour, so acceptable — just worth noting.

No default value registered for avatar_driver setting
The Settings extender is not used to register a default for avatar_driver. Calling $settings->get('avatar_driver', '') with an empty-string default works, but it's inconsistent — an empty string is not the same as 'default'. If a driver named '' were ever registered, this would resolve incorrectly. Using 'default' as the fallback default would be clearer.

@imorland imorland modified the milestones: 2.0.0-beta.7, 2.0.0-beta.8 Feb 21, 2026
Replaces the `originalAvatarUrl` attribute with `hasUploadedAvatar` for
a more semantic check of whether a user has uploaded a custom avatar.
This change improves clarity and maintainability of avatar-related
logic.
Remove unnecessary null check for avatar_url. The `isset()` check
already
handles cases where the key is not present or its value is null.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants