2 Commits

Author SHA1 Message Date
0fa8f951ae ci: rebuild trigger 3 (transient TLS-timeout mot docker.io i run #8)
All checks were successful
build-and-push / build (push) Successful in 1m54s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 22:56:21 +02:00
cdc9652842 fix(acl): dölj redigeringsknappen för användare utan edit-rättighet
Some checks failed
build-and-push / build (push) Failing after 1m3s
VisualEditors 'Börja redigera'-banner styrdes bara av internt state och
visades för alla, även publika läs-användare. Ny editable-prop gate:ar
bannern och tvingar läsläge när rättigheten saknas. SourceEditor får en
readonly-prop (CodeMirror-Compartment) så Source-fliken inte längre är
redigerbar utan rättighet. Versionsvyn är nu alltid read-only.

Backend nekade redan save utan Edit/Create — detta var enbart ett UI-fel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 22:54:16 +02:00
4 changed files with 24 additions and 7 deletions

View File

@@ -7,7 +7,7 @@ name: build-and-push
# (ingen insecure-registry-config behövs där).
#
# Pull:a på Pi5 med localhost:5000/archivum:latest (t.ex. i dockge-stacken).
# rebuild trigger: 2 (previous run was interrupted by a host overload)
# rebuild trigger: 3 (run #8 hit a transient TLS timeout pulling alpine:3.20)
on:
push:
branches: [main, master]

View File

@@ -1,14 +1,18 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import { EditorView, basicSetup } from 'codemirror'
import { EditorState } from '@codemirror/state'
import { EditorState, Compartment } from '@codemirror/state'
import { markdown } from '@codemirror/lang-markdown'
import { oneDark } from '@codemirror/theme-one-dark'
const model = defineModel<string>({ required: true })
const props = withDefaults(defineProps<{ readonly?: boolean }>(), { readonly: false })
const container = ref<HTMLElement | null>(null)
let view: EditorView | null = null
const readonlyCompartment = new Compartment()
const readonlyExtensions = (ro: boolean) => [EditorState.readOnly.of(ro), EditorView.editable.of(!ro)]
onMounted(() => {
if (!container.value) return
@@ -20,6 +24,7 @@ onMounted(() => {
basicSetup,
markdown(),
oneDark,
readonlyCompartment.of(readonlyExtensions(props.readonly)),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
model.value = update.state.doc.toString()
@@ -30,6 +35,10 @@ onMounted(() => {
})
})
watch(() => props.readonly, (ro) => {
view?.dispatch({ effects: readonlyCompartment.reconfigure(readonlyExtensions(ro)) })
})
// Sync external model changes into CodeMirror.
watch(model, (adoc) => {
if (!view) return

View File

@@ -14,9 +14,15 @@ import ImageEditDialog from './ImageEditDialog.vue'
import LinkDialog from './LinkDialog.vue'
const model = defineModel<string>({ required: true })
const props = defineProps<{ slug?: string }>()
const props = withDefaults(defineProps<{ slug?: string; editable?: boolean }>(), { editable: true })
const isEditing = ref(false)
// Force read mode if edit permission disappears (e.g. navigating to a doc
// the user may not edit while this component instance is reused).
watch(() => props.editable, (editable) => {
if (!editable) isEditing.value = false
})
const showImagePicker = ref(false)
const showImageEditor = ref(false)
const showLinkDialog = ref({ open: false, url: '', text: '' })
@@ -473,9 +479,9 @@ function handleEditorClick(e: MouseEvent) {
</button>
</div>
<!-- Banner when not editing -->
<!-- Banner when not editing (hidden entirely without edit permission) -->
<div
v-if="!isEditing"
v-if="!isEditing && props.editable"
class="bg-indigo-50 dark:bg-slate-800/80 border-b border-indigo-100 dark:border-slate-700/60 flex items-center justify-between p-2 flex-shrink-0"
>
<span class="text-sm text-indigo-700 dark:text-indigo-300 ml-2">Läsläge</span>

View File

@@ -357,11 +357,13 @@ function formatDate(dateStr: string) {
<VisualEditor
v-else-if="versionEditorMode === 'visual'"
:slug="slug"
:editable="false"
:model-value="versionContent"
@update:model-value="() => {}"
/>
<SourceEditor
v-else
readonly
:model-value="versionContent"
@update:model-value="() => {}"
/>
@@ -370,8 +372,8 @@ function formatDate(dateStr: string) {
<!-- Normal editor -->
<template v-else>
<div v-if="loading" class="p-6 text-slate-400 animate-pulse">Loading…</div>
<VisualEditor v-else-if="editorMode === 'visual'" v-model="content" :slug="slug" />
<SourceEditor v-else v-model="content" />
<VisualEditor v-else-if="editorMode === 'visual'" v-model="content" :slug="slug" :editable="canEditDoc" />
<SourceEditor v-else v-model="content" :readonly="!canEditDoc" />
</template>
</div>
</div>