# Avatar-System
Wie Profilbilder für Benutzer und Kontakte aufgelöst, gerendert und gespeichert werden.
# Übersicht
SeminarPlan zeigt Avatare an über 15 Stellen im Frontend (Tabellen, Chat, Taskbar, Popups, Wochenplan, ...). Das System unterscheidet zwei Entitäten mit Avataren:
| Entität | Upload-Feld | Gravatar-Quelle | Fallback |
|---|---|---|---|
Benutzer (IUserEntity) | media.avatar | user.email | Gravatar Mystery Person (d=mp) |
Kontakt (IContactEntity) | media.avatar | Primäre E-Mail | Statischer Gravatar-Hash |
# Auflösungs-Reihenfolge
# Benutzer (userHelper.getUserAvatarUrl)
src/modules/helper/userHelper.ts
getUserAvatarUrl(user: IUser, size = 64) {
// 1. Lokaler Upload → media.avatar
if (user?.media?.avatar) {
return pathHelper.getImagePath(user.media.avatar);
}
// 2. Gravatar via E-Mail
return getEmailAvatarUrl(user.email, size);
}
getEmailAvatarUrl(email: string, size = 64) {
const hash = md5(email.trim().toLowerCase());
return `https://www.gravatar.com/avatar/${hash}/?s=${size}&d=mp`;
}
Fallback-Kette: media.avatar → Gravatar (E-Mail-Hash) → d=mp (Gravatar liefert generisches Personen-Icon)
# Kontakt (contactHelper.getAvatar)
src/composables/contactHelper.ts
getAvatar(contact: ContactReadable) {
// 1. Lokaler Upload
if (contact.media?.avatar) {
return pathHelper.getImagePath(contact.media.avatar);
}
// 2. Gravatar via primäre E-Mail
const primaryEmail = contact.primaryEmail
|| contact.emailAddresses?.find(e => e.isPrimary)?.value;
if (primaryEmail) {
return userHelper.getEmailAvatarUrl(primaryEmail);
}
// 3. Statischer Fallback (leerer Gravatar)
return 'https://www.gravatar.com/avatar/adefa.../?s=64';
}
Fallback-Kette: media.avatar → Gravatar (primäre E-Mail) → statischer Gravatar-Platzhalter
# Datenmodell
# media.avatar in der Datenbank
Das Feld speichert einen relativen Pfad zum hochgeladenen Bild:
{
"media": {
"avatar": "uploads/avatars/64a1b2c3d4e5f6.png"
}
}
pathHelper.getImagePath() löst diesen Pfad zur vollständigen URL auf:
- Relativer Pfad:
config.baseUrl + '/' + path - Absoluter Pfad (
/...): unverändert - URL (
http...): unverändert
# Type Definitions
// IUserEntity (src/api/types/types.ts)
interface IUser {
media: {
avatar?: string; // Pfad zum Profilbild
signature?: string; // Pfad zur Unterschrift
};
// ...
}
// IContactEntity (src/api/types/types.ts)
interface IContactEntity {
media: {
avatar: string; // Pfad zum Profilbild
logo: string; // Pfad zum Firmenlogo
};
// ...
}
# Komponenten
# SemAvatar.vue - Zentrale Avatar-Komponente
src/components/UI/SemAvatar.vue
Die Haupt-Komponente, die überall verwendet wird. Akzeptiert drei Bild-Quellen (Priorität):
| Prop | Typ | Beschreibung |
|---|---|---|
src | string | Direkte Bild-URL (höchste Prio, überschreibt user/contact) |
user | IUserEntity \| string | Benutzer-Objekt oder Benutzer-ID |
contact | IContactEntity | Kontakt-Objekt |
variant | 'rounded' \| 'square' | Form (Standard: rounded) |
size | string | CSS-Klasse für die Größe (Standard: w-32) |
showOnline | boolean | Online-Punkt anzeigen (Standard: true) |
Interne Auflösung:
src→ direkt verwendenuser→userHelper.getUserAvatarUrl(user)contact→contactHelper.getAvatar(contact)
Online-Indikator: Wenn showOnline aktiv ist und ein user übergeben wurde, prüft die Komponente über runtimeStore.onlineMembers, ob der Benutzer gerade verbunden ist und zeigt einen grünen Punkt.
# SemUserAvatarWithInfo.vue - Avatar mit Hover-Popup
src/components/UI/SemUserAvatarWithInfo.vue
Zeigt einen SemAvatar mit einem Hover-Popup (VMenu), das UserInfo anzeigt (Name, E-Mail, Telefon, Rolle).
# ContactAvatarCell.vue - Kontakt-Avatar in Tabellen
src/components/UI/DataTable/ContactAvatarCell.vue
Spezialisierte Tabellenzelle für Kontakt-Avatare. Unterstützt:
- Store-Lookup: Wenn der
contactStoregeladen ist, wird der Kontakt dort gesucht - V2-API-Fallback: Wenn der Store nicht geladen ist, wird der Kontakt über
getV2('/contacts/:id')geladen - Prop-Fallback:
displayName,avatarUrl,primaryEmailals direkte Props (für denormalisierte V2-Listendaten) - Hover-Popup: Zeigt
ContactInfomit Adresse, E-Mail, Telefon und Quick-Actions
# AvatarCell.vue - Benutzer-Avatar in Tabellen
src/components/UI/DataTable/AvatarCell.vue
Zeigt SemUserAvatarWithInfo für eine Benutzer-ID. Wird in den Tabellenspalten "Erstellt von" und "Geändert von" verwendet.
# Upload-Mechanismus
Der Avatar-Upload nutzt die SemSelectImage-Komponente:
src/components/UI/Inputs/SemSelectImage.vue
Diese Komponente bietet zwei Wege:
- Medienmanager: Auswahl eines bereits hochgeladenen Bilds
- Neuer Upload: Datei direkt hochladen (wird im Medienmanager gespeichert)
Nach der Auswahl wird der Pfad über ein @image-selected-Event an den Parent emittiert. Bei Benutzern aktualisiert SettingsUserSection.vue das media.avatar-Feld via updateUserMedia-Event.
# Gravatar-Details
SeminarPlan nutzt die Gravatar-API (opens new window):
- Hash: MD5 der getrimmten, lowercase E-Mail-Adresse (
md5viajs-md5) - URL-Format:
https://www.gravatar.com/avatar/{hash}/?s={size}&d=mp - Parameter
d=mp: "Mystery Person" - ein generisches Personen-Icon als Fallback, wenn kein Gravatar für die E-Mail existiert - Parameter
s: Bildgröße in Pixeln (Standard: 64)
Datenschutz
Gravatar-Requests senden einen MD5-Hash der E-Mail-Adresse an gravatar.com. Das ist ein externer Service. Wenn das ein Problem ist, solltest du lokale Uploads verwenden.
# Eigene Avatare in V2-Programmen verwenden
<template>
<!-- Für Benutzer (mit Online-Status) -->
<SemAvatar :user="userId" size="w-10" />
<!-- Für Kontakte -->
<SemAvatar :contact="contactObject" size="w-10" />
<!-- Direkte URL -->
<SemAvatar src="https://example.com/avatar.png" size="w-10" />
<!-- Benutzer mit Hover-Info -->
<SemUserAvatarWithInfo :user="userObject" size="w-10" />
</template>
# Siehe auch
- Avatare und Profilbilder - Anwender-Dokumentation
- API-Dokumentation - REST-Endpunkte
- Icons - Icon-System