This commit was merged in pull request #88.
This commit is contained in:
@@ -60,5 +60,14 @@ return [
|
||||
['name' => 'fee#batchCalculate', 'url' => '/api/v1/fees/batch-calculate', 'verb' => 'POST'],
|
||||
['name' => 'fee#markPaid', 'url' => '/api/v1/fees/records/{recordId}/paid', 'verb' => 'PUT'],
|
||||
['name' => 'fee#manualOverride', 'url' => '/api/v1/fees/records/{recordId}/override', 'verb' => 'PUT'],
|
||||
|
||||
// ── Permissions ─────────────────────────────────────────────
|
||||
['name' => 'permission#index', 'url' => '/api/v1/permissions', 'verb' => 'GET'],
|
||||
['name' => 'permission#listUsers', 'url' => '/api/v1/permissions/users', 'verb' => 'GET'],
|
||||
['name' => 'permission#setPermission', 'url' => '/api/v1/permissions/{ncUserId}', 'verb' => 'PUT'],
|
||||
['name' => 'permission#removePermission', 'url' => '/api/v1/permissions/{ncUserId}', 'verb' => 'DELETE'],
|
||||
|
||||
// ── Audit log ───────────────────────────────────────────────
|
||||
['name' => 'audit#index', 'url' => '/api/v1/audit-log', 'verb' => 'GET'],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Mitgliederverwaltung\Controller;
|
||||
|
||||
use OCA\Mitgliederverwaltung\Service\AuditService;
|
||||
use OCP\AppFramework\ApiController;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\IRequest;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* REST API controller for the audit log.
|
||||
*
|
||||
* Part of Issue #40.
|
||||
*/
|
||||
class AuditController extends ApiController {
|
||||
|
||||
private AuditService $auditService;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
AuditService $auditService,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->auditService = $auditService;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* List audit log entries with optional filters.
|
||||
*
|
||||
* GET /api/v1/audit-log?entitaet=member&ncUserId=admin&from=2026-01-01&to=2026-12-31&limit=50&offset=0
|
||||
*/
|
||||
#[NoCSRFRequired]
|
||||
public function index(): JSONResponse {
|
||||
try {
|
||||
$entitaet = $this->request->getParam('entitaet');
|
||||
$entitaetId = $this->request->getParam('entitaetId');
|
||||
$ncUserId = $this->request->getParam('ncUserId');
|
||||
$fromDate = $this->request->getParam('from');
|
||||
$toDate = $this->request->getParam('to');
|
||||
$limit = $this->request->getParam('limit');
|
||||
$offset = $this->request->getParam('offset');
|
||||
|
||||
$result = $this->auditService->getLog(
|
||||
$entitaet ?: null,
|
||||
$entitaetId !== null ? (int)$entitaetId : null,
|
||||
$ncUserId ?: null,
|
||||
$fromDate ?: null,
|
||||
$toDate ?: null,
|
||||
$limit !== null ? (int)$limit : 50,
|
||||
$offset !== null ? (int)$offset : 0
|
||||
);
|
||||
|
||||
return new JSONResponse($result);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Failed to query audit log', [
|
||||
'exception' => $e,
|
||||
'app' => 'mitgliederverwaltung',
|
||||
]);
|
||||
return new JSONResponse(
|
||||
['error' => 'Internal server error'],
|
||||
Http::STATUS_INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Mitgliederverwaltung\Controller;
|
||||
|
||||
use OCA\Mitgliederverwaltung\Service\PermissionService;
|
||||
use OCA\Mitgliederverwaltung\Service\ValidationException;
|
||||
use OCP\AppFramework\ApiController;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* REST API controller for permission management.
|
||||
*
|
||||
* Part of Issue #37.
|
||||
*/
|
||||
class PermissionController extends ApiController {
|
||||
|
||||
private PermissionService $permissionService;
|
||||
private IUserManager $userManager;
|
||||
private IUserSession $userSession;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
PermissionService $permissionService,
|
||||
IUserManager $userManager,
|
||||
IUserSession $userSession,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->permissionService = $permissionService;
|
||||
$this->userManager = $userManager;
|
||||
$this->userSession = $userSession;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all permissions with user info.
|
||||
*
|
||||
* GET /api/v1/permissions
|
||||
*/
|
||||
#[NoCSRFRequired]
|
||||
public function index(): JSONResponse {
|
||||
try {
|
||||
$permissions = $this->permissionService->getAllPermissions();
|
||||
$result = array_map(fn($p) => $p->jsonSerialize(), $permissions);
|
||||
return new JSONResponse($result);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Failed to list permissions', [
|
||||
'exception' => $e,
|
||||
'app' => 'mitgliederverwaltung',
|
||||
]);
|
||||
return new JSONResponse(
|
||||
['error' => 'Internal server error'],
|
||||
Http::STATUS_INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List Nextcloud users for the user picker.
|
||||
*
|
||||
* GET /api/v1/permissions/users
|
||||
*/
|
||||
#[NoCSRFRequired]
|
||||
public function listUsers(): JSONResponse {
|
||||
try {
|
||||
$users = [];
|
||||
$this->userManager->callForAllUsers(function ($user) use (&$users) {
|
||||
$users[] = [
|
||||
'uid' => $user->getUID(),
|
||||
'displayName' => $user->getDisplayName(),
|
||||
];
|
||||
});
|
||||
|
||||
return new JSONResponse($users);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Failed to list users', [
|
||||
'exception' => $e,
|
||||
'app' => 'mitgliederverwaltung',
|
||||
]);
|
||||
return new JSONResponse(
|
||||
['error' => 'Internal server error'],
|
||||
Http::STATUS_INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set permission for a user (create or update).
|
||||
*
|
||||
* PUT /api/v1/permissions/{ncUserId}
|
||||
*/
|
||||
public function setPermission(string $ncUserId): JSONResponse {
|
||||
try {
|
||||
// Safety check: cannot remove own admin permission
|
||||
$currentUser = $this->userSession->getUser();
|
||||
$currentUserId = $currentUser ? $currentUser->getUID() : null;
|
||||
|
||||
$data = $this->getRequestData();
|
||||
$level = $data['level'] ?? 'none';
|
||||
|
||||
if ($currentUserId === $ncUserId && $level !== 'admin') {
|
||||
$currentPerm = $this->permissionService->getUserPermission($ncUserId);
|
||||
if ($currentPerm !== null && $currentPerm->getLevel() === 'admin') {
|
||||
return new JSONResponse(
|
||||
['error' => 'Eigene Admin-Berechtigung kann nicht entfernt werden.'],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$allowedStufen = $data['allowedStufen'] ?? null;
|
||||
$canSeeBanking = (bool)($data['canSeeBanking'] ?? false);
|
||||
|
||||
$perm = $this->permissionService->setPermission(
|
||||
$ncUserId,
|
||||
$level,
|
||||
$allowedStufen,
|
||||
$canSeeBanking
|
||||
);
|
||||
|
||||
return new JSONResponse($perm);
|
||||
} catch (ValidationException $e) {
|
||||
return new JSONResponse(
|
||||
['error' => $e->getMessage()],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Failed to set permission', [
|
||||
'ncUserId' => $ncUserId,
|
||||
'exception' => $e,
|
||||
'app' => 'mitgliederverwaltung',
|
||||
]);
|
||||
return new JSONResponse(
|
||||
['error' => 'Internal server error'],
|
||||
Http::STATUS_INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove permission for a user.
|
||||
*
|
||||
* DELETE /api/v1/permissions/{ncUserId}
|
||||
*/
|
||||
public function removePermission(string $ncUserId): JSONResponse {
|
||||
try {
|
||||
// Safety check: cannot remove own permission
|
||||
$currentUser = $this->userSession->getUser();
|
||||
$currentUserId = $currentUser ? $currentUser->getUID() : null;
|
||||
|
||||
if ($currentUserId === $ncUserId) {
|
||||
return new JSONResponse(
|
||||
['error' => 'Eigene Berechtigung kann nicht entfernt werden.'],
|
||||
Http::STATUS_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
$this->permissionService->removePermission($ncUserId);
|
||||
return new JSONResponse(['status' => 'removed']);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Failed to remove permission', [
|
||||
'ncUserId' => $ncUserId,
|
||||
'exception' => $e,
|
||||
'app' => 'mitgliederverwaltung',
|
||||
]);
|
||||
return new JSONResponse(
|
||||
['error' => 'Internal server error'],
|
||||
Http::STATUS_INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function getRequestData(): array {
|
||||
$input = file_get_contents('php://input');
|
||||
if ($input === false || $input === '') {
|
||||
return [];
|
||||
}
|
||||
$data = json_decode($input, true);
|
||||
return is_array($data) ? $data : [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
<template>
|
||||
<div class="permission-settings">
|
||||
<h3>Berechtigungen verwalten</h3>
|
||||
<p class="permission-settings__description">
|
||||
Weisen Sie Nextcloud-Benutzern Zugriffsrechte zu.
|
||||
</p>
|
||||
|
||||
<!-- Error -->
|
||||
<div v-if="permStore.error" class="permission-settings__error">
|
||||
{{ permStore.error }}
|
||||
<NcButton @click="permStore.clearError()">Schliessen</NcButton>
|
||||
</div>
|
||||
|
||||
<!-- Search/Filter -->
|
||||
<div class="permission-settings__filter">
|
||||
<NcTextField :value.sync="filterQuery"
|
||||
placeholder="Benutzer filtern..."
|
||||
@update:value="filterQuery = $event" />
|
||||
</div>
|
||||
|
||||
<!-- Loading -->
|
||||
<NcLoadingIcon v-if="permStore.loading && permStore.permissions.length === 0"
|
||||
:size="32"
|
||||
class="permission-settings__loading" />
|
||||
|
||||
<!-- User list with permissions -->
|
||||
<div v-else class="permission-settings__list">
|
||||
<div v-for="userPerm in filteredUsers"
|
||||
:key="userPerm.uid"
|
||||
class="permission-settings__user-row">
|
||||
|
||||
<!-- User info -->
|
||||
<div class="permission-settings__user-info">
|
||||
<strong>{{ userPerm.displayName || userPerm.uid }}</strong>
|
||||
<span class="permission-settings__uid">{{ userPerm.uid }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Permission level dropdown -->
|
||||
<div class="permission-settings__level">
|
||||
<label>Zugriff</label>
|
||||
<select :value="userPerm.level"
|
||||
class="permission-settings__select"
|
||||
@change="onLevelChange(userPerm.uid, $event.target.value)">
|
||||
<option value="none">Kein Zugriff</option>
|
||||
<option value="read">Lesezugriff</option>
|
||||
<option value="stufe">Stufenzugriff</option>
|
||||
<option value="full">Vollzugriff</option>
|
||||
<option value="admin">Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Stufen multi-select (only when level = stufe) -->
|
||||
<div v-if="userPerm.level === 'stufe'" class="permission-settings__stufen">
|
||||
<label>Erlaubte Stufen</label>
|
||||
<div class="permission-settings__stufen-checks">
|
||||
<label v-for="stufe in stufen"
|
||||
:key="stufe.id"
|
||||
class="permission-settings__stufe-check">
|
||||
<input type="checkbox"
|
||||
:checked="(userPerm.allowedStufen || []).includes(stufe.id)"
|
||||
@change="onStufenChange(userPerm.uid, stufe.id, $event.target.checked)">
|
||||
<span :style="{ color: stufe.color }">{{ stufe.name }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Banking visibility toggle -->
|
||||
<div class="permission-settings__banking">
|
||||
<label class="permission-settings__toggle-label">
|
||||
<input type="checkbox"
|
||||
:checked="userPerm.canSeeBanking"
|
||||
@change="onBankingChange(userPerm.uid, $event.target.checked)">
|
||||
Bankdaten sichtbar
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- No users found -->
|
||||
<p v-if="!permStore.loading && filteredUsers.length === 0" class="permission-settings__empty">
|
||||
Keine Benutzer gefunden.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { NcButton, NcTextField, NcLoadingIcon } from '@nextcloud/vue'
|
||||
import { usePermissionsStore } from '../stores/permissions.js'
|
||||
import { useStufenStore } from '../stores/stufen.js'
|
||||
|
||||
const permStore = usePermissionsStore()
|
||||
const stufenStore = useStufenStore()
|
||||
|
||||
const filterQuery = ref('')
|
||||
|
||||
const stufen = computed(() => stufenStore.stufen)
|
||||
|
||||
/**
|
||||
* Merge NC users with their permissions for display.
|
||||
*/
|
||||
const filteredUsers = computed(() => {
|
||||
const permMap = {}
|
||||
for (const perm of permStore.permissions) {
|
||||
permMap[perm.ncUserId] = perm
|
||||
}
|
||||
|
||||
let users = permStore.users.map(user => ({
|
||||
uid: user.uid,
|
||||
displayName: user.displayName,
|
||||
level: permMap[user.uid]?.level || 'none',
|
||||
allowedStufen: permMap[user.uid]?.allowedStufen || [],
|
||||
canSeeBanking: permMap[user.uid]?.canSeeBanking || false,
|
||||
}))
|
||||
|
||||
// Filter by search query
|
||||
if (filterQuery.value.trim()) {
|
||||
const q = filterQuery.value.trim().toLowerCase()
|
||||
users = users.filter(u =>
|
||||
u.uid.toLowerCase().includes(q)
|
||||
|| (u.displayName || '').toLowerCase().includes(q),
|
||||
)
|
||||
}
|
||||
|
||||
return users
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([
|
||||
permStore.fetchPermissions(),
|
||||
permStore.fetchUsers(),
|
||||
stufenStore.fetchStufen(),
|
||||
])
|
||||
})
|
||||
|
||||
/**
|
||||
* Handle permission level change.
|
||||
*/
|
||||
async function onLevelChange(uid, newLevel) {
|
||||
const user = filteredUsers.value.find(u => u.uid === uid)
|
||||
if (!user) return
|
||||
|
||||
try {
|
||||
await permStore.setPermission(
|
||||
uid,
|
||||
newLevel,
|
||||
newLevel === 'stufe' ? user.allowedStufen : null,
|
||||
user.canSeeBanking,
|
||||
)
|
||||
} catch {
|
||||
// Error displayed by store
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Stufen checkbox change.
|
||||
*/
|
||||
async function onStufenChange(uid, stufeId, checked) {
|
||||
const user = filteredUsers.value.find(u => u.uid === uid)
|
||||
if (!user) return
|
||||
|
||||
let allowedStufen = [...(user.allowedStufen || [])]
|
||||
if (checked) {
|
||||
if (!allowedStufen.includes(stufeId)) {
|
||||
allowedStufen.push(stufeId)
|
||||
}
|
||||
} else {
|
||||
allowedStufen = allowedStufen.filter(id => id !== stufeId)
|
||||
}
|
||||
|
||||
try {
|
||||
await permStore.setPermission(
|
||||
uid,
|
||||
user.level,
|
||||
allowedStufen,
|
||||
user.canSeeBanking,
|
||||
)
|
||||
} catch {
|
||||
// Error displayed by store
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle banking visibility toggle.
|
||||
*/
|
||||
async function onBankingChange(uid, canSeeBanking) {
|
||||
const user = filteredUsers.value.find(u => u.uid === uid)
|
||||
if (!user) return
|
||||
|
||||
try {
|
||||
await permStore.setPermission(
|
||||
uid,
|
||||
user.level,
|
||||
user.level === 'stufe' ? user.allowedStufen : null,
|
||||
canSeeBanking,
|
||||
)
|
||||
} catch {
|
||||
// Error displayed by store
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.permission-settings__description {
|
||||
color: var(--color-text-lighter);
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.permission-settings__error {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 8px 12px;
|
||||
background: var(--color-error);
|
||||
color: white;
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.permission-settings__filter {
|
||||
margin-bottom: 16px;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.permission-settings__loading {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.permission-settings__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.permission-settings__user-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 12px 16px;
|
||||
background: var(--color-background-dark);
|
||||
border-radius: var(--border-radius);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.permission-settings__user-info {
|
||||
min-width: 200px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.permission-settings__uid {
|
||||
display: block;
|
||||
font-size: 0.85em;
|
||||
color: var(--color-text-lighter);
|
||||
}
|
||||
|
||||
.permission-settings__level {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.permission-settings__level label {
|
||||
font-size: 0.8em;
|
||||
color: var(--color-text-lighter);
|
||||
}
|
||||
|
||||
.permission-settings__select {
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
background: var(--color-main-background);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.permission-settings__stufen {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.permission-settings__stufen > label {
|
||||
font-size: 0.8em;
|
||||
color: var(--color-text-lighter);
|
||||
}
|
||||
|
||||
.permission-settings__stufen-checks {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.permission-settings__stufe-check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.permission-settings__banking {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.permission-settings__toggle-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.permission-settings__empty {
|
||||
color: var(--color-text-lighter);
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Pinia store for permission state management.
|
||||
*
|
||||
* Part of Issue #37.
|
||||
*/
|
||||
import { defineStore } from 'pinia'
|
||||
import axios from '@nextcloud/axios'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
export const usePermissionsStore = defineStore('permissions', {
|
||||
state: () => ({
|
||||
/** @type {Array} Permission records */
|
||||
permissions: [],
|
||||
/** @type {Array} Nextcloud users */
|
||||
users: [],
|
||||
/** @type {boolean} Loading state */
|
||||
loading: false,
|
||||
/** @type {string|null} Error message */
|
||||
error: null,
|
||||
}),
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* Fetch all permissions.
|
||||
*/
|
||||
async fetchPermissions() {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
const url = generateUrl('/apps/mitgliederverwaltung/api/v1/permissions')
|
||||
const response = await axios.get(url)
|
||||
this.permissions = response.data
|
||||
} catch (err) {
|
||||
this.error = err.response?.data?.error || 'Fehler beim Laden der Berechtigungen'
|
||||
console.error('Failed to fetch permissions:', err)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch Nextcloud users for the user picker.
|
||||
*/
|
||||
async fetchUsers() {
|
||||
try {
|
||||
const url = generateUrl('/apps/mitgliederverwaltung/api/v1/permissions/users')
|
||||
const response = await axios.get(url)
|
||||
this.users = response.data
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch users:', err)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set permission for a user.
|
||||
*/
|
||||
async setPermission(ncUserId, level, allowedStufen, canSeeBanking) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
const url = generateUrl(`/apps/mitgliederverwaltung/api/v1/permissions/${ncUserId}`)
|
||||
const response = await axios.put(url, {
|
||||
level,
|
||||
allowedStufen,
|
||||
canSeeBanking,
|
||||
})
|
||||
|
||||
// Update local state
|
||||
const index = this.permissions.findIndex(p => p.ncUserId === ncUserId)
|
||||
if (index !== -1) {
|
||||
this.permissions[index] = response.data
|
||||
} else {
|
||||
this.permissions.push(response.data)
|
||||
}
|
||||
|
||||
return response.data
|
||||
} catch (err) {
|
||||
this.error = err.response?.data?.error || 'Fehler beim Setzen der Berechtigung'
|
||||
throw err
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove permission for a user.
|
||||
*/
|
||||
async removePermission(ncUserId) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
const url = generateUrl(`/apps/mitgliederverwaltung/api/v1/permissions/${ncUserId}`)
|
||||
await axios.delete(url)
|
||||
this.permissions = this.permissions.filter(p => p.ncUserId !== ncUserId)
|
||||
} catch (err) {
|
||||
this.error = err.response?.data?.error || 'Fehler beim Entfernen der Berechtigung'
|
||||
throw err
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
clearError() {
|
||||
this.error = null
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -107,6 +107,11 @@
|
||||
</NcButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Permission Management Section -->
|
||||
<div class="settings__section">
|
||||
<PermissionSettings />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -114,6 +119,7 @@
|
||||
import { onMounted } from 'vue'
|
||||
import { NcButton, NcTextField, NcLoadingIcon } from '@nextcloud/vue'
|
||||
import { useStufenStore } from '../stores/stufen.js'
|
||||
import PermissionSettings from '../components/PermissionSettings.vue'
|
||||
import Plus from 'vue-material-design-icons/Plus.vue'
|
||||
|
||||
const stufenStore = useStufenStore()
|
||||
|
||||
Reference in New Issue
Block a user