Respect per-user visibility; only 'vorstand' overrides visibility; UI shows contactHidden per-member
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 47s
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 47s
This commit is contained in:
@@ -115,7 +115,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
<template v-if="canViewContactData">
|
<template v-if="member.contactHidden">
|
||||||
|
<span class="text-sm text-gray-400">Kontaktdaten nur für Vorstand sichtbar</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<a
|
<a
|
||||||
v-if="member.email"
|
v-if="member.email"
|
||||||
:href="`mailto:${member.email}`"
|
:href="`mailto:${member.email}`"
|
||||||
@@ -123,18 +126,14 @@
|
|||||||
>
|
>
|
||||||
{{ member.email }}
|
{{ member.email }}
|
||||||
</a>
|
</a>
|
||||||
<span
|
<span v-else class="text-sm text-gray-400">-</span>
|
||||||
v-else
|
|
||||||
class="text-sm text-gray-400"
|
|
||||||
>-</span>
|
|
||||||
</template>
|
</template>
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
class="text-sm text-gray-400"
|
|
||||||
>Nur für Vorstand</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
<template v-if="canViewContactData">
|
<template v-if="member.contactHidden">
|
||||||
|
<span class="text-sm text-gray-400">Kontaktdaten nur für Vorstand sichtbar</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<a
|
<a
|
||||||
v-if="member.phone"
|
v-if="member.phone"
|
||||||
:href="`tel:${member.phone}`"
|
:href="`tel:${member.phone}`"
|
||||||
@@ -142,15 +141,8 @@
|
|||||||
>
|
>
|
||||||
{{ member.phone }}
|
{{ member.phone }}
|
||||||
</a>
|
</a>
|
||||||
<span
|
<span v-else class="text-sm text-gray-400">-</span>
|
||||||
v-else
|
|
||||||
class="text-sm text-gray-400"
|
|
||||||
>-</span>
|
|
||||||
</template>
|
</template>
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
class="text-sm text-gray-400"
|
|
||||||
>Nur für Vorstand</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="px-4 py-3 whitespace-nowrap">
|
<td class="px-4 py-3 whitespace-nowrap">
|
||||||
<button
|
<button
|
||||||
@@ -296,44 +288,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid sm:grid-cols-2 gap-3 text-gray-600">
|
<div class="grid sm:grid-cols-2 gap-3 text-gray-600">
|
||||||
<template v-if="canViewContactData">
|
<template v-if="member.contactHidden">
|
||||||
<div
|
<div class="col-span-2 flex items-center text-gray-500 text-sm italic">
|
||||||
v-if="member.email"
|
<Mail :size="16" class="mr-2" />
|
||||||
class="flex items-center"
|
Kontaktdaten nur für Vorstand sichtbar
|
||||||
>
|
</div>
|
||||||
<Mail
|
</template>
|
||||||
:size="16"
|
<template v-else>
|
||||||
class="mr-2 text-primary-600"
|
<div v-if="member.email" class="flex items-center">
|
||||||
/>
|
<Mail :size="16" class="mr-2 text-primary-600" />
|
||||||
<a
|
<a :href="`mailto:${member.email}`" class="hover:text-primary-600">{{ member.email }}</a>
|
||||||
:href="`mailto:${member.email}`"
|
</div>
|
||||||
class="hover:text-primary-600"
|
<div v-if="member.phone" class="flex items-center">
|
||||||
>{{ member.email }}</a>
|
<Phone :size="16" class="mr-2 text-primary-600" />
|
||||||
</div>
|
<a :href="`tel:${member.phone}`" class="hover:text-primary-600">{{ member.phone }}</a>
|
||||||
<div
|
|
||||||
v-if="member.phone"
|
|
||||||
class="flex items-center"
|
|
||||||
>
|
|
||||||
<Phone
|
|
||||||
:size="16"
|
|
||||||
class="mr-2 text-primary-600"
|
|
||||||
/>
|
|
||||||
<a
|
|
||||||
:href="`tel:${member.phone}`"
|
|
||||||
class="hover:text-primary-600"
|
|
||||||
>{{ member.phone }}</a>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="col-span-2 flex items-center text-gray-500 text-sm italic"
|
|
||||||
>
|
|
||||||
<Mail
|
|
||||||
:size="16"
|
|
||||||
class="mr-2"
|
|
||||||
/>
|
|
||||||
Kontaktdaten nur für Vorstand sichtbar
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
v-if="member.address"
|
v-if="member.address"
|
||||||
class="flex items-start col-span-2"
|
class="flex items-start col-span-2"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentUser = await getUserFromToken(token)
|
const currentUser = await getUserFromToken(token)
|
||||||
|
|
||||||
// Get manual members and registered users
|
// Get manual members and registered users
|
||||||
const manualMembers = await readMembers()
|
const manualMembers = await readMembers()
|
||||||
@@ -150,8 +150,10 @@ export default defineEventHandler(async (event) => {
|
|||||||
mergedMembers.sort((a, b) => a.name.localeCompare(b.name))
|
mergedMembers.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
// Die Mitgliederliste ist nur für authentifizierte Nutzer sichtbar (siehe oben).
|
// Die Mitgliederliste ist nur für authentifizierte Nutzer sichtbar (siehe oben).
|
||||||
// Respektiere individuelle Sichtbarkeitspräferenzen (user.visibility)
|
// Respektiere individuelle Sichtbarkeitspräferenzen (user.visibility)
|
||||||
const currentUserToken = token
|
const currentUserToken = token
|
||||||
const isViewerAuthenticated = !!currentUser
|
const isViewerAuthenticated = !!currentUser
|
||||||
|
// Only 'vorstand' may override member visibility
|
||||||
|
const isPrivilegedViewer = currentUser ? hasRole(currentUser, 'vorstand') : false
|
||||||
|
|
||||||
const sanitizedMembers = mergedMembers.map(member => {
|
const sanitizedMembers = mergedMembers.map(member => {
|
||||||
// Default: show email/phone/address to other logged-in members unless member.visibility explicitly hides them
|
// Default: show email/phone/address to other logged-in members unless member.visibility explicitly hides them
|
||||||
@@ -161,7 +163,16 @@ export default defineEventHandler(async (event) => {
|
|||||||
const showPhone = visibility.showPhone === undefined ? true : Boolean(visibility.showPhone)
|
const showPhone = visibility.showPhone === undefined ? true : Boolean(visibility.showPhone)
|
||||||
const showAddress = visibility.showAddress === undefined ? false : Boolean(visibility.showAddress)
|
const showAddress = visibility.showAddress === undefined ? false : Boolean(visibility.showAddress)
|
||||||
|
|
||||||
return {
|
// Determine if contact info existed but was hidden to the viewer
|
||||||
|
const hadEmail = !!member.email
|
||||||
|
const hadPhone = !!member.phone
|
||||||
|
const hadAddress = !!member.address
|
||||||
|
const emailVisible = (isPrivilegedViewer || (isViewerAuthenticated && showEmail))
|
||||||
|
const phoneVisible = (isPrivilegedViewer || (isViewerAuthenticated && showPhone))
|
||||||
|
const addressVisible = (isPrivilegedViewer || (isViewerAuthenticated && showAddress))
|
||||||
|
const contactHidden = (!emailVisible && hadEmail) || (!phoneVisible && hadPhone) || (!addressVisible && hadAddress)
|
||||||
|
|
||||||
|
return {
|
||||||
id: member.id,
|
id: member.id,
|
||||||
name: member.name,
|
name: member.name,
|
||||||
source: member.source,
|
source: member.source,
|
||||||
@@ -172,10 +183,12 @@ export default defineEventHandler(async (event) => {
|
|||||||
lastLogin: member.lastLogin,
|
lastLogin: member.lastLogin,
|
||||||
isMannschaftsspieler: member.isMannschaftsspieler,
|
isMannschaftsspieler: member.isMannschaftsspieler,
|
||||||
notes: member.notes || '',
|
notes: member.notes || '',
|
||||||
// Only include contact fields when viewer is authenticated and the member allows it
|
// Privileged viewers (vorstand) always see contact fields
|
||||||
email: (isViewerAuthenticated && showEmail) ? member.email : undefined,
|
email: emailVisible ? member.email : undefined,
|
||||||
phone: (isViewerAuthenticated && showPhone) ? member.phone : undefined,
|
phone: phoneVisible ? member.phone : undefined,
|
||||||
address: (isViewerAuthenticated && showAddress) ? member.address : undefined
|
address: addressVisible ? member.address : undefined,
|
||||||
|
// Flag for UI: data existed but is hidden to the current viewer
|
||||||
|
contactHidden
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -64,9 +64,10 @@ export default defineEventHandler(async (event) => {
|
|||||||
const visibility = body.visibility || body.visibilityPreferences || null
|
const visibility = body.visibility || body.visibilityPreferences || null
|
||||||
if (visibility && typeof visibility === 'object') {
|
if (visibility && typeof visibility === 'object') {
|
||||||
user.visibility = user.visibility || {}
|
user.visibility = user.visibility || {}
|
||||||
if (typeof visibility.showEmail === 'boolean') user.visibility.showEmail = visibility.showEmail
|
// Coerce values to booleans to be robust against string values from clients
|
||||||
if (typeof visibility.showPhone === 'boolean') user.visibility.showPhone = visibility.showPhone
|
if (visibility.showEmail !== undefined) user.visibility.showEmail = Boolean(visibility.showEmail)
|
||||||
if (typeof visibility.showAddress === 'boolean') user.visibility.showAddress = visibility.showAddress
|
if (visibility.showPhone !== undefined) user.visibility.showPhone = Boolean(visibility.showPhone)
|
||||||
|
if (visibility.showAddress !== undefined) user.visibility.showAddress = Boolean(visibility.showAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle password change
|
// Handle password change
|
||||||
|
|||||||
Reference in New Issue
Block a user